@mojir/lits 2.1.31 → 2.1.33

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.
package/dist/cli/cli.js CHANGED
@@ -92,7 +92,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
92
92
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
93
93
  };
94
94
 
95
- var version = "2.1.31";
95
+ var version = "2.1.33";
96
96
 
97
97
  function getCodeMarker(sourceCodeInfo) {
98
98
  if (!sourceCodeInfo.position || !sourceCodeInfo.code)
@@ -187,21 +187,21 @@ var specialExpressionTypes = {
187
187
  'cond': 4,
188
188
  '0_def': 5,
189
189
  'defined?': 6,
190
- '0_defn': 7,
191
- 'block': 8,
192
- 'doseq': 9,
193
- '0_fn': 10,
194
- 'for': 11,
195
- 'function': 12,
196
- 'if': 13,
197
- 'let': 14,
198
- 'loop': 15,
199
- 'object': 16,
200
- 'recur': 17,
201
- 'switch': 18,
202
- 'throw': 19,
203
- 'try': 20,
204
- 'unless': 21,
190
+ // '0_defn': 7,
191
+ 'block': 7,
192
+ 'doseq': 8,
193
+ '0_lambda': 9,
194
+ 'for': 10,
195
+ // 'function': 10,
196
+ 'if': 11,
197
+ 'let': 12,
198
+ 'loop': 13,
199
+ 'object': 14,
200
+ 'recur': 15,
201
+ 'switch': 16,
202
+ 'throw': 17,
203
+ 'try': 18,
204
+ 'unless': 19,
205
205
  };
206
206
 
207
207
  var NodeTypes = {
@@ -9239,7 +9239,7 @@ var collatzSequence = {
9239
9239
  'noNth': true,
9240
9240
  };
9241
9241
 
9242
- function isPrime$1(num) {
9242
+ function isPrime(num) {
9243
9243
  if (num <= 1) {
9244
9244
  return false;
9245
9245
  }
@@ -9261,18 +9261,18 @@ var primeSequence = {
9261
9261
  var primes = [];
9262
9262
  var num = 2;
9263
9263
  while (primes.length < length) {
9264
- if (isPrime$1(num)) {
9264
+ if (isPrime(num)) {
9265
9265
  primes.push(num);
9266
9266
  }
9267
9267
  num += 1;
9268
9268
  }
9269
9269
  return primes;
9270
9270
  },
9271
- 'nth:prime?': function (n) { return isPrime$1(n); },
9271
+ 'nth:prime?': function (n) { return isPrime(n); },
9272
9272
  'nth:prime-take-while': function (takeWhile) {
9273
9273
  var primes = [];
9274
9274
  for (var i = 2;; i += 1) {
9275
- if (!isPrime$1(i)) {
9275
+ if (!isPrime(i)) {
9276
9276
  continue;
9277
9277
  }
9278
9278
  if (!takeWhile(i, primes.length)) {
@@ -9288,7 +9288,7 @@ function isComposite(num) {
9288
9288
  if (num <= 1) {
9289
9289
  return false;
9290
9290
  }
9291
- return !isPrime$1(num);
9291
+ return !isPrime(num);
9292
9292
  }
9293
9293
  var compositeSequence = {
9294
9294
  'nth:composite-seq': function (length) {
@@ -11785,140 +11785,13 @@ var doSpecialExpression = {
11785
11785
  },
11786
11786
  };
11787
11787
 
11788
- var nonNumberReservedSymbolRecord = {
11789
- true: true,
11790
- false: false,
11791
- null: null,
11792
- else: null,
11793
- case: null,
11794
- each: null,
11795
- in: null,
11796
- when: null,
11797
- while: null,
11798
- catch: null,
11799
- function: null,
11800
- export: null,
11801
- as: null,
11802
- _: null,
11803
- };
11804
- var phi = (1 + Math.sqrt(5)) / 2;
11805
- var numberReservedSymbolRecord = {
11806
- 'E': Math.E,
11807
- '-E': -Math.E,
11808
- 'ε': Math.E,
11809
- '-ε': -Math.E,
11810
- 'PI': Math.PI,
11811
- '-PI': -Math.PI,
11812
- 'π': Math.PI,
11813
- '-π': -Math.PI,
11814
- 'PHI': phi,
11815
- '-PHI': -phi,
11816
- 'φ': phi,
11817
- '-φ': -phi,
11818
- 'POSITIVE_INFINITY': Number.POSITIVE_INFINITY,
11819
- '∞': Number.POSITIVE_INFINITY,
11820
- 'NEGATIVE_INFINITY': Number.NEGATIVE_INFINITY,
11821
- '-∞': Number.NEGATIVE_INFINITY,
11822
- 'MAX_SAFE_INTEGER': Number.MAX_SAFE_INTEGER,
11823
- 'MIN_SAFE_INTEGER': Number.MIN_SAFE_INTEGER,
11824
- 'MAX_VALUE': Number.MAX_VALUE,
11825
- 'MIN_VALUE': Number.MIN_VALUE,
11826
- 'NaN': Number.NaN,
11827
- };
11828
- var reservedSymbolRecord = __assign(__assign({}, nonNumberReservedSymbolRecord), numberReservedSymbolRecord);
11829
- function isReservedSymbol(symbol) {
11830
- return symbol in reservedSymbolRecord;
11831
- }
11832
- function isNumberReservedSymbol(symbol) {
11833
- return symbol in numberReservedSymbolRecord;
11834
- }
11835
-
11836
- function assertNameNotDefined(name, contextStack, builtin, sourceCodeInfo) {
11837
- if (typeof name !== 'string')
11838
- return;
11839
- // TODO only subset of special expressions are necessary to check (CommonSpecialExpressionType)
11840
- if (specialExpressionTypes[name])
11841
- throw new LitsError("Cannot define variable ".concat(name, ", it's a special expression."), sourceCodeInfo);
11842
- if (builtin.normalExpressions[name])
11843
- throw new LitsError("Cannot define variable ".concat(name, ", it's a builtin function."), sourceCodeInfo);
11844
- if (isReservedSymbol(name))
11845
- throw new LitsError("Cannot define variable ".concat(name, ", it's a reserved name."), sourceCodeInfo);
11846
- if (contextStack.globalContext[name])
11847
- throw new LitsError("Name already defined \"".concat(name, "\"."), sourceCodeInfo);
11848
- }
11849
-
11850
- var functionSpecialExpression = {
11851
- arity: {},
11852
- evaluate: function (node, contextStack, _a) {
11853
- var _b, _c;
11854
- var builtin = _a.builtin, getUndefinedSymbols = _a.getUndefinedSymbols, evaluateNode = _a.evaluateNode;
11855
- var _d = __read(node[1], 4), functionSymbol = _d[1], fn = _d[2], docString = _d[3];
11856
- assertUserDefinedSymbolNode(functionSymbol, node[2]);
11857
- assertNameNotDefined(functionSymbol[1], contextStack, builtin, node[2]);
11858
- var evaluatedFunction = evaluateFunction(fn, contextStack, builtin, getUndefinedSymbols, evaluateNode);
11859
- var min = evaluatedFunction[0].filter(function (arg) { return arg[0] !== bindingTargetTypes.rest && arg[1][1] === undefined; }).length;
11860
- var max = evaluatedFunction[0].some(function (arg) { return arg[0] === bindingTargetTypes.rest; }) ? undefined : evaluatedFunction[0].length;
11861
- var arity = { min: min > 0 ? min : undefined, max: max };
11862
- var litsFunction = (_b = {},
11863
- _b[FUNCTION_SYMBOL] = true,
11864
- _b.sourceCodeInfo = node[2],
11865
- _b.functionType = 'UserDefined',
11866
- _b.name = functionSymbol[1],
11867
- _b.evaluatedfunction = evaluatedFunction,
11868
- _b.arity = arity,
11869
- _b.docString = docString,
11870
- _b);
11871
- contextStack.addValues((_c = {}, _c[functionSymbol[1]] = litsFunction, _c), functionSymbol[2]);
11872
- return litsFunction;
11873
- },
11874
- getUndefinedSymbols: function (node, contextStack, _a) {
11875
- var _b, _c;
11876
- var getUndefinedSymbols = _a.getUndefinedSymbols, builtin = _a.builtin, evaluateNode = _a.evaluateNode;
11877
- var functionName = node[1][1][1];
11878
- contextStack.addValues((_b = {}, _b[functionName] = true, _b), node[1][1][2]);
11879
- var newContext = (_c = {}, _c[functionName] = { value: true }, _c);
11880
- return getFunctionUnresolvedSymbols(node[1][2], contextStack, getUndefinedSymbols, builtin, evaluateNode, newContext);
11881
- },
11882
- };
11883
- var defnSpecialExpression = {
11884
- arity: {},
11885
- evaluate: function (node, contextStack, _a) {
11886
- var _b, _c;
11887
- var builtin = _a.builtin, getUndefinedSymbols = _a.getUndefinedSymbols, evaluateNode = _a.evaluateNode;
11888
- var _d = __read(node[1], 4), functionSymbol = _d[1], fn = _d[2], docString = _d[3];
11889
- assertUserDefinedSymbolNode(functionSymbol, node[2]);
11890
- assertNameNotDefined(functionSymbol[1], contextStack, builtin, node[2]);
11891
- var evaluatedFunction = evaluateFunction(fn, contextStack, builtin, getUndefinedSymbols, evaluateNode);
11892
- var min = evaluatedFunction[0].filter(function (arg) { return arg[0] !== bindingTargetTypes.rest && arg[1][1] === undefined; }).length;
11893
- var arity = { min: min };
11894
- var litsFunction = (_b = {},
11895
- _b[FUNCTION_SYMBOL] = true,
11896
- _b.sourceCodeInfo = node[2],
11897
- _b.functionType = 'UserDefined',
11898
- _b.name = functionSymbol[1],
11899
- _b.evaluatedfunction = evaluatedFunction,
11900
- _b.arity = arity,
11901
- _b.docString = docString,
11902
- _b);
11903
- contextStack.exportValues((_c = {}, _c[functionSymbol[1]] = litsFunction, _c), functionSymbol[2]);
11904
- return litsFunction;
11905
- },
11906
- getUndefinedSymbols: function (node, contextStack, _a) {
11907
- var _b, _c;
11908
- var getUndefinedSymbols = _a.getUndefinedSymbols, builtin = _a.builtin, evaluateNode = _a.evaluateNode;
11909
- var functionName = node[1][1][1];
11910
- var fn = node[1][2];
11911
- contextStack.exportValues((_b = {}, _b[functionName] = true, _b), node[1][1][2]);
11912
- var newContext = (_c = {}, _c[functionName] = { value: true }, _c);
11913
- return getFunctionUnresolvedSymbols(fn, contextStack, getUndefinedSymbols, builtin, evaluateNode, newContext);
11914
- },
11915
- };
11916
- var fnSpecialExpression = {
11788
+ var lambdaSpecialExpression = {
11917
11789
  arity: {},
11918
11790
  evaluate: function (node, contextStack, _a) {
11919
11791
  var _b;
11920
11792
  var builtin = _a.builtin, getUndefinedSymbols = _a.getUndefinedSymbols, evaluateNode = _a.evaluateNode;
11921
11793
  var fn = node[1][1];
11794
+ var docString = node[1][2];
11922
11795
  var evaluatedFunction = evaluateFunction(fn, contextStack, builtin, getUndefinedSymbols, evaluateNode);
11923
11796
  var min = evaluatedFunction[0].filter(function (arg) { return arg[0] !== bindingTargetTypes.rest && arg[1][1] === undefined; }).length;
11924
11797
  var max = evaluatedFunction[0].some(function (arg) { return arg[0] === bindingTargetTypes.rest; }) ? undefined : evaluatedFunction[0].length;
@@ -11930,8 +11803,9 @@ var fnSpecialExpression = {
11930
11803
  _b.name = undefined,
11931
11804
  _b.evaluatedfunction = evaluatedFunction,
11932
11805
  _b.arity = arity,
11933
- _b.docString = '',
11806
+ _b.docString = docString,
11934
11807
  _b);
11808
+ evaluatedFunction[2].self = { value: litsFunction };
11935
11809
  return litsFunction;
11936
11810
  },
11937
11811
  getUndefinedSymbols: function (node, contextStack, _a) {
@@ -11962,17 +11836,16 @@ function evaluateFunction(fn, contextStack, builtin, getUndefinedSymbols, evalua
11962
11836
  ];
11963
11837
  return evaluatedFunction;
11964
11838
  }
11965
- function getFunctionUnresolvedSymbols(fn, contextStack, getUndefinedSymbols, builtin, evaluateNode, functionNameContext) {
11839
+ function getFunctionUnresolvedSymbols(fn, contextStack, getUndefinedSymbols, builtin, evaluateNode) {
11966
11840
  var result = new Set();
11967
- var contextStackWithFunctionName = functionNameContext ? contextStack.create(functionNameContext) : contextStack;
11968
- var newContext = {};
11841
+ var newContext = { self: { value: null } };
11969
11842
  fn[0].forEach(function (arg) {
11970
11843
  Object.assign(newContext, getAllBindingTargetNames(arg));
11971
11844
  walkDefaults(arg, function (defaultNode) {
11972
11845
  addToSet(result, getUndefinedSymbols([defaultNode], contextStack, builtin, evaluateNode));
11973
11846
  });
11974
11847
  });
11975
- var newContextStack = contextStackWithFunctionName.create(newContext);
11848
+ var newContextStack = contextStack.create(newContext);
11976
11849
  var overloadResult = getUndefinedSymbols(fn[1], newContextStack, builtin, evaluateNode);
11977
11850
  addToSet(result, overloadResult);
11978
11851
  return result;
@@ -12059,22 +11932,9 @@ var loopSpecialExpression = {
12059
11932
  var newContextStack = contextStack.create(bindingContext);
12060
11933
  var body = node[1][2];
12061
11934
  var _loop_1 = function () {
12062
- var e_1, _b;
12063
11935
  var result = null;
12064
11936
  try {
12065
- try {
12066
- for (var body_1 = (e_1 = void 0, __values(body)), body_1_1 = body_1.next(); !body_1_1.done; body_1_1 = body_1.next()) {
12067
- var form = body_1_1.value;
12068
- result = evaluateNode(form, newContextStack);
12069
- }
12070
- }
12071
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
12072
- finally {
12073
- try {
12074
- if (body_1_1 && !body_1_1.done && (_b = body_1.return)) _b.call(body_1);
12075
- }
12076
- finally { if (e_1) throw e_1.error; }
12077
- }
11937
+ result = evaluateNode(body, newContextStack);
12078
11938
  }
12079
11939
  catch (error) {
12080
11940
  if (error instanceof RecurSignal) {
@@ -12083,20 +11943,20 @@ var loopSpecialExpression = {
12083
11943
  throw new LitsError("recur expected ".concat(bindingNodes.length, " parameters, got ").concat(valueToString(params_1.length)), node[2]);
12084
11944
  }
12085
11945
  bindingNodes.forEach(function (bindingNode, index) {
12086
- var e_2, _a;
11946
+ var e_1, _a;
12087
11947
  var valueRecord = evalueateBindingNodeValues(bindingNode[1][0], asAny(params_1[index]), function (Node) { return evaluateNode(Node, contextStack); });
12088
11948
  try {
12089
- for (var _b = (e_2 = void 0, __values(Object.entries(valueRecord))), _c = _b.next(); !_c.done; _c = _b.next()) {
11949
+ for (var _b = (e_1 = void 0, __values(Object.entries(valueRecord))), _c = _b.next(); !_c.done; _c = _b.next()) {
12090
11950
  var _d = __read(_c.value, 2), name_1 = _d[0], value = _d[1];
12091
11951
  bindingContext[name_1].value = value;
12092
11952
  }
12093
11953
  }
12094
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
11954
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
12095
11955
  finally {
12096
11956
  try {
12097
11957
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
12098
11958
  }
12099
- finally { if (e_2) throw e_2.error; }
11959
+ finally { if (e_1) throw e_1.error; }
12100
11960
  }
12101
11961
  });
12102
11962
  return "continue";
@@ -12124,7 +11984,7 @@ var loopSpecialExpression = {
12124
11984
  }, {});
12125
11985
  var bindingValueNodes = bindingNodes.map(function (bindingNode) { return bindingNode[1][1]; });
12126
11986
  var bindingsResult = getUndefinedSymbols(bindingValueNodes, contextStack, builtin, evaluateNode);
12127
- var paramsResult = getUndefinedSymbols(node[1][2], contextStack.create(newContext), builtin, evaluateNode);
11987
+ var paramsResult = getUndefinedSymbols([node[1][2]], contextStack.create(newContext), builtin, evaluateNode);
12128
11988
  return joinSets(bindingsResult, paramsResult);
12129
11989
  },
12130
11990
  };
@@ -12519,12 +12379,12 @@ var specialExpressions = [
12519
12379
  condSpecialExpression,
12520
12380
  defSpecialExpression,
12521
12381
  definedSpecialExpression,
12522
- defnSpecialExpression,
12382
+ // defnSpecialExpression,
12523
12383
  doSpecialExpression,
12524
12384
  doseqSpecialExpression,
12525
- fnSpecialExpression,
12385
+ lambdaSpecialExpression,
12526
12386
  forSpecialExpression,
12527
- functionSpecialExpression,
12387
+ // functionSpecialExpression,
12528
12388
  ifSpecialExpression,
12529
12389
  letSpecialExpression,
12530
12390
  loopSpecialExpression,
@@ -12546,6 +12406,53 @@ new Set(specialExpressionKeys);
12546
12406
  // TODO, remove
12547
12407
  // console.log('builtin', [...specialExpressionKeys, ...normalExpressionKeys].length)
12548
12408
 
12409
+ var nonNumberReservedSymbolRecord = {
12410
+ true: true,
12411
+ false: false,
12412
+ null: null,
12413
+ else: null,
12414
+ case: null,
12415
+ each: null,
12416
+ in: null,
12417
+ when: null,
12418
+ while: null,
12419
+ catch: null,
12420
+ function: null,
12421
+ export: null,
12422
+ as: null,
12423
+ then: null,
12424
+ end: null,
12425
+ _: null,
12426
+ };
12427
+ var phi = (1 + Math.sqrt(5)) / 2;
12428
+ var numberReservedSymbolRecord = {
12429
+ 'E': Math.E,
12430
+ '-E': -Math.E,
12431
+ 'ε': Math.E,
12432
+ '-ε': -Math.E,
12433
+ 'PI': Math.PI,
12434
+ '-PI': -Math.PI,
12435
+ 'π': Math.PI,
12436
+ '-π': -Math.PI,
12437
+ 'PHI': phi,
12438
+ '-PHI': -phi,
12439
+ 'φ': phi,
12440
+ '-φ': -phi,
12441
+ 'POSITIVE_INFINITY': Number.POSITIVE_INFINITY,
12442
+ '∞': Number.POSITIVE_INFINITY,
12443
+ 'NEGATIVE_INFINITY': Number.NEGATIVE_INFINITY,
12444
+ '-∞': Number.NEGATIVE_INFINITY,
12445
+ 'MAX_SAFE_INTEGER': Number.MAX_SAFE_INTEGER,
12446
+ 'MIN_SAFE_INTEGER': Number.MIN_SAFE_INTEGER,
12447
+ 'MAX_VALUE': Number.MAX_VALUE,
12448
+ 'MIN_VALUE': Number.MIN_VALUE,
12449
+ 'NaN': Number.NaN,
12450
+ };
12451
+ var reservedSymbolRecord = __assign(__assign({}, nonNumberReservedSymbolRecord), numberReservedSymbolRecord);
12452
+ function isNumberReservedSymbol(symbol) {
12453
+ return symbol in numberReservedSymbolRecord;
12454
+ }
12455
+
12549
12456
  var functionExecutors = {
12550
12457
  NativeJsFunction: function (fn, params, sourceCodeInfo) {
12551
12458
  var _a;
@@ -12994,6 +12901,9 @@ var ContextStackImpl = /** @class */ (function () {
12994
12901
  if (normalExpressionKeys.includes(name_1)) {
12995
12902
  throw new LitsError("Cannot shadow builtin function \"".concat(name_1, "\""), sourceCodeInfo);
12996
12903
  }
12904
+ if (name_1 === 'self') {
12905
+ throw new LitsError("Cannot shadow builtin value \"".concat(name_1, "\""), sourceCodeInfo);
12906
+ }
12997
12907
  this.globalContext[name_1] = { value: value };
12998
12908
  }
12999
12909
  }
@@ -13023,6 +12933,9 @@ var ContextStackImpl = /** @class */ (function () {
13023
12933
  if (normalExpressionKeys.includes(name_2)) {
13024
12934
  throw new LitsError("Cannot shadow builtin function \"".concat(name_2, "\""), sourceCodeInfo);
13025
12935
  }
12936
+ if (name_2 === 'self') {
12937
+ throw new LitsError("Cannot shadow builtin value \"".concat(name_2, "\""), sourceCodeInfo);
12938
+ }
13026
12939
  currentContext[name_2] = { value: toAny(value) };
13027
12940
  }
13028
12941
  }
@@ -14018,9 +13931,6 @@ var Parser = /** @class */ (function () {
14018
13931
  break;
14019
13932
  }
14020
13933
  }
14021
- else if (isReservedSymbolToken(firstToken, 'function')) {
14022
- return this.parseFunction(firstToken);
14023
- }
14024
13934
  else if (isReservedSymbolToken(firstToken, 'export')) {
14025
13935
  if (!moduleScope) {
14026
13936
  throw new LitsError('export is only allowed in module scope', firstToken[2]);
@@ -14313,9 +14223,8 @@ var Parser = /** @class */ (function () {
14313
14223
  var _c = __read(params, 1), param = _c[0];
14314
14224
  return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, param]], symbol[2]);
14315
14225
  }
14316
- case specialExpressionTypes['0_fn']:
14226
+ case specialExpressionTypes['0_lambda']:
14317
14227
  case specialExpressionTypes['0_def']:
14318
- case specialExpressionTypes['0_defn']:
14319
14228
  throw new LitsError("".concat(type, " is not allowed"), symbol[2]);
14320
14229
  /* v8 ignore next 2 */
14321
14230
  default:
@@ -14342,11 +14251,27 @@ var Parser = /** @class */ (function () {
14342
14251
  return null;
14343
14252
  }
14344
14253
  this.advance();
14345
- var body = this.parseExpression();
14346
- return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes['0_fn'], [
14254
+ var nodes = void 0;
14255
+ var docString = '';
14256
+ if (isLBraceToken(this.peek())) {
14257
+ var parsedBlock = this.parseBlock(true);
14258
+ docString = parsedBlock[1];
14259
+ nodes = parsedBlock[0][1][1];
14260
+ }
14261
+ else {
14262
+ nodes = [this.parseExpression()];
14263
+ }
14264
+ return withSourceCodeInfo([
14265
+ NodeTypes.SpecialExpression,
14266
+ [
14267
+ specialExpressionTypes['0_lambda'],
14268
+ [
14347
14269
  functionArguments,
14348
- [body],
14349
- ]]], firstToken[2]);
14270
+ nodes,
14271
+ ],
14272
+ docString,
14273
+ ],
14274
+ ], firstToken[2]);
14350
14275
  }
14351
14276
  catch (_a) {
14352
14277
  return null;
@@ -14395,7 +14320,16 @@ var Parser = /** @class */ (function () {
14395
14320
  var firstToken = this.asToken(this.peek());
14396
14321
  this.advance();
14397
14322
  var startPos = this.parseState.position;
14398
- var exprNode = this.parseExpression();
14323
+ var nodes;
14324
+ var docString = '';
14325
+ if (isLBraceToken(this.peek())) {
14326
+ var parsedBlock = this.parseBlock(true);
14327
+ docString = parsedBlock[1];
14328
+ nodes = parsedBlock[0][1][1];
14329
+ }
14330
+ else {
14331
+ nodes = [this.parseExpression()];
14332
+ }
14399
14333
  var endPos = this.parseState.position - 1;
14400
14334
  var arity = 0;
14401
14335
  var dollar1 = 'NOT_SET'; // referring to argument bindings. $ = NAKED, $1, $2, $3, etc = WITH_1
@@ -14426,10 +14360,10 @@ var Parser = /** @class */ (function () {
14426
14360
  functionArguments.push(withSourceCodeInfo([bindingTargetTypes.symbol, [[NodeTypes.UserDefinedSymbol, "$".concat(i)], undefined]], firstToken[2]));
14427
14361
  }
14428
14362
  }
14429
- var node = withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes['0_fn'], [
14363
+ var node = withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes['0_lambda'], [
14430
14364
  functionArguments,
14431
- [exprNode],
14432
- ]]], firstToken[2]);
14365
+ nodes,
14366
+ ], docString]], firstToken[2]);
14433
14367
  return node;
14434
14368
  };
14435
14369
  Parser.prototype.parseOptionalDefaulValue = function () {
@@ -14620,21 +14554,10 @@ var Parser = /** @class */ (function () {
14620
14554
  }
14621
14555
  assertRParenToken(token);
14622
14556
  this.advance();
14623
- assertLBraceToken(this.peek());
14557
+ assertOperatorToken(this.peek(), '->');
14624
14558
  this.advance();
14625
- var params = [];
14626
- while (!this.isAtEnd() && !isRBraceToken(this.peek())) {
14627
- params.push(this.parseExpression());
14628
- if (isOperatorToken(this.peek(), ';')) {
14629
- this.advance();
14630
- }
14631
- else if (!isRBraceToken(this.peek())) {
14632
- throw new LitsError('Expected ;', this.peekSourceCodeInfo());
14633
- }
14634
- }
14635
- assertRBraceToken(this.peek());
14636
- this.advance();
14637
- return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.loop, bindingNodes, params]], firstToken[2]);
14559
+ var expression = this.parseExpression();
14560
+ return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.loop, bindingNodes, expression]], firstToken[2]);
14638
14561
  };
14639
14562
  Parser.prototype.parseTry = function (token) {
14640
14563
  this.advance();
@@ -14665,7 +14588,7 @@ var Parser = /** @class */ (function () {
14665
14588
  throw new LitsError('Duplicate binding', loopBinding[0][2]);
14666
14589
  }
14667
14590
  forLoopBindings.push(loopBinding);
14668
- if (isOperatorToken(this_1.peek(), ';')) {
14591
+ if (isOperatorToken(this_1.peek(), ',')) {
14669
14592
  this_1.advance();
14670
14593
  }
14671
14594
  };
@@ -14675,6 +14598,8 @@ var Parser = /** @class */ (function () {
14675
14598
  }
14676
14599
  assertRParenToken(this.peek());
14677
14600
  this.advance();
14601
+ assertOperatorToken(this.peek(), '->');
14602
+ this.advance();
14678
14603
  var expression = this.parseExpression();
14679
14604
  return isDoseq
14680
14605
  ? withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.doseq, forLoopBindings, expression]], firstToken[2])
@@ -14684,20 +14609,7 @@ var Parser = /** @class */ (function () {
14684
14609
  var bindingNode = this.parseBinding();
14685
14610
  var modifiers = [];
14686
14611
  var token = this.asToken(this.peek());
14687
- if (!isRParenToken(token) && !isOperatorToken(this.peek(), ';') && !isOperatorToken(token, ',')) {
14688
- throw new LitsError('Expected ")", ";" or ","', token[2]);
14689
- }
14690
- if (isOperatorToken(token, ',')) {
14691
- this.advance();
14692
- token = this.asToken(this.peek());
14693
- }
14694
- if (!isSymbolToken(token, 'let')
14695
- && !isReservedSymbolToken(token, 'when')
14696
- && !isReservedSymbolToken(token, 'while')
14697
- && !isRParenToken(token)
14698
- && !isOperatorToken(token, ';')) {
14699
- throw new LitsError('Expected symbol ";", ")", let, when or while', token[2]);
14700
- }
14612
+ this.assertInternalLoopBindingDelimiter(token, ['let', 'when', 'while']);
14701
14613
  var letBindings = [];
14702
14614
  if (token[1] === 'let') {
14703
14615
  modifiers.push('&let');
@@ -14710,12 +14622,7 @@ var Parser = /** @class */ (function () {
14710
14622
  }
14711
14623
  letBindings.push(letNode[1][1]);
14712
14624
  token = this_2.asToken(this_2.peek());
14713
- if (!isRParenToken(token) && !isOperatorToken(token, ';') && !isOperatorToken(token, ',')) {
14714
- throw new LitsError('Expected ")", ";" or ","', token[2]);
14715
- }
14716
- if (isOperatorToken(token, ',')) {
14717
- this_2.advance();
14718
- }
14625
+ this_2.assertInternalLoopBindingDelimiter(token, ['let', 'when', 'while']);
14719
14626
  token = this_2.asToken(this_2.peek());
14720
14627
  };
14721
14628
  var this_2 = this;
@@ -14729,33 +14636,57 @@ var Parser = /** @class */ (function () {
14729
14636
  || isReservedSymbolToken(token, 'while')) {
14730
14637
  this.advance();
14731
14638
  if (token[1] === 'when') {
14732
- if (modifiers.includes('&when')) {
14733
- throw new LitsError('Multiple when modifiers in for loop', token[2]);
14734
- }
14735
14639
  modifiers.push('&when');
14736
14640
  whenNode = this.parseExpression();
14737
14641
  }
14738
14642
  else {
14739
- if (modifiers.includes('&while')) {
14740
- throw new LitsError('Multiple while modifiers in for loop', token[2]);
14741
- }
14742
14643
  modifiers.push('&while');
14743
14644
  whileNode = this.parseExpression();
14744
14645
  }
14745
14646
  token = this.asToken(this.peek());
14746
- if (!isRParenToken(token) && !isOperatorToken(token, ';') && !isOperatorToken(token, ',')) {
14747
- throw new LitsError('Expected do or comma', token[2]);
14748
- }
14749
- if (isOperatorToken(token, ',')) {
14750
- this.advance();
14751
- }
14647
+ var symbols = modifiers.includes('&when') && modifiers.includes('&while')
14648
+ ? []
14649
+ : modifiers.includes('&when')
14650
+ ? ['while']
14651
+ : ['when'];
14652
+ this.assertInternalLoopBindingDelimiter(token, symbols);
14752
14653
  token = this.asToken(this.peek());
14753
14654
  }
14754
- if (!isRParenToken(token) && !isOperatorToken(token, ';')) {
14755
- throw new LitsError('Expected "{" or ";"', token[2]);
14756
- }
14655
+ this.assertInternalLoopBindingDelimiter(token, []);
14757
14656
  return [bindingNode, letBindings, whenNode, whileNode];
14758
14657
  };
14658
+ Parser.prototype.assertInternalLoopBindingDelimiter = function (token, symbols) {
14659
+ if (!this.isInternalLoopBindingDelimiter(token, symbols)) {
14660
+ var symbolsString = "".concat(__spreadArray(__spreadArray([], __read(symbols), false), [','], false).map(function (symbol) { return "\"".concat(symbol, "\""); }).join(', '), " or \")\"");
14661
+ throw new LitsError("Expected symbol ".concat(symbolsString), token[2]);
14662
+ }
14663
+ };
14664
+ Parser.prototype.isInternalLoopBindingDelimiter = function (token, symbols) {
14665
+ var e_1, _a;
14666
+ // end of loop binding
14667
+ if (isOperatorToken(token, ',') || isRParenToken(token)) {
14668
+ return true;
14669
+ }
14670
+ try {
14671
+ for (var symbols_1 = __values(symbols), symbols_1_1 = symbols_1.next(); !symbols_1_1.done; symbols_1_1 = symbols_1.next()) {
14672
+ var symbol = symbols_1_1.value;
14673
+ if (symbol === 'let' && isSymbolToken(token, 'let')) {
14674
+ return true;
14675
+ }
14676
+ if (['when', 'while'].includes(symbol) && isReservedSymbolToken(token, symbol)) {
14677
+ return true;
14678
+ }
14679
+ }
14680
+ }
14681
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
14682
+ finally {
14683
+ try {
14684
+ if (symbols_1_1 && !symbols_1_1.done && (_a = symbols_1.return)) _a.call(symbols_1);
14685
+ }
14686
+ finally { if (e_1) throw e_1.error; }
14687
+ }
14688
+ return false;
14689
+ };
14759
14690
  Parser.prototype.parseBinding = function () {
14760
14691
  var firstToken = asSymbolToken(this.peek());
14761
14692
  var name = asUserDefinedSymbolNode(this.parseSymbol());
@@ -14774,10 +14705,8 @@ var Parser = /** @class */ (function () {
14774
14705
  Parser.prototype.parseIfOrUnless = function (token) {
14775
14706
  var isUnless = token[1] === 'unless';
14776
14707
  this.advance();
14777
- assertLParenToken(this.peek());
14778
- this.advance();
14779
14708
  var condition = this.parseExpression();
14780
- assertRParenToken(this.peek());
14709
+ assertReservedSymbolToken(this.peek(), 'then');
14781
14710
  this.advance();
14782
14711
  var thenExpression = this.parseExpression();
14783
14712
  var elseExpression;
@@ -14790,100 +14719,74 @@ var Parser = /** @class */ (function () {
14790
14719
  : withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.if, [condition, thenExpression, elseExpression]]], token[2]);
14791
14720
  };
14792
14721
  Parser.prototype.parseCond = function (token) {
14793
- this.advance();
14794
- assertLBraceToken(this.peek());
14795
14722
  this.advance();
14796
14723
  var params = [];
14797
- while (!this.isAtEnd() && !isRBraceToken(this.peek())) {
14724
+ while (!this.isAtEnd() && !isReservedSymbolToken(this.peek(), 'end')) {
14798
14725
  assertReservedSymbolToken(this.peek(), 'case');
14799
14726
  this.advance();
14800
14727
  var caseExpression = this.parseExpression();
14801
- assertOperatorToken(this.peek(), ':');
14728
+ assertReservedSymbolToken(this.peek(), 'then');
14802
14729
  this.advance();
14803
14730
  var expressions = [];
14804
14731
  while (!this.isAtEnd()
14805
14732
  && !isReservedSymbolToken(this.peek(), 'case')
14806
- && !isRBraceToken(this.peek())) {
14733
+ && !isReservedSymbolToken(this.peek(), 'end')) {
14807
14734
  expressions.push(this.parseExpression());
14808
14735
  if (isOperatorToken(this.peek(), ';')) {
14809
14736
  this.advance();
14810
14737
  }
14811
- else if (!isReservedSymbolToken(this.peek(), 'case') && !isRBraceToken(this.peek())) {
14812
- throw new LitsError('Expected ;', this.peekSourceCodeInfo());
14738
+ else if (!isReservedSymbolToken(this.peek(), 'case') && !isReservedSymbolToken(this.peek(), 'end')) {
14739
+ throw new LitsError('Expected case or end', this.peekSourceCodeInfo());
14813
14740
  }
14814
14741
  }
14815
14742
  var thenExpression = expressions.length === 1
14816
14743
  ? expressions[0]
14817
14744
  : withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.block, expressions]], token[2]);
14818
14745
  params.push([caseExpression, thenExpression]);
14819
- if (isRBraceToken(this.peek())) {
14746
+ if (isReservedSymbolToken(this.peek(), 'end')) {
14820
14747
  break;
14821
14748
  }
14822
14749
  assertReservedSymbolToken(this.peek(), 'case');
14823
14750
  }
14824
- assertRBraceToken(this.peek());
14751
+ assertReservedSymbolToken(this.peek());
14825
14752
  this.advance();
14826
14753
  return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.cond, params]], token[2]);
14827
14754
  };
14828
14755
  Parser.prototype.parseSwitch = function (token) {
14829
- this.advance();
14830
- assertLParenToken(this.peek());
14831
14756
  this.advance();
14832
14757
  var valueExpression = this.parseExpression();
14833
- assertRParenToken(this.peek());
14834
- this.advance();
14835
- assertLBraceToken(this.peek());
14836
- this.advance();
14837
14758
  var params = [];
14838
- while (!this.isAtEnd() && !isRBraceToken(this.peek())) {
14759
+ while (!this.isAtEnd() && !isReservedSymbolToken(this.peek(), 'end')) {
14839
14760
  assertReservedSymbolToken(this.peek(), 'case');
14840
14761
  this.advance();
14841
14762
  var caseExpression = this.parseExpression();
14842
- assertOperatorToken(this.peek(), ':');
14763
+ assertReservedSymbolToken(this.peek(), 'then');
14843
14764
  this.advance();
14844
14765
  var expressions = [];
14845
14766
  while (!this.isAtEnd()
14846
14767
  && !isReservedSymbolToken(this.peek(), 'case')
14847
- && !isRBraceToken(this.peek())) {
14768
+ && !isReservedSymbolToken(this.peek(), 'end')) {
14848
14769
  expressions.push(this.parseExpression());
14849
14770
  if (isOperatorToken(this.peek(), ';')) {
14850
14771
  this.advance();
14851
14772
  }
14852
- else if (!isReservedSymbolToken(this.peek(), 'case') && !isRBraceToken(this.peek())) {
14853
- throw new LitsError('Expected ;', this.peekSourceCodeInfo());
14773
+ else if (!isReservedSymbolToken(this.peek(), 'case') && !isReservedSymbolToken(this.peek(), 'end')) {
14774
+ throw new LitsError('Expected case or end', this.peekSourceCodeInfo());
14854
14775
  }
14855
14776
  }
14856
14777
  var thenExpression = expressions.length === 1
14857
14778
  ? expressions[0]
14858
14779
  : withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.block, expressions]], token[2]);
14859
14780
  params.push([caseExpression, thenExpression]);
14860
- if (isRBraceToken(this.peek())) {
14781
+ if (isReservedSymbolToken(this.peek(), 'end')) {
14861
14782
  break;
14862
14783
  }
14863
14784
  assertReservedSymbolToken(this.peek(), 'case');
14864
14785
  }
14865
- assertRBraceToken(this.peek());
14786
+ assertReservedSymbolToken(this.peek(), 'end');
14866
14787
  this.advance();
14867
14788
  return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.switch, valueExpression, params]], token[2]);
14868
14789
  };
14869
- Parser.prototype.parseFunction = function (token) {
14870
- this.advance();
14871
- var symbol = this.parseSymbol();
14872
- var functionArguments = this.parseFunctionArguments();
14873
- var _a = __read(this.parseBlock(true), 2), block = _a[0], docString = _a[1];
14874
- return withSourceCodeInfo([
14875
- NodeTypes.SpecialExpression,
14876
- [
14877
- specialExpressionTypes.function,
14878
- symbol,
14879
- [
14880
- functionArguments,
14881
- block[1][1],
14882
- ],
14883
- docString,
14884
- ],
14885
- ], token[2]);
14886
- };
14887
14790
  Parser.prototype.isAtEnd = function () {
14888
14791
  return this.parseState.position >= this.tokenStream.tokens.length;
14889
14792
  };
@@ -14896,7 +14799,7 @@ var Parser = /** @class */ (function () {
14896
14799
  return [';', ',', ':'].includes(token[1]);
14897
14800
  }
14898
14801
  if (isReservedSymbolToken(token)) {
14899
- return ['else', 'when', 'while', 'case', 'catch'].includes(token[1]);
14802
+ return ['else', 'when', 'while', 'case', 'catch', 'let', 'then'].includes(token[1]);
14900
14803
  }
14901
14804
  return false;
14902
14805
  };
@@ -14907,13 +14810,8 @@ var Parser = /** @class */ (function () {
14907
14810
  var letNode = this.parseLet(asSymbolToken(token));
14908
14811
  return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes['0_def'], letNode[1][1]]], exportToken[2]);
14909
14812
  }
14910
- else if (isReservedSymbolToken(token, 'function')) {
14911
- var functionNode = this.parseFunction(token);
14912
- functionNode[1][0] = specialExpressionTypes['0_defn'];
14913
- return functionNode;
14914
- }
14915
14813
  else {
14916
- throw new LitsError('Expected let or function', this.peekSourceCodeInfo());
14814
+ throw new LitsError('Expected let', this.peekSourceCodeInfo());
14917
14815
  }
14918
14816
  };
14919
14817
  Parser.prototype.stringToSymbolNode = function (value, sourceCodeInfo) {
@@ -15103,7 +15001,7 @@ var AutoCompleter = /** @class */ (function () {
15103
15001
  };
15104
15002
  AutoCompleter.prototype.generateSuggestions = function (params) {
15105
15003
  var _this = this;
15106
- var blacklist = new Set(['0_def', '0_defn', '0_fn']);
15004
+ var blacklist = new Set(['0_def', '0_defn', '0_lambda']);
15107
15005
  var startsWithCaseSensitive = this.generateWithPredicate(params, function (suggestion) {
15108
15006
  return !blacklist.has(suggestion) && suggestion.startsWith(_this.searchString);
15109
15007
  });
@@ -15732,7 +15630,7 @@ var arrayReference = {
15732
15630
  '[[3, 2, 1, 0], [6, 5, 4], [9, 8, 7]] mapcat reverse',
15733
15631
  'mapcat([[3, 2, 1, 0], [6, 5, 4], [9, 8, 7]], reverse)',
15734
15632
  '[[3, 2, 1, 0,], [6, 5, 4,], [9, 8, 7]] mapcat reverse',
15735
- "\nfunction foo(n) {\n [n - 1, n, n + 1]\n};\n[1, 2, 3] mapcat foo",
15633
+ "\nlet foo = (n) -> {\n [n - 1, n, n + 1]\n};\n[1, 2, 3] mapcat foo",
15736
15634
  "\nmapcat(\n [[1, 2], [2, 2], [2, 3]],\n -> $ remove even?\n)",
15737
15635
  ],
15738
15636
  },
@@ -20463,7 +20361,7 @@ var metaReference = {
20463
20361
  description: 'Returns documentation string of the $fun.',
20464
20362
  examples: [
20465
20363
  'doc(+)',
20466
- "\nfunction add(x, y) {\n \"\"\"\n Adds two numbers.\n Args:\n x: First number.\n y: Second number.\n Returns:\n Sum of x and y.\n \"\"\"\n x + y;\n};\n\ndoc(add)",
20364
+ "\nlet add = (x, y) -> {\n \"\"\"\n Adds two numbers.\n Args:\n x: First number.\n y: Second number.\n Returns:\n Sum of x and y.\n \"\"\"\n x + y;\n};\n\ndoc(add)",
20467
20365
  ],
20468
20366
  },
20469
20367
  arity: {
@@ -20484,8 +20382,8 @@ var metaReference = {
20484
20382
  examples: [
20485
20383
  'arity(+)',
20486
20384
  'arity(defined?)',
20487
- "\nfunction add(x, y = 0) {\n x + y;\n};\n\narity(add)",
20488
- "\nfunction foo(k, ...x) {\n k + x;\n};\n arity(foo)",
20385
+ "\nlet add = (x, y = 0) -> {\n x + y;\n};\n\narity(add)",
20386
+ "\nlet foo = (k, ...x) -> {\n k + x;\n};\n arity(foo)",
20489
20387
  ],
20490
20388
  },
20491
20389
  };
@@ -26365,8 +26263,8 @@ var sequenceReference = {
26365
26263
  examples: [
26366
26264
  '[3, 1, 2] sort (a, b) -> b - a',
26367
26265
  'sort([3, 1, 2])',
26368
- "\nsort(\n [3, 1, 2],\n (a, b) -> cond { case a < b: -1 case a > b: 1 case true: -1 }\n)",
26369
- "\nsort(\n [3, 1, 2],\n (a, b) -> cond { case a > b: -1 case a < b: 1 case true: -1 }\n)",
26266
+ "\nsort(\n [3, 1, 2],\n (a, b) -> cond case a < b then -1 case a > b then 1 case true then -1 end\n)",
26267
+ "\nsort(\n [3, 1, 2],\n (a, b) -> cond case a > b then -1 case a < b then 1 case true then -1 end\n)",
26370
26268
  ],
26371
26269
  },
26372
26270
  'sort-by': {
@@ -26843,26 +26741,6 @@ var specialExpressionsReference = {
26843
26741
  description: "\n Binds local variables s to `value`. `value` can be any expression. The scope of the variables is the body of the let expression.",
26844
26742
  examples: ["\nlet a = 1 + 2 + 3 + 4;\nlet b = -> $ * ( $ + 1 );\nwrite!(\"a\", a, \"b\", b)"],
26845
26743
  },
26846
- 'function': {
26847
- title: 'function',
26848
- category: 'Special expression',
26849
- customVariants: ['function name(...arg) { body }'],
26850
- details: [
26851
- ['name', 'symbol', 'The name of the function.'],
26852
- ['arg', '[...]arg-name [= value]', 'Arguments of the function.'],
26853
- ['...', 'rest-symbol', 'Optional. The rest argument of the function.'],
26854
- ['arg-name', 'symbol', 'The name of the argument.'],
26855
- ['value', 'any', 'Optional. The default value of the argument.'],
26856
- ['let-binding', 'symbol', 'Optional. The let bindings of the function.'],
26857
- ['body', 'one or more expressions', 'The body of the function.'],
26858
- ],
26859
- description: 'Creates a named function. When called, evaluation of the last expression in the body is returned.',
26860
- examples: [
26861
- "\nfunction hyp (a, b) {\n sqrt(a * a + b * b)\n};\n\nhyp(3, 4)",
26862
- "\nfunction sumOfSquares(...s) {\n apply(\n +,\n map(s, -> $ ^ 2)\n )\n};\n\nsumOfSquares(1, 2, 3, 4, 5)",
26863
- "\nfunction withOptional(a, b = 42) {\n a + b\n};\n\nwrite!(withOptional(1), withOptional(1, 2))",
26864
- ],
26865
- },
26866
26744
  'try': {
26867
26745
  title: 'try',
26868
26746
  category: 'Special expression',
@@ -26902,7 +26780,7 @@ var specialExpressionsReference = {
26902
26780
  'if': {
26903
26781
  title: 'if',
26904
26782
  category: 'Special expression',
26905
- customVariants: ['if (test) true-expr else false-expr', 'if (test) true-expr'],
26783
+ customVariants: ['if test then true-expr else false-expr', 'if test then true-expr'],
26906
26784
  details: [
26907
26785
  ['test', 'expression', 'The condition to test.'],
26908
26786
  ['true-expr', 'expression', 'The expression to evaluate if the test is truthy.'],
@@ -26910,16 +26788,16 @@ var specialExpressionsReference = {
26910
26788
  ],
26911
26789
  description: 'Either `true-expr` or `false-expr` branch is taken. `true-expr` is selected when $test is truthy. If $test is falsy `false-expr` is executed, if no `false-expr` exists, `null` is returned.',
26912
26790
  examples: [
26913
- "\nif (true) {\n write!(\"TRUE\")\n} else {\n write!(\"FALSE\")\n}",
26914
- 'if (false) write!("TRUE") else write!("FALSE")',
26915
- 'if (true) write!("TRUE")',
26916
- 'if (false) write!("TRUE")',
26791
+ "\nif true then {\n write!(\"TRUE\")\n} else {\n write!(\"FALSE\")\n}",
26792
+ 'if false then write!("TRUE") else write!("FALSE")',
26793
+ 'if true then write!("TRUE")',
26794
+ 'if false then write!("TRUE")',
26917
26795
  ],
26918
26796
  },
26919
26797
  'unless': {
26920
26798
  title: 'unless',
26921
26799
  category: 'Special expression',
26922
- customVariants: ['unless (test) true-expr else false-expr end', 'unless test true-expr end'],
26800
+ customVariants: ['unless test then true-expr else false-expr end', 'unless test true-expr end'],
26923
26801
  details: [
26924
26802
  ['test', 'expression', 'The condition to test.'],
26925
26803
  ['true-expr', 'expression', 'The expressions to evaluate if the test is falsy.'],
@@ -26927,16 +26805,16 @@ var specialExpressionsReference = {
26927
26805
  ],
26928
26806
  description: 'Either `true-expr` or `false-expr` branch is taken. `true-expr` is selected when $test is falsy. If $test is truthy `false-expr` is executed, if no `false-expr` exists, `null` is returned.',
26929
26807
  examples: [
26930
- "\nunless (true) {\n write!(\"TRUE\")\n} else {\n write!(\"FALSE\")\n}",
26931
- 'unless (false) write!("TRUE") else write!("FALSE")',
26932
- 'unless (true) write!("TRUE")',
26933
- 'unless (false) write!("TRUE")',
26808
+ "\nunless true then {\n write!(\"TRUE\")\n} else {\n write!(\"FALSE\")\n}",
26809
+ 'unless false then write!("TRUE") else write!("FALSE")',
26810
+ 'unless true then write!("TRUE")',
26811
+ 'unless false then write!("TRUE")',
26934
26812
  ],
26935
26813
  },
26936
26814
  'cond': {
26937
26815
  title: 'cond',
26938
26816
  category: 'Special expression',
26939
- customVariants: ['cond { cond-branch cond-branch ... }'],
26817
+ customVariants: ['cond cond-branch cond-branch ... end'],
26940
26818
  details: [
26941
26819
  ['cond-branch', 'case test then body', 'A branch of the cond expression.'],
26942
26820
  ['test', 'expression', 'The condition to test.'],
@@ -26944,15 +26822,15 @@ var specialExpressionsReference = {
26944
26822
  ],
26945
26823
  description: 'Used for branching. `cond-branches` are tested sequentially from the top. If no branch is tested truthy, `null` is returned.',
26946
26824
  examples: [
26947
- "\ncond {\n case false: write!(\"FALSE\")\n case true: write!(\"TRUE\")\n}",
26948
- "\ncond {\n case false: write!(\"FALSE\")\n case null: write!(\"null\")\n} ?? write!(\"TRUE\")",
26949
- "\ncond {\n case false: write!(\"FALSE\")\n case null: write!(\"null\")\n} ?? write!(\"TRUE\")",
26825
+ "\ncond\n case false then write!(\"FALSE\")\n case true then write!(\"TRUE\")\nend",
26826
+ "\ncond\n case false then write!(\"FALSE\")\n case null then write!(\"null\")\nend ?? write!(\"TRUE\")",
26827
+ "\ncond\n case false then write!(\"FALSE\")\n case null then write!(\"null\")\nend ?? write!(\"TRUE\")",
26950
26828
  ],
26951
26829
  },
26952
26830
  'switch': {
26953
26831
  title: 'switch',
26954
26832
  category: 'Special expression',
26955
- customVariants: ['switch (value) { switch-branch switch-branch ... }'],
26833
+ customVariants: ['switch value switch-branch switch-branch ... end'],
26956
26834
  details: [
26957
26835
  ['value', 'any', 'The value to test.'],
26958
26836
  ['switch-branch', 'case test then body', 'A branch of the switch expression.'],
@@ -26961,9 +26839,9 @@ var specialExpressionsReference = {
26961
26839
  ],
26962
26840
  description: 'Used for branching. `switch-branches` are tested sequentially from the top against `value`. If no branch is tested truthy, `null` is returned.',
26963
26841
  examples: [
26964
- "\nswitch (1) {\n case 1: write!(\"One\")\n case 2: write!(\"Two\")\n}",
26965
- "\nswitch (2) {\n case 1: write!(\"One\")\n case 2: write!(\"Two\")\n}",
26966
- "\nswitch (3) {\n case 1: write!(\"One\")\n case 2: write!(\"Two\")\n}",
26842
+ "\nswitch 1\n case 1 then write!(\"One\")\n case 2 then write!(\"Two\")\nend",
26843
+ "\nswitch 2\n case 1 then write!(\"One\")\n case 2 then write!(\"Two\")\nend",
26844
+ "\nswitch 3\n case 1 then write!(\"One\")\n case 2 then write!(\"Two\")\nend",
26967
26845
  ],
26968
26846
  },
26969
26847
  'block': {
@@ -26984,9 +26862,9 @@ var specialExpressionsReference = {
26984
26862
  customVariants: ['recur(...recur-args)'],
26985
26863
  description: 'Recursevly calls enclosing function or loop with its evaluated `recur-args`.',
26986
26864
  examples: [
26987
- "\nfunction foo(n) {\n write!(n);\n if (!(zero?(n))) {\n recur(n - 1)\n }\n};\nfoo(3)",
26988
- "\n(n -> {\n write!(n);\n if (!(zero?(n))) {\n recur(n - 1)\n }\n})(3)",
26989
- "\nloop (n = 3) {\n write!(n);\n if (!(zero?(n))) {\n recur(n - 1)\n }\n}",
26865
+ "\nlet foo = (n) -> {\n write!(n);\n if !(zero?(n)) then {\n recur(n - 1)\n }\n};\nfoo(3)",
26866
+ "\n(n -> {\n write!(n);\n if !(zero?(n)) then {\n recur(n - 1)\n }\n})(3)",
26867
+ "\nloop (n = 3) -> {\n write!(n);\n if !(zero?(n)) then {\n recur(n - 1)\n }\n}",
26990
26868
  ],
26991
26869
  },
26992
26870
  };
@@ -31444,1382 +31322,6 @@ function isAbstractLitsError(error) {
31444
31322
  return error instanceof LitsError;
31445
31323
  }
31446
31324
 
31447
- const defaultConfig = {
31448
- spaceSeparation: false,
31449
- precision: 8,
31450
- epsilon: 1e-10,
31451
- };
31452
- const CONFIG = {
31453
- spaceSeparation: false,
31454
- precision: 8,
31455
- epsilon: 1e-10,
31456
- };
31457
- function setConfig(newConfig) {
31458
- CONFIG.spaceSeparation = newConfig.spaceSeparation ?? defaultConfig.spaceSeparation;
31459
- CONFIG.precision = newConfig.precision ?? defaultConfig.precision;
31460
- CONFIG.epsilon = newConfig.epsilon ?? defaultConfig.epsilon;
31461
- }
31462
- /**
31463
- * Mathematical constants used for symbolic representation
31464
- */
31465
- const CONSTANTS = [
31466
- { getSymbol: () => 'π', value: Math.PI },
31467
- { getSymbol: () => 'e', value: Math.E },
31468
- { getSymbol: () => 'φ', value: (1 + Math.sqrt(5)) / 2 },
31469
- { getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(2)' : '√2', value: Math.sqrt(2) },
31470
- { getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(3)' : '√3', value: Math.sqrt(3) },
31471
- { getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(5)' : '√5', value: Math.sqrt(5) },
31472
- { getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(7)' : '√7', value: Math.sqrt(7) },
31473
- { getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(11)' : '√11', value: Math.sqrt(11) },
31474
- { getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(13)' : '√13', value: Math.sqrt(13) },
31475
- { getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(17)' : '√17', value: Math.sqrt(17) },
31476
- { getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(19)' : '√19', value: Math.sqrt(19) },
31477
- { getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(π)' : '√π', value: Math.sqrt(Math.PI) },
31478
- { getSymbol: () => 'ln(2)', value: Math.LN2 },
31479
- { getSymbol: () => 'ln(10)', value: Math.LN10 },
31480
- { getSymbol: () => 'log₂(e)', value: Math.LOG2E },
31481
- { getSymbol: () => 'log₁₀(e)', value: Math.LOG10E },
31482
- ];
31483
- /**
31484
- * Trigonometric values with their exact symbolic representations
31485
- */
31486
- const TRIG_VALUES = [
31487
- // sin values
31488
- { value: Math.sin(Math.PI / 6), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'sin(π / 6)' : 'sin(π/6)', getExactForm: () => CONFIG.spaceSeparation ? '1 / 2' : '1/2' },
31489
- { value: Math.sin(Math.PI / 4), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'sin(π / 4)' : 'sin(π/4)', getExactForm: () => CONFIG.spaceSeparation ? '√2 / 2' : '√2/2' },
31490
- { value: Math.sin(Math.PI / 3), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'sin(π / 3)' : 'sin(π/3)', getExactForm: () => CONFIG.spaceSeparation ? '√3 / 2' : '√3/2' },
31491
- { value: Math.sin(Math.PI / 2), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'sin(π / 2)' : 'sin(π/2)', getExactForm: () => '1' },
31492
- // cos values
31493
- { value: Math.cos(0), getSymbol: () => 'cos(0)', getExactForm: () => '1' },
31494
- { value: Math.cos(Math.PI / 6), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'cos(π / 6)' : 'cos(π/6)', getExactForm: () => CONFIG.spaceSeparation ? '√(3) / 2' : '√3/2' },
31495
- { value: Math.cos(Math.PI / 4), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'cos(π / 4)' : 'cos(π/4)', getExactForm: () => CONFIG.spaceSeparation ? '√(2) / 2' : '√2/2' },
31496
- { value: Math.cos(Math.PI / 3), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'cos(π / 3)' : 'cos(π/3)', getExactForm: () => CONFIG.spaceSeparation ? '1 / 2' : '1/2' },
31497
- { value: Math.cos(Math.PI / 2), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'cos(π / 2)' : 'cos(π/2)', getExactForm: () => '0' },
31498
- // tan values
31499
- { value: Math.tan(Math.PI / 6), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'tan(π / 6)' : 'tan(π/6)', getExactForm: () => CONFIG.spaceSeparation ? '1 / √(3)' : '1/√3' },
31500
- { value: Math.tan(Math.PI / 4), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'tan(π / 4)' : 'tan(π/4)', getExactForm: () => '1' },
31501
- { value: Math.tan(Math.PI / 3), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'tan(π / 3)' : 'tan(π/3)', getExactForm: () => CONFIG.spaceSeparation ? '√(3)' : '√3' },
31502
- ];
31503
-
31504
- /**
31505
- * Factorizes a number under a radical to extract perfect square factors
31506
- * @param n The number to factorize
31507
- * @returns Object containing the coefficient and remaining radicand
31508
- */
31509
- function factorizeRadicand(n) {
31510
- // Find largest perfect square factor
31511
- let coefficient = 1;
31512
- let i = 2;
31513
- while (i * i <= n) {
31514
- // Check if i² is a factor of n
31515
- if (n % (i * i) === 0) {
31516
- coefficient *= i;
31517
- n /= (i * i);
31518
- // Continue with the same i to check for multiple powers
31519
- continue;
31520
- }
31521
- i++;
31522
- }
31523
- return { coefficient, radicand: n };
31524
- }
31525
- /**
31526
- * Finds the greatest common divisor of two numbers
31527
- * @param a First number
31528
- * @param b Second number
31529
- * @returns The GCD of a and b
31530
- */
31531
- function findGCD(a, b) {
31532
- a = Math.abs(a);
31533
- b = Math.abs(b);
31534
- while (b !== 0) {
31535
- const temp = b;
31536
- b = a % b;
31537
- a = temp;
31538
- }
31539
- return a;
31540
- }
31541
- /**
31542
- * Checks if a number is prime
31543
- * @param n The number to check
31544
- * @returns True if n is prime, false otherwise
31545
- */
31546
- function isPrime(n) {
31547
- if (n <= 1)
31548
- return false;
31549
- if (n <= 3)
31550
- return true;
31551
- if (n % 2 === 0 || n % 3 === 0)
31552
- return false;
31553
- let i = 5;
31554
- while (i * i <= n) {
31555
- if (n % i === 0 || n % (i + 2) === 0)
31556
- return false;
31557
- i += 6;
31558
- }
31559
- return true;
31560
- }
31561
- /**
31562
- * Determines if a number's square root should be preserved in direct form
31563
- * @param n The number to check
31564
- * @returns True if the square root should be kept in direct form
31565
- */
31566
- function shouldPreserveDirectRadical(n) {
31567
- // Always preserve direct form for prime numbers
31568
- if (isPrime(n))
31569
- return true;
31570
- // Always preserve direct form for small integers
31571
- if (n <= 30)
31572
- return true;
31573
- // Check if n has any perfect square factors
31574
- const factorized = factorizeRadicand(n);
31575
- // If no perfect square factors were found, preserve direct form
31576
- return factorized.coefficient === 1;
31577
- }
31578
-
31579
- function isNumberNode(node) {
31580
- return node.type === 'Number';
31581
- }
31582
- function isUnaryOpNode(node) {
31583
- return node.type === 'UnaryOp';
31584
- }
31585
- function isBinaryOpNode(node) {
31586
- return node.type === 'BinaryOp';
31587
- }
31588
- function isRootNode(node) {
31589
- return node.type === 'Root';
31590
- }
31591
- function isConstantNode(node) {
31592
- return node.type === 'Constant';
31593
- }
31594
- function isPowerNode(node) {
31595
- return node.type === 'Power';
31596
- }
31597
-
31598
- /**
31599
- * Helper function to determine expression type priority for ordering
31600
- * @returns A priority number (lower number = higher priority)
31601
- */
31602
- function getExpressionPriority(node) {
31603
- // Priority order: Number < Root/Power < Constant < Other
31604
- if (isNumberNode(node))
31605
- return 1;
31606
- if (isRootNode(node))
31607
- return 2;
31608
- // Handle other node types here
31609
- return 6; // Default for other types
31610
- }
31611
- /**
31612
- * Determines if two expressions should be swapped in multiplication
31613
- * @returns true if expressions should be swapped
31614
- */
31615
- function shouldSwapInMultiplication(left, right) {
31616
- const leftPriority = getExpressionPriority(left);
31617
- const rightPriority = getExpressionPriority(right);
31618
- // Lower priority number should come first
31619
- return leftPriority > rightPriority;
31620
- }
31621
-
31622
- /**
31623
- * Base abstract class for all expression nodes
31624
- */
31625
- class ExprNode {
31626
- }
31627
-
31628
- /**
31629
- * Checks if a node represents exactly zero within epsilon tolerance
31630
- */
31631
- function isZero(node) {
31632
- return isNumberNode(node) && Math.abs(node.evaluate()) < 1e-10;
31633
- }
31634
- /**
31635
- * Checks if a node represents exactly one within epsilon tolerance
31636
- */
31637
- function isOne(node) {
31638
- return isNumberNode(node) && Math.abs(node.evaluate() - 1) < 1e-10;
31639
- }
31640
- /**
31641
- * Node for numeric constants (integers, simple fractions, etc.)
31642
- */
31643
- class NumberNode extends ExprNode {
31644
- constructor(value) {
31645
- super();
31646
- Object.defineProperty(this, "value", {
31647
- enumerable: true,
31648
- configurable: true,
31649
- writable: true,
31650
- value: value
31651
- });
31652
- Object.defineProperty(this, "type", {
31653
- enumerable: true,
31654
- configurable: true,
31655
- writable: true,
31656
- value: 'Number'
31657
- });
31658
- }
31659
- evaluate() {
31660
- return this.value;
31661
- }
31662
- toString() {
31663
- // Round values extremely close to zero to exactly zero
31664
- if (Math.abs(this.value) < CONFIG.epsilon) {
31665
- return "0";
31666
- }
31667
- if (this.value === Number.POSITIVE_INFINITY) {
31668
- return "∞";
31669
- }
31670
- if (this.value === Number.NEGATIVE_INFINITY) {
31671
- return "-∞";
31672
- }
31673
- if (Number.isInteger(this.value)) {
31674
- return this.value.toString();
31675
- }
31676
- // Handle fractions if it's a "nice" fraction
31677
- const MAX_DENOMINATOR = 1000;
31678
- for (let denominator = 2; denominator <= MAX_DENOMINATOR; denominator++) {
31679
- const numerator = Math.round(this.value * denominator);
31680
- if (Math.abs(this.value - numerator / denominator) < CONFIG.epsilon) {
31681
- const gcd = findGCD(Math.abs(numerator), denominator);
31682
- return CONFIG.spaceSeparation ? `${numerator / gcd} / ${denominator / gcd}` : `${numerator / gcd}/${denominator / gcd}`;
31683
- }
31684
- }
31685
- // Just return the decimal representation
31686
- const valueStr = this.value.toString();
31687
- if (valueStr.includes('.')) {
31688
- const [, decimalPart] = valueStr.split('.');
31689
- if (decimalPart.length > 8) {
31690
- return this.value.toFixed(CONFIG.precision);
31691
- }
31692
- }
31693
- return this.value.toString();
31694
- }
31695
- simplify() {
31696
- return this; // Numbers are already in simplest form
31697
- }
31698
- equals(other) {
31699
- if (!(isNumberNode(other)))
31700
- return false;
31701
- return Math.abs(this.value - other.evaluate()) < CONFIG.epsilon;
31702
- }
31703
- }
31704
-
31705
- /**
31706
- * Node for binary operations (+, -, *, /)
31707
- */
31708
- class BinaryOpNode extends ExprNode {
31709
- constructor(op, left, right) {
31710
- super();
31711
- Object.defineProperty(this, "op", {
31712
- enumerable: true,
31713
- configurable: true,
31714
- writable: true,
31715
- value: op
31716
- });
31717
- Object.defineProperty(this, "left", {
31718
- enumerable: true,
31719
- configurable: true,
31720
- writable: true,
31721
- value: left
31722
- });
31723
- Object.defineProperty(this, "right", {
31724
- enumerable: true,
31725
- configurable: true,
31726
- writable: true,
31727
- value: right
31728
- });
31729
- Object.defineProperty(this, "type", {
31730
- enumerable: true,
31731
- configurable: true,
31732
- writable: true,
31733
- value: 'BinaryOp'
31734
- });
31735
- // Apply automatic ordering for multiplication operations
31736
- if (this.op === '*' && shouldSwapInMultiplication(left, right)) {
31737
- // Swap the operands if they're in the wrong order
31738
- this.left = right;
31739
- this.right = left;
31740
- }
31741
- }
31742
- evaluate() {
31743
- const leftVal = this.left.evaluate();
31744
- const rightVal = this.right.evaluate();
31745
- switch (this.op) {
31746
- case '+': return leftVal + rightVal;
31747
- case '-': return leftVal - rightVal;
31748
- case '*': return leftVal * rightVal;
31749
- case '/': return leftVal / rightVal;
31750
- }
31751
- }
31752
- toString() {
31753
- let leftStr = this.left.toString();
31754
- let rightStr = this.right.toString();
31755
- // Add parentheses if needed
31756
- if ((this.op === '+' || this.op === '-')
31757
- && (isBinaryOpNode(this.left) && (this.left.getOp() === '+' || this.left.getOp() === '-'))) {
31758
- leftStr = `(${leftStr})`;
31759
- }
31760
- else if ((this.op === '+' || this.op === '-')
31761
- && (isBinaryOpNode(this.right) && (this.right.getOp() === '+' || this.right.getOp() === '-'))) {
31762
- rightStr = `(${rightStr})`;
31763
- }
31764
- if (this.op === '*') {
31765
- // Double-check order at display time
31766
- if (shouldSwapInMultiplication(this.left, this.right)) {
31767
- return CONFIG.spaceSeparation
31768
- ? `${this.right.toString()} · ${this.left.toString()}`
31769
- : `${this.right.toString()}·${this.left.toString()}`;
31770
- }
31771
- return CONFIG.spaceSeparation
31772
- ? `${this.left.toString()} · ${this.right.toString()}`
31773
- : `${this.left.toString()}·${this.right.toString()}`;
31774
- }
31775
- else {
31776
- return CONFIG.spaceSeparation
31777
- ? `${leftStr} ${this.op} ${rightStr}`
31778
- : `${leftStr}${this.op}${rightStr}`;
31779
- }
31780
- }
31781
- getOp() {
31782
- return this.op;
31783
- }
31784
- getLeft() {
31785
- return this.left;
31786
- }
31787
- getRight() {
31788
- return this.right;
31789
- }
31790
- simplify() {
31791
- // First simplify both children
31792
- const left = this.left.simplify();
31793
- const right = this.right.simplify();
31794
- // If both are numbers, perform the operation
31795
- if (isNumberNode(left) && isNumberNode(right)) {
31796
- return new NumberNode(this.evaluate());
31797
- }
31798
- // Simplification rules for addition
31799
- if (this.op === '+') {
31800
- // a + 0 = a
31801
- if (isNumberNode(right) && right.evaluate() === 0) {
31802
- return left;
31803
- }
31804
- // 0 + a = a
31805
- if (isNumberNode(left) && left.evaluate() === 0) {
31806
- return right;
31807
- }
31808
- }
31809
- // Simplification rules for subtraction
31810
- if (this.op === '-') {
31811
- // a - 0 = a
31812
- if (isNumberNode(right) && right.evaluate() === 0) {
31813
- return left;
31814
- }
31815
- // 0 - a = -a
31816
- if (isNumberNode(left) && left.evaluate() === 0) {
31817
- return new UnaryOpNode('-', right);
31818
- }
31819
- // a - a = 0
31820
- if (left.equals(right)) {
31821
- return new NumberNode(0);
31822
- }
31823
- }
31824
- // Simplification rules for multiplication
31825
- if (this.op === '*') {
31826
- // a * 0 = 0
31827
- if (isZero(left) || isZero(right)) {
31828
- return new NumberNode(0);
31829
- }
31830
- // a * 1 = a
31831
- if (isOne(right)) {
31832
- return left;
31833
- }
31834
- // 1 * a = a
31835
- if (isOne(left)) {
31836
- return right;
31837
- }
31838
- // Special case for n·√m/2 (12·√3/2 case)
31839
- if (isNumberNode(left)
31840
- && isBinaryOpNode(right)
31841
- && right.getOp() === '/'
31842
- && isNumberNode(right.getRight())
31843
- && right.getRight().evaluate() === 2
31844
- && isRootNode(right.getLeft())) {
31845
- const n = left.evaluate();
31846
- const rootNode = right.getLeft();
31847
- return new BinaryOpNode('*', new NumberNode(n / 2), rootNode);
31848
- }
31849
- // Handle multiplications with fractions
31850
- if (isBinaryOpNode(left) && left.getOp() === '/') {
31851
- // (a/b) * c = (a*c)/b
31852
- const fraction = left;
31853
- const numerator = fraction.getLeft();
31854
- const denominator = fraction.getRight();
31855
- return new BinaryOpNode('/', new BinaryOpNode('*', numerator, right), denominator).simplify();
31856
- }
31857
- if (isBinaryOpNode(right) && right.getOp() === '/') {
31858
- // c * (a/b) = (c*a)/b
31859
- const fraction = right;
31860
- const numerator = fraction.getLeft();
31861
- const denominator = fraction.getRight();
31862
- return new BinaryOpNode('/', new BinaryOpNode('*', left, numerator), denominator).simplify();
31863
- }
31864
- // Multiple numeric factors: (2·3)·π = 6·π
31865
- if (isNumberNode(left)
31866
- && isBinaryOpNode(right)
31867
- && right.getOp() === '*'
31868
- && isNumberNode(right.getLeft())) {
31869
- const n1 = left.evaluate();
31870
- const n2 = (right).getLeft().evaluate();
31871
- const restTerm = right.getRight();
31872
- return new BinaryOpNode('*', new NumberNode(n1 * n2), restTerm).simplify();
31873
- }
31874
- // Consolidate products of square roots: √a·√b = √(a·b)
31875
- if (isRootNode(left) && isRootNode(right)) {
31876
- const leftOperand = getRootOperand(left);
31877
- const rightOperand = getRootOperand(right);
31878
- // Create √(a·b) instead of √a·√b
31879
- if (isNumberNode(leftOperand) && isNumberNode(rightOperand)) {
31880
- const newRadicand = leftOperand.evaluate() * rightOperand.evaluate();
31881
- return new RootNode(new NumberNode(newRadicand)).simplify();
31882
- }
31883
- }
31884
- }
31885
- // Simplification rules for division
31886
- if (this.op === '/') {
31887
- // 0 / a = 0
31888
- if (isZero(left)) {
31889
- return new NumberNode(0);
31890
- }
31891
- // a / 1 = a
31892
- if (isOne(right)) {
31893
- return left;
31894
- }
31895
- // a / a = 1
31896
- if (left.equals(right)) {
31897
- return new NumberNode(1);
31898
- }
31899
- // Simplify numeric fractions
31900
- if (isNumberNode(left) && isNumberNode(right)) {
31901
- const num = left.evaluate();
31902
- const denom = right.evaluate();
31903
- if (Number.isInteger(num) && Number.isInteger(denom)) {
31904
- const gcd = findGCD(Math.abs(num), Math.abs(denom));
31905
- if (gcd > 1) {
31906
- return new BinaryOpNode('/', new NumberNode(num / gcd), new NumberNode(denom / gcd));
31907
- }
31908
- }
31909
- }
31910
- // (a*b) / b = a
31911
- if (isBinaryOpNode(left) && left.getOp() === '*') {
31912
- const product = left;
31913
- // Case for n·something / n = something (6·√3/6 = √3)
31914
- if (isNumberNode(product.getLeft())
31915
- && isNumberNode(right)
31916
- && Math.abs(product.getLeft().evaluate() - right.evaluate()) < 1e-10) {
31917
- return product.getRight();
31918
- }
31919
- if (isNumberNode(product.getRight())
31920
- && isNumberNode(right)
31921
- && Math.abs(product.getRight().evaluate() - right.evaluate()) < 1e-10) {
31922
- return product.getLeft();
31923
- }
31924
- // General case
31925
- if (product.getLeft().equals(right)) {
31926
- return product.getRight();
31927
- }
31928
- if (product.getRight().equals(right)) {
31929
- return product.getLeft();
31930
- }
31931
- }
31932
- }
31933
- // If no simplification rules apply, return a new node with simplified children
31934
- return new BinaryOpNode(this.op, left, right);
31935
- }
31936
- equals(other) {
31937
- if (!(isBinaryOpNode(other)))
31938
- return false;
31939
- const otherOp = other;
31940
- return this.op === otherOp.getOp()
31941
- && this.left.equals(otherOp.getLeft())
31942
- && this.right.equals(otherOp.getRight());
31943
- }
31944
- }
31945
- /**
31946
- * Extracts the radicand from a square root expression
31947
- */
31948
- function getRootOperand(node) {
31949
- if (isRootNode(node)) {
31950
- return node.getOperand();
31951
- }
31952
- if (isUnaryOpNode(node) && node.getOp() === 'sqrt') {
31953
- return node.getOperand();
31954
- }
31955
- throw new Error('Not a root node');
31956
- }
31957
- /**
31958
- * Checks if a node represents a square root expression
31959
- */
31960
- function isRootLikeNode(node) {
31961
- return isRootNode(node)
31962
- || (isUnaryOpNode(node) && node.getOp() === 'sqrt');
31963
- }
31964
- /**
31965
- * Specialized node for square roots for nicer formatting
31966
- */
31967
- class RootNode extends ExprNode {
31968
- constructor(operand) {
31969
- super();
31970
- Object.defineProperty(this, "operand", {
31971
- enumerable: true,
31972
- configurable: true,
31973
- writable: true,
31974
- value: operand
31975
- });
31976
- Object.defineProperty(this, "type", {
31977
- enumerable: true,
31978
- configurable: true,
31979
- writable: true,
31980
- value: 'Root'
31981
- });
31982
- }
31983
- evaluate() {
31984
- return Math.sqrt(this.operand.evaluate());
31985
- }
31986
- toString() {
31987
- if (CONFIG.spaceSeparation) {
31988
- const operandStr = this.operand.toString();
31989
- if (!isNumberNode(this.operand) || operandStr.includes('/')) {
31990
- return `√(${operandStr})`;
31991
- }
31992
- else {
31993
- return `√${operandStr}`;
31994
- }
31995
- }
31996
- else {
31997
- return `√${this.operand.toString()}`;
31998
- }
31999
- }
32000
- simplify() {
32001
- const operand = this.operand.simplify();
32002
- // Simplify sqrt of perfect squares
32003
- if (isNumberNode(operand)) {
32004
- const val = operand.evaluate();
32005
- const sqrtVal = Math.sqrt(val);
32006
- // If it's a perfect square, return the number
32007
- if (Number.isInteger(sqrtVal)) {
32008
- return new NumberNode(sqrtVal);
32009
- }
32010
- // Otherwise, check if we can simplify the radicand
32011
- const factorized = factorizeRadicand(val);
32012
- if (factorized.coefficient > 1) {
32013
- // If there's a perfect square factor, extract it
32014
- return new BinaryOpNode('*', new NumberNode(factorized.coefficient), new RootNode(new NumberNode(factorized.radicand)));
32015
- }
32016
- }
32017
- // Handle product of radicands under a root
32018
- if (isBinaryOpNode(operand) && operand.getOp() === '*') {
32019
- const left = operand.getLeft();
32020
- const right = operand.getRight();
32021
- // We can't simplify if they aren't both numbers
32022
- if (isNumberNode(left) && isNumberNode(right)) {
32023
- const leftVal = left.evaluate();
32024
- const rightVal = right.evaluate();
32025
- // Try to factorize the product
32026
- const factorized = factorizeRadicand(leftVal * rightVal);
32027
- if (factorized.coefficient > 1) {
32028
- // If there's a perfect square factor, extract it
32029
- return new BinaryOpNode('*', new NumberNode(factorized.coefficient), new RootNode(new NumberNode(factorized.radicand))).simplify();
32030
- }
32031
- }
32032
- }
32033
- return new RootNode(operand);
32034
- }
32035
- getOperand() {
32036
- return this.operand;
32037
- }
32038
- equals(other) {
32039
- if (!(isRootLikeNode(other)))
32040
- return false;
32041
- return this.operand.equals(other.getOperand());
32042
- }
32043
- }
32044
- /**
32045
- * Specialized node for power expressions
32046
- */
32047
- class PowerNode extends ExprNode {
32048
- constructor(base, exponent) {
32049
- super();
32050
- Object.defineProperty(this, "base", {
32051
- enumerable: true,
32052
- configurable: true,
32053
- writable: true,
32054
- value: base
32055
- });
32056
- Object.defineProperty(this, "exponent", {
32057
- enumerable: true,
32058
- configurable: true,
32059
- writable: true,
32060
- value: exponent
32061
- });
32062
- Object.defineProperty(this, "type", {
32063
- enumerable: true,
32064
- configurable: true,
32065
- writable: true,
32066
- value: 'Power'
32067
- });
32068
- }
32069
- evaluate() {
32070
- return this.base.evaluate() ** this.exponent.evaluate();
32071
- }
32072
- toString() {
32073
- const baseStr = this.base.toString();
32074
- const expVal = this.exponent.evaluate();
32075
- // Use superscript for powers 2 and 3
32076
- if (expVal === 2)
32077
- return `${baseStr}²`;
32078
- if (expVal === 3)
32079
- return `${baseStr}³`;
32080
- return CONFIG.spaceSeparation ? `${baseStr} ^ ${this.exponent.toString()}` : `${baseStr}^${this.exponent.toString()}`;
32081
- }
32082
- simplify() {
32083
- const base = this.base.simplify();
32084
- const exponent = this.exponent.simplify();
32085
- // Anything to the power of 0 is 1
32086
- if (isNumberNode(exponent) && exponent.evaluate() === 0) {
32087
- return new NumberNode(1);
32088
- }
32089
- // Anything to the power of 1 is itself
32090
- if (isNumberNode(exponent) && exponent.evaluate() === 1) {
32091
- return base;
32092
- }
32093
- // 0 to any positive power is 0
32094
- if (isNumberNode(base) && base.evaluate() === 0
32095
- && isNumberNode(exponent) && exponent.evaluate() > 0) {
32096
- return new NumberNode(0);
32097
- }
32098
- // 1 to any power is 1
32099
- if (isNumberNode(base) && base.evaluate() === 1) {
32100
- return new NumberNode(1);
32101
- }
32102
- // If both are numbers, compute the power
32103
- if (isNumberNode(base) && isNumberNode(exponent)) {
32104
- return new NumberNode(base.evaluate() ** exponent.evaluate());
32105
- }
32106
- return new PowerNode(base, exponent);
32107
- }
32108
- equals(other) {
32109
- if (!(isPowerNode(other)))
32110
- return false;
32111
- const otherPower = other;
32112
- return this.base.equals(otherPower.base)
32113
- && this.exponent.equals(otherPower.exponent);
32114
- }
32115
- getBase() {
32116
- return this.base;
32117
- }
32118
- getExponent() {
32119
- return this.exponent;
32120
- }
32121
- }
32122
- /**
32123
- * Node for unary operations (-, sqrt, cbrt, etc.)
32124
- */
32125
- class UnaryOpNode extends ExprNode {
32126
- constructor(op, operand) {
32127
- super();
32128
- Object.defineProperty(this, "op", {
32129
- enumerable: true,
32130
- configurable: true,
32131
- writable: true,
32132
- value: op
32133
- });
32134
- Object.defineProperty(this, "operand", {
32135
- enumerable: true,
32136
- configurable: true,
32137
- writable: true,
32138
- value: operand
32139
- });
32140
- Object.defineProperty(this, "type", {
32141
- enumerable: true,
32142
- configurable: true,
32143
- writable: true,
32144
- value: 'UnaryOp'
32145
- });
32146
- }
32147
- evaluate() {
32148
- const val = this.operand.evaluate();
32149
- switch (this.op) {
32150
- case '-': return -val;
32151
- case 'sqrt': return Math.sqrt(val);
32152
- case 'cbrt': return Math.cbrt(val);
32153
- }
32154
- }
32155
- toString() {
32156
- const operandStr = this.operand.toString();
32157
- if (operandStr === '0') {
32158
- return '0';
32159
- }
32160
- switch (this.op) {
32161
- case '-': return CONFIG.spaceSeparation && (!isNumberNode(this.operand) || operandStr.includes('/')) ? `-(${operandStr})` : `-${operandStr}`;
32162
- case 'sqrt': return CONFIG.spaceSeparation ? `√(${operandStr})` : `√${operandStr}`;
32163
- case 'cbrt': return CONFIG.spaceSeparation ? `∛(${operandStr})` : `∛${operandStr}`;
32164
- }
32165
- }
32166
- simplify() {
32167
- const operand = this.operand.simplify();
32168
- // Double negation: --a = a
32169
- if (this.op === '-' && isUnaryOpNode(operand)
32170
- && operand.getOp() === '-') {
32171
- return operand.getOperand();
32172
- }
32173
- // Simplify sqrt of perfect squares
32174
- if (this.op === 'sqrt' && isNumberNode(operand)) {
32175
- const val = operand.evaluate();
32176
- const sqrtVal = Math.sqrt(val);
32177
- if (Number.isInteger(sqrtVal)) {
32178
- return new NumberNode(sqrtVal);
32179
- }
32180
- }
32181
- // Simplify cbrt of perfect cubes
32182
- if (this.op === 'cbrt' && isNumberNode(operand)) {
32183
- const val = operand.evaluate();
32184
- const cbrtVal = Math.cbrt(val);
32185
- if (Number.isInteger(cbrtVal)) {
32186
- return new NumberNode(cbrtVal);
32187
- }
32188
- }
32189
- return new UnaryOpNode(this.op, operand);
32190
- }
32191
- getOp() {
32192
- return this.op;
32193
- }
32194
- getOperand() {
32195
- return this.operand;
32196
- }
32197
- equals(other) {
32198
- if (!(isUnaryOpNode(other)))
32199
- return false;
32200
- const otherOp = other;
32201
- return this.op === otherOp.getOp()
32202
- && this.operand.equals(otherOp.getOperand());
32203
- }
32204
- }
32205
-
32206
- /**
32207
- * Node for mathematical constants (π, e, etc.)
32208
- */
32209
- class ConstantNode extends ExprNode {
32210
- constructor(symbol, value) {
32211
- super();
32212
- Object.defineProperty(this, "symbol", {
32213
- enumerable: true,
32214
- configurable: true,
32215
- writable: true,
32216
- value: symbol
32217
- });
32218
- Object.defineProperty(this, "value", {
32219
- enumerable: true,
32220
- configurable: true,
32221
- writable: true,
32222
- value: value
32223
- });
32224
- Object.defineProperty(this, "type", {
32225
- enumerable: true,
32226
- configurable: true,
32227
- writable: true,
32228
- value: 'Constant'
32229
- });
32230
- }
32231
- evaluate() {
32232
- return this.value;
32233
- }
32234
- toString() {
32235
- return this.symbol;
32236
- }
32237
- simplify() {
32238
- return this; // Constants are already in simplest form
32239
- }
32240
- equals(other) {
32241
- if (!(isConstantNode(other)))
32242
- return false;
32243
- return this.symbol === other.symbol;
32244
- }
32245
- getSymbol() {
32246
- return this.symbol;
32247
- }
32248
- }
32249
-
32250
- /**
32251
- * Converts a number to its continued fraction representation
32252
- * @param x The number to convert
32253
- * @param maxTerms Maximum number of terms to compute
32254
- * @returns Array of continued fraction terms [a0, a1, a2, ...]
32255
- */
32256
- function toContinuedFraction(x, maxTerms = 20) {
32257
- const terms = [];
32258
- for (let i = 0; i < maxTerms; i++) {
32259
- const a = Math.floor(x);
32260
- terms.push(a);
32261
- // If we've reached a very small remainder, we're done
32262
- if (Math.abs(x - a) < CONFIG.epsilon) {
32263
- break;
32264
- }
32265
- // Prevent division by zero and avoid floating point issues
32266
- const remainder = x - a;
32267
- if (Math.abs(remainder) < CONFIG.epsilon) {
32268
- break;
32269
- }
32270
- x = 1 / remainder;
32271
- }
32272
- return terms;
32273
- }
32274
- /**
32275
- * Converts continued fraction terms to a fraction [numerator, denominator]
32276
- * @param terms Array of continued fraction terms
32277
- * @returns [numerator, denominator]
32278
- */
32279
- function fromContinuedFraction(terms) {
32280
- if (terms.length === 0)
32281
- return [0, 1];
32282
- if (terms.length === 1)
32283
- return [terms[0], 1];
32284
- // Start with the last term
32285
- let numerator = terms[terms.length - 1];
32286
- let denominator = 1;
32287
- // Work backwards through the terms
32288
- for (let i = terms.length - 2; i >= 0; i--) {
32289
- // For each term, compute the new fraction
32290
- // a_i + 1/previous
32291
- const newNumerator = terms[i] * numerator + denominator;
32292
- const newDenominator = numerator;
32293
- // Update for next iteration
32294
- numerator = newNumerator;
32295
- denominator = newDenominator;
32296
- }
32297
- return [numerator, denominator];
32298
- }
32299
- /**
32300
- * Generates all convergents (approximations) from continued fraction terms
32301
- * @param terms Array of continued fraction terms
32302
- * @returns Array of [numerator, denominator] pairs
32303
- */
32304
- function getConvergents(terms) {
32305
- const convergents = [];
32306
- for (let i = 1; i <= terms.length; i++) {
32307
- convergents.push(fromContinuedFraction(terms.slice(0, i)));
32308
- }
32309
- return convergents;
32310
- }
32311
- /**
32312
- * Tries to identify if a number is a simple quadratic irrational
32313
- * @param x The number to check
32314
- * @returns The identified form or null if not recognized
32315
- */
32316
- function identifyQuadraticIrrational(x) {
32317
- // Direct checks for common values
32318
- // Check for square roots of integers
32319
- for (let n = 2; n <= 20; n++) {
32320
- if (Math.abs(x - Math.sqrt(n)) < CONFIG.epsilon) {
32321
- return `√${n}`;
32322
- }
32323
- }
32324
- // Check for golden ratio and its conjugate
32325
- if (Math.abs(x - (1 + Math.sqrt(5)) / 2) < CONFIG.epsilon) {
32326
- return "φ"; // Already defined as a constant
32327
- }
32328
- if (Math.abs(x - (Math.sqrt(5) - 1) / 2) < CONFIG.epsilon) {
32329
- return "(√5-1)/2";
32330
- }
32331
- // Check for expressions of form (√n ± m)/k where n, m, k are small integers
32332
- for (let n = 2; n <= 30; n++) {
32333
- const sqrtN = Math.sqrt(n);
32334
- for (let m = 1; m <= 10; m++) {
32335
- for (let k = 2; k <= 10; k++) {
32336
- // Check (√n + m)/k
32337
- if (Math.abs(x - (sqrtN + m) / k) < CONFIG.epsilon) {
32338
- if (m === 1 && k === 2) {
32339
- return `(√${n}+1)/2`;
32340
- }
32341
- else {
32342
- return `(√${n}+${m})/${k}`;
32343
- }
32344
- }
32345
- // Check (√n - m)/k
32346
- if (Math.abs(x - (sqrtN - m) / k) < CONFIG.epsilon) {
32347
- if (m === 1 && k === 2) {
32348
- return `(√${n}-1)/2`;
32349
- }
32350
- else {
32351
- return `(√${n}-${m})/${k}`;
32352
- }
32353
- }
32354
- }
32355
- }
32356
- }
32357
- // Get continued fraction for pattern analysis
32358
- const terms = toContinuedFraction(x, 30);
32359
- // Analyze the continued fraction for periodic patterns
32360
- const period = detectPeriod(terms);
32361
- if (period) {
32362
- // For purely periodic CFs (√D): [a0; a1, a2, ..., aN, a1, a2, ...]
32363
- // where a0 = floor(√D) and the sequence after a0 is periodic
32364
- // For √2: [1; 2, 2, 2, ...]
32365
- if (period.length === 1 && period[0] === 2 && terms[0] === 1) {
32366
- return "√2";
32367
- }
32368
- // For √3: [1; 1, 2, 1, 2, ...]
32369
- if (period.length === 2 && period[0] === 1 && period[1] === 2 && terms[0] === 1) {
32370
- return "√3";
32371
- }
32372
- // For √5: [2; 4, 4, 4, ...]
32373
- if (period.length === 1 && period[0] === 4 && terms[0] === 2) {
32374
- return "√5";
32375
- }
32376
- // For √6: [2; 2, 4, 2, 4, ...]
32377
- if (period.length === 2 && period[0] === 2 && period[1] === 4 && terms[0] === 2) {
32378
- return "√6";
32379
- }
32380
- // For √7: [2; 1, 1, 1, 4, 1, 1, 1, 4, ...]
32381
- if (period.length === 4 &&
32382
- period[0] === 1 && period[1] === 1 &&
32383
- period[2] === 1 && period[3] === 4 &&
32384
- terms[0] === 2) {
32385
- return "√7";
32386
- }
32387
- // For √8: [2; 1, 4, 1, 4, ...]
32388
- if (period.length === 2 && period[0] === 1 && period[1] === 4 && terms[0] === 2) {
32389
- return "√8";
32390
- }
32391
- // Try to reconstruct the quadratic form from the pattern
32392
- // (Complex algorithm that would determine D from periodic CF)
32393
- const D = reconstructD(terms[0], period);
32394
- if (D > 0 && Math.abs(x - Math.sqrt(D)) < CONFIG.epsilon) {
32395
- return `√${D}`;
32396
- }
32397
- }
32398
- return null; // Could not identify
32399
- }
32400
- // Helper function to detect periodic patterns in continued fractions
32401
- function detectPeriod(terms) {
32402
- // Need at least a few terms to detect patterns
32403
- if (terms.length < 6)
32404
- return null;
32405
- // Check for single repeating digit (like √2 = [1; 2, 2, 2, ...])
32406
- const allSameAfterFirst = terms.slice(1).every(t => t === terms[1]);
32407
- if (allSameAfterFirst) {
32408
- return [terms[1]];
32409
- }
32410
- // For period of length 2
32411
- const periodLength2 = terms.slice(1, 3).every((val, idx) => {
32412
- for (let i = 1; i < Math.floor((terms.length - 1) / 2); i++) {
32413
- if (terms[1 + idx + 2 * i] !== val)
32414
- return false;
32415
- }
32416
- return true;
32417
- });
32418
- if (periodLength2) {
32419
- return terms.slice(1, 3);
32420
- }
32421
- // For period of length 4
32422
- if (terms.length >= 9) {
32423
- const periodLength4 = terms.slice(1, 5).every((val, idx) => {
32424
- return terms[1 + idx + 4] === val && (terms.length >= 13 ? terms[1 + idx + 8] === val : true);
32425
- });
32426
- if (periodLength4) {
32427
- return terms.slice(1, 5);
32428
- }
32429
- }
32430
- // Could add more pattern detection logic here
32431
- return null;
32432
- }
32433
- // Helper function to reconstruct D from continued fraction pattern
32434
- function reconstructD(a0, period) {
32435
- // Simple cases
32436
- if (period.length === 1 && period[0] === 2)
32437
- return 2;
32438
- if (period.length === 2 && period[0] === 1 && period[1] === 2)
32439
- return 3;
32440
- if (period.length === 1 && period[0] === 4)
32441
- return 5;
32442
- if (period.length === 2 && period[0] === 2 && period[1] === 4)
32443
- return 6;
32444
- if (period.length === 4 &&
32445
- period[0] === 1 && period[1] === 1 &&
32446
- period[2] === 1 && period[3] === 4)
32447
- return 7;
32448
- if (period.length === 2 && period[0] === 1 && period[1] === 4)
32449
- return 8;
32450
- // For purely periodic CFs, D = a0² + P where P depends on the period
32451
- // This is a simplified heuristic approach
32452
- if (period.length === 1) {
32453
- // If period has form [n], then D ≈ a0² + 1/n
32454
- return Math.round(a0 * a0 + 1 / period[0]);
32455
- }
32456
- // Generalized approach for reconstructing D would be more complex
32457
- // and would involve solving the Pell equation
32458
- return -1; // Couldn't determine D
32459
- }
32460
-
32461
- /**
32462
- * Parser to convert numeric values to expression trees
32463
- */
32464
- class ExpressionParser {
32465
- /**
32466
- * Main parsing function that converts a number to an expression tree
32467
- */
32468
- parseNumber(num, depth = 0) {
32469
- const MAX_DEPTH = 3;
32470
- // Handle zero
32471
- if (Math.abs(num) < CONFIG.epsilon) {
32472
- return new NumberNode(0);
32473
- }
32474
- // Handle negative numbers
32475
- if (num < 0) {
32476
- return new UnaryOpNode('-', this.parseNumber(-num, depth));
32477
- }
32478
- // Handle integers
32479
- if (Math.abs(num - Math.round(num)) < CONFIG.epsilon) {
32480
- return new NumberNode(Math.round(num));
32481
- }
32482
- const cfTerms = toContinuedFraction(num, 20);
32483
- const convergents = getConvergents(cfTerms);
32484
- for (const [numerator, denominator] of convergents) {
32485
- // Only consider fractions with reasonably-sized components
32486
- if (denominator <= 1000 && Math.abs(numerator) <= 10000) {
32487
- // Verify the approximation is within our epsilon
32488
- if (Math.abs(num - numerator / denominator) < CONFIG.epsilon) {
32489
- const gcd = this.findGCD(Math.abs(numerator), denominator);
32490
- return new BinaryOpNode('/', new NumberNode(numerator / gcd), new NumberNode(denominator / gcd));
32491
- }
32492
- }
32493
- }
32494
- // Try direct matches to constants
32495
- for (const constant of CONSTANTS) {
32496
- if (Math.abs(num - constant.value) < CONFIG.epsilon) {
32497
- return new ConstantNode(constant.getSymbol(), constant.value);
32498
- }
32499
- }
32500
- // Try direct matches to trig values
32501
- for (const trigValue of TRIG_VALUES) {
32502
- if (Math.abs(num - trigValue.value) < CONFIG.epsilon) {
32503
- // If it has an exact form, parse that instead
32504
- if (trigValue.getExactForm) {
32505
- return this.parseExactForm(trigValue.getExactForm());
32506
- }
32507
- return new ConstantNode(trigValue.getSymbol(), trigValue.value);
32508
- }
32509
- }
32510
- const quadraticForm = identifyQuadraticIrrational(num);
32511
- if (quadraticForm) {
32512
- // If we identified something like "√7" or "(√13-3)/2"
32513
- return this.parseExactForm(quadraticForm);
32514
- }
32515
- // Direct check for square roots before other decompositions
32516
- // This ensures square roots of integers are handled directly
32517
- for (let i = 2; i <= 100; i++) {
32518
- if (Math.abs(num - Math.sqrt(i)) < CONFIG.epsilon) {
32519
- // Use our enhanced check to determine if we should keep direct form
32520
- if (shouldPreserveDirectRadical(i)) {
32521
- return new RootNode(new NumberNode(i));
32522
- }
32523
- // Only factorize if appropriate
32524
- const factorized = factorizeRadicand(i);
32525
- if (factorized.coefficient > 1) {
32526
- return new BinaryOpNode('*', new NumberNode(factorized.coefficient), new RootNode(new NumberNode(factorized.radicand)));
32527
- }
32528
- // Default to direct representation
32529
- return new RootNode(new NumberNode(i));
32530
- }
32531
- }
32532
- // Check for powers
32533
- for (const constant of [...CONSTANTS, ...TRIG_VALUES]) {
32534
- for (let power = 2; power <= 3; power++) {
32535
- if (Math.abs(num - constant.value ** power) < CONFIG.epsilon) {
32536
- const baseNode = constant.getExactForm
32537
- ? this.parseExactForm(constant.getExactForm())
32538
- : new ConstantNode(constant.getSymbol(), constant.value);
32539
- return new PowerNode(baseNode, new NumberNode(power));
32540
- }
32541
- }
32542
- }
32543
- // Recursive decomposition with extra care for square roots
32544
- if (depth < MAX_DEPTH) {
32545
- // Check for π/n pattern
32546
- for (const constant of CONSTANTS) {
32547
- for (let denominator = 2; denominator <= 12; denominator++) {
32548
- if (Math.abs(num - constant.value / denominator) < CONFIG.epsilon) {
32549
- return new BinaryOpNode('/', constant.getExactForm
32550
- ? this.parseExactForm(constant.getExactForm())
32551
- : new ConstantNode(constant.getSymbol(), constant.value), new NumberNode(denominator));
32552
- }
32553
- }
32554
- }
32555
- // Check for products with more careful handling of square roots
32556
- for (const constant of [...CONSTANTS, ...TRIG_VALUES]) {
32557
- if (Math.abs(constant.value) > CONFIG.epsilon) {
32558
- const multiplier = num / constant.value;
32559
- // Skip square root decomposition attempts for prime square roots
32560
- if (constant.getSymbol() && constant.getSymbol().startsWith('√')
32561
- && isPrime(Number.parseInt(constant.getSymbol(true).substring(1)))) {
32562
- continue;
32563
- }
32564
- // Skip common fractions that might be misidentified as complex expressions
32565
- if ((Math.abs(multiplier - 0.75) < CONFIG.epsilon)
32566
- || (Math.abs(multiplier - 0.5) < CONFIG.epsilon)
32567
- || (Math.abs(multiplier - 0.25) < CONFIG.epsilon)
32568
- || (Math.abs(multiplier - 0.3333333333333333) < CONFIG.epsilon)
32569
- || (Math.abs(multiplier - 0.6666666666666666) < CONFIG.epsilon)) {
32570
- continue;
32571
- }
32572
- if (this.isNiceValue(multiplier)) {
32573
- const multiplierNode = this.parseNumber(multiplier, depth + 1);
32574
- const valueNode = constant.getExactForm
32575
- ? this.parseExactForm(constant.getExactForm())
32576
- : new ConstantNode(constant.getSymbol(), constant.value);
32577
- // If multiplier is 1, just return the constant
32578
- if (isNumberNode(multiplierNode)
32579
- && Math.abs(multiplierNode.evaluate() - 1) < CONFIG.epsilon) {
32580
- return valueNode;
32581
- }
32582
- // Create the product node with canonical ordering
32583
- let productNode;
32584
- // Define a priority order for constants
32585
- // Lower number = higher priority (should appear first)
32586
- const getConstantPriority = (node) => {
32587
- if (!(isConstantNode(node)))
32588
- return 100;
32589
- const symbol = node.getSymbol();
32590
- if (symbol === 'e')
32591
- return 1;
32592
- if (symbol === 'π')
32593
- return 2;
32594
- if (symbol === 'φ')
32595
- return 3;
32596
- if (symbol.startsWith('√'))
32597
- return 10;
32598
- return 50; // Other constants
32599
- };
32600
- // Apply the priority ordering
32601
- if (isConstantNode(multiplierNode) && isConstantNode(valueNode)) {
32602
- // When multiplying two constants, order them by priority
32603
- const multiplierPriority = getConstantPriority(multiplierNode);
32604
- const valuePriority = getConstantPriority(valueNode);
32605
- if (multiplierPriority <= valuePriority) {
32606
- productNode = new BinaryOpNode('*', multiplierNode, valueNode);
32607
- }
32608
- else {
32609
- productNode = new BinaryOpNode('*', valueNode, multiplierNode);
32610
- }
32611
- }
32612
- else if (isNumberNode(multiplierNode)) {
32613
- // Number multiplier always goes first
32614
- productNode = new BinaryOpNode('*', multiplierNode, valueNode);
32615
- }
32616
- else if (getConstantPriority(valueNode) < 50) {
32617
- // Special constants (e, π, φ) go before other expressions
32618
- productNode = new BinaryOpNode('*', valueNode, multiplierNode);
32619
- }
32620
- else {
32621
- // Default ordering
32622
- productNode = new BinaryOpNode('*', multiplierNode, valueNode);
32623
- }
32624
- // Check if this product is part of a sum
32625
- // Try to see if the product plus a nice value equals our number
32626
- for (let addend = 1; addend <= 10; addend++) {
32627
- const productValue = productNode.evaluate();
32628
- const sum = productValue + addend;
32629
- if (Math.abs(num - sum) < CONFIG.epsilon) {
32630
- return new BinaryOpNode('+', productNode, new NumberNode(addend));
32631
- }
32632
- }
32633
- return productNode;
32634
- }
32635
- }
32636
- }
32637
- // Check for sums and differences with base constants
32638
- for (const constant of [...CONSTANTS, ...TRIG_VALUES]) {
32639
- const remainder = num - constant.value;
32640
- if (this.isNiceValue(remainder)) {
32641
- const remainderNode = this.parseNumber(remainder, depth + 1);
32642
- const valueNode = constant.getExactForm
32643
- ? this.parseExactForm(constant.getExactForm())
32644
- : new ConstantNode(constant.getSymbol(), constant.value);
32645
- // If remainder is 0, just return the constant
32646
- if (isNumberNode(remainderNode)
32647
- && Math.abs(remainderNode.evaluate()) < CONFIG.epsilon) {
32648
- return valueNode;
32649
- }
32650
- // If remainder is negative, use subtraction
32651
- if (remainder < 0) {
32652
- return new BinaryOpNode('-', valueNode, this.parseNumber(-remainder, depth + 1));
32653
- }
32654
- return new BinaryOpNode('+', valueNode, remainderNode);
32655
- }
32656
- }
32657
- // Check for sums and differences with multiples of constants
32658
- for (const constant of [...CONSTANTS, ...TRIG_VALUES]) {
32659
- for (let multiplier = 2; multiplier <= 5; multiplier++) {
32660
- const multipleValue = multiplier * constant.value;
32661
- const remainder = num - multipleValue;
32662
- if (this.isNiceValue(remainder)) {
32663
- // Create the multiple constant node
32664
- const constantNode = constant.getExactForm
32665
- ? this.parseExactForm(constant.getExactForm())
32666
- : new ConstantNode(constant.getSymbol(), constant.value);
32667
- const multipleNode = new BinaryOpNode('*', new NumberNode(multiplier), constantNode);
32668
- // Create the remainder node
32669
- const remainderNode = this.parseNumber(remainder, depth + 1);
32670
- // If remainder is 0, just return the multiple
32671
- if (isNumberNode(remainderNode)
32672
- && Math.abs(remainderNode.evaluate()) < CONFIG.epsilon) {
32673
- return multipleNode;
32674
- }
32675
- // If remainder is negative, use subtraction
32676
- if (remainder < 0) {
32677
- return new BinaryOpNode('-', multipleNode, this.parseNumber(-remainder, depth + 1));
32678
- }
32679
- return new BinaryOpNode('+', multipleNode, remainderNode);
32680
- }
32681
- }
32682
- }
32683
- // Check for cube roots of integers
32684
- for (let i = 2; i <= 100; i++) {
32685
- if (Math.abs(num - Math.cbrt(i)) < CONFIG.epsilon) {
32686
- return new UnaryOpNode('cbrt', new NumberNode(i));
32687
- }
32688
- }
32689
- }
32690
- // Final direct check for square roots before giving up
32691
- // This ensures we catch any square roots we might have missed
32692
- const possibleRadicand = Math.round(num * num);
32693
- if (Math.abs(num - Math.sqrt(possibleRadicand)) < CONFIG.epsilon) {
32694
- return new RootNode(new NumberNode(possibleRadicand));
32695
- }
32696
- // Fallback to decimal representation
32697
- return new NumberNode(num);
32698
- }
32699
- /**
32700
- * Helper method to parse exact forms like "√2/2"
32701
- */
32702
- parseExactForm(exactForm) {
32703
- // Handle fractions
32704
- if (exactForm.includes('/')) {
32705
- const [numerator, denominator] = exactForm.split('/');
32706
- return new BinaryOpNode('/', this.parseExactForm(numerator), this.parseExactForm(denominator));
32707
- }
32708
- // Handle square roots
32709
- if (exactForm.startsWith('√')) {
32710
- const argument = exactForm.substring(1);
32711
- return new RootNode(this.parseExactForm(argument));
32712
- }
32713
- // Handle simple numbers
32714
- if (/^\d+$/.test(exactForm)) {
32715
- return new NumberNode(Number.parseInt(exactForm));
32716
- }
32717
- // Handle known constants
32718
- for (const constant of CONSTANTS) {
32719
- if (exactForm === constant.getSymbol()) {
32720
- return new ConstantNode(constant.getSymbol(), constant.value);
32721
- }
32722
- }
32723
- // If we don't recognize it, treat it as a symbol
32724
- return new ConstantNode(exactForm, Number.NaN);
32725
- }
32726
- /**
32727
- * Helper to determine if a value is "nice"
32728
- */
32729
- isNiceValue(num) {
32730
- // Check if it's close to an integer
32731
- if (Math.abs(num - Math.round(num)) < CONFIG.epsilon) {
32732
- return true;
32733
- }
32734
- const cfTerms = toContinuedFraction(num, 10);
32735
- const convergents = getConvergents(cfTerms);
32736
- // Check if any convergent is a good approximation and reasonably simple
32737
- for (const [numerator, denominator] of convergents) {
32738
- // Only consider "nice" fractions (reasonable size numerator/denominator)
32739
- if (denominator <= 20 && Math.abs(numerator) <= 50) {
32740
- if (Math.abs(num - numerator / denominator) < CONFIG.epsilon) {
32741
- return true;
32742
- }
32743
- }
32744
- }
32745
- // Check if it's a square root of a small integer (highest priority)
32746
- // This helps prefer direct √n forms
32747
- for (let i = 2; i <= 30; i++) {
32748
- if (Math.abs(num - Math.sqrt(i)) < CONFIG.epsilon) {
32749
- // Give very high priority to prime square roots
32750
- if (isPrime(i)) {
32751
- return true;
32752
- }
32753
- // Also prioritize other small square roots
32754
- if (i <= 20) {
32755
- return true;
32756
- }
32757
- }
32758
- }
32759
- // Check if it's close to a common constant
32760
- for (const constant of CONSTANTS) {
32761
- if (Math.abs(num - constant.value) < CONFIG.epsilon) {
32762
- return true;
32763
- }
32764
- }
32765
- // Check trig values
32766
- for (const value of TRIG_VALUES) {
32767
- if (Math.abs(num - value.value) < CONFIG.epsilon) {
32768
- return true;
32769
- }
32770
- }
32771
- // Other common values with lower priority
32772
- const COMMON_VALUES = [
32773
- Math.sin(Math.PI / 3),
32774
- Math.cos(Math.PI / 4),
32775
- ];
32776
- for (const value of COMMON_VALUES) {
32777
- if (Math.abs(num - value) < CONFIG.epsilon) {
32778
- return true;
32779
- }
32780
- }
32781
- return false;
32782
- }
32783
- /**
32784
- * Find greatest common divisor helper method
32785
- */
32786
- findGCD(a, b) {
32787
- a = Math.abs(a);
32788
- b = Math.abs(b);
32789
- while (b !== 0) {
32790
- const temp = b;
32791
- b = a % b;
32792
- a = temp;
32793
- }
32794
- return a;
32795
- }
32796
- }
32797
-
32798
- /**
32799
- * Symbolic Number Printer using Expression Tree Representation
32800
- *
32801
- * This implementation uses a proper expression tree to represent mathematical
32802
- * expressions, enabling more sophisticated simplification and consistent formatting.
32803
- */
32804
- /**
32805
- * Main function to convert a number to its symbolic representation
32806
- * @param num The number to convert to symbolic form
32807
- * @returns A string containing the symbolic representation
32808
- */
32809
- function prettyPi(num, config = {}) {
32810
- if (!isFinite(num)) {
32811
- if (isNaN(num)) {
32812
- return "NaN";
32813
- }
32814
- return num > 0 ? "∞" : "-∞";
32815
- }
32816
- setConfig(config);
32817
- const parser = new ExpressionParser();
32818
- const exprTree = parser.parseNumber(num);
32819
- const simplified = exprTree.simplify();
32820
- return simplified.toString();
32821
- }
32822
-
32823
31325
  function stringifyValue(value, html) {
32824
31326
  var _a;
32825
31327
  var gt = '>';
@@ -32837,7 +31339,7 @@ function stringifyValue(value, html) {
32837
31339
  if (typeof value === 'object' && value instanceof RegExp)
32838
31340
  return "".concat(value);
32839
31341
  if (typeof value === 'number') {
32840
- return prettyPi(value);
31342
+ return "".concat(value);
32841
31343
  }
32842
31344
  if (isRegularExpression(value))
32843
31345
  return "/".concat(value.s, "/").concat(value.f);
@@ -32850,12 +31352,12 @@ function stringifyValue(value, html) {
32850
31352
  return '[]';
32851
31353
  if (value.length > 8) {
32852
31354
  return "[\n ".concat(value.map(function (cell) {
32853
- return prettyPi(cell);
31355
+ return cell;
32854
31356
  }).join(',\n '), "\n]");
32855
31357
  }
32856
31358
  else {
32857
31359
  return "[".concat(value.map(function (cell) {
32858
- return prettyPi(cell);
31360
+ return cell;
32859
31361
  }).join(', '), "]");
32860
31362
  }
32861
31363
  }
@@ -32891,45 +31393,11 @@ function replaceInfinities(value) {
32891
31393
  }
32892
31394
  return value;
32893
31395
  }
32894
- function prettyIfNumber(value) {
32895
- if (typeof value === 'number') {
32896
- return prettyPi(value);
32897
- }
32898
- return "".concat(value);
32899
- }
32900
31396
  function stringifyMatrix(matrix) {
32901
- var padding = matrix.flat().reduce(function (max, cell) { return Math.max(max, prettyIfNumber(cell).length); }, 0) + 1;
32902
- var rows = matrix.map(function (row) { return "[".concat(row.map(function (cell) { return prettyIfNumber(cell).padStart(padding); }).join(' '), " ]"); });
31397
+ var padding = matrix.flat().reduce(function (max, cell) { return Math.max(max, "".concat(cell).length); }, 0) + 1;
31398
+ var rows = matrix.map(function (row) { return "[".concat(row.map(function (cell) { return "".concat(cell).padStart(padding); }).join(' '), " ]"); });
32903
31399
  return rows.join('\n');
32904
31400
  }
32905
- // function isMatrix(value: unknown): value is (null | number | string | boolean)[][] {
32906
- // if (!Array.isArray(value)) {
32907
- // return false
32908
- // }
32909
- // if (!value.every(row => Array.isArray(row))) {
32910
- // return false
32911
- // }
32912
- // let cols = -1
32913
- // for (const row of value) {
32914
- // if (cols === -1) {
32915
- // cols = row.length
32916
- // if (cols === 0) {
32917
- // return false
32918
- // }
32919
- // }
32920
- // else {
32921
- // if (row.length !== cols) {
32922
- // return false
32923
- // }
32924
- // }
32925
- // for (const cell of row) {
32926
- // if (typeof cell !== 'number' && typeof cell !== 'string' && typeof cell !== 'boolean' && cell !== null) {
32927
- // return false
32928
- // }
32929
- // }
32930
- // }
32931
- // return true
32932
- // }
32933
31401
  function findAllOccurrences(input, pattern) {
32934
31402
  var matches = __spreadArray([], __read(input.matchAll(pattern)), false);
32935
31403
  return new Set(matches.map(function (match) { return match[0]; }));