@weborigami/language 0.3.4-jse.7 → 0.3.4-jse.9
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 +42 -26
- package/src/compiler/origami.pegjs +18 -4
- package/src/compiler/parse.js +128 -32
- package/src/compiler/parserHelpers.js +4 -3
- package/src/runtime/errors.js +56 -24
- package/src/runtime/evaluate.js +5 -27
- package/src/runtime/getHandlers.js +1 -1
- package/src/runtime/ops.js +2 -2
- package/src/runtime/symbols.js +1 -0
- package/test/compiler/codeHelpers.js +9 -0
- package/test/compiler/optimize.test.js +9 -4
- package/test/compiler/parse.test.js +2 -10
- package/test/runtime/expressionObject.test.js +1 -1
- package/test/runtime/ops.test.js +2 -2
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.9",
|
|
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.9",
|
|
15
|
+
"@weborigami/types": "0.3.4-jse.9",
|
|
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);
|
|
@@ -56,7 +56,7 @@ export default function optimize(code, options = {}) {
|
|
|
56
56
|
|
|
57
57
|
case ops.object:
|
|
58
58
|
const entries = args;
|
|
59
|
-
const keys = entries.map(entryKey);
|
|
59
|
+
const keys = entries.map((entry) => entryKey(entry));
|
|
60
60
|
locals.push(keys);
|
|
61
61
|
break;
|
|
62
62
|
}
|
|
@@ -70,13 +70,16 @@ export default function optimize(code, options = {}) {
|
|
|
70
70
|
} else if (op === ops.object && index > 0) {
|
|
71
71
|
const [key, value] = child;
|
|
72
72
|
const adjustedLocals = avoidLocalRecursion(locals, key);
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
return annotate(
|
|
74
|
+
[
|
|
75
|
+
key,
|
|
76
|
+
optimize(/** @type {AnnotatedCode} */ (value), {
|
|
77
|
+
...options,
|
|
78
|
+
locals: adjustedLocals,
|
|
79
|
+
}),
|
|
80
|
+
],
|
|
81
|
+
child.location
|
|
82
|
+
);
|
|
80
83
|
} else if (Array.isArray(child) && "location" in child) {
|
|
81
84
|
// Review: Aside from ops.object (above), what non-instruction arrays
|
|
82
85
|
// does this descend into?
|
|
@@ -140,7 +143,7 @@ function compoundReference(key, globals, locals, location) {
|
|
|
140
143
|
const type = referenceType(head, globals, locals);
|
|
141
144
|
let result;
|
|
142
145
|
if (type === REFERENCE_GLOBAL) {
|
|
143
|
-
result = globalReference(head, globals
|
|
146
|
+
result = globalReference(head, globals);
|
|
144
147
|
} else if (type === REFERENCE_LOCAL) {
|
|
145
148
|
result = localReference(head, locals, location);
|
|
146
149
|
} else {
|
|
@@ -177,9 +180,9 @@ function getLocalReferenceDepth(locals, key) {
|
|
|
177
180
|
return depth;
|
|
178
181
|
}
|
|
179
182
|
|
|
180
|
-
function globalReference(key, globals
|
|
183
|
+
function globalReference(key, globals) {
|
|
181
184
|
const normalized = trailingSlash.remove(key);
|
|
182
|
-
return
|
|
185
|
+
return globals[normalized];
|
|
183
186
|
}
|
|
184
187
|
|
|
185
188
|
function inlineLiteral(code) {
|
|
@@ -191,6 +194,7 @@ function inlineLiteral(code) {
|
|
|
191
194
|
function localReference(key, locals, location) {
|
|
192
195
|
const normalized = trailingSlash.remove(key);
|
|
193
196
|
const depth = getLocalReferenceDepth(locals, normalized);
|
|
197
|
+
/** @type {any[]} */
|
|
194
198
|
const context = [ops.context];
|
|
195
199
|
if (depth > 0) {
|
|
196
200
|
context.push(depth);
|
|
@@ -249,24 +253,29 @@ function reference(code, globals, locals) {
|
|
|
249
253
|
|
|
250
254
|
// See if the whole key is a global or local variable
|
|
251
255
|
let type = referenceType(key, globals, locals);
|
|
252
|
-
let result;
|
|
253
256
|
if (type === REFERENCE_GLOBAL) {
|
|
254
|
-
|
|
257
|
+
return {
|
|
258
|
+
type,
|
|
259
|
+
result: globalReference(key, globals),
|
|
260
|
+
};
|
|
255
261
|
} else if (type === REFERENCE_LOCAL) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
result = compound?.result;
|
|
261
|
-
type = compound?.type;
|
|
262
|
+
return {
|
|
263
|
+
type,
|
|
264
|
+
result: localReference(key, locals, location),
|
|
265
|
+
};
|
|
262
266
|
}
|
|
263
267
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
268
|
+
// Try key as a compound reference x.y.z
|
|
269
|
+
const compound = compoundReference(key, globals, locals, location);
|
|
270
|
+
if (compound.type !== REFERENCE_EXTERNAL) {
|
|
271
|
+
return compound;
|
|
267
272
|
}
|
|
268
273
|
|
|
269
|
-
|
|
274
|
+
// Not a compound reference, must be external
|
|
275
|
+
return {
|
|
276
|
+
type: REFERENCE_EXTERNAL,
|
|
277
|
+
result: externalReference(key, locals, location),
|
|
278
|
+
};
|
|
270
279
|
}
|
|
271
280
|
|
|
272
281
|
function referenceType(key, globals, locals) {
|
|
@@ -287,7 +296,13 @@ function resolvePath(code, globals, locals, cache) {
|
|
|
287
296
|
|
|
288
297
|
let { type, result } = reference(head, globals, locals);
|
|
289
298
|
|
|
290
|
-
|
|
299
|
+
if (tail.length > 0) {
|
|
300
|
+
if (result instanceof Array) {
|
|
301
|
+
result.push(...tail);
|
|
302
|
+
} else {
|
|
303
|
+
result = annotate([result, ...tail], code.location);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
291
306
|
|
|
292
307
|
if (type === REFERENCE_EXTERNAL && cache !== null) {
|
|
293
308
|
// Cache external path
|
|
@@ -299,6 +314,7 @@ function resolvePath(code, globals, locals, cache) {
|
|
|
299
314
|
|
|
300
315
|
function scopeCall(locals, location) {
|
|
301
316
|
const depth = locals.length;
|
|
317
|
+
/** @type {any[]} */
|
|
302
318
|
const code = [ops.scope];
|
|
303
319
|
if (depth > 0) {
|
|
304
320
|
// Add context for appropriate depth to scope call
|
|
@@ -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
|
+
// Reject whitespace; see notes for `whitespace` term
|
|
870
|
+
= char:[^/,\)\]\}] !&{ return /\s/.test(char); } { return char; }
|
|
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 = /^[^\/,)\]}]/;
|
|
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(["/", ",", ")", "]", "}"], 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) {
|
|
@@ -775,12 +780,16 @@ function peg$parse(input, options) {
|
|
|
775
780
|
// A single slash is a path key
|
|
776
781
|
return annotate([ops.literal, ""], location());
|
|
777
782
|
};
|
|
778
|
-
var peg$f110 = function(
|
|
783
|
+
var peg$f110 = function(char) { return /\s/.test(char); };
|
|
784
|
+
var peg$f111 = function(char) { return char; };
|
|
785
|
+
var peg$f112 = function(keys) {
|
|
779
786
|
return annotate(keys, location());
|
|
780
787
|
};
|
|
781
|
-
var peg$
|
|
788
|
+
var peg$f113 = function() {
|
|
782
789
|
return annotate([markers.global, text()], location());
|
|
783
790
|
};
|
|
791
|
+
var peg$f114 = function(char) { return /\s/.test(char); };
|
|
792
|
+
var peg$f115 = function(char) { return char; };
|
|
784
793
|
var peg$currPos = options.peg$currPos | 0;
|
|
785
794
|
var peg$savedPos = peg$currPos;
|
|
786
795
|
var peg$posDetailsCache = [{ line: 1, column: 1 }];
|
|
@@ -1896,21 +1905,26 @@ function peg$parse(input, options) {
|
|
|
1896
1905
|
var s0, s1, s2, s3, s4;
|
|
1897
1906
|
|
|
1898
1907
|
s0 = peg$currPos;
|
|
1899
|
-
s1 = peg$
|
|
1900
|
-
if (
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
peg$
|
|
1913
|
-
|
|
1908
|
+
s1 = peg$parsecomputedPropertySpace();
|
|
1909
|
+
if (s1 !== peg$FAILED) {
|
|
1910
|
+
if (input.charCodeAt(peg$currPos) === 91) {
|
|
1911
|
+
s2 = peg$c6;
|
|
1912
|
+
peg$currPos++;
|
|
1913
|
+
} else {
|
|
1914
|
+
s2 = peg$FAILED;
|
|
1915
|
+
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
|
1916
|
+
}
|
|
1917
|
+
if (s2 !== peg$FAILED) {
|
|
1918
|
+
s3 = peg$parseexpectExpression();
|
|
1919
|
+
if (s3 !== peg$FAILED) {
|
|
1920
|
+
s4 = peg$parseexpectClosingBracket();
|
|
1921
|
+
if (s4 !== peg$FAILED) {
|
|
1922
|
+
peg$savedPos = s0;
|
|
1923
|
+
s0 = peg$f16(s3);
|
|
1924
|
+
} else {
|
|
1925
|
+
peg$currPos = s0;
|
|
1926
|
+
s0 = peg$FAILED;
|
|
1927
|
+
}
|
|
1914
1928
|
} else {
|
|
1915
1929
|
peg$currPos = s0;
|
|
1916
1930
|
s0 = peg$FAILED;
|
|
@@ -1927,6 +1941,35 @@ function peg$parse(input, options) {
|
|
|
1927
1941
|
return s0;
|
|
1928
1942
|
}
|
|
1929
1943
|
|
|
1944
|
+
function peg$parsecomputedPropertySpace() {
|
|
1945
|
+
var s0, s1, s2;
|
|
1946
|
+
|
|
1947
|
+
s0 = peg$parseshellMode();
|
|
1948
|
+
if (s0 === peg$FAILED) {
|
|
1949
|
+
s0 = peg$currPos;
|
|
1950
|
+
s1 = peg$currPos;
|
|
1951
|
+
peg$silentFails++;
|
|
1952
|
+
s2 = peg$parseshellMode();
|
|
1953
|
+
peg$silentFails--;
|
|
1954
|
+
if (s2 === peg$FAILED) {
|
|
1955
|
+
s1 = undefined;
|
|
1956
|
+
} else {
|
|
1957
|
+
peg$currPos = s1;
|
|
1958
|
+
s1 = peg$FAILED;
|
|
1959
|
+
}
|
|
1960
|
+
if (s1 !== peg$FAILED) {
|
|
1961
|
+
s2 = peg$parse__();
|
|
1962
|
+
s1 = [s1, s2];
|
|
1963
|
+
s0 = s1;
|
|
1964
|
+
} else {
|
|
1965
|
+
peg$currPos = s0;
|
|
1966
|
+
s0 = peg$FAILED;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
return s0;
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1930
1973
|
function peg$parseconditionalExpression() {
|
|
1931
1974
|
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10;
|
|
1932
1975
|
|
|
@@ -6009,15 +6052,44 @@ function peg$parse(input, options) {
|
|
|
6009
6052
|
}
|
|
6010
6053
|
|
|
6011
6054
|
function peg$parseuriKeyChar() {
|
|
6012
|
-
var s0;
|
|
6055
|
+
var s0, s1, s2, s3;
|
|
6013
6056
|
|
|
6014
|
-
s0 =
|
|
6015
|
-
|
|
6057
|
+
s0 = peg$currPos;
|
|
6058
|
+
s1 = input.charAt(peg$currPos);
|
|
6059
|
+
if (peg$r10.test(s1)) {
|
|
6016
6060
|
peg$currPos++;
|
|
6017
6061
|
} else {
|
|
6018
|
-
|
|
6062
|
+
s1 = peg$FAILED;
|
|
6019
6063
|
if (peg$silentFails === 0) { peg$fail(peg$e108); }
|
|
6020
6064
|
}
|
|
6065
|
+
if (s1 !== peg$FAILED) {
|
|
6066
|
+
s2 = peg$currPos;
|
|
6067
|
+
peg$silentFails++;
|
|
6068
|
+
peg$savedPos = peg$currPos;
|
|
6069
|
+
s3 = peg$f110(s1);
|
|
6070
|
+
if (s3) {
|
|
6071
|
+
s3 = undefined;
|
|
6072
|
+
} else {
|
|
6073
|
+
s3 = peg$FAILED;
|
|
6074
|
+
}
|
|
6075
|
+
peg$silentFails--;
|
|
6076
|
+
if (s3 === peg$FAILED) {
|
|
6077
|
+
s2 = undefined;
|
|
6078
|
+
} else {
|
|
6079
|
+
peg$currPos = s2;
|
|
6080
|
+
s2 = peg$FAILED;
|
|
6081
|
+
}
|
|
6082
|
+
if (s2 !== peg$FAILED) {
|
|
6083
|
+
peg$savedPos = s0;
|
|
6084
|
+
s0 = peg$f111(s1);
|
|
6085
|
+
} else {
|
|
6086
|
+
peg$currPos = s0;
|
|
6087
|
+
s0 = peg$FAILED;
|
|
6088
|
+
}
|
|
6089
|
+
} else {
|
|
6090
|
+
peg$currPos = s0;
|
|
6091
|
+
s0 = peg$FAILED;
|
|
6092
|
+
}
|
|
6021
6093
|
if (s0 === peg$FAILED) {
|
|
6022
6094
|
s0 = peg$parseescapedChar();
|
|
6023
6095
|
}
|
|
@@ -6045,7 +6117,7 @@ function peg$parse(input, options) {
|
|
|
6045
6117
|
}
|
|
6046
6118
|
if (s1 !== peg$FAILED) {
|
|
6047
6119
|
peg$savedPos = s0;
|
|
6048
|
-
s1 = peg$
|
|
6120
|
+
s1 = peg$f112(s1);
|
|
6049
6121
|
}
|
|
6050
6122
|
s0 = s1;
|
|
6051
6123
|
peg$silentFails--;
|
|
@@ -6096,7 +6168,7 @@ function peg$parse(input, options) {
|
|
|
6096
6168
|
}
|
|
6097
6169
|
if (s3 !== peg$FAILED) {
|
|
6098
6170
|
peg$savedPos = s0;
|
|
6099
|
-
s0 = peg$
|
|
6171
|
+
s0 = peg$f113();
|
|
6100
6172
|
} else {
|
|
6101
6173
|
peg$currPos = s0;
|
|
6102
6174
|
s0 = peg$FAILED;
|
|
@@ -6164,14 +6236,37 @@ function peg$parse(input, options) {
|
|
|
6164
6236
|
}
|
|
6165
6237
|
|
|
6166
6238
|
function peg$parsewhitespace() {
|
|
6167
|
-
var s0;
|
|
6239
|
+
var s0, s1, s2;
|
|
6168
6240
|
|
|
6169
|
-
s0 = peg$
|
|
6170
|
-
if (
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6241
|
+
s0 = peg$currPos;
|
|
6242
|
+
if (input.length > peg$currPos) {
|
|
6243
|
+
s1 = input.charAt(peg$currPos);
|
|
6244
|
+
peg$currPos++;
|
|
6245
|
+
} else {
|
|
6246
|
+
s1 = peg$FAILED;
|
|
6247
|
+
if (peg$silentFails === 0) { peg$fail(peg$e41); }
|
|
6248
|
+
}
|
|
6249
|
+
if (s1 !== peg$FAILED) {
|
|
6250
|
+
peg$savedPos = peg$currPos;
|
|
6251
|
+
s2 = peg$f114(s1);
|
|
6252
|
+
if (s2) {
|
|
6253
|
+
s2 = undefined;
|
|
6254
|
+
} else {
|
|
6255
|
+
s2 = peg$FAILED;
|
|
6174
6256
|
}
|
|
6257
|
+
if (s2 !== peg$FAILED) {
|
|
6258
|
+
peg$savedPos = s0;
|
|
6259
|
+
s0 = peg$f115(s1);
|
|
6260
|
+
} else {
|
|
6261
|
+
peg$currPos = s0;
|
|
6262
|
+
s0 = peg$FAILED;
|
|
6263
|
+
}
|
|
6264
|
+
} else {
|
|
6265
|
+
peg$currPos = s0;
|
|
6266
|
+
s0 = peg$FAILED;
|
|
6267
|
+
}
|
|
6268
|
+
if (s0 === peg$FAILED) {
|
|
6269
|
+
s0 = peg$parsecomment();
|
|
6175
6270
|
}
|
|
6176
6271
|
|
|
6177
6272
|
return s0;
|
|
@@ -6255,6 +6350,7 @@ const peg$allowedStartRules = [
|
|
|
6255
6350
|
"commaExpression",
|
|
6256
6351
|
"comment",
|
|
6257
6352
|
"computedPropertyAccess",
|
|
6353
|
+
"computedPropertySpace",
|
|
6258
6354
|
"conditionalExpression",
|
|
6259
6355
|
"digits",
|
|
6260
6356
|
"doubleArrow",
|
|
@@ -28,7 +28,7 @@ export const markers = {
|
|
|
28
28
|
* If a parse result is an object that will be evaluated at runtime, attach the
|
|
29
29
|
* location of the source code that produced it for debugging and error messages.
|
|
30
30
|
*
|
|
31
|
-
* @param {
|
|
31
|
+
* @param {any[]} code
|
|
32
32
|
* @param {CodeLocation} location
|
|
33
33
|
*/
|
|
34
34
|
export function annotate(code, location) {
|
|
@@ -375,11 +375,12 @@ export function makePath(keys) {
|
|
|
375
375
|
const reference = annotate([markers.reference, headKey], head.location);
|
|
376
376
|
|
|
377
377
|
let code = [markers.traverse, reference, ...tail];
|
|
378
|
-
|
|
378
|
+
const location = spanLocations(code);
|
|
379
|
+
code = annotate(code, location);
|
|
379
380
|
|
|
380
381
|
// Last key has trailing slash implies unpack operation
|
|
381
382
|
if (trailingSlash.has(args.at(-1)[1])) {
|
|
382
|
-
code = annotate([ops.unpack, code],
|
|
383
|
+
code = annotate([ops.unpack, code], location);
|
|
383
384
|
}
|
|
384
385
|
|
|
385
386
|
return code;
|
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/ops.js
CHANGED
|
@@ -204,7 +204,7 @@ addOpLabel(greaterThanOrEqual, "«ops.greaterThanOrEqual»");
|
|
|
204
204
|
export async function homeDirectory(...keys) {
|
|
205
205
|
const tree = new OrigamiFiles(os.homedir());
|
|
206
206
|
// Use the same handlers as the current tree
|
|
207
|
-
tree.handlers = getHandlers(this);
|
|
207
|
+
/** @type {any} */ (tree).handlers = getHandlers(this);
|
|
208
208
|
return keys.length > 0 ? Tree.traverse(tree, ...keys) : tree;
|
|
209
209
|
}
|
|
210
210
|
addOpLabel(homeDirectory, "«ops.homeDirectory»");
|
|
@@ -424,7 +424,7 @@ addOpLabel(remainder, "«ops.remainder»");
|
|
|
424
424
|
export async function rootDirectory(...keys) {
|
|
425
425
|
const tree = new OrigamiFiles("/");
|
|
426
426
|
// Use the same handlers as the current tree
|
|
427
|
-
tree.handlers = getHandlers(this);
|
|
427
|
+
/** @type {any} */ (tree).handlers = getHandlers(this);
|
|
428
428
|
return keys.length > 0 ? Tree.traverse(tree, ...keys) : tree;
|
|
429
429
|
}
|
|
430
430
|
addOpLabel(rootDirectory, "«ops.rootDirectory»");
|
package/src/runtime/symbols.js
CHANGED
|
@@ -7,6 +7,15 @@ export function assertCodeEqual(actual, expected) {
|
|
|
7
7
|
assert.deepStrictEqual(actualStripped, expectedStripped);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
export function assertCodeLocations(code) {
|
|
11
|
+
assert(code.location, "no location");
|
|
12
|
+
for (const item of code) {
|
|
13
|
+
if (Array.isArray(item)) {
|
|
14
|
+
assertCodeLocations(item);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
/**
|
|
11
20
|
* Adds a fake source to code.
|
|
12
21
|
*
|
|
@@ -4,7 +4,11 @@ import * as compile from "../../src/compiler/compile.js";
|
|
|
4
4
|
import optimize from "../../src/compiler/optimize.js";
|
|
5
5
|
import { markers } from "../../src/compiler/parserHelpers.js";
|
|
6
6
|
import { ops } from "../../src/runtime/internal.js";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
assertCodeEqual,
|
|
9
|
+
assertCodeLocations,
|
|
10
|
+
createCode,
|
|
11
|
+
} from "./codeHelpers.js";
|
|
8
12
|
|
|
9
13
|
describe("optimize", () => {
|
|
10
14
|
test("change local references to context references", () => {
|
|
@@ -155,8 +159,8 @@ describe("optimize", () => {
|
|
|
155
159
|
test("global reference", () => {
|
|
156
160
|
// Compilation of `Math` where Math is a global variable
|
|
157
161
|
const code = createCode([markers.traverse, [markers.reference, "Math"]]);
|
|
158
|
-
const globals = { Math:
|
|
159
|
-
const expected =
|
|
162
|
+
const globals = { Math: {} }; // value doesn't matter
|
|
163
|
+
const expected = globals.Math;
|
|
160
164
|
assertCodeEqual(optimize(code, { globals }), expected);
|
|
161
165
|
});
|
|
162
166
|
|
|
@@ -167,7 +171,7 @@ describe("optimize", () => {
|
|
|
167
171
|
[markers.reference, "Math.PI"],
|
|
168
172
|
]);
|
|
169
173
|
const globals = { Math: { PI: null } }; // value doesn't matter
|
|
170
|
-
const expected = [
|
|
174
|
+
const expected = [globals.Math, "PI"];
|
|
171
175
|
assertCodeEqual(optimize(code, { globals }), expected);
|
|
172
176
|
});
|
|
173
177
|
|
|
@@ -277,5 +281,6 @@ function assertCompile(expression, expected, mode = "shell") {
|
|
|
277
281
|
const globals = new ObjectTree({});
|
|
278
282
|
const fn = compile.expression(expression, { globals, mode, parent });
|
|
279
283
|
const actual = fn.code;
|
|
284
|
+
assertCodeLocations(actual);
|
|
280
285
|
assertCodeEqual(actual, expected);
|
|
281
286
|
}
|
|
@@ -3,7 +3,7 @@ import { describe, test } from "node:test";
|
|
|
3
3
|
import { parse } from "../../src/compiler/parse.js";
|
|
4
4
|
import { markers } from "../../src/compiler/parserHelpers.js";
|
|
5
5
|
import * as ops from "../../src/runtime/ops.js";
|
|
6
|
-
import { assertCodeEqual } from "./codeHelpers.js";
|
|
6
|
+
import { assertCodeEqual, assertCodeLocations } from "./codeHelpers.js";
|
|
7
7
|
|
|
8
8
|
describe("Origami parser", () => {
|
|
9
9
|
test("additiveExpression", () => {
|
|
@@ -1596,16 +1596,8 @@ function assertParse(
|
|
|
1596
1596
|
assertCodeEqual(code, expected);
|
|
1597
1597
|
}
|
|
1598
1598
|
|
|
1599
|
-
function assertCodeLocations(code) {
|
|
1600
|
-
assert(code.location, "no location");
|
|
1601
|
-
for (const item of code) {
|
|
1602
|
-
if (Array.isArray(item)) {
|
|
1603
|
-
assertCodeLocations(item);
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
1599
|
function assertThrows(startRule, source, message, position, mode = "shell") {
|
|
1600
|
+
// @ts-ignore We declare this so we can inspect it in debugger
|
|
1609
1601
|
let code;
|
|
1610
1602
|
try {
|
|
1611
1603
|
code = parse(source, {
|
|
@@ -54,7 +54,7 @@ describe("expressionObject", () => {
|
|
|
54
54
|
test("returned object values can be unpacked", async () => {
|
|
55
55
|
const entries = [["data.json", `{ "a": 1 }`]];
|
|
56
56
|
const parent = new ObjectTree({});
|
|
57
|
-
parent.handlers = {
|
|
57
|
+
/** @type {any} */ (parent).handlers = {
|
|
58
58
|
"json.handler": {
|
|
59
59
|
unpack: JSON.parse,
|
|
60
60
|
},
|
package/test/runtime/ops.test.js
CHANGED
|
@@ -358,7 +358,7 @@ describe("ops", () => {
|
|
|
358
358
|
const a = await tree.get("a");
|
|
359
359
|
const b = await a.get("b");
|
|
360
360
|
const scope = await ops.scope.call(b);
|
|
361
|
-
assert.equal(await scope
|
|
361
|
+
assert.equal(await scope?.get("c"), 1);
|
|
362
362
|
});
|
|
363
363
|
|
|
364
364
|
test("accepts an optional context", async () => {
|
|
@@ -372,7 +372,7 @@ describe("ops", () => {
|
|
|
372
372
|
const a = await tree.get("a");
|
|
373
373
|
const b = await a.get("b");
|
|
374
374
|
const scope = await ops.scope.call(b, tree);
|
|
375
|
-
assert.equal(await scope
|
|
375
|
+
assert.equal(await scope?.get("c"), 1);
|
|
376
376
|
});
|
|
377
377
|
});
|
|
378
378
|
|