多类型值系统

MULTI 是 OpenModsLib 计算器引擎中最复杂的后端(TypedValueCalculatorFactory,2415 行,是次大后端 DOUBLE 的 6 倍)。与其它四个"单一 Java 类型"后端不同,它实现了一套带静态类型域、隐式强制转换和元对象(metatype)分派的动态值系统,整体风格接近 Scheme/Lisp。

其余四个后端见类型后端,引擎流水线见计算器引擎。

基本信息

属性 值
工厂类 openmods.calc.types.multi.TypedValueCalculatorFactory
值包装 openmods.calc.types.multi.TypedValue(携带 TypeDomain + Class<?> + 原值 + MetaObject)
类型域 openmods.calc.types.multi.TypeDomain
注册类型数 12
转换器数 6
强制转换规则 6 条对称规则 → 12 条有向规则
内建全局符号 66
二元算符 / 一元算符 30 / 6

功能

类型域

TypeDomain(src/main/java/openmods/calc/types/multi/TypeDomain.java)是整个系统的注册中心,维护三张表:

  • allowedTypes(:90)—— 已注册类型 → TypeInfo(短名 + 默认 MetaObject + 默认值)
  • converters(:92)—— HashBasedTable<Class,Class,RawConverter>,来源类型 × 目标类型 → 转换器
  • coercionRules(:213)—— HashBasedTable<Class,Class,Coercion>,决定二元运算时值往哪边靠

registerType 有 4 个重载(:93,96,99,103),从"仅类"到"类 + 短名 + 默认元对象 + 默认值"。重复注册类型转换器会直接 checkState 失败(:158,166)。

12 种注册类型

短名 Java 类 注册行 附带的元对象能力
type TypeUserdata TypedValueCalculatorFactory.java:303 由 TypeUserdata.defaultMetaObject(domain) 提供
<null> UnitType :311 恒假布尔、长度 0、str/repr 恒为 null
int BigInteger :364 SlotBool
float Double :411 SlotBool
bool Boolean :447 SlotBool
str String :484 SlotLength
complex Complex :560 SlotBool
cons Cons :596 SlotLength
symbol Symbol :633 恒真布尔、类型恒为符号类型、带 SlotStr
function CallableValue :659 SlotCall(可调用分派)
env EnvHolder :675 SlotAttr(属性存取)
code Code :694 恒真布尔、类型恒为代码类型

其中 10 种会同时作为字面量关键字放入 basicTypes(:361,409,445,481,558,594,631,657,673,692),即在 MULTI 后端里可直接写 int float bool str complex cons symbol function env code 来取到对应的空值。

隐式强制转换

转换器共 6 个(TypedValueCalculatorFactory.java:701,708,715,723,730,738),构成一条单向数值升级链:

Boolean → BigInteger → Double → Complex
   └──────────────→ Double
   └──────────────────────────→ Complex
BigInteger ──────────────────→ Complex

对应的 6 条对称强制转换规则全部为 Coercion.TO_RIGHT(即"向右侧操作数靠拢",:746-753):Boolean↔BigInteger、Boolean↔Double、Boolean↔Complex、BigInteger↔Double、BigInteger↔Complex、Double↔Complex。registerSymmetricCoercionRule(TypeDomain.java:206)会同时写入正反两个方向并取反的规则(TO_LEFT/TO_RIGHT 互为逆,TypeDomain.java:194-199)。

判定为"数值"(isnumber)的类型集为 4 个:Double、Boolean、BigInteger、Complex(:241-242)—— 注意 String 不算数值。

优先级体系

MULTI 后端定义了 19 档算符优先级(:90-108),跨度 10–180,每档配注释说明对应算符:

优先级 常量 算符(注释)
180 PRIORITY_MAX // basically magic
175 PRIORITY_NULL_AWARE // ??
170 PRIORITY_EXP **
160 PRIORITY_MULTIPLY * / % //
150 PRIORITY_ADD + -
140 PRIORITY_BITSHIFT << >>
130/120/110 PRIORITY_BITWISE_AND / _XOR / _OR & / ^ / |
100 PRIORITY_COMPARE < > <= >= <=>
90 PRIORITY_SPACESHIP <=>
80 PRIORITY_EQUALS == !=
70/60/50 PRIORITY_LOGIC_AND / _XOR / _OR && / || / ^^
40 PRIORITY_CONS :
30 PRIORITY_LAMBDA ->
20 PRIORITY_SPLIT \
10 PRIORITY_ASSIGN =

注意 ^(异或)优先级 120 高于 &&(70)高于 ||(60)高于 ^^(逻辑异或,50)—— 三种"逻辑"算符的优先级并不按直觉排列。

内建符号

66 个全局符号(env.setGlobalSymbol,TypedValueCalculatorFactory.java),按功能分组:

  • 常量:E PI(:1359,1360),另有 4 个进 coreMap 的常量 null true false NAN INF I(虚数单位,:1338-1343)
  • 类型自省:iscallable isnumber type repr(:1362,1372,1381,1389)
  • 数值函数:number parse isnan isinf abs sqrt floor ceil cos cosh acos acosh sin sinh asin asinh tan atan atan2 tanh atanh exp ln log sgn rad deg modpow gcd re im phase conj min max sum avg
  • 复数构造:polar cartesian(:1396 起)
  • 列表/元操作:car cdr len
  • 控制流:execute fail and or struct dict globals locals dir random eval

另有 LibListFunctions(15 个符号)、LibFunctional(3 个符号)以辅助类形式注册。

语言关键字常量集中在 TypedCalcConstants(src/main/java/openmods/calc/types/multi/TypedCalcConstants.java),含 30 个 SYMBOL_*(if let letseq letrec code closure delay with apply slice case match andthen orelse nonnull nexecute const alt do pattern flatten applyvar 等)与 9 个 SLOT_* 元对象槽名(bool len attribute equal call type slice str repr)。

数值

数值名 值 来源
注册类型数 12 grep -c 'domain.registerType(' 于 TypedValueCalculatorFactory.java
字面量关键字(basicTypes) 10 :361,409,445,481,558,594,631,657,673,692
转换器数 6 :701,708,715,723,730,738
对称强制转换规则 6(展开 12 条有向) :746-753
算符优先级档数 19 :90-108
setGlobalSymbol 调用 66(全部唯一) grep -c + 去重校验一致
二元 / 一元算符 30 / 6 grep -c 'registerBinaryOperator(' / registerUnaryOperator(

交互

MULTI 后端与其余后端共用同一套指令,切换方式见类型后端。差异在于 MULTI 是唯一绑定 player 符号的后端(src/main/java/openmods/calc/command/CalcState.java:229),可在表达式中直接读取玩家状态;DOUBLE/FRACTION/BIGINT 绑定的则是 _x/_y/_z 坐标符号。

MULTI 无方块、物品、GUI 或 TileEntity,不产生任何世界存档内容。

已核实缺陷

TypedCalcConstants 中两个布尔常量的名称与取值互换。

TypedCalcConstants.java:14-15:

public static final String SYMBOL_FALSE = "true";
public static final String SYMBOL_TRUE = "false";

消费点 TypedValueCalculatorFactory.java:1339-1340:

coreMap.put(TypedCalcConstants.SYMBOL_FALSE, domain.create(Boolean.class, Boolean.TRUE));
coreMap.put(TypedCalcConstants.SYMBOL_TRUE, domain.create(Boolean.class, Boolean.FALSE));

实际求值行为是正确的 —— 字符串键 "true" 绑定 Boolean.TRUE、"false" 绑定 Boolean.FALSE,与玩家输入一致。缺陷仅在于常量名与字面值相反,阅读源码时极易误判。不可据此推断 true/false 关键字求值出错。

相关条目