@cloudpss/expression 0.6.0-alpha.14 → 0.6.0-alpha.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/definitions/argument.d.ts +1 -1
  2. package/dist/definitions/argument.d.ts.map +1 -1
  3. package/dist/definitions/parameter.d.ts.map +1 -1
  4. package/dist/definitions/parameter.js.map +1 -1
  5. package/dist/definitions/utils.d.ts +2 -1
  6. package/dist/definitions/utils.d.ts.map +1 -1
  7. package/dist/definitions/utils.js +24 -7
  8. package/dist/definitions/utils.js.map +1 -1
  9. package/dist/main.d.ts +1 -1
  10. package/dist/main.d.ts.map +1 -1
  11. package/dist/main.js +25 -17
  12. package/dist/main.js.map +1 -1
  13. package/dist/migrator/access.d.ts.map +1 -1
  14. package/dist/migrator/access.js +3 -3
  15. package/dist/migrator/access.js.map +1 -1
  16. package/dist/migrator/call.d.ts.map +1 -1
  17. package/dist/migrator/call.js +39 -13
  18. package/dist/migrator/call.js.map +1 -1
  19. package/dist/migrator/node.d.ts.map +1 -1
  20. package/dist/migrator/node.js +23 -3
  21. package/dist/migrator/node.js.map +1 -1
  22. package/dist/migrator/operator.d.ts.map +1 -1
  23. package/dist/migrator/operator.js +56 -26
  24. package/dist/migrator/operator.js.map +1 -1
  25. package/dist/migrator/state.d.ts.map +1 -1
  26. package/dist/migrator/state.js +20 -5
  27. package/dist/migrator/state.js.map +1 -1
  28. package/dist/migrator/symbol.js +2 -2
  29. package/dist/migrator/symbol.js.map +1 -1
  30. package/dist/migrator/to-type.js +2 -2
  31. package/dist/migrator/to-type.js.map +1 -1
  32. package/dist/re-exports.d.ts +3 -3
  33. package/dist/re-exports.d.ts.map +1 -1
  34. package/dist/re-exports.js +2 -2
  35. package/dist/re-exports.js.map +1 -1
  36. package/dist/scope.d.ts +0 -2
  37. package/dist/scope.d.ts.map +1 -1
  38. package/dist/scope.js +3 -3
  39. package/dist/scope.js.map +1 -1
  40. package/dist/type.d.ts +22 -10
  41. package/dist/type.d.ts.map +1 -1
  42. package/dist/type.js +19 -26
  43. package/dist/type.js.map +1 -1
  44. package/package.json +3 -3
  45. package/src/definitions/argument.ts +1 -1
  46. package/src/definitions/parameter.ts +15 -8
  47. package/src/definitions/utils.ts +29 -13
  48. package/src/main.ts +21 -14
  49. package/src/migrator/access.ts +3 -3
  50. package/src/migrator/call.ts +37 -15
  51. package/src/migrator/node.ts +24 -3
  52. package/src/migrator/operator.ts +55 -26
  53. package/src/migrator/state.ts +19 -4
  54. package/src/migrator/symbol.ts +2 -2
  55. package/src/migrator/to-type.ts +2 -2
  56. package/src/re-exports.ts +20 -1
  57. package/src/scope.ts +3 -3
  58. package/src/type.ts +36 -27
  59. package/tests/condition.ts +20 -12
  60. package/tests/definition.ts +12 -10
  61. package/tests/import.ts +3 -3
  62. package/tests/migrate.ts +52 -9
  63. package/tests/scope.ts +3 -3
@@ -1,4 +1,4 @@
1
- import { serializePropName } from '@mirascript/mirascript/subtle';
1
+ import { serializeRecordKey } from '@mirascript/mirascript/subtle';
2
2
  import {
3
3
  type MathNode,
4
4
  isAccessorNode,
@@ -17,7 +17,7 @@ import {
17
17
  import type { State } from './state.js';
18
18
  import type { Options, Result } from './interface.js';
19
19
  import type { VmConst } from '@mirascript/mirascript';
20
- import { unsupportedNode } from './utils.js';
20
+ import { constantValue, unsupportedNode } from './utils.js';
21
21
  import { migrateAccess } from './access.js';
22
22
  import { migrateCall } from './call.js';
23
23
  import { migrateOperator } from './operator.js';
@@ -25,6 +25,7 @@ import { migrateSymbol } from './symbol.js';
25
25
  import { migrateCondition } from './condition.js';
26
26
  import { migrateFunctionAssignment } from './function.js';
27
27
  import { serialize } from './serialize.js';
28
+ import { toNumber } from './to-type.js';
28
29
 
29
30
  /** 转换 AST */
30
31
  export function migrateAtomic(state: State, node: MathNode): Result {
@@ -82,7 +83,7 @@ export function migrateNode(state: State, node: MathNode, options: Options): Res
82
83
  } else {
83
84
  literals[key] = item.literal;
84
85
  }
85
- code.push(`${serializePropName(key)}: ${item.code}`);
86
+ code.push(`${serializeRecordKey(key)}: ${item.code}`);
86
87
  }
87
88
  return {
88
89
  type: 'record',
@@ -113,6 +114,26 @@ export function migrateNode(state: State, node: MathNode, options: Options): Res
113
114
  }
114
115
  if (isRelationalNode(node)) {
115
116
  const { conditionals, params } = node;
117
+ // 优化范围判断 a <= b <= c
118
+ if (
119
+ conditionals.length === 2 &&
120
+ params.length === 3 &&
121
+ conditionals[0] === 'smallerEq' &&
122
+ conditionals[1] === 'smallerEq'
123
+ ) {
124
+ // 模式匹配只支持常量
125
+ const l = constantValue(params[0]!);
126
+ const r = constantValue(params[2]!);
127
+ if (typeof l == 'number' && typeof r == 'number' && l < r) {
128
+ const v = toNumber(state, migrateNode(state, params[1]!, options));
129
+ let code = `${v.code} is ${l}..${r}`;
130
+ if (options.format !== 'no-paren') code = `(${code})`;
131
+ return {
132
+ type: 'boolean',
133
+ code,
134
+ };
135
+ }
136
+ }
116
137
  const exprs = [];
117
138
  for (let i = 0; i < conditionals.length; i++) {
118
139
  const fn = conditionals[i]! as OperatorNodeFn;
@@ -8,6 +8,7 @@ import {
8
8
  isOperatorNode,
9
9
  isNode,
10
10
  } from 'mathjs';
11
+ import { isVmArray, type TypeName, type VmAny } from '@mirascript/mirascript';
11
12
  import { operations } from '@mirascript/mirascript/subtle';
12
13
  import { symbolName, constantValue, equalText, scalar, globalFnName } from './utils.js';
13
14
  import type { State } from './state.js';
@@ -91,10 +92,12 @@ const BIT_OPS_TO_BOOL_OPS = {
91
92
  } as const;
92
93
 
93
94
  const COMPARE_OPERATORS = {
94
- smaller: '<',
95
- smallerEq: '<=',
96
- larger: '>',
97
- largerEq: '>=',
95
+ smaller: ['<'],
96
+ smallerEq: ['<='],
97
+ larger: ['>'],
98
+ largerEq: ['>='],
99
+ equal: ['=~', '=='],
100
+ unequal: ['!~', '!='],
98
101
  } as const;
99
102
 
100
103
  /** 转换为 boolean */
@@ -103,6 +106,21 @@ function b(op: string, state: State, node: MathNode): string {
103
106
  return toBoolean(state, scalar(op, state, re)).code;
104
107
  }
105
108
 
109
+ /** 数组元素类型 */
110
+ function elementType(lit: VmAny): TypeName | undefined {
111
+ if (!isVmArray(lit) || !lit.length) return undefined;
112
+ let type: TypeName | undefined = undefined;
113
+ for (const e of lit) {
114
+ const t = operations.$Type(e as VmAny);
115
+ if (!type) {
116
+ type = t;
117
+ } else if (type !== t) {
118
+ return undefined;
119
+ }
120
+ }
121
+ return type;
122
+ }
123
+
106
124
  /** 二元操作 */
107
125
  function binary(
108
126
  state: State,
@@ -115,23 +133,27 @@ function binary(
115
133
  return op(state, l, r);
116
134
  }
117
135
  if (Array.isArray(l.literal) && l.literal.every((e) => !Array.isArray(e))) {
136
+ const it = { code: 'it', type: elementType(l.literal) };
118
137
  return {
119
138
  type: 'array',
120
- code: `${l.code}::${globalFnName(state, 'map')}(fn { ${op(state, { code: 'it' }, r).code} })`,
139
+ code: `${l.code}::${globalFnName(state, 'map')}(fn { ${op(state, it, r).code} })`,
121
140
  };
122
141
  }
123
142
  if (Array.isArray(r.literal) && r.literal.every((e) => !Array.isArray(e))) {
143
+ const it = { code: 'it', type: elementType(r.literal) };
124
144
  return {
125
145
  type: 'array',
126
- code: `${r.code}::${globalFnName(state, 'map')}(fn { ${op(state, l, { code: 'it' }).code} })`,
146
+ code: `${r.code}::${globalFnName(state, 'map')}(fn { ${op(state, l, it).code} })`,
127
147
  };
128
148
  }
129
149
  if (alt) {
130
150
  return alt(state, l, r);
131
151
  }
152
+ const a = { code: 'a', type: l.type === 'array' ? elementType(l.literal) : l.type };
153
+ const b = { code: 'b', type: r.type === 'array' ? elementType(r.literal) : r.type };
132
154
  return {
133
155
  type: l.type === 'array' || r.type === 'array' ? 'array' : undefined,
134
- code: `${globalFnName(state, 'matrix')}.entrywise(${l.code}, ${r.code}, fn (a, b) { ${op(state, { code: 'a' }, { code: 'b' }).code} })`,
156
+ code: `${globalFnName(state, 'matrix')}.entrywise(${l.code}, ${r.code}, fn (a, b) { ${op(state, a, b).code} })`,
135
157
  };
136
158
  }
137
159
 
@@ -141,9 +163,10 @@ function unary(state: State, v: Result, op: (state: State, v: Result) => Result)
141
163
  return op(state, v);
142
164
  }
143
165
  if (Array.isArray(v.literal) && v.literal.every((e) => !Array.isArray(e))) {
166
+ const it = { code: 'it', type: elementType(v.literal) };
144
167
  return {
145
168
  type: 'array',
146
- code: `${v.code}::${globalFnName(state, 'map')}(fn { ${op(state, { code: 'it' }).code} })`,
169
+ code: `${v.code}::${globalFnName(state, 'map')}(fn { ${op(state, it).code} })`,
147
170
  };
148
171
  }
149
172
  return {
@@ -288,17 +311,21 @@ export function migrateOperator(
288
311
  }
289
312
 
290
313
  case 'equal':
291
- case 'unequal': {
292
- const op = fn === 'equal' ? '==' : '!=';
314
+ case 'unequal':
315
+ case 'smaller':
316
+ case 'smallerEq':
317
+ case 'larger':
318
+ case 'largerEq': {
319
+ const [op, eqOp] = COMPARE_OPERATORS[fn];
293
320
  const c0 = constantValue(a0);
294
321
  const c1 = constantValue(a1);
295
- if (c0 === null || c1 === null) {
296
- state.loose();
297
- const p0 = c0 === null ? { code: `nil` } : scalar(op, state, migrateExpr(state, a0));
298
- const p1 = c1 === null ? { code: `nil` } : scalar(op, state, migrateExpr(state, a1));
322
+ if (eqOp && (c0 === null || c1 === null)) {
323
+ // Mathjs 只支持标量与 null 比较
324
+ const p0 = c0 === null ? { code: `nil` } : migrateExpr(state, a0);
325
+ const p1 = c1 === null ? { code: `nil` } : migrateExpr(state, a1);
299
326
  return {
300
327
  type: 'boolean',
301
- code: `${open}${p0.code} ${op} ${p1.code}${close}`,
328
+ code: `${open}${p0.code} ${eqOp} ${p1.code}${close}`,
302
329
  };
303
330
  }
304
331
  // PI, E 与常量比较时,不进行转换
@@ -317,7 +344,19 @@ export function migrateOperator(
317
344
  };
318
345
 
319
346
  return binary(state, a(a0), a(a1), (state, l, r) => {
320
- if (l.type === 'boolean' && r.type === 'boolean') {
347
+ if (l.literal !== undefined) {
348
+ return {
349
+ type: 'boolean',
350
+ code: `${toNumber(state, l).code} ${op} ${r.code}`,
351
+ };
352
+ }
353
+ if (r.literal !== undefined) {
354
+ return {
355
+ type: 'boolean',
356
+ code: `${l.code} ${op} ${toNumber(state, r).code}`,
357
+ };
358
+ }
359
+ if (l.type === 'number' || r.type === 'number') {
321
360
  return {
322
361
  type: 'boolean',
323
362
  code: `${l.code} ${op} ${r.code}`,
@@ -329,16 +368,6 @@ export function migrateOperator(
329
368
  };
330
369
  });
331
370
  }
332
- case 'smaller':
333
- case 'smallerEq':
334
- case 'larger':
335
- case 'largerEq': {
336
- const op = COMPARE_OPERATORS[fn];
337
- return {
338
- type: 'boolean',
339
- code: `${open}${scalar(op, state, migrateExpr(state, a0)).code} ${op} ${scalar(op, state, migrateExpr(state, a1)).code}${close}`,
340
- };
341
- }
342
371
 
343
372
  default: {
344
373
  state.err(`不支持的运算符: ${fn}`);
@@ -4,6 +4,7 @@ import {
4
4
  type AssignmentNode,
5
5
  isAssignmentNode,
6
6
  isBlockNode,
7
+ isConstantNode,
7
8
  isFunctionAssignmentNode,
8
9
  isSymbolNode,
9
10
  type MathNode,
@@ -109,7 +110,7 @@ export class BaseState {
109
110
  const r = isFunc ? migrateFunctionAssignment(this, node) : migrator(this, node);
110
111
  let { code } = r;
111
112
  if (stmt && !isFunc && !code.endsWith(';')) code += ';';
112
- if (node.comment) code += ' //' + node.comment;
113
+ if (node.comment) code += ' //' + node.comment.slice(1);
113
114
  this.ret.push(code);
114
115
  }
115
116
  /** 转换 */
@@ -142,6 +143,8 @@ export class BaseState {
142
143
  }
143
144
  }
144
145
  return;
146
+ } else if (isConstantNode(ast) && ast.value === undefined && ast.comment) {
147
+ this.ret.push(`//${ast.comment.slice(1)}`);
145
148
  } else {
146
149
  this.migrateStmt(migrateLast, false, ast);
147
150
  return;
@@ -160,21 +163,33 @@ export class BaseState {
160
163
  // 写入原始 math.js 作为注释
161
164
  const lines = this.expr.split('\n').map((line) => `// ${line}`);
162
165
  lines.unshift(`// # 原始 math.js 表达式`);
166
+ const addComment = (prefix: string, item: string) => {
167
+ if (!item.includes('\n')) {
168
+ lines.unshift(`// ${prefix}: ${item}`);
169
+ return;
170
+ }
171
+ const l = item.split('\n');
172
+ const indent = ' '.repeat(prefix.length + 2);
173
+ for (let i = l.length - 1; i >= 1; i--) {
174
+ lines.unshift(`// ${indent}${l[i]}`);
175
+ }
176
+ lines.unshift(`// ${prefix}: ${l[0]}`);
177
+ };
163
178
  if (this.errors.size || this.warnings.size || this.critical.size) {
164
179
  const warnings = [...this.warnings];
165
180
  for (let i = warnings.length - 1; i >= 0; i--) {
166
181
  const warn = warnings[i]!;
167
- lines.unshift(`// - W: ${warn}`);
182
+ addComment('- W', warn);
168
183
  }
169
184
  const errors = [...this.errors];
170
185
  for (let i = errors.length - 1; i >= 0; i--) {
171
186
  const err = errors[i]!;
172
- lines.unshift(`// - E: ${err}`);
187
+ addComment('- E', err);
173
188
  }
174
189
  const critical = [...this.critical];
175
190
  for (let i = critical.length - 1; i >= 0; i--) {
176
191
  const crit = critical[i]!;
177
- lines.unshift(`// - C: ${crit}`);
192
+ addComment('- C', crit);
178
193
  }
179
194
  lines.unshift(`// # 转换日志`);
180
195
  }
@@ -14,8 +14,8 @@ export function migrateSymbol(state: State, node: SymbolNode, migrateConst: bool
14
14
  code: migrateSymbolName(name, true),
15
15
  };
16
16
  }
17
- const global = state.globals.get(name);
18
- if (global != null) {
17
+ if (state.globals.has(name)) {
18
+ const global = state.globals.get(name);
19
19
  return {
20
20
  type: operations.$Type(global),
21
21
  code: migrateSymbolName(name, false),
@@ -9,7 +9,7 @@ import { globalFnName } from './utils.js';
9
9
  export function toBoolean(state: State, node: MathNode | Result): Result {
10
10
  const helper = (): void => {
11
11
  state.helper(
12
- "fn @@to_boolean { if it is nil { return nil; } it != '' && it != '0' && it != 0 && it is not nan && !!it }",
12
+ "fn @@to_boolean { if it is nil { nil } else { it != '' && it != '0' && it != 0 && it is not nan && !!it } }",
13
13
  );
14
14
  };
15
15
  if (!isNode(node)) {
@@ -85,7 +85,7 @@ export function toNumber(state: State, node: MathNode | Result): Result {
85
85
  code: node.as_number,
86
86
  };
87
87
  }
88
- if (node.type === 'string' && node.literal !== undefined) {
88
+ if ((node.type === 'string' || node.type === 'boolean') && node.literal !== undefined) {
89
89
  const lit = Number(node.literal);
90
90
  return {
91
91
  type: 'number',
package/src/re-exports.ts CHANGED
@@ -9,13 +9,22 @@ export {
9
9
  isVmModule,
10
10
  isVmPrimitive,
11
11
  isVmRecord,
12
+ isVmArrayLikeRecord,
13
+ isVmArrayLikeRecordByEntires,
14
+ isVmArrayLikeRecordByKeys,
15
+ isVmCallable,
16
+ isVmScript,
17
+ isVmWrapper,
12
18
  isVmValue,
13
19
  VmExtern,
14
20
  VmFunction,
15
21
  VmModule,
22
+ VmError,
23
+ defineVmContextValue,
16
24
  } from '@mirascript/mirascript';
17
- export { serialize, serializePropName, serializeString, lib, operations } from '@mirascript/mirascript/subtle';
18
25
  export type {
26
+ VmScript,
27
+ VmValueMap,
19
28
  VmAny,
20
29
  VmArray,
21
30
  VmConst,
@@ -26,3 +35,13 @@ export type {
26
35
  VmUninitialized,
27
36
  VmValue,
28
37
  } from '@mirascript/mirascript';
38
+ export {
39
+ serialize,
40
+ serializeRecordKey,
41
+ serializeString,
42
+ type SerializeOptions,
43
+ constants,
44
+ DefaultVmContext,
45
+ lib,
46
+ operations,
47
+ } from '@mirascript/mirascript/subtle';
package/src/scope.ts CHANGED
@@ -80,10 +80,10 @@ class ScopeProxyHandler implements ProxyHandler<object> {
80
80
  }
81
81
  }
82
82
 
83
+ /** 最大求值计数 */
84
+ const MAX_RECURSION = 500;
83
85
  /** 函数的执行环境 */
84
86
  export class Scope {
85
- /** 最大求值计数 */
86
- static readonly MAX_RECURSION = 500;
87
87
  constructor(
88
88
  /** 生成环境的工厂函数 */
89
89
  readonly scope:
@@ -193,7 +193,7 @@ export class Scope {
193
193
 
194
194
  /** 表达式递归求值 */
195
195
  eval(expression: Expression | CompiledExpression, path: readonly PropertyKey[]): VmValue {
196
- if (this.evalCounter >= Scope.MAX_RECURSION) {
196
+ if (this.evalCounter >= MAX_RECURSION) {
197
197
  this.evalCounter = 0;
198
198
  throw new Error(`Execution recursion exceeds limit`);
199
199
  }
package/src/type.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import type { TypeName, VmValue, VmValueMap } from '@mirascript/mirascript';
2
- import { lib } from '@mirascript/mirascript/subtle';
3
- const { to_string, to_number, to_boolean } = lib;
2
+ import { convert } from '@mirascript/mirascript/subtle';
4
3
 
5
4
  /** 类型信息 */
6
5
  export type TypeInfo = 'string' | 'number' | 'boolean';
@@ -13,10 +12,11 @@ type TypeMap = VmValueMap & {
13
12
  s: string;
14
13
  f: number;
15
14
  b: boolean;
15
+ '': VmValue;
16
16
  };
17
17
 
18
18
  /** Ts 类型 */
19
- export type TsTypeOf<T extends TypeInfo | LegacyType> = TypeMap[T];
19
+ export type TsTypeOf<T extends TypeInfo | LegacyType | '', I extends VmValue = VmValue> = T extends '' ? I : TypeMap[T];
20
20
 
21
21
  /** 兼容的旧类型 */
22
22
  export type LegacyType = 's' | 'f' | 'b';
@@ -49,39 +49,48 @@ export namespace TypeInfo {
49
49
  }
50
50
 
51
51
  /** 转换类型 */
52
- export function to<T extends TypeInfo | LegacyType>(value: VmValue | undefined, type: T): TsTypeOf<T> {
53
- /** 返回类型 */
54
- type R = TsTypeOf<T>;
55
- value ??= null;
56
- if (!type) return value as R;
52
+ export function to<T extends TypeInfo | LegacyType | '', I extends VmValue = VmValue, F = undefined>(
53
+ value: I | undefined,
54
+ type: T,
55
+ fallback?: F,
56
+ ): TsTypeOf<T> | Exclude<F, undefined> {
57
+ const c = converter(type);
58
+ return (c as typeof convert.toNumber)(value, fallback) as TsTypeOf<T>;
59
+ }
60
+
61
+ /** 不转换 */
62
+ function identity<T extends VmValue>(value: T | undefined): T | null {
63
+ return value ?? null;
64
+ }
65
+
66
+ /** 获取转换器 */
67
+ type Converters = {
68
+ string: typeof convert.toString;
69
+ s: typeof convert.toString;
70
+ number: typeof convert.toNumber;
71
+ f: typeof convert.toNumber;
72
+ boolean: typeof convert.toBoolean;
73
+ b: typeof convert.toBoolean;
74
+ '': typeof identity;
75
+ };
76
+ /** 获取转换器 */
77
+ export function converter<T extends TypeInfo | LegacyType | ''>(type: T): Converters[T] {
78
+ // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
57
79
  switch (type) {
58
80
  case 'b':
59
81
  case 'boolean':
60
- return toBoolean(value) as R;
82
+ return toBoolean as Converters[T];
61
83
  case 'f':
62
84
  case 'number':
63
- return toNumber(value) as R;
85
+ return toNumber as Converters[T];
64
86
  case 's':
65
87
  case 'string':
66
- return toString(value) as R;
88
+ return toString as Converters[T];
67
89
  default:
68
- (type) satisfies never;
69
- return value as R;
90
+ (type) satisfies '';
91
+ return identity as Converters[T];
70
92
  }
71
93
  }
72
94
 
73
- /** 转换为字符串 */
74
- export function toString(value: VmValue | undefined): string {
75
- return to_string(value ?? null);
76
- }
77
-
78
- /** 转换为数字 */
79
- export function toNumber(value: VmValue | undefined): number {
80
- return to_number(value ?? null);
81
- }
82
-
83
- /** 转换为布尔值 */
84
- export function toBoolean(value: VmValue | undefined): boolean {
85
- return to_boolean(value ?? null);
86
- }
95
+ export const { toBoolean, toFormat, toNumber, toString } = convert;
87
96
  }
@@ -27,20 +27,28 @@ describe('Evaluate conditions', () => {
27
27
  expect(e.evaluateCondition('true' as ExpressionSource<boolean>, s)).toBe(true);
28
28
  expect(e.evaluateCondition('false' as ExpressionSource<boolean>, s)).toBe(false);
29
29
  });
30
- it('can convert to boolean', () => {
31
- expect(e.evaluateCondition('""' as ExpressionSource<boolean>, s)).toBe(true);
32
- expect(e.evaluateCondition('nil' as ExpressionSource<boolean>, s)).toBe(true);
33
- expect(e.evaluateCondition('undefined' as ExpressionSource<boolean>, s)).toBe(true);
34
- expect(e.evaluateCondition('0' as ExpressionSource<boolean>, s)).toBe(true);
35
- expect(e.evaluateCondition('nan' as ExpressionSource<boolean>, s)).toBe(true);
30
+ it('can evaluate to boolean', () => {
36
31
  expect(e.evaluateCondition('(false)' as ExpressionSource<boolean>, s)).toBe(false);
37
-
38
- expect(e.evaluateCondition('""' as ExpressionSource<boolean>, s, false)).toBe(true);
39
- expect(e.evaluateCondition('nil' as ExpressionSource<boolean>, s, false)).toBe(false);
40
- expect(e.evaluateCondition('undefined' as ExpressionSource<boolean>, s, false)).toBe(false);
41
- expect(e.evaluateCondition('0' as ExpressionSource<boolean>, s, false)).toBe(true);
42
- expect(e.evaluateCondition('nan' as ExpressionSource<boolean>, s, false)).toBe(true);
32
+ expect(e.evaluateCondition('(true)' as ExpressionSource<boolean>, s)).toBe(true);
43
33
  expect(e.evaluateCondition('(false)' as ExpressionSource<boolean>, s, false)).toBe(false);
34
+ expect(e.evaluateCondition('(true)' as ExpressionSource<boolean>, s, false)).toBe(true);
35
+ });
36
+ it('can evaluate to nil', () => {
37
+ expect(e.evaluateCondition('{}' as ExpressionSource<boolean>, s)).toBe(true);
38
+ expect(e.evaluateCondition('{}' as ExpressionSource<boolean>, s, false)).toBe(false);
39
+ });
40
+ it('do not convert to boolean', () => {
41
+ expect(en.evaluateCondition('""' as ExpressionSource<boolean>, s)).toBe(true);
42
+ expect(en.evaluateCondition('nil' as ExpressionSource<boolean>, s)).toBe(true);
43
+ expect(en.evaluateCondition('undefined' as ExpressionSource<boolean>, s)).toBe(true);
44
+ expect(en.evaluateCondition('0' as ExpressionSource<boolean>, s)).toBe(true);
45
+ expect(en.evaluateCondition('nan' as ExpressionSource<boolean>, s)).toBe(true);
46
+
47
+ expect(en.evaluateCondition('""' as ExpressionSource<boolean>, s, false)).toBe(false);
48
+ expect(en.evaluateCondition('nil' as ExpressionSource<boolean>, s, false)).toBe(false);
49
+ expect(en.evaluateCondition('undefined' as ExpressionSource<boolean>, s, false)).toBe(false);
50
+ expect(en.evaluateCondition('0' as ExpressionSource<boolean>, s, false)).toBe(false);
51
+ expect(en.evaluateCondition('nan' as ExpressionSource<boolean>, s, false)).toBe(false);
44
52
  });
45
53
  it('can evaluate complex', () => {
46
54
  expect(e.evaluateCondition('12>3' as ExpressionSource<boolean>, s)).toBe(true);
@@ -85,8 +85,10 @@ describe('definitions', () => {
85
85
  null,
86
86
  );
87
87
  // @ts-expect-error 测试 undefined 输入
88
- expect(def.toArgumentValue(undefined, { type: 'real', key: '', name: '', description: '', value: 1 })).toBe(0);
89
- expect(def.toArgumentValue(null, { type: 'real', key: '', name: '', description: '', value: 1 })).toBe(0);
88
+ expect(def.toArgumentValue(undefined, { type: 'real', key: '', name: '', description: '', value: 1 })).toBe(
89
+ null,
90
+ );
91
+ expect(def.toArgumentValue(null, { type: 'real', key: '', name: '', description: '', value: 1 })).toBe(null);
90
92
  expect(def.toArgumentValue('12', { type: 'real', key: '', name: '', description: '', value: 1 })).toBe(12);
91
93
  expect(def.toArgumentValue(null, { type: 'text', key: '', name: '', description: '', value: '1' })).toBe('');
92
94
  expect(def.toArgumentValue(12, { type: 'text', key: '', name: '', description: '', value: '1' })).toBe('12');
@@ -96,7 +98,7 @@ describe('definitions', () => {
96
98
 
97
99
  expect(
98
100
  def.toArgumentValue(null, { type: 'choice', key: '', name: '', description: '', value: 1, choices: [] }),
99
- ).toBe(0);
101
+ ).toBe(null);
100
102
  expect(
101
103
  def.toArgumentValue(null, {
102
104
  type: 'choice',
@@ -120,7 +122,7 @@ describe('definitions', () => {
120
122
  ],
121
123
  }),
122
124
  ).toBe('0');
123
- expect(def.toArgumentValue(null, { type: 'logical', key: '', name: '', description: '', value: 1 })).toBe(0);
125
+ expect(def.toArgumentValue(null, { type: 'logical', key: '', name: '', description: '', value: 1 })).toBe(null);
124
126
  expect(
125
127
  def.toArgumentValue(null, {
126
128
  type: 'logical',
@@ -142,7 +144,7 @@ describe('definitions', () => {
142
144
  description: '',
143
145
  value: true,
144
146
  }),
145
- ).toBe(false);
147
+ ).toBe(null);
146
148
  expect(
147
149
  def.toArgumentValue(null, {
148
150
  type: 'logical',
@@ -194,7 +196,7 @@ describe('definitions', () => {
194
196
  value: [1],
195
197
  choices: [],
196
198
  }),
197
- ).toStrictEqual([0]);
199
+ ).toStrictEqual([]);
198
200
  expect(
199
201
  def.toArgumentValue('', {
200
202
  type: 'multiSelect',
@@ -206,7 +208,7 @@ describe('definitions', () => {
206
208
  }),
207
209
  ).toStrictEqual(['']);
208
210
  expect(
209
- def.toArgumentValue(['', '1'], {
211
+ def.toArgumentValue(['', '1', ' 2 '], {
210
212
  type: 'multiSelect',
211
213
  key: '',
212
214
  name: '',
@@ -214,9 +216,9 @@ describe('definitions', () => {
214
216
  value: [1],
215
217
  choices: [],
216
218
  }),
217
- ).toStrictEqual([0, 1]);
219
+ ).toStrictEqual([1, 2]);
218
220
  expect(
219
- def.toArgumentValue(['', '1'], {
221
+ def.toArgumentValue(['', '1', ' 2 '], {
220
222
  type: 'multiSelect',
221
223
  key: '',
222
224
  name: '',
@@ -224,6 +226,6 @@ describe('definitions', () => {
224
226
  value: [1],
225
227
  choices: Expression(''),
226
228
  }),
227
- ).toStrictEqual(['', '1']);
229
+ ).toStrictEqual(['', '1', ' 2 ']);
228
230
  });
229
231
  });
package/tests/import.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { VmFunction } from '@mirascript/mirascript';
2
2
  import { Evaluator, Expression, Scope } from '../dist/index.js';
3
- import { lib } from '@mirascript/mirascript/subtle';
3
+ import { convert, lib } from '@mirascript/mirascript/subtle';
4
4
 
5
5
  const e = new Evaluator({
6
6
  logger: {
@@ -26,7 +26,7 @@ describe('Import should work correctly', () => {
26
26
  expect(e.evaluate(Expression('add(1,2)'), s)).toBe(3);
27
27
  expect(e.evaluate(Expression('add::type()'), s)).toBe('extern');
28
28
  expect(e2.evaluate(Expression('add(1,2)'), s)).toBe(null);
29
- expect(e2.evaluate(Expression('add::type()'), s)).toBe('nil');
29
+ expect(e2.evaluate(Expression('add::type()'), s)).toBe(null);
30
30
  });
31
31
 
32
32
  it('can remove modules', () => {
@@ -39,7 +39,7 @@ describe('Import should work correctly', () => {
39
39
  it('can replace modules', () => {
40
40
  e.import({ add: (x: number, y: number) => x + y });
41
41
  expect(e.evaluate(Expression('add::type()'), s)).toBe('extern');
42
- e.import({ add: VmFunction((x, y) => lib.to_number(x) + lib.to_number(y)) });
42
+ e.import({ add: VmFunction((x, y) => convert.toNumber(x, 0) + convert.toNumber(y, 0)) });
43
43
  expect(e.evaluate(Expression('add::type()'), s)).toBe('function');
44
44
  });
45
45
  });