@weborigami/language 0.3.4-jse.7 → 0.3.4-jse.8
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 +3 -3
- package/src/compiler/optimize.js +29 -18
- package/src/compiler/origami.pegjs +18 -4
- package/src/compiler/parse.js +89 -24
- package/src/runtime/errors.js +56 -24
- package/src/runtime/evaluate.js +5 -27
- package/src/runtime/symbols.js +1 -0
- package/test/compiler/optimize.test.js +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.3.4-jse.
|
|
3
|
+
"version": "0.3.4-jse.8",
|
|
4
4
|
"description": "Web Origami expression language compiler and runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./main.js",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"typescript": "5.8.2"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@weborigami/async-tree": "0.3.4-jse.
|
|
15
|
-
"@weborigami/types": "0.3.4-jse.
|
|
14
|
+
"@weborigami/async-tree": "0.3.4-jse.8",
|
|
15
|
+
"@weborigami/types": "0.3.4-jse.8",
|
|
16
16
|
"watcher": "2.3.1",
|
|
17
17
|
"yaml": "2.7.0"
|
|
18
18
|
},
|
package/src/compiler/optimize.js
CHANGED
|
@@ -37,8 +37,8 @@ export default function optimize(code, options = {}) {
|
|
|
37
37
|
const [op, ...args] = code;
|
|
38
38
|
switch (op) {
|
|
39
39
|
case markers.global:
|
|
40
|
-
// Replace
|
|
41
|
-
return
|
|
40
|
+
// Replace with the indicated global
|
|
41
|
+
return globals[args[0]];
|
|
42
42
|
|
|
43
43
|
case markers.traverse:
|
|
44
44
|
return resolvePath(code, globals, locals, cache);
|
|
@@ -140,7 +140,7 @@ function compoundReference(key, globals, locals, location) {
|
|
|
140
140
|
const type = referenceType(head, globals, locals);
|
|
141
141
|
let result;
|
|
142
142
|
if (type === REFERENCE_GLOBAL) {
|
|
143
|
-
result = globalReference(head, globals
|
|
143
|
+
result = globalReference(head, globals);
|
|
144
144
|
} else if (type === REFERENCE_LOCAL) {
|
|
145
145
|
result = localReference(head, locals, location);
|
|
146
146
|
} else {
|
|
@@ -177,9 +177,9 @@ function getLocalReferenceDepth(locals, key) {
|
|
|
177
177
|
return depth;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
function globalReference(key, globals
|
|
180
|
+
function globalReference(key, globals) {
|
|
181
181
|
const normalized = trailingSlash.remove(key);
|
|
182
|
-
return
|
|
182
|
+
return globals[normalized];
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
function inlineLiteral(code) {
|
|
@@ -249,24 +249,29 @@ function reference(code, globals, locals) {
|
|
|
249
249
|
|
|
250
250
|
// See if the whole key is a global or local variable
|
|
251
251
|
let type = referenceType(key, globals, locals);
|
|
252
|
-
let result;
|
|
253
252
|
if (type === REFERENCE_GLOBAL) {
|
|
254
|
-
|
|
253
|
+
return {
|
|
254
|
+
type,
|
|
255
|
+
result: globalReference(key, globals),
|
|
256
|
+
};
|
|
255
257
|
} else if (type === REFERENCE_LOCAL) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
result = compound?.result;
|
|
261
|
-
type = compound?.type;
|
|
258
|
+
return {
|
|
259
|
+
type,
|
|
260
|
+
result: localReference(key, locals, location),
|
|
261
|
+
};
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
264
|
+
// Try key as a compound reference x.y.z
|
|
265
|
+
const compound = compoundReference(key, globals, locals, location);
|
|
266
|
+
if (compound.type !== REFERENCE_EXTERNAL) {
|
|
267
|
+
return compound;
|
|
267
268
|
}
|
|
268
269
|
|
|
269
|
-
|
|
270
|
+
// Not a compound reference, must be external
|
|
271
|
+
return {
|
|
272
|
+
type: REFERENCE_EXTERNAL,
|
|
273
|
+
result: externalReference(key, locals, location),
|
|
274
|
+
};
|
|
270
275
|
}
|
|
271
276
|
|
|
272
277
|
function referenceType(key, globals, locals) {
|
|
@@ -287,7 +292,13 @@ function resolvePath(code, globals, locals, cache) {
|
|
|
287
292
|
|
|
288
293
|
let { type, result } = reference(head, globals, locals);
|
|
289
294
|
|
|
290
|
-
|
|
295
|
+
if (tail.length > 0) {
|
|
296
|
+
if (result instanceof Array) {
|
|
297
|
+
result.push(...tail);
|
|
298
|
+
} else {
|
|
299
|
+
result = annotate([result, ...tail], code.location);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
291
302
|
|
|
292
303
|
if (type === REFERENCE_EXTERNAL && cache !== null) {
|
|
293
304
|
// Cache external path
|
|
@@ -164,10 +164,17 @@ comment "comment"
|
|
|
164
164
|
/ singleLineComment
|
|
165
165
|
|
|
166
166
|
computedPropertyAccess
|
|
167
|
-
=
|
|
167
|
+
= computedPropertySpace "[" expression:expectExpression expectClosingBracket {
|
|
168
168
|
return annotate([markers.property, expression], location());
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
// A space before a computed property access. This is allowed when in not in
|
|
172
|
+
// shell mode, but not in shell mode. In shell mode `foo [bar]` should parse as
|
|
173
|
+
// a function call with a single argument of an array, not as a property access.
|
|
174
|
+
computedPropertySpace
|
|
175
|
+
= shellMode
|
|
176
|
+
/ !shellMode __
|
|
177
|
+
|
|
171
178
|
conditionalExpression
|
|
172
179
|
= condition:logicalOrExpression tail:(__
|
|
173
180
|
"?" __ @shorthandFunction __
|
|
@@ -773,7 +780,12 @@ templateBodyText "template text"
|
|
|
773
780
|
|
|
774
781
|
templateDocument "template document"
|
|
775
782
|
= front:frontMatterExpression __ body:templateBody {
|
|
783
|
+
// TODO: Deprecate @template
|
|
776
784
|
const macroName = text().includes("@template") ? "@template" : "_template";
|
|
785
|
+
if (macroName === "@template") {
|
|
786
|
+
// If the front matter is a macro, apply it to the body
|
|
787
|
+
console.warn("Warning: the @template() macro is deprecated. Use _template() instead.");
|
|
788
|
+
}
|
|
777
789
|
return annotate(applyMacro(front, macroName, body), location());
|
|
778
790
|
}
|
|
779
791
|
/ front:frontMatterYaml body:templateBody {
|
|
@@ -854,7 +866,8 @@ uriKey
|
|
|
854
866
|
// A single character in a URI key
|
|
855
867
|
uriKeyChar
|
|
856
868
|
// Accept anything that doesn't end the URI key or path
|
|
857
|
-
|
|
869
|
+
// Note: Peggy doesn't support `/s` so we list space and tab explicitly.
|
|
870
|
+
= [^/,\)\]\} \t]
|
|
858
871
|
/ escapedChar
|
|
859
872
|
|
|
860
873
|
// A slash-separated path of keys: `a/b/c`
|
|
@@ -877,8 +890,9 @@ unaryOperator
|
|
|
877
890
|
/ minus
|
|
878
891
|
|
|
879
892
|
whitespace
|
|
880
|
-
|
|
881
|
-
|
|
893
|
+
// JavaScript considers a large number of characters whitespace so we use the
|
|
894
|
+
// `/s` definition to avoid missing any.
|
|
895
|
+
= char:. &{ return /\s/.test(char); } { return char; }
|
|
882
896
|
/ comment
|
|
883
897
|
|
|
884
898
|
whitespaceWithNewLine
|
package/src/compiler/parse.js
CHANGED
|
@@ -205,7 +205,7 @@ function peg$parse(input, options) {
|
|
|
205
205
|
var peg$FAILED = {};
|
|
206
206
|
var peg$source = options.grammarSource;
|
|
207
207
|
|
|
208
|
-
var peg$startRuleFunctions = { __: peg$parse__, additiveExpression: peg$parseadditiveExpression, additiveOperator: peg$parseadditiveOperator, angleBracketLiteral: peg$parseangleBracketLiteral, angleBracketPath: peg$parseangleBracketPath, angleBracketKey: peg$parseangleBracketKey, angleBracketPathChar: peg$parseangleBracketPathChar, 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, commaExpression: peg$parsecommaExpression, comment: peg$parsecomment, computedPropertyAccess: peg$parsecomputedPropertyAccess, 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, expectBacktick: peg$parseexpectBacktick, expectClosingBrace: peg$parseexpectClosingBrace, expectClosingBracket: peg$parseexpectClosingBracket, expectClosingParenthesis: peg$parseexpectClosingParenthesis, expectDoubleQuote: peg$parseexpectDoubleQuote, expectExpression: peg$parseexpectExpression, expectFrontDelimiter: peg$parseexpectFrontDelimiter, expectGuillemet: peg$parseexpectGuillemet, expectSingleQuote: peg$parseexpectSingleQuote, expectPipelineExpression: peg$parseexpectPipelineExpression, expectUnaryExpression: peg$parseexpectUnaryExpression, exponentiationExpression: peg$parseexponentiationExpression, expression: peg$parseexpression, floatLiteral: peg$parsefloatLiteral, frontDelimiter: peg$parsefrontDelimiter, frontMatterExpression: peg$parsefrontMatterExpression, frontMatterText: peg$parsefrontMatterText, frontMatterYaml: peg$parsefrontMatterYaml, group: peg$parsegroup, guillemetString: peg$parseguillemetString, guillemetStringChar: peg$parseguillemetStringChar, host: peg$parsehost, hostname: peg$parsehostname, identifier: peg$parseidentifier, identifierLiteral: peg$parseidentifierLiteral, identifierPart: peg$parseidentifierPart, identifierStart: peg$parseidentifierStart, implicitParenthesesCallExpression: peg$parseimplicitParenthesesCallExpression, implicitParensthesesArguments: peg$parseimplicitParensthesesArguments, inlineSpace: peg$parseinlineSpace, integerLiteral: peg$parseintegerLiteral, key: peg$parsekey, keyChar: peg$parsekeyChar, keyCharStart: peg$parsekeyCharStart, list: peg$parselist, logicalAndExpression: peg$parselogicalAndExpression, logicalOrExpression: peg$parselogicalOrExpression, minus: peg$parseminus, multiLineComment: peg$parsemultiLineComment, multiplicativeExpression: peg$parsemultiplicativeExpression, multiplicativeOperator: peg$parsemultiplicativeOperator, newExpression: peg$parsenewExpression, 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, optionalChaining: peg$parseoptionalChaining, parameter: peg$parseparameter, parameterList: peg$parseparameterList, parameterSingleton: peg$parseparameterSingleton, parenthesesArguments: peg$parseparenthesesArguments, pathArguments: peg$parsepathArguments, pathKeys: peg$parsepathKeys, pathLiteral: peg$parsepathLiteral, pathSegment: peg$parsepathSegment, pipelineExpression: peg$parsepipelineExpression, primary: peg$parseprimary, program: peg$parseprogram, propertyAccess: peg$parsepropertyAccess, regexFlags: peg$parseregexFlags, regexLiteral: peg$parseregexLiteral, regexLiteralChar: peg$parseregexLiteralChar, relationalExpression: peg$parserelationalExpression, relationalOperator: peg$parserelationalOperator, separator: peg$parseseparator, shebang: peg$parseshebang, shellMode: peg$parseshellMode, shiftExpression: peg$parseshiftExpression, shiftOperator: peg$parseshiftOperator, shorthandFunction: peg$parseshorthandFunction, singleArrow: peg$parsesingleArrow, singleLineComment: peg$parsesingleLineComment, singleQuoteString: peg$parsesingleQuoteString, singleQuoteStringChar: peg$parsesingleQuoteStringChar, slash: peg$parseslash, slashes: peg$parseslashes, slashFollows: peg$parseslashFollows, spreadElement: peg$parsespreadElement, stringLiteral: peg$parsestringLiteral, templateBody: peg$parsetemplateBody, templateBodyChar: peg$parsetemplateBodyChar, templateBodyText: peg$parsetemplateBodyText, templateDocument: peg$parsetemplateDocument, templateLiteral: peg$parsetemplateLiteral, templateLiteralChar: peg$parsetemplateLiteralChar, templateLiteralText: peg$parsetemplateLiteralText, templateSubstitution: peg$parsetemplateSubstitution, textChar: peg$parsetextChar, unaryExpression: peg$parseunaryExpression, uri: peg$parseuri, uriExpression: peg$parseuriExpression, uriKey: peg$parseuriKey, uriKeyChar: peg$parseuriKeyChar, uriPath: peg$parseuriPath, uriScheme: peg$parseuriScheme, unaryOperator: peg$parseunaryOperator, whitespace: peg$parsewhitespace, whitespaceWithNewLine: peg$parsewhitespaceWithNewLine };
|
|
208
|
+
var peg$startRuleFunctions = { __: peg$parse__, additiveExpression: peg$parseadditiveExpression, additiveOperator: peg$parseadditiveOperator, angleBracketLiteral: peg$parseangleBracketLiteral, angleBracketPath: peg$parseangleBracketPath, angleBracketKey: peg$parseangleBracketKey, angleBracketPathChar: peg$parseangleBracketPathChar, 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, commaExpression: peg$parsecommaExpression, comment: peg$parsecomment, computedPropertyAccess: peg$parsecomputedPropertyAccess, computedPropertySpace: peg$parsecomputedPropertySpace, 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, expectBacktick: peg$parseexpectBacktick, expectClosingBrace: peg$parseexpectClosingBrace, expectClosingBracket: peg$parseexpectClosingBracket, expectClosingParenthesis: peg$parseexpectClosingParenthesis, expectDoubleQuote: peg$parseexpectDoubleQuote, expectExpression: peg$parseexpectExpression, expectFrontDelimiter: peg$parseexpectFrontDelimiter, expectGuillemet: peg$parseexpectGuillemet, expectSingleQuote: peg$parseexpectSingleQuote, expectPipelineExpression: peg$parseexpectPipelineExpression, expectUnaryExpression: peg$parseexpectUnaryExpression, exponentiationExpression: peg$parseexponentiationExpression, expression: peg$parseexpression, floatLiteral: peg$parsefloatLiteral, frontDelimiter: peg$parsefrontDelimiter, frontMatterExpression: peg$parsefrontMatterExpression, frontMatterText: peg$parsefrontMatterText, frontMatterYaml: peg$parsefrontMatterYaml, group: peg$parsegroup, guillemetString: peg$parseguillemetString, guillemetStringChar: peg$parseguillemetStringChar, host: peg$parsehost, hostname: peg$parsehostname, identifier: peg$parseidentifier, identifierLiteral: peg$parseidentifierLiteral, identifierPart: peg$parseidentifierPart, identifierStart: peg$parseidentifierStart, implicitParenthesesCallExpression: peg$parseimplicitParenthesesCallExpression, implicitParensthesesArguments: peg$parseimplicitParensthesesArguments, inlineSpace: peg$parseinlineSpace, integerLiteral: peg$parseintegerLiteral, key: peg$parsekey, keyChar: peg$parsekeyChar, keyCharStart: peg$parsekeyCharStart, list: peg$parselist, logicalAndExpression: peg$parselogicalAndExpression, logicalOrExpression: peg$parselogicalOrExpression, minus: peg$parseminus, multiLineComment: peg$parsemultiLineComment, multiplicativeExpression: peg$parsemultiplicativeExpression, multiplicativeOperator: peg$parsemultiplicativeOperator, newExpression: peg$parsenewExpression, 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, optionalChaining: peg$parseoptionalChaining, parameter: peg$parseparameter, parameterList: peg$parseparameterList, parameterSingleton: peg$parseparameterSingleton, parenthesesArguments: peg$parseparenthesesArguments, pathArguments: peg$parsepathArguments, pathKeys: peg$parsepathKeys, pathLiteral: peg$parsepathLiteral, pathSegment: peg$parsepathSegment, pipelineExpression: peg$parsepipelineExpression, primary: peg$parseprimary, program: peg$parseprogram, propertyAccess: peg$parsepropertyAccess, regexFlags: peg$parseregexFlags, regexLiteral: peg$parseregexLiteral, regexLiteralChar: peg$parseregexLiteralChar, relationalExpression: peg$parserelationalExpression, relationalOperator: peg$parserelationalOperator, separator: peg$parseseparator, shebang: peg$parseshebang, shellMode: peg$parseshellMode, shiftExpression: peg$parseshiftExpression, shiftOperator: peg$parseshiftOperator, shorthandFunction: peg$parseshorthandFunction, singleArrow: peg$parsesingleArrow, singleLineComment: peg$parsesingleLineComment, singleQuoteString: peg$parsesingleQuoteString, singleQuoteStringChar: peg$parsesingleQuoteStringChar, slash: peg$parseslash, slashes: peg$parseslashes, slashFollows: peg$parseslashFollows, spreadElement: peg$parsespreadElement, stringLiteral: peg$parsestringLiteral, templateBody: peg$parsetemplateBody, templateBodyChar: peg$parsetemplateBodyChar, templateBodyText: peg$parsetemplateBodyText, templateDocument: peg$parsetemplateDocument, templateLiteral: peg$parsetemplateLiteral, templateLiteralChar: peg$parsetemplateLiteralChar, templateLiteralText: peg$parsetemplateLiteralText, templateSubstitution: peg$parsetemplateSubstitution, textChar: peg$parsetextChar, unaryExpression: peg$parseunaryExpression, uri: peg$parseuri, uriExpression: peg$parseuriExpression, uriKey: peg$parseuriKey, uriKeyChar: peg$parseuriKeyChar, uriPath: peg$parseuriPath, uriScheme: peg$parseuriScheme, unaryOperator: peg$parseunaryOperator, whitespace: peg$parsewhitespace, whitespaceWithNewLine: peg$parsewhitespaceWithNewLine };
|
|
209
209
|
var peg$startRuleFunction = peg$parse__;
|
|
210
210
|
|
|
211
211
|
var peg$c0 = "+";
|
|
@@ -285,7 +285,7 @@ function peg$parse(input, options) {
|
|
|
285
285
|
var peg$r7 = /^[gimuy]/;
|
|
286
286
|
var peg$r8 = /^[^\/\n\r]/;
|
|
287
287
|
var peg$r9 = /^[^\n\r]/;
|
|
288
|
-
var peg$r10 = /^[^\/,)\]}
|
|
288
|
+
var peg$r10 = /^[^\/,)\]} \t]/;
|
|
289
289
|
var peg$r11 = /^[a-z]/;
|
|
290
290
|
var peg$r12 = /^[a-z0-9+-.]/;
|
|
291
291
|
var peg$r13 = /^[:]/;
|
|
@@ -399,7 +399,7 @@ function peg$parse(input, options) {
|
|
|
399
399
|
var peg$e105 = peg$otherExpectation("template document");
|
|
400
400
|
var peg$e106 = peg$otherExpectation("template literal");
|
|
401
401
|
var peg$e107 = peg$otherExpectation("template substitution");
|
|
402
|
-
var peg$e108 = peg$classExpectation(["/", ",", ")", "]", "}", "
|
|
402
|
+
var peg$e108 = peg$classExpectation(["/", ",", ")", "]", "}", " ", "\t"], true, false);
|
|
403
403
|
var peg$e109 = peg$otherExpectation("slash-separated path");
|
|
404
404
|
var peg$e110 = peg$classExpectation([["a", "z"]], false, false);
|
|
405
405
|
var peg$e111 = peg$classExpectation([["a", "z"], ["0", "9"], ["+", "."]], false, false);
|
|
@@ -732,7 +732,12 @@ function peg$parse(input, options) {
|
|
|
732
732
|
return annotate([ops.literal, chars.join("")], location());
|
|
733
733
|
};
|
|
734
734
|
var peg$f99 = function(front, body) {
|
|
735
|
+
// TODO: Deprecate @template
|
|
735
736
|
const macroName = text().includes("@template") ? "@template" : "_template";
|
|
737
|
+
if (macroName === "@template") {
|
|
738
|
+
// If the front matter is a macro, apply it to the body
|
|
739
|
+
console.warn("Warning: the @template() macro is deprecated. Use _template() instead.");
|
|
740
|
+
}
|
|
736
741
|
return annotate(applyMacro(front, macroName, body), location());
|
|
737
742
|
};
|
|
738
743
|
var peg$f100 = function(front, body) {
|
|
@@ -781,6 +786,8 @@ function peg$parse(input, options) {
|
|
|
781
786
|
var peg$f111 = function() {
|
|
782
787
|
return annotate([markers.global, text()], location());
|
|
783
788
|
};
|
|
789
|
+
var peg$f112 = function(char) { return /\s/.test(char); };
|
|
790
|
+
var peg$f113 = function(char) { return char; };
|
|
784
791
|
var peg$currPos = options.peg$currPos | 0;
|
|
785
792
|
var peg$savedPos = peg$currPos;
|
|
786
793
|
var peg$posDetailsCache = [{ line: 1, column: 1 }];
|
|
@@ -1896,21 +1903,26 @@ function peg$parse(input, options) {
|
|
|
1896
1903
|
var s0, s1, s2, s3, s4;
|
|
1897
1904
|
|
|
1898
1905
|
s0 = peg$currPos;
|
|
1899
|
-
s1 = peg$
|
|
1900
|
-
if (
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
peg$
|
|
1913
|
-
|
|
1906
|
+
s1 = peg$parsecomputedPropertySpace();
|
|
1907
|
+
if (s1 !== peg$FAILED) {
|
|
1908
|
+
if (input.charCodeAt(peg$currPos) === 91) {
|
|
1909
|
+
s2 = peg$c6;
|
|
1910
|
+
peg$currPos++;
|
|
1911
|
+
} else {
|
|
1912
|
+
s2 = peg$FAILED;
|
|
1913
|
+
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
|
1914
|
+
}
|
|
1915
|
+
if (s2 !== peg$FAILED) {
|
|
1916
|
+
s3 = peg$parseexpectExpression();
|
|
1917
|
+
if (s3 !== peg$FAILED) {
|
|
1918
|
+
s4 = peg$parseexpectClosingBracket();
|
|
1919
|
+
if (s4 !== peg$FAILED) {
|
|
1920
|
+
peg$savedPos = s0;
|
|
1921
|
+
s0 = peg$f16(s3);
|
|
1922
|
+
} else {
|
|
1923
|
+
peg$currPos = s0;
|
|
1924
|
+
s0 = peg$FAILED;
|
|
1925
|
+
}
|
|
1914
1926
|
} else {
|
|
1915
1927
|
peg$currPos = s0;
|
|
1916
1928
|
s0 = peg$FAILED;
|
|
@@ -1927,6 +1939,35 @@ function peg$parse(input, options) {
|
|
|
1927
1939
|
return s0;
|
|
1928
1940
|
}
|
|
1929
1941
|
|
|
1942
|
+
function peg$parsecomputedPropertySpace() {
|
|
1943
|
+
var s0, s1, s2;
|
|
1944
|
+
|
|
1945
|
+
s0 = peg$parseshellMode();
|
|
1946
|
+
if (s0 === peg$FAILED) {
|
|
1947
|
+
s0 = peg$currPos;
|
|
1948
|
+
s1 = peg$currPos;
|
|
1949
|
+
peg$silentFails++;
|
|
1950
|
+
s2 = peg$parseshellMode();
|
|
1951
|
+
peg$silentFails--;
|
|
1952
|
+
if (s2 === peg$FAILED) {
|
|
1953
|
+
s1 = undefined;
|
|
1954
|
+
} else {
|
|
1955
|
+
peg$currPos = s1;
|
|
1956
|
+
s1 = peg$FAILED;
|
|
1957
|
+
}
|
|
1958
|
+
if (s1 !== peg$FAILED) {
|
|
1959
|
+
s2 = peg$parse__();
|
|
1960
|
+
s1 = [s1, s2];
|
|
1961
|
+
s0 = s1;
|
|
1962
|
+
} else {
|
|
1963
|
+
peg$currPos = s0;
|
|
1964
|
+
s0 = peg$FAILED;
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
return s0;
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1930
1971
|
function peg$parseconditionalExpression() {
|
|
1931
1972
|
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10;
|
|
1932
1973
|
|
|
@@ -6164,14 +6205,37 @@ function peg$parse(input, options) {
|
|
|
6164
6205
|
}
|
|
6165
6206
|
|
|
6166
6207
|
function peg$parsewhitespace() {
|
|
6167
|
-
var s0;
|
|
6208
|
+
var s0, s1, s2;
|
|
6168
6209
|
|
|
6169
|
-
s0 = peg$
|
|
6170
|
-
if (
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6210
|
+
s0 = peg$currPos;
|
|
6211
|
+
if (input.length > peg$currPos) {
|
|
6212
|
+
s1 = input.charAt(peg$currPos);
|
|
6213
|
+
peg$currPos++;
|
|
6214
|
+
} else {
|
|
6215
|
+
s1 = peg$FAILED;
|
|
6216
|
+
if (peg$silentFails === 0) { peg$fail(peg$e41); }
|
|
6217
|
+
}
|
|
6218
|
+
if (s1 !== peg$FAILED) {
|
|
6219
|
+
peg$savedPos = peg$currPos;
|
|
6220
|
+
s2 = peg$f112(s1);
|
|
6221
|
+
if (s2) {
|
|
6222
|
+
s2 = undefined;
|
|
6223
|
+
} else {
|
|
6224
|
+
s2 = peg$FAILED;
|
|
6174
6225
|
}
|
|
6226
|
+
if (s2 !== peg$FAILED) {
|
|
6227
|
+
peg$savedPos = s0;
|
|
6228
|
+
s0 = peg$f113(s1);
|
|
6229
|
+
} else {
|
|
6230
|
+
peg$currPos = s0;
|
|
6231
|
+
s0 = peg$FAILED;
|
|
6232
|
+
}
|
|
6233
|
+
} else {
|
|
6234
|
+
peg$currPos = s0;
|
|
6235
|
+
s0 = peg$FAILED;
|
|
6236
|
+
}
|
|
6237
|
+
if (s0 === peg$FAILED) {
|
|
6238
|
+
s0 = peg$parsecomment();
|
|
6175
6239
|
}
|
|
6176
6240
|
|
|
6177
6241
|
return s0;
|
|
@@ -6255,6 +6319,7 @@ const peg$allowedStartRules = [
|
|
|
6255
6319
|
"commaExpression",
|
|
6256
6320
|
"comment",
|
|
6257
6321
|
"computedPropertyAccess",
|
|
6322
|
+
"computedPropertySpace",
|
|
6258
6323
|
"conditionalExpression",
|
|
6259
6324
|
"digits",
|
|
6260
6325
|
"doubleArrow",
|
package/src/runtime/errors.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Text we look for in an error stack to guess whether a given line represents a
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
box,
|
|
4
5
|
scope as scopeFn,
|
|
5
6
|
trailingSlash,
|
|
6
7
|
TraverseError,
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
import path from "node:path";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
10
11
|
import codeFragment from "./codeFragment.js";
|
|
12
|
+
import * as symbols from "./symbols.js";
|
|
11
13
|
import { typos } from "./typos.js";
|
|
12
14
|
|
|
13
15
|
// function in the Origami source code.
|
|
@@ -18,6 +20,19 @@ const origamiSourceSignals = [
|
|
|
18
20
|
"at Scope.evaluate",
|
|
19
21
|
];
|
|
20
22
|
|
|
23
|
+
const displayedWarnings = new Set();
|
|
24
|
+
|
|
25
|
+
export function attachWarning(value, message) {
|
|
26
|
+
if (typeof value === "object" && value?.[symbols.warningSymbol]) {
|
|
27
|
+
// Already has a warning, don't overwrite it
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const boxed = box(value);
|
|
32
|
+
boxed[symbols.warningSymbol] = message;
|
|
33
|
+
return boxed;
|
|
34
|
+
}
|
|
35
|
+
|
|
21
36
|
export async function builtinReferenceError(tree, builtins, key) {
|
|
22
37
|
// See if the key is in scope (but not as a builtin)
|
|
23
38
|
const scope = scopeFn(tree);
|
|
@@ -40,6 +55,16 @@ export async function builtinReferenceError(tree, builtins, key) {
|
|
|
40
55
|
return new ReferenceError(message);
|
|
41
56
|
}
|
|
42
57
|
|
|
58
|
+
// Display a warning message in the console, but only once for each unique
|
|
59
|
+
// message and location.
|
|
60
|
+
export function displayWarning(message, location) {
|
|
61
|
+
const warning = "Warning: " + message + lineInfo(location);
|
|
62
|
+
if (!displayedWarnings.has(warning)) {
|
|
63
|
+
displayedWarnings.add(warning);
|
|
64
|
+
console.warn(warning);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
43
68
|
/**
|
|
44
69
|
* Format an error for display in the console.
|
|
45
70
|
*
|
|
@@ -80,33 +105,10 @@ export function formatError(error) {
|
|
|
80
105
|
|
|
81
106
|
// Add location
|
|
82
107
|
if (location) {
|
|
83
|
-
let { source, start } = location;
|
|
84
|
-
// Adjust line number with offset if present (for example, if the code is in
|
|
85
|
-
// an Origami template document with front matter that was stripped)
|
|
86
|
-
let line = start.line + (source.offset ?? 0);
|
|
87
108
|
if (!fragmentInMessage) {
|
|
88
109
|
message += `\nevaluating: ${fragment}`;
|
|
89
110
|
}
|
|
90
|
-
|
|
91
|
-
if (typeof source === "object" && source.url) {
|
|
92
|
-
const { url } = source;
|
|
93
|
-
let fileRef;
|
|
94
|
-
// If URL is a file: URL, change to a relative path
|
|
95
|
-
if (url.protocol === "file:") {
|
|
96
|
-
fileRef = fileURLToPath(url);
|
|
97
|
-
const relativePath = path.relative(process.cwd(), fileRef);
|
|
98
|
-
if (!relativePath.startsWith("..")) {
|
|
99
|
-
fileRef = relativePath;
|
|
100
|
-
}
|
|
101
|
-
} else {
|
|
102
|
-
// Not a file: URL, use as is
|
|
103
|
-
fileRef = url.href;
|
|
104
|
-
}
|
|
105
|
-
message += `\n at ${fileRef}:${line}:${start.column}`;
|
|
106
|
-
} else if (source.text.includes("\n")) {
|
|
107
|
-
// Don't know the URL, but has multiple lines so add line number
|
|
108
|
-
message += `\n at line ${line}, column ${start.column}`;
|
|
109
|
-
}
|
|
111
|
+
message += lineInfo(location);
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
return message;
|
|
@@ -128,6 +130,36 @@ export function maybeOrigamiSourceCode(text) {
|
|
|
128
130
|
return origamiSourceSignals.some((signal) => text.includes(signal));
|
|
129
131
|
}
|
|
130
132
|
|
|
133
|
+
// Return user-friendly line information for the error location
|
|
134
|
+
function lineInfo(location) {
|
|
135
|
+
let { source, start } = location;
|
|
136
|
+
// Adjust line number with offset if present (for example, if the code is in
|
|
137
|
+
// an Origami template document with front matter that was stripped)
|
|
138
|
+
let line = start.line + (source.offset ?? 0);
|
|
139
|
+
|
|
140
|
+
if (typeof source === "object" && source.url) {
|
|
141
|
+
const { url } = source;
|
|
142
|
+
let fileRef;
|
|
143
|
+
// If URL is a file: URL, change to a relative path
|
|
144
|
+
if (url.protocol === "file:") {
|
|
145
|
+
fileRef = fileURLToPath(url);
|
|
146
|
+
const relativePath = path.relative(process.cwd(), fileRef);
|
|
147
|
+
if (!relativePath.startsWith("..")) {
|
|
148
|
+
fileRef = relativePath;
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
// Not a file: URL, use as is
|
|
152
|
+
fileRef = url.href;
|
|
153
|
+
}
|
|
154
|
+
return `\n at ${fileRef}:${line}:${start.column}`;
|
|
155
|
+
} else if (source.text.includes("\n")) {
|
|
156
|
+
// Don't know the URL, but has multiple lines so add line number
|
|
157
|
+
return `\n at line ${line}, column ${start.column}`;
|
|
158
|
+
} else {
|
|
159
|
+
return "";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
131
163
|
export async function scopeReferenceError(scope, key) {
|
|
132
164
|
const messages = [
|
|
133
165
|
`"${key}" is not in scope or is undefined.`,
|
package/src/runtime/evaluate.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Tree, isUnpackable } from "@weborigami/async-tree";
|
|
2
|
+
import { displayWarning, symbols } from "@weborigami/language";
|
|
2
3
|
import codeFragment from "./codeFragment.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -66,33 +67,10 @@ export default async function evaluate(code) {
|
|
|
66
67
|
throw error;
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// Object.defineProperty(result, sourceSymbol, {
|
|
74
|
-
// value: codeFragment(code.location),
|
|
75
|
-
// enumerable: false,
|
|
76
|
-
// });
|
|
77
|
-
// }
|
|
78
|
-
// if (!result[codeSymbol]) {
|
|
79
|
-
// Object.defineProperty(result, codeSymbol, {
|
|
80
|
-
// value: code,
|
|
81
|
-
// enumerable: false,
|
|
82
|
-
// });
|
|
83
|
-
// }
|
|
84
|
-
// if (!result[scopeSymbol]) {
|
|
85
|
-
// Object.defineProperty(result, scopeSymbol, {
|
|
86
|
-
// get() {
|
|
87
|
-
// return scope(this).trees;
|
|
88
|
-
// },
|
|
89
|
-
// enumerable: false,
|
|
90
|
-
// });
|
|
91
|
-
// }
|
|
92
|
-
// } catch (/** @type {any} */ error) {
|
|
93
|
-
// // Ignore errors.
|
|
94
|
-
// }
|
|
95
|
-
// }
|
|
70
|
+
if (result?.[symbols.warningSymbol]) {
|
|
71
|
+
displayWarning(result[symbols.warningSymbol], code.location);
|
|
72
|
+
delete result[symbols.warningSymbol];
|
|
73
|
+
}
|
|
96
74
|
|
|
97
75
|
return result;
|
|
98
76
|
}
|
package/src/runtime/symbols.js
CHANGED
|
@@ -155,8 +155,8 @@ describe("optimize", () => {
|
|
|
155
155
|
test("global reference", () => {
|
|
156
156
|
// Compilation of `Math` where Math is a global variable
|
|
157
157
|
const code = createCode([markers.traverse, [markers.reference, "Math"]]);
|
|
158
|
-
const globals = { Math:
|
|
159
|
-
const expected =
|
|
158
|
+
const globals = { Math: {} }; // value doesn't matter
|
|
159
|
+
const expected = globals.Math;
|
|
160
160
|
assertCodeEqual(optimize(code, { globals }), expected);
|
|
161
161
|
});
|
|
162
162
|
|
|
@@ -167,7 +167,7 @@ describe("optimize", () => {
|
|
|
167
167
|
[markers.reference, "Math.PI"],
|
|
168
168
|
]);
|
|
169
169
|
const globals = { Math: { PI: null } }; // value doesn't matter
|
|
170
|
-
const expected = [
|
|
170
|
+
const expected = [globals.Math, "PI"];
|
|
171
171
|
assertCodeEqual(optimize(code, { globals }), expected);
|
|
172
172
|
});
|
|
173
173
|
|