@weborigami/language 0.2.1 → 0.2.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/language",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Web Origami expression language compiler and runtime",
5
5
  "type": "module",
6
6
  "main": "./main.js",
@@ -12,8 +12,8 @@
12
12
  "yaml": "2.5.1"
13
13
  },
14
14
  "dependencies": {
15
- "@weborigami/async-tree": "0.2.1",
16
- "@weborigami/types": "0.2.1",
15
+ "@weborigami/async-tree": "0.2.2",
16
+ "@weborigami/types": "0.2.2",
17
17
  "watcher": "2.3.1"
18
18
  },
19
19
  "scripts": {
@@ -61,7 +61,7 @@ arrayEntries
61
61
  }
62
62
 
63
63
  arrayEntry
64
- = spread
64
+ = spreadElement
65
65
  / pipelineExpression
66
66
  // JavaScript treats a missing value as `undefined`
67
67
  / __ !"]" {
@@ -113,7 +113,7 @@ callExpression "function call"
113
113
  closingBrace
114
114
  = "}"
115
115
  / .? {
116
- error("Expected right curly brace");
116
+ error(`An object ended without a closing brace, or contained something that wasn't expected.\nThe top level of an object can only contain definitions ("a: b" or "a = b") or spreads ("...a").`);
117
117
  }
118
118
 
119
119
  // Required closing bracket
@@ -147,18 +147,20 @@ comment "comment"
147
147
  / singleLineComment
148
148
 
149
149
  conditionalExpression
150
- = condition:logicalOrExpression __
151
- "?" __ truthy:pipelineExpression __
152
- ":" __ falsy:pipelineExpression
150
+ = condition:logicalOrExpression tail:(__
151
+ "?" __ @pipelineExpression __
152
+ ":" __ @pipelineExpression)?
153
153
  {
154
+ if (!tail) {
155
+ return condition;
156
+ }
154
157
  return annotate([
155
158
  ops.conditional,
156
159
  downgradeReference(condition),
157
- [ops.lambda, [], downgradeReference(truthy)],
158
- [ops.lambda, [], downgradeReference(falsy)]
160
+ [ops.lambda, [], downgradeReference(tail[0])],
161
+ [ops.lambda, [], downgradeReference(tail[1])]
159
162
  ], location());
160
163
  }
161
- / logicalOrExpression
162
164
 
163
165
  digits
164
166
  = @[0-9]+
@@ -197,10 +199,9 @@ escapedChar "backslash-escaped character"
197
199
  / "\\" @.
198
200
 
199
201
  exponentiationExpression
200
- = left:unaryExpression __ "**" __ right:exponentiationExpression {
201
- return annotate([ops.exponentiation, left, right], location());
202
+ = left:unaryExpression right:(__ "**" __ @exponentiationExpression)? {
203
+ return right ? annotate([ops.exponentiation, left, right], location()) : left;
202
204
  }
203
- / unaryExpression
204
205
 
205
206
  // A top-level expression, possibly with leading/trailing whitespace
206
207
  expression
@@ -358,7 +359,7 @@ objectEntries
358
359
  }
359
360
 
360
361
  objectEntry
361
- = spread
362
+ = spreadElement
362
363
  / objectProperty
363
364
  / objectGetter
364
365
  / objectShorthandProperty
@@ -557,8 +558,8 @@ singleQuoteString "single quote string"
557
558
  singleQuoteStringChar
558
559
  = !("'" / newLine) @textChar
559
560
 
560
- spread
561
- = ellipsis __ value:conditionalExpression {
561
+ spreadElement
562
+ = ellipsis __ value:pipelineExpression {
562
563
  return annotate([ops.spread, value], location());
563
564
  }
564
565
 
@@ -202,7 +202,7 @@ function peg$parse(input, options) {
202
202
  var peg$FAILED = {};
203
203
  var peg$source = options.grammarSource;
204
204
 
205
- var peg$startRuleFunctions = { __: peg$parse__, additiveExpression: peg$parseadditiveExpression, additiveOperator: peg$parseadditiveOperator, arguments: peg$parsearguments, arrayLiteral: peg$parsearrayLiteral, arrayEntries: peg$parsearrayEntries, arrayEntry: peg$parsearrayEntry, arrowFunction: peg$parsearrowFunction, bitwiseAndExpression: peg$parsebitwiseAndExpression, bitwiseAndOperator: peg$parsebitwiseAndOperator, bitwiseOrExpression: peg$parsebitwiseOrExpression, bitwiseOrOperator: peg$parsebitwiseOrOperator, bitwiseXorExpression: peg$parsebitwiseXorExpression, bitwiseXorOperator: peg$parsebitwiseXorOperator, callExpression: peg$parsecallExpression, closingBrace: peg$parseclosingBrace, closingBracket: peg$parseclosingBracket, closingParenthesis: peg$parseclosingParenthesis, commaExpression: peg$parsecommaExpression, comment: peg$parsecomment, conditionalExpression: peg$parseconditionalExpression, digits: peg$parsedigits, doubleArrow: peg$parsedoubleArrow, doubleQuoteString: peg$parsedoubleQuoteString, doubleQuoteStringChar: peg$parsedoubleQuoteStringChar, ellipsis: peg$parseellipsis, equalityExpression: peg$parseequalityExpression, equalityOperator: peg$parseequalityOperator, escapedChar: peg$parseescapedChar, exponentiationExpression: peg$parseexponentiationExpression, expression: peg$parseexpression, floatLiteral: peg$parsefloatLiteral, group: peg$parsegroup, guillemetString: peg$parseguillemetString, guillemetStringChar: peg$parseguillemetStringChar, homeDirectory: peg$parsehomeDirectory, host: peg$parsehost, identifier: peg$parseidentifier, identifierChar: peg$parseidentifierChar, identifierList: peg$parseidentifierList, implicitParenthesesCallExpression: peg$parseimplicitParenthesesCallExpression, implicitParensthesesArguments: peg$parseimplicitParensthesesArguments, inlineSpace: peg$parseinlineSpace, integerLiteral: peg$parseintegerLiteral, list: peg$parselist, literal: peg$parseliteral, logicalAndExpression: peg$parselogicalAndExpression, logicalOrExpression: peg$parselogicalOrExpression, multiLineComment: peg$parsemultiLineComment, multiplicativeExpression: peg$parsemultiplicativeExpression, multiplicativeOperator: peg$parsemultiplicativeOperator, namespace: peg$parsenamespace, newLine: peg$parsenewLine, numericLiteral: peg$parsenumericLiteral, nullishCoalescingExpression: peg$parsenullishCoalescingExpression, objectLiteral: peg$parseobjectLiteral, objectEntries: peg$parseobjectEntries, objectEntry: peg$parseobjectEntry, objectGetter: peg$parseobjectGetter, objectHiddenKey: peg$parseobjectHiddenKey, objectKey: peg$parseobjectKey, objectProperty: peg$parseobjectProperty, objectShorthandProperty: peg$parseobjectShorthandProperty, objectPublicKey: peg$parseobjectPublicKey, parenthesesArguments: peg$parseparenthesesArguments, path: peg$parsepath, pathArguments: peg$parsepathArguments, pathKey: peg$parsepathKey, pathSegment: peg$parsepathSegment, pathSegmentChar: peg$parsepathSegmentChar, pipelineExpression: peg$parsepipelineExpression, primary: peg$parseprimary, program: peg$parseprogram, protocolExpression: peg$parseprotocolExpression, qualifiedReference: peg$parsequalifiedReference, reference: peg$parsereference, relationalExpression: peg$parserelationalExpression, relationalOperator: peg$parserelationalOperator, rootDirectory: peg$parserootDirectory, scopeReference: peg$parsescopeReference, separator: peg$parseseparator, slashFollows: peg$parseslashFollows, shebang: peg$parseshebang, shiftExpression: peg$parseshiftExpression, shiftOperator: peg$parseshiftOperator, shorthandFunction: peg$parseshorthandFunction, singleArrow: peg$parsesingleArrow, singleLineComment: peg$parsesingleLineComment, singleQuoteString: peg$parsesingleQuoteString, singleQuoteStringChar: peg$parsesingleQuoteStringChar, spread: peg$parsespread, stringLiteral: peg$parsestringLiteral, templateDocument: peg$parsetemplateDocument, templateDocumentChar: peg$parsetemplateDocumentChar, templateDocumentContents: peg$parsetemplateDocumentContents, templateDocumentText: peg$parsetemplateDocumentText, templateLiteral: peg$parsetemplateLiteral, templateLiteralChar: peg$parsetemplateLiteralChar, templateLiteralContents: peg$parsetemplateLiteralContents, templateLiteralText: peg$parsetemplateLiteralText, templateSubstitution: peg$parsetemplateSubstitution, textChar: peg$parsetextChar, unaryExpression: peg$parseunaryExpression, unaryOperator: peg$parseunaryOperator, whitespaceWithNewLine: peg$parsewhitespaceWithNewLine };
205
+ var peg$startRuleFunctions = { __: peg$parse__, additiveExpression: peg$parseadditiveExpression, additiveOperator: peg$parseadditiveOperator, arguments: peg$parsearguments, arrayLiteral: peg$parsearrayLiteral, arrayEntries: peg$parsearrayEntries, arrayEntry: peg$parsearrayEntry, arrowFunction: peg$parsearrowFunction, bitwiseAndExpression: peg$parsebitwiseAndExpression, bitwiseAndOperator: peg$parsebitwiseAndOperator, bitwiseOrExpression: peg$parsebitwiseOrExpression, bitwiseOrOperator: peg$parsebitwiseOrOperator, bitwiseXorExpression: peg$parsebitwiseXorExpression, bitwiseXorOperator: peg$parsebitwiseXorOperator, callExpression: peg$parsecallExpression, closingBrace: peg$parseclosingBrace, closingBracket: peg$parseclosingBracket, closingParenthesis: peg$parseclosingParenthesis, commaExpression: peg$parsecommaExpression, comment: peg$parsecomment, conditionalExpression: peg$parseconditionalExpression, digits: peg$parsedigits, doubleArrow: peg$parsedoubleArrow, doubleQuoteString: peg$parsedoubleQuoteString, doubleQuoteStringChar: peg$parsedoubleQuoteStringChar, ellipsis: peg$parseellipsis, equalityExpression: peg$parseequalityExpression, equalityOperator: peg$parseequalityOperator, escapedChar: peg$parseescapedChar, exponentiationExpression: peg$parseexponentiationExpression, expression: peg$parseexpression, floatLiteral: peg$parsefloatLiteral, group: peg$parsegroup, guillemetString: peg$parseguillemetString, guillemetStringChar: peg$parseguillemetStringChar, homeDirectory: peg$parsehomeDirectory, host: peg$parsehost, identifier: peg$parseidentifier, identifierChar: peg$parseidentifierChar, identifierList: peg$parseidentifierList, implicitParenthesesCallExpression: peg$parseimplicitParenthesesCallExpression, implicitParensthesesArguments: peg$parseimplicitParensthesesArguments, inlineSpace: peg$parseinlineSpace, integerLiteral: peg$parseintegerLiteral, list: peg$parselist, literal: peg$parseliteral, logicalAndExpression: peg$parselogicalAndExpression, logicalOrExpression: peg$parselogicalOrExpression, multiLineComment: peg$parsemultiLineComment, multiplicativeExpression: peg$parsemultiplicativeExpression, multiplicativeOperator: peg$parsemultiplicativeOperator, namespace: peg$parsenamespace, newLine: peg$parsenewLine, numericLiteral: peg$parsenumericLiteral, nullishCoalescingExpression: peg$parsenullishCoalescingExpression, objectLiteral: peg$parseobjectLiteral, objectEntries: peg$parseobjectEntries, objectEntry: peg$parseobjectEntry, objectGetter: peg$parseobjectGetter, objectHiddenKey: peg$parseobjectHiddenKey, objectKey: peg$parseobjectKey, objectProperty: peg$parseobjectProperty, objectShorthandProperty: peg$parseobjectShorthandProperty, objectPublicKey: peg$parseobjectPublicKey, parenthesesArguments: peg$parseparenthesesArguments, path: peg$parsepath, pathArguments: peg$parsepathArguments, pathKey: peg$parsepathKey, pathSegment: peg$parsepathSegment, pathSegmentChar: peg$parsepathSegmentChar, pipelineExpression: peg$parsepipelineExpression, primary: peg$parseprimary, program: peg$parseprogram, protocolExpression: peg$parseprotocolExpression, qualifiedReference: peg$parsequalifiedReference, reference: peg$parsereference, relationalExpression: peg$parserelationalExpression, relationalOperator: peg$parserelationalOperator, rootDirectory: peg$parserootDirectory, scopeReference: peg$parsescopeReference, separator: peg$parseseparator, slashFollows: peg$parseslashFollows, shebang: peg$parseshebang, shiftExpression: peg$parseshiftExpression, shiftOperator: peg$parseshiftOperator, shorthandFunction: peg$parseshorthandFunction, singleArrow: peg$parsesingleArrow, singleLineComment: peg$parsesingleLineComment, singleQuoteString: peg$parsesingleQuoteString, singleQuoteStringChar: peg$parsesingleQuoteStringChar, spreadElement: peg$parsespreadElement, stringLiteral: peg$parsestringLiteral, templateDocument: peg$parsetemplateDocument, templateDocumentChar: peg$parsetemplateDocumentChar, templateDocumentContents: peg$parsetemplateDocumentContents, templateDocumentText: peg$parsetemplateDocumentText, templateLiteral: peg$parsetemplateLiteral, templateLiteralChar: peg$parsetemplateLiteralChar, templateLiteralContents: peg$parsetemplateLiteralContents, templateLiteralText: peg$parsetemplateLiteralText, templateSubstitution: peg$parsetemplateSubstitution, textChar: peg$parsetextChar, unaryExpression: peg$parseunaryExpression, unaryOperator: peg$parseunaryOperator, whitespaceWithNewLine: peg$parsewhitespaceWithNewLine };
206
206
  var peg$startRuleFunction = peg$parse__;
207
207
 
208
208
  var peg$c0 = "[";
@@ -412,7 +412,7 @@ function peg$parse(input, options) {
412
412
  return annotate(tail.reduce(makeCall, head), location());
413
413
  };
414
414
  var peg$f11 = function() {
415
- error("Expected right curly brace");
415
+ error(`An object ended without a closing brace, or contained something that wasn't expected.\nThe top level of an object can only contain definitions ("a: b" or "a = b") or spreads ("...a").`);
416
416
  };
417
417
  var peg$f12 = function() {
418
418
  error("Expected right bracket");
@@ -425,12 +425,15 @@ function peg$parse(input, options) {
425
425
  ? list[0]
426
426
  : annotate([ops.comma, ...list], location());
427
427
  };
428
- var peg$f15 = function(condition, truthy, falsy) {
428
+ var peg$f15 = function(condition, tail) {
429
+ if (!tail) {
430
+ return condition;
431
+ }
429
432
  return annotate([
430
433
  ops.conditional,
431
434
  downgradeReference(condition),
432
- [ops.lambda, [], downgradeReference(truthy)],
433
- [ops.lambda, [], downgradeReference(falsy)]
435
+ [ops.lambda, [], downgradeReference(tail[0])],
436
+ [ops.lambda, [], downgradeReference(tail[1])]
434
437
  ], location());
435
438
  };
436
439
  var peg$f16 = function(chars) {
@@ -447,7 +450,7 @@ function peg$parse(input, options) {
447
450
  var peg$f23 = function() { return "\t"; };
448
451
  var peg$f24 = function() { return "\v"; };
449
452
  var peg$f25 = function(left, right) {
450
- return annotate([ops.exponentiation, left, right], location());
453
+ return right ? annotate([ops.exponentiation, left, right], location()) : left;
451
454
  };
452
455
  var peg$f26 = function() {
453
456
  return annotate([ops.literal, parseFloat(text())], location());
@@ -985,7 +988,7 @@ function peg$parse(input, options) {
985
988
  function peg$parsearrayEntry() {
986
989
  var s0, s1, s2, s3;
987
990
 
988
- s0 = peg$parsespread();
991
+ s0 = peg$parsespreadElement();
989
992
  if (s0 === peg$FAILED) {
990
993
  s0 = peg$parsepipelineExpression();
991
994
  if (s0 === peg$FAILED) {
@@ -1546,60 +1549,62 @@ function peg$parse(input, options) {
1546
1549
  }
1547
1550
 
1548
1551
  function peg$parseconditionalExpression() {
1549
- var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
1552
+ var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10;
1550
1553
 
1551
1554
  s0 = peg$currPos;
1552
1555
  s1 = peg$parselogicalOrExpression();
1553
1556
  if (s1 !== peg$FAILED) {
1554
- s2 = peg$parse__();
1557
+ s2 = peg$currPos;
1558
+ s3 = peg$parse__();
1555
1559
  if (input.charCodeAt(peg$currPos) === 63) {
1556
- s3 = peg$c9;
1560
+ s4 = peg$c9;
1557
1561
  peg$currPos++;
1558
1562
  } else {
1559
- s3 = peg$FAILED;
1563
+ s4 = peg$FAILED;
1560
1564
  if (peg$silentFails === 0) { peg$fail(peg$e15); }
1561
1565
  }
1562
- if (s3 !== peg$FAILED) {
1563
- s4 = peg$parse__();
1564
- s5 = peg$parsepipelineExpression();
1565
- if (s5 !== peg$FAILED) {
1566
- s6 = peg$parse__();
1566
+ if (s4 !== peg$FAILED) {
1567
+ s5 = peg$parse__();
1568
+ s6 = peg$parsepipelineExpression();
1569
+ if (s6 !== peg$FAILED) {
1570
+ s7 = peg$parse__();
1567
1571
  if (input.charCodeAt(peg$currPos) === 58) {
1568
- s7 = peg$c10;
1572
+ s8 = peg$c10;
1569
1573
  peg$currPos++;
1570
1574
  } else {
1571
- s7 = peg$FAILED;
1575
+ s8 = peg$FAILED;
1572
1576
  if (peg$silentFails === 0) { peg$fail(peg$e16); }
1573
1577
  }
1574
- if (s7 !== peg$FAILED) {
1575
- s8 = peg$parse__();
1576
- s9 = peg$parsepipelineExpression();
1577
- if (s9 !== peg$FAILED) {
1578
- peg$savedPos = s0;
1579
- s0 = peg$f15(s1, s5, s9);
1578
+ if (s8 !== peg$FAILED) {
1579
+ s9 = peg$parse__();
1580
+ s10 = peg$parsepipelineExpression();
1581
+ if (s10 !== peg$FAILED) {
1582
+ s2 = [ s6, s10 ];
1580
1583
  } else {
1581
- peg$currPos = s0;
1582
- s0 = peg$FAILED;
1584
+ peg$currPos = s2;
1585
+ s2 = peg$FAILED;
1583
1586
  }
1584
1587
  } else {
1585
- peg$currPos = s0;
1586
- s0 = peg$FAILED;
1588
+ peg$currPos = s2;
1589
+ s2 = peg$FAILED;
1587
1590
  }
1588
1591
  } else {
1589
- peg$currPos = s0;
1590
- s0 = peg$FAILED;
1592
+ peg$currPos = s2;
1593
+ s2 = peg$FAILED;
1591
1594
  }
1592
1595
  } else {
1593
- peg$currPos = s0;
1594
- s0 = peg$FAILED;
1596
+ peg$currPos = s2;
1597
+ s2 = peg$FAILED;
1598
+ }
1599
+ if (s2 === peg$FAILED) {
1600
+ s2 = null;
1595
1601
  }
1602
+ peg$savedPos = s0;
1603
+ s0 = peg$f15(s1, s2);
1596
1604
  } else {
1597
1605
  peg$currPos = s0;
1598
1606
  s0 = peg$FAILED;
1599
1607
  }
1600
- if (s0 === peg$FAILED) {
1601
- s0 = peg$parselogicalOrExpression();
1602
- }
1603
1608
 
1604
1609
  return s0;
1605
1610
  }
@@ -2009,40 +2014,42 @@ function peg$parse(input, options) {
2009
2014
  }
2010
2015
 
2011
2016
  function peg$parseexponentiationExpression() {
2012
- var s0, s1, s2, s3, s4, s5;
2017
+ var s0, s1, s2, s3, s4, s5, s6;
2013
2018
 
2014
2019
  s0 = peg$currPos;
2015
2020
  s1 = peg$parseunaryExpression();
2016
2021
  if (s1 !== peg$FAILED) {
2017
- s2 = peg$parse__();
2022
+ s2 = peg$currPos;
2023
+ s3 = peg$parse__();
2018
2024
  if (input.substr(peg$currPos, 2) === peg$c28) {
2019
- s3 = peg$c28;
2025
+ s4 = peg$c28;
2020
2026
  peg$currPos += 2;
2021
2027
  } else {
2022
- s3 = peg$FAILED;
2028
+ s4 = peg$FAILED;
2023
2029
  if (peg$silentFails === 0) { peg$fail(peg$e37); }
2024
2030
  }
2025
- if (s3 !== peg$FAILED) {
2026
- s4 = peg$parse__();
2027
- s5 = peg$parseexponentiationExpression();
2028
- if (s5 !== peg$FAILED) {
2029
- peg$savedPos = s0;
2030
- s0 = peg$f25(s1, s5);
2031
+ if (s4 !== peg$FAILED) {
2032
+ s5 = peg$parse__();
2033
+ s6 = peg$parseexponentiationExpression();
2034
+ if (s6 !== peg$FAILED) {
2035
+ s2 = s6;
2031
2036
  } else {
2032
- peg$currPos = s0;
2033
- s0 = peg$FAILED;
2037
+ peg$currPos = s2;
2038
+ s2 = peg$FAILED;
2034
2039
  }
2035
2040
  } else {
2036
- peg$currPos = s0;
2037
- s0 = peg$FAILED;
2041
+ peg$currPos = s2;
2042
+ s2 = peg$FAILED;
2043
+ }
2044
+ if (s2 === peg$FAILED) {
2045
+ s2 = null;
2038
2046
  }
2047
+ peg$savedPos = s0;
2048
+ s0 = peg$f25(s1, s2);
2039
2049
  } else {
2040
2050
  peg$currPos = s0;
2041
2051
  s0 = peg$FAILED;
2042
2052
  }
2043
- if (s0 === peg$FAILED) {
2044
- s0 = peg$parseunaryExpression();
2045
- }
2046
2053
 
2047
2054
  return s0;
2048
2055
  }
@@ -3173,7 +3180,7 @@ function peg$parse(input, options) {
3173
3180
  function peg$parseobjectEntry() {
3174
3181
  var s0;
3175
3182
 
3176
- s0 = peg$parsespread();
3183
+ s0 = peg$parsespreadElement();
3177
3184
  if (s0 === peg$FAILED) {
3178
3185
  s0 = peg$parseobjectProperty();
3179
3186
  if (s0 === peg$FAILED) {
@@ -4307,14 +4314,14 @@ function peg$parse(input, options) {
4307
4314
  return s0;
4308
4315
  }
4309
4316
 
4310
- function peg$parsespread() {
4317
+ function peg$parsespreadElement() {
4311
4318
  var s0, s1, s2, s3;
4312
4319
 
4313
4320
  s0 = peg$currPos;
4314
4321
  s1 = peg$parseellipsis();
4315
4322
  if (s1 !== peg$FAILED) {
4316
4323
  s2 = peg$parse__();
4317
- s3 = peg$parseconditionalExpression();
4324
+ s3 = peg$parsepipelineExpression();
4318
4325
  if (s3 !== peg$FAILED) {
4319
4326
  peg$savedPos = s0;
4320
4327
  s0 = peg$f68(s3);
@@ -4845,7 +4852,7 @@ const peg$allowedStartRules = [
4845
4852
  "singleLineComment",
4846
4853
  "singleQuoteString",
4847
4854
  "singleQuoteStringChar",
4848
- "spread",
4855
+ "spreadElement",
4849
4856
  "stringLiteral",
4850
4857
  "templateDocument",
4851
4858
  "templateDocumentChar",
@@ -1,6 +1,10 @@
1
1
  // Text we look for in an error stack to guess whether a given line represents a
2
2
 
3
- import { scope as scopeFn, trailingSlash } from "@weborigami/async-tree";
3
+ import {
4
+ scope as scopeFn,
5
+ trailingSlash,
6
+ TraverseError,
7
+ } from "@weborigami/async-tree";
4
8
  import codeFragment from "./codeFragment.js";
5
9
  import { typos } from "./typos.js";
6
10
 
@@ -13,19 +17,24 @@ const origamiSourceSignals = [
13
17
  ];
14
18
 
15
19
  export async function builtinReferenceError(tree, builtins, key) {
16
- const messages = [
17
- `"${key}" is being called as if it were a builtin function, but it's not.`,
18
- ];
19
20
  // See if the key is in scope (but not as a builtin)
20
21
  const scope = scopeFn(tree);
21
22
  const value = await scope.get(key);
23
+ let message;
22
24
  if (value === undefined) {
25
+ const messages = [
26
+ `"${key}" is being called as if it were a builtin function, but it's not.`,
27
+ ];
23
28
  const typos = await formatScopeTypos(builtins, key);
24
29
  messages.push(typos);
30
+ message = messages.join(" ");
25
31
  } else {
26
- messages.push(`Use "${key}/" instead.`);
32
+ const messages = [
33
+ `To call a function like "${key}" that's not a builtin, include a slash: ${key}/( )`,
34
+ `Details: https://weborigami.org/language/syntax.html#shorthand-for-builtin-functions`,
35
+ ];
36
+ message = messages.join("\n");
27
37
  }
28
- const message = messages.join(" ");
29
38
  return new ReferenceError(message);
30
39
  }
31
40
 
@@ -36,37 +45,50 @@ export async function builtinReferenceError(tree, builtins, key) {
36
45
  */
37
46
  export function formatError(error) {
38
47
  let message;
48
+
49
+ let location = /** @type {any} */ (error).location;
50
+ const fragment = location ? codeFragment(location) : null;
51
+ let fragmentInMessage = false;
52
+
39
53
  if (error.stack) {
40
54
  // Display the stack only until we reach the Origami source code.
41
55
  message = "";
42
56
  let lines = error.stack.split("\n");
43
57
  for (let i = 0; i < lines.length; i++) {
44
- const line = lines[i];
58
+ let line = lines[i];
45
59
  if (maybeOrigamiSourceCode(line)) {
46
60
  break;
47
61
  }
62
+ if (
63
+ error instanceof TraverseError &&
64
+ error.message === "A null or undefined value can't be traversed"
65
+ ) {
66
+ // Provide more meaningful message for TraverseError
67
+ line = `TraverseError: This part of the path is null or undefined: ${fragment}`;
68
+ fragmentInMessage = true;
69
+ }
48
70
  if (message) {
49
71
  message += "\n";
50
72
  }
51
- message += lines[i];
73
+ message += line;
52
74
  }
53
75
  } else {
54
76
  message = error.toString();
55
77
  }
56
78
 
57
79
  // Add location
58
- let location = /** @type {any} */ (error).location;
59
80
  if (location) {
60
- const fragment = codeFragment(location);
61
81
  let { source, start } = location;
62
-
63
- message += `\nevaluating: ${fragment}`;
82
+ if (!fragmentInMessage) {
83
+ message += `\nevaluating: ${fragment}`;
84
+ }
64
85
  if (typeof source === "object" && source.url) {
65
86
  message += `\n at ${source.url.href}:${start.line}:${start.column}`;
66
87
  } else if (source.text.includes("\n")) {
67
88
  message += `\n at line ${start.line}, column ${start.column}`;
68
89
  }
69
90
  }
91
+
70
92
  return message;
71
93
  }
72
94
 
@@ -687,7 +687,7 @@ describe("Origami parser", () => {
687
687
  assertParse("objectLiteral", "{ a: 1, ...b }", [
688
688
  ops.merge,
689
689
  [ops.object, ["a", [ops.literal, 1]]],
690
- [undetermined, "b"],
690
+ [ops.scope, "b"],
691
691
  ]);
692
692
  assertParse("objectLiteral", "{ (a): 1 }", [
693
693
  ops.object,
@@ -946,9 +946,9 @@ describe("Origami parser", () => {
946
946
  assertParse("singleLineComment", "// Hello, world!", null, false);
947
947
  });
948
948
 
949
- test("spread", () => {
950
- assertParse("spread", "...a", [ops.spread, [undetermined, "a"]]);
951
- assertParse("spread", "…a", [ops.spread, [undetermined, "a"]]);
949
+ test("spreadElement", () => {
950
+ assertParse("spreadElement", "...a", [ops.spread, [ops.scope, "a"]]);
951
+ assertParse("spreadElement", "…a", [ops.spread, [ops.scope, "a"]]);
952
952
  });
953
953
 
954
954
  test("stringLiteral", () => {