多类型值系统
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),按功能分组:
- 常量:
EPI(:1359,1360),另有 4 个进coreMap的常量nulltruefalseNANINFI(虚数单位,:1338-1343) - 类型自省:
iscallableisnumbertyperepr(:1362,1372,1381,1389) - 数值函数:
numberparseisnanisinfabssqrtfloorceilcoscoshacosacoshsinsinhasinasinhtanatanatan2tanhatanhexplnlogsgnraddegmodpowgcdreimphaseconjminmaxsumavg - 复数构造:
polarcartesian(:1396起) - 列表/元操作:
carcdrlen - 控制流:
executefailandorstructdictglobalslocalsdirrandomeval
另有 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 关键字求值出错。