@weborigami/language 0.0.48 → 0.0.50

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,19 +1,19 @@
1
1
  {
2
2
  "name": "@weborigami/language",
3
- "version": "0.0.48",
3
+ "version": "0.0.50",
4
4
  "description": "Web Origami expression language compiler and runtime",
5
5
  "type": "module",
6
6
  "main": "./main.js",
7
7
  "types": "./index.ts",
8
8
  "devDependencies": {
9
- "@types/node": "20.11.7",
10
- "typescript": "5.3.3"
9
+ "@types/node": "20.12.8",
10
+ "peggy": "4.0.2",
11
+ "typescript": "5.4.5"
11
12
  },
12
13
  "dependencies": {
13
- "@weborigami/async-tree": "0.0.48",
14
- "@weborigami/types": "0.0.48",
15
- "peggy": "4.0.2",
16
- "watcher": "2.3.0"
14
+ "@weborigami/async-tree": "0.0.50",
15
+ "@weborigami/types": "0.0.50",
16
+ "watcher": "2.3.1"
17
17
  },
18
18
  "scripts": {
19
19
  "build": "peggy --allowed-start-rules=\"*\" --format es src/compiler/origami.pegjs --output src/compiler/parse.js",
@@ -114,9 +114,10 @@ escapedChar "backslash-escaped character"
114
114
  expr
115
115
  = pipeline
116
116
 
117
- // Top-level Origami expression, possible leading/trailing whitepsace.
117
+ // Top-level Origami expression, possible shebang directive and leading/trailing
118
+ // whitepsace.
118
119
  expression "Origami expression"
119
- = __ @expr __
120
+ = shebang? __ @expr __
120
121
 
121
122
  float "floating-point number"
122
123
  = sign? digits? "." digits {
@@ -147,7 +148,7 @@ identifier "identifier"
147
148
  = chars:identifierChar+ { return chars.join(""); }
148
149
 
149
150
  identifierChar
150
- = [^(){}\[\]<>\-=,/:\`"'\\# →⇒\t\n\r] // No unescaped whitespace or special chars
151
+ = [^(){}\[\]<>\-=,/:\`"'\\ →⇒\t\n\r] // No unescaped whitespace or special chars
151
152
  / @'-' !'>' // Accept a hyphen but not in a single arrow combination
152
153
  / escapedChar
153
154
 
@@ -254,6 +255,7 @@ protocol "protocol"
254
255
  reservedProtocol "reserved protocol"
255
256
  = "https" { return ops.https; }
256
257
  / "http" { return ops.http; }
258
+ / "new" { return ops.constructor; }
257
259
  / "package" { return [ops.scope, "@package"] } // Alias
258
260
  / "treehttps" { return ops.treeHttps; }
259
261
  / "treehttp" { return ops.treeHttp; }
@@ -268,14 +270,16 @@ separator
268
270
  = __ "," __
269
271
  / whitespaceWithNewLine
270
272
 
273
+ shebang
274
+ = "#!" [^\n\r]* { return null; }
275
+
271
276
  sign
272
277
  = [+\-]
273
278
 
274
279
  singleArrow = "→" / "->"
275
280
 
276
281
  singleLineComment
277
- = "#" [^\n\r]* { return null; }
278
- / "//" [^\n\r]* { return null; }
282
+ = "//" [^\n\r]* { return null; }
279
283
 
280
284
  singleQuoteString "single quote string"
281
285
  = "'" chars:singleQuoteStringChar* "'" { return chars.join(""); }
@@ -322,7 +326,7 @@ templateDocument "template"
322
326
 
323
327
  // Template documents can contain backticks at the top level.
324
328
  templateDocumentChar
325
- = !("{{" / "${") @textChar
329
+ = !("${") @textChar
326
330
 
327
331
  // The contents of a template document containing plain text and substitutions
328
332
  templateDocumentContents
@@ -338,7 +342,7 @@ templateLiteral "template literal"
338
342
  = "`" @templateLiteralContents "`"
339
343
 
340
344
  templateLiteralChar
341
- = !("`" / "{{" / "${") @textChar
345
+ = !("`" / "${") @textChar
342
346
 
343
347
  // The contents of a template literal containing plain text and substitutions
344
348
  templateLiteralContents
@@ -350,10 +354,9 @@ templateLiteralContents
350
354
  templateLiteralText
351
355
  = chars:templateLiteralChar+ { return chars.join(""); }
352
356
 
353
- // A substitution in a template literal: `{{ fn() }}`
357
+ // A substitution in a template literal: `${x}`
354
358
  templateSubstitution "template substitution"
355
- = "{{" @expression "}}"
356
- / "${" @expression "}"
359
+ = "${" @expression "}"
357
360
 
358
361
  textChar
359
362
  = escapedChar / .
@@ -193,7 +193,7 @@ function peg$parse(input, options) {
193
193
  var peg$FAILED = {};
194
194
  var peg$source = options.grammarSource;
195
195
 
196
- var peg$startRuleFunctions = { __: peg$parse__, absoluteFilePath: peg$parseabsoluteFilePath, args: peg$parseargs, array: peg$parsearray, assignment: peg$parseassignment, assignmentOrShorthand: peg$parseassignmentOrShorthand, callTarget: peg$parsecallTarget, closingBrace: peg$parseclosingBrace, closingBracket: peg$parseclosingBracket, closingParen: peg$parseclosingParen, comment: peg$parsecomment, digits: peg$parsedigits, doubleArrow: peg$parsedoubleArrow, doubleQuoteString: peg$parsedoubleQuoteString, doubleQuoteStringChar: peg$parsedoubleQuoteStringChar, escapedChar: peg$parseescapedChar, expr: peg$parseexpr, expression: peg$parseexpression, float: peg$parsefloat, functionComposition: peg$parsefunctionComposition, group: peg$parsegroup, host: peg$parsehost, identifier: peg$parseidentifier, identifierChar: peg$parseidentifierChar, identifierList: peg$parseidentifierList, implicitParensArgs: peg$parseimplicitParensArgs, inlineSpace: peg$parseinlineSpace, integer: peg$parseinteger, lambda: peg$parselambda, leadingSlashPath: peg$parseleadingSlashPath, list: peg$parselist, multiLineComment: peg$parsemultiLineComment, newLine: peg$parsenewLine, number: peg$parsenumber, object: peg$parseobject, objectProperties: peg$parseobjectProperties, objectProperty: peg$parseobjectProperty, objectPropertyOrShorthand: peg$parseobjectPropertyOrShorthand, parameterizedLambda: peg$parseparameterizedLambda, parensArgs: peg$parseparensArgs, pipeline: peg$parsepipeline, path: peg$parsepath, pathKey: peg$parsepathKey, protocolCall: peg$parseprotocolCall, protocol: peg$parseprotocol, reservedProtocol: peg$parsereservedProtocol, scopeReference: peg$parsescopeReference, separator: peg$parseseparator, sign: peg$parsesign, singleArrow: peg$parsesingleArrow, singleLineComment: peg$parsesingleLineComment, singleQuoteString: peg$parsesingleQuoteString, singleQuoteStringChar: peg$parsesingleQuoteStringChar, step: peg$parsestep, start: peg$parsestart, string: peg$parsestring, 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, tree: peg$parsetree, treeAssignments: peg$parsetreeAssignments, whitespaceWithNewLine: peg$parsewhitespaceWithNewLine };
196
+ var peg$startRuleFunctions = { __: peg$parse__, absoluteFilePath: peg$parseabsoluteFilePath, args: peg$parseargs, array: peg$parsearray, assignment: peg$parseassignment, assignmentOrShorthand: peg$parseassignmentOrShorthand, callTarget: peg$parsecallTarget, closingBrace: peg$parseclosingBrace, closingBracket: peg$parseclosingBracket, closingParen: peg$parseclosingParen, comment: peg$parsecomment, digits: peg$parsedigits, doubleArrow: peg$parsedoubleArrow, doubleQuoteString: peg$parsedoubleQuoteString, doubleQuoteStringChar: peg$parsedoubleQuoteStringChar, escapedChar: peg$parseescapedChar, expr: peg$parseexpr, expression: peg$parseexpression, float: peg$parsefloat, functionComposition: peg$parsefunctionComposition, group: peg$parsegroup, host: peg$parsehost, identifier: peg$parseidentifier, identifierChar: peg$parseidentifierChar, identifierList: peg$parseidentifierList, implicitParensArgs: peg$parseimplicitParensArgs, inlineSpace: peg$parseinlineSpace, integer: peg$parseinteger, lambda: peg$parselambda, leadingSlashPath: peg$parseleadingSlashPath, list: peg$parselist, multiLineComment: peg$parsemultiLineComment, newLine: peg$parsenewLine, number: peg$parsenumber, object: peg$parseobject, objectProperties: peg$parseobjectProperties, objectProperty: peg$parseobjectProperty, objectPropertyOrShorthand: peg$parseobjectPropertyOrShorthand, parameterizedLambda: peg$parseparameterizedLambda, parensArgs: peg$parseparensArgs, pipeline: peg$parsepipeline, path: peg$parsepath, pathKey: peg$parsepathKey, protocolCall: peg$parseprotocolCall, protocol: peg$parseprotocol, reservedProtocol: peg$parsereservedProtocol, scopeReference: peg$parsescopeReference, separator: peg$parseseparator, shebang: peg$parseshebang, sign: peg$parsesign, singleArrow: peg$parsesingleArrow, singleLineComment: peg$parsesingleLineComment, singleQuoteString: peg$parsesingleQuoteString, singleQuoteStringChar: peg$parsesingleQuoteStringChar, step: peg$parsestep, start: peg$parsestart, string: peg$parsestring, 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, tree: peg$parsetree, treeAssignments: peg$parsetreeAssignments, whitespaceWithNewLine: peg$parsewhitespaceWithNewLine };
197
197
  var peg$startRuleFunction = peg$parse__;
198
198
 
199
199
  var peg$c0 = "//";
@@ -220,25 +220,24 @@ function peg$parse(input, options) {
220
220
  var peg$c21 = "{";
221
221
  var peg$c22 = "https";
222
222
  var peg$c23 = "http";
223
- var peg$c24 = "package";
224
- var peg$c25 = "treehttps";
225
- var peg$c26 = "treehttp";
226
- var peg$c27 = "tree";
227
- var peg$c28 = ",";
228
- var peg$c29 = "\u2192";
229
- var peg$c30 = "->";
230
- var peg$c31 = "#";
231
- var peg$c32 = "'";
232
- var peg$c33 = "{{";
223
+ var peg$c24 = "new";
224
+ var peg$c25 = "package";
225
+ var peg$c26 = "treehttps";
226
+ var peg$c27 = "treehttp";
227
+ var peg$c28 = "tree";
228
+ var peg$c29 = ",";
229
+ var peg$c30 = "#!";
230
+ var peg$c31 = "\u2192";
231
+ var peg$c32 = "->";
232
+ var peg$c33 = "'";
233
233
  var peg$c34 = "${";
234
234
  var peg$c35 = "`";
235
- var peg$c36 = "}}";
236
235
 
237
236
  var peg$r0 = /^[0-9]/;
238
- var peg$r1 = /^[^(){}[\]<>\-=,\/:`"'\\# \u2192\u21D2\t\n\r]/;
237
+ var peg$r1 = /^[^(){}[\]<>\-=,\/:`"'\\ \u2192\u21D2\t\n\r]/;
239
238
  var peg$r2 = /^[ \t]/;
240
- var peg$r3 = /^[+\-]/;
241
- var peg$r4 = /^[^\n\r]/;
239
+ var peg$r3 = /^[^\n\r]/;
240
+ var peg$r4 = /^[+\-]/;
242
241
 
243
242
  var peg$e0 = peg$otherExpectation("absolute file path");
244
243
  var peg$e1 = peg$literalExpectation("//", false);
@@ -269,7 +268,7 @@ function peg$parse(input, options) {
269
268
  var peg$e26 = peg$otherExpectation("HTTP/HTTPS host");
270
269
  var peg$e27 = peg$literalExpectation(":", false);
271
270
  var peg$e28 = peg$otherExpectation("identifier");
272
- var peg$e29 = peg$classExpectation(["(", ")", "{", "}", "[", "]", "<", ">", "-", "=", ",", "/", ":", "`", "\"", "'", "\\", "#", " ", "\u2192", "\u21D2", "\t", "\n", "\r"], true, false);
271
+ var peg$e29 = peg$classExpectation(["(", ")", "{", "}", "[", "]", "<", ">", "-", "=", ",", "/", ":", "`", "\"", "'", "\\", " ", "\u2192", "\u21D2", "\t", "\n", "\r"], true, false);
273
272
  var peg$e30 = peg$literalExpectation("-", false);
274
273
  var peg$e31 = peg$literalExpectation(">", false);
275
274
  var peg$e32 = peg$otherExpectation("arguments with implicit parentheses");
@@ -296,29 +295,28 @@ function peg$parse(input, options) {
296
295
  var peg$e53 = peg$otherExpectation("reserved protocol");
297
296
  var peg$e54 = peg$literalExpectation("https", false);
298
297
  var peg$e55 = peg$literalExpectation("http", false);
299
- var peg$e56 = peg$literalExpectation("package", false);
300
- var peg$e57 = peg$literalExpectation("treehttps", false);
301
- var peg$e58 = peg$literalExpectation("treehttp", false);
302
- var peg$e59 = peg$literalExpectation("tree", false);
303
- var peg$e60 = peg$otherExpectation("scope reference");
304
- var peg$e61 = peg$literalExpectation(",", false);
305
- var peg$e62 = peg$classExpectation(["+", "-"], false, false);
306
- var peg$e63 = peg$literalExpectation("\u2192", false);
307
- var peg$e64 = peg$literalExpectation("->", false);
308
- var peg$e65 = peg$literalExpectation("#", false);
309
- var peg$e66 = peg$classExpectation(["\n", "\r"], true, false);
310
- var peg$e67 = peg$otherExpectation("single quote string");
311
- var peg$e68 = peg$literalExpectation("'", false);
312
- var peg$e69 = peg$otherExpectation("string");
313
- var peg$e70 = peg$otherExpectation("template");
314
- var peg$e71 = peg$literalExpectation("{{", false);
298
+ var peg$e56 = peg$literalExpectation("new", false);
299
+ var peg$e57 = peg$literalExpectation("package", false);
300
+ var peg$e58 = peg$literalExpectation("treehttps", false);
301
+ var peg$e59 = peg$literalExpectation("treehttp", false);
302
+ var peg$e60 = peg$literalExpectation("tree", false);
303
+ var peg$e61 = peg$otherExpectation("scope reference");
304
+ var peg$e62 = peg$literalExpectation(",", false);
305
+ var peg$e63 = peg$literalExpectation("#!", false);
306
+ var peg$e64 = peg$classExpectation(["\n", "\r"], true, false);
307
+ var peg$e65 = peg$classExpectation(["+", "-"], false, false);
308
+ var peg$e66 = peg$literalExpectation("\u2192", false);
309
+ var peg$e67 = peg$literalExpectation("->", false);
310
+ var peg$e68 = peg$otherExpectation("single quote string");
311
+ var peg$e69 = peg$literalExpectation("'", false);
312
+ var peg$e70 = peg$otherExpectation("string");
313
+ var peg$e71 = peg$otherExpectation("template");
315
314
  var peg$e72 = peg$literalExpectation("${", false);
316
315
  var peg$e73 = peg$otherExpectation("template text");
317
316
  var peg$e74 = peg$otherExpectation("template literal");
318
317
  var peg$e75 = peg$literalExpectation("`", false);
319
318
  var peg$e76 = peg$otherExpectation("template substitution");
320
- var peg$e77 = peg$literalExpectation("}}", false);
321
- var peg$e78 = peg$otherExpectation("tree literal");
319
+ var peg$e77 = peg$otherExpectation("tree literal");
322
320
 
323
321
  var peg$f0 = function() { return ""; };
324
322
  var peg$f1 = function(path) {
@@ -383,28 +381,29 @@ function peg$parse(input, options) {
383
381
  };
384
382
  var peg$f24 = function() { return ops.https; };
385
383
  var peg$f25 = function() { return ops.http; };
386
- var peg$f26 = function() { return [ops.scope, "@package"] };
387
- var peg$f27 = function() { return ops.treeHttps; };
388
- var peg$f28 = function() { return ops.treeHttp; };
389
- var peg$f29 = function() { return ops.treeHttps; };
390
- var peg$f30 = function(key) {
384
+ var peg$f26 = function() { return ops.constructor; };
385
+ var peg$f27 = function() { return [ops.scope, "@package"] };
386
+ var peg$f28 = function() { return ops.treeHttps; };
387
+ var peg$f29 = function() { return ops.treeHttp; };
388
+ var peg$f30 = function() { return ops.treeHttps; };
389
+ var peg$f31 = function(key) {
391
390
  return annotate([ops.scope, key], location());
392
391
  };
393
- var peg$f31 = function() { return null; };
394
392
  var peg$f32 = function() { return null; };
395
- var peg$f33 = function(chars) { return chars.join(""); };
396
- var peg$f34 = function(contents) {
393
+ var peg$f33 = function() { return null; };
394
+ var peg$f34 = function(chars) { return chars.join(""); };
395
+ var peg$f35 = function(contents) {
397
396
  return annotate([ops.lambda, null, contents], location());
398
397
  };
399
- var peg$f35 = function(parts) {
398
+ var peg$f36 = function(parts) {
400
399
  return annotate(makeTemplate(parts), location());
401
400
  };
402
- var peg$f36 = function(chars) { return chars.join(""); };
403
- var peg$f37 = function(parts) {
401
+ var peg$f37 = function(chars) { return chars.join(""); };
402
+ var peg$f38 = function(parts) {
404
403
  return annotate(makeTemplate(parts), location());
405
404
  };
406
- var peg$f38 = function(chars) { return chars.join(""); };
407
- var peg$f39 = function(assignments) {
405
+ var peg$f39 = function(chars) { return chars.join(""); };
406
+ var peg$f40 = function(assignments) {
408
407
  return annotate([ops.tree, ...(assignments ?? [])], location());
409
408
  };
410
409
  var peg$currPos = options.peg$currPos | 0;
@@ -1099,15 +1098,19 @@ function peg$parse(input, options) {
1099
1098
  }
1100
1099
 
1101
1100
  function peg$parseexpression() {
1102
- var s0, s1, s2, s3;
1101
+ var s0, s1, s2, s3, s4;
1103
1102
 
1104
1103
  peg$silentFails++;
1105
1104
  s0 = peg$currPos;
1106
- s1 = peg$parse__();
1107
- s2 = peg$parsepipeline();
1108
- if (s2 !== peg$FAILED) {
1109
- s3 = peg$parse__();
1110
- s0 = s2;
1105
+ s1 = peg$parseshebang();
1106
+ if (s1 === peg$FAILED) {
1107
+ s1 = null;
1108
+ }
1109
+ s2 = peg$parse__();
1110
+ s3 = peg$parsepipeline();
1111
+ if (s3 !== peg$FAILED) {
1112
+ s4 = peg$parse__();
1113
+ s0 = s3;
1111
1114
  } else {
1112
1115
  peg$currPos = s0;
1113
1116
  s0 = peg$FAILED;
@@ -2246,9 +2249,9 @@ function peg$parse(input, options) {
2246
2249
  s0 = s1;
2247
2250
  if (s0 === peg$FAILED) {
2248
2251
  s0 = peg$currPos;
2249
- if (input.substr(peg$currPos, 7) === peg$c24) {
2252
+ if (input.substr(peg$currPos, 3) === peg$c24) {
2250
2253
  s1 = peg$c24;
2251
- peg$currPos += 7;
2254
+ peg$currPos += 3;
2252
2255
  } else {
2253
2256
  s1 = peg$FAILED;
2254
2257
  if (peg$silentFails === 0) { peg$fail(peg$e56); }
@@ -2260,9 +2263,9 @@ function peg$parse(input, options) {
2260
2263
  s0 = s1;
2261
2264
  if (s0 === peg$FAILED) {
2262
2265
  s0 = peg$currPos;
2263
- if (input.substr(peg$currPos, 9) === peg$c25) {
2266
+ if (input.substr(peg$currPos, 7) === peg$c25) {
2264
2267
  s1 = peg$c25;
2265
- peg$currPos += 9;
2268
+ peg$currPos += 7;
2266
2269
  } else {
2267
2270
  s1 = peg$FAILED;
2268
2271
  if (peg$silentFails === 0) { peg$fail(peg$e57); }
@@ -2274,9 +2277,9 @@ function peg$parse(input, options) {
2274
2277
  s0 = s1;
2275
2278
  if (s0 === peg$FAILED) {
2276
2279
  s0 = peg$currPos;
2277
- if (input.substr(peg$currPos, 8) === peg$c26) {
2280
+ if (input.substr(peg$currPos, 9) === peg$c26) {
2278
2281
  s1 = peg$c26;
2279
- peg$currPos += 8;
2282
+ peg$currPos += 9;
2280
2283
  } else {
2281
2284
  s1 = peg$FAILED;
2282
2285
  if (peg$silentFails === 0) { peg$fail(peg$e58); }
@@ -2288,9 +2291,9 @@ function peg$parse(input, options) {
2288
2291
  s0 = s1;
2289
2292
  if (s0 === peg$FAILED) {
2290
2293
  s0 = peg$currPos;
2291
- if (input.substr(peg$currPos, 4) === peg$c27) {
2294
+ if (input.substr(peg$currPos, 8) === peg$c27) {
2292
2295
  s1 = peg$c27;
2293
- peg$currPos += 4;
2296
+ peg$currPos += 8;
2294
2297
  } else {
2295
2298
  s1 = peg$FAILED;
2296
2299
  if (peg$silentFails === 0) { peg$fail(peg$e59); }
@@ -2300,6 +2303,21 @@ function peg$parse(input, options) {
2300
2303
  s1 = peg$f29();
2301
2304
  }
2302
2305
  s0 = s1;
2306
+ if (s0 === peg$FAILED) {
2307
+ s0 = peg$currPos;
2308
+ if (input.substr(peg$currPos, 4) === peg$c28) {
2309
+ s1 = peg$c28;
2310
+ peg$currPos += 4;
2311
+ } else {
2312
+ s1 = peg$FAILED;
2313
+ if (peg$silentFails === 0) { peg$fail(peg$e60); }
2314
+ }
2315
+ if (s1 !== peg$FAILED) {
2316
+ peg$savedPos = s0;
2317
+ s1 = peg$f30();
2318
+ }
2319
+ s0 = s1;
2320
+ }
2303
2321
  }
2304
2322
  }
2305
2323
  }
@@ -2322,13 +2340,13 @@ function peg$parse(input, options) {
2322
2340
  s1 = peg$parseidentifier();
2323
2341
  if (s1 !== peg$FAILED) {
2324
2342
  peg$savedPos = s0;
2325
- s1 = peg$f30(s1);
2343
+ s1 = peg$f31(s1);
2326
2344
  }
2327
2345
  s0 = s1;
2328
2346
  peg$silentFails--;
2329
2347
  if (s0 === peg$FAILED) {
2330
2348
  s1 = peg$FAILED;
2331
- if (peg$silentFails === 0) { peg$fail(peg$e60); }
2349
+ if (peg$silentFails === 0) { peg$fail(peg$e61); }
2332
2350
  }
2333
2351
 
2334
2352
  return s0;
@@ -2340,11 +2358,11 @@ function peg$parse(input, options) {
2340
2358
  s0 = peg$currPos;
2341
2359
  s1 = peg$parse__();
2342
2360
  if (input.charCodeAt(peg$currPos) === 44) {
2343
- s2 = peg$c28;
2361
+ s2 = peg$c29;
2344
2362
  peg$currPos++;
2345
2363
  } else {
2346
2364
  s2 = peg$FAILED;
2347
- if (peg$silentFails === 0) { peg$fail(peg$e61); }
2365
+ if (peg$silentFails === 0) { peg$fail(peg$e62); }
2348
2366
  }
2349
2367
  if (s2 !== peg$FAILED) {
2350
2368
  s3 = peg$parse__();
@@ -2361,15 +2379,55 @@ function peg$parse(input, options) {
2361
2379
  return s0;
2362
2380
  }
2363
2381
 
2382
+ function peg$parseshebang() {
2383
+ var s0, s1, s2, s3;
2384
+
2385
+ s0 = peg$currPos;
2386
+ if (input.substr(peg$currPos, 2) === peg$c30) {
2387
+ s1 = peg$c30;
2388
+ peg$currPos += 2;
2389
+ } else {
2390
+ s1 = peg$FAILED;
2391
+ if (peg$silentFails === 0) { peg$fail(peg$e63); }
2392
+ }
2393
+ if (s1 !== peg$FAILED) {
2394
+ s2 = [];
2395
+ s3 = input.charAt(peg$currPos);
2396
+ if (peg$r3.test(s3)) {
2397
+ peg$currPos++;
2398
+ } else {
2399
+ s3 = peg$FAILED;
2400
+ if (peg$silentFails === 0) { peg$fail(peg$e64); }
2401
+ }
2402
+ while (s3 !== peg$FAILED) {
2403
+ s2.push(s3);
2404
+ s3 = input.charAt(peg$currPos);
2405
+ if (peg$r3.test(s3)) {
2406
+ peg$currPos++;
2407
+ } else {
2408
+ s3 = peg$FAILED;
2409
+ if (peg$silentFails === 0) { peg$fail(peg$e64); }
2410
+ }
2411
+ }
2412
+ peg$savedPos = s0;
2413
+ s0 = peg$f32();
2414
+ } else {
2415
+ peg$currPos = s0;
2416
+ s0 = peg$FAILED;
2417
+ }
2418
+
2419
+ return s0;
2420
+ }
2421
+
2364
2422
  function peg$parsesign() {
2365
2423
  var s0;
2366
2424
 
2367
2425
  s0 = input.charAt(peg$currPos);
2368
- if (peg$r3.test(s0)) {
2426
+ if (peg$r4.test(s0)) {
2369
2427
  peg$currPos++;
2370
2428
  } else {
2371
2429
  s0 = peg$FAILED;
2372
- if (peg$silentFails === 0) { peg$fail(peg$e62); }
2430
+ if (peg$silentFails === 0) { peg$fail(peg$e65); }
2373
2431
  }
2374
2432
 
2375
2433
  return s0;
@@ -2379,19 +2437,19 @@ function peg$parse(input, options) {
2379
2437
  var s0;
2380
2438
 
2381
2439
  if (input.charCodeAt(peg$currPos) === 8594) {
2382
- s0 = peg$c29;
2440
+ s0 = peg$c31;
2383
2441
  peg$currPos++;
2384
2442
  } else {
2385
2443
  s0 = peg$FAILED;
2386
- if (peg$silentFails === 0) { peg$fail(peg$e63); }
2444
+ if (peg$silentFails === 0) { peg$fail(peg$e66); }
2387
2445
  }
2388
2446
  if (s0 === peg$FAILED) {
2389
- if (input.substr(peg$currPos, 2) === peg$c30) {
2390
- s0 = peg$c30;
2447
+ if (input.substr(peg$currPos, 2) === peg$c32) {
2448
+ s0 = peg$c32;
2391
2449
  peg$currPos += 2;
2392
2450
  } else {
2393
2451
  s0 = peg$FAILED;
2394
- if (peg$silentFails === 0) { peg$fail(peg$e64); }
2452
+ if (peg$silentFails === 0) { peg$fail(peg$e67); }
2395
2453
  }
2396
2454
  }
2397
2455
 
@@ -2402,73 +2460,38 @@ function peg$parse(input, options) {
2402
2460
  var s0, s1, s2, s3;
2403
2461
 
2404
2462
  s0 = peg$currPos;
2405
- if (input.charCodeAt(peg$currPos) === 35) {
2406
- s1 = peg$c31;
2407
- peg$currPos++;
2463
+ if (input.substr(peg$currPos, 2) === peg$c0) {
2464
+ s1 = peg$c0;
2465
+ peg$currPos += 2;
2408
2466
  } else {
2409
2467
  s1 = peg$FAILED;
2410
- if (peg$silentFails === 0) { peg$fail(peg$e65); }
2468
+ if (peg$silentFails === 0) { peg$fail(peg$e1); }
2411
2469
  }
2412
2470
  if (s1 !== peg$FAILED) {
2413
2471
  s2 = [];
2414
2472
  s3 = input.charAt(peg$currPos);
2415
- if (peg$r4.test(s3)) {
2473
+ if (peg$r3.test(s3)) {
2416
2474
  peg$currPos++;
2417
2475
  } else {
2418
2476
  s3 = peg$FAILED;
2419
- if (peg$silentFails === 0) { peg$fail(peg$e66); }
2477
+ if (peg$silentFails === 0) { peg$fail(peg$e64); }
2420
2478
  }
2421
2479
  while (s3 !== peg$FAILED) {
2422
2480
  s2.push(s3);
2423
2481
  s3 = input.charAt(peg$currPos);
2424
- if (peg$r4.test(s3)) {
2482
+ if (peg$r3.test(s3)) {
2425
2483
  peg$currPos++;
2426
2484
  } else {
2427
2485
  s3 = peg$FAILED;
2428
- if (peg$silentFails === 0) { peg$fail(peg$e66); }
2486
+ if (peg$silentFails === 0) { peg$fail(peg$e64); }
2429
2487
  }
2430
2488
  }
2431
2489
  peg$savedPos = s0;
2432
- s0 = peg$f31();
2490
+ s0 = peg$f33();
2433
2491
  } else {
2434
2492
  peg$currPos = s0;
2435
2493
  s0 = peg$FAILED;
2436
2494
  }
2437
- if (s0 === peg$FAILED) {
2438
- s0 = peg$currPos;
2439
- if (input.substr(peg$currPos, 2) === peg$c0) {
2440
- s1 = peg$c0;
2441
- peg$currPos += 2;
2442
- } else {
2443
- s1 = peg$FAILED;
2444
- if (peg$silentFails === 0) { peg$fail(peg$e1); }
2445
- }
2446
- if (s1 !== peg$FAILED) {
2447
- s2 = [];
2448
- s3 = input.charAt(peg$currPos);
2449
- if (peg$r4.test(s3)) {
2450
- peg$currPos++;
2451
- } else {
2452
- s3 = peg$FAILED;
2453
- if (peg$silentFails === 0) { peg$fail(peg$e66); }
2454
- }
2455
- while (s3 !== peg$FAILED) {
2456
- s2.push(s3);
2457
- s3 = input.charAt(peg$currPos);
2458
- if (peg$r4.test(s3)) {
2459
- peg$currPos++;
2460
- } else {
2461
- s3 = peg$FAILED;
2462
- if (peg$silentFails === 0) { peg$fail(peg$e66); }
2463
- }
2464
- }
2465
- peg$savedPos = s0;
2466
- s0 = peg$f32();
2467
- } else {
2468
- peg$currPos = s0;
2469
- s0 = peg$FAILED;
2470
- }
2471
- }
2472
2495
 
2473
2496
  return s0;
2474
2497
  }
@@ -2479,11 +2502,11 @@ function peg$parse(input, options) {
2479
2502
  peg$silentFails++;
2480
2503
  s0 = peg$currPos;
2481
2504
  if (input.charCodeAt(peg$currPos) === 39) {
2482
- s1 = peg$c32;
2505
+ s1 = peg$c33;
2483
2506
  peg$currPos++;
2484
2507
  } else {
2485
2508
  s1 = peg$FAILED;
2486
- if (peg$silentFails === 0) { peg$fail(peg$e68); }
2509
+ if (peg$silentFails === 0) { peg$fail(peg$e69); }
2487
2510
  }
2488
2511
  if (s1 !== peg$FAILED) {
2489
2512
  s2 = [];
@@ -2493,15 +2516,15 @@ function peg$parse(input, options) {
2493
2516
  s3 = peg$parsesingleQuoteStringChar();
2494
2517
  }
2495
2518
  if (input.charCodeAt(peg$currPos) === 39) {
2496
- s3 = peg$c32;
2519
+ s3 = peg$c33;
2497
2520
  peg$currPos++;
2498
2521
  } else {
2499
2522
  s3 = peg$FAILED;
2500
- if (peg$silentFails === 0) { peg$fail(peg$e68); }
2523
+ if (peg$silentFails === 0) { peg$fail(peg$e69); }
2501
2524
  }
2502
2525
  if (s3 !== peg$FAILED) {
2503
2526
  peg$savedPos = s0;
2504
- s0 = peg$f33(s2);
2527
+ s0 = peg$f34(s2);
2505
2528
  } else {
2506
2529
  peg$currPos = s0;
2507
2530
  s0 = peg$FAILED;
@@ -2513,7 +2536,7 @@ function peg$parse(input, options) {
2513
2536
  peg$silentFails--;
2514
2537
  if (s0 === peg$FAILED) {
2515
2538
  s1 = peg$FAILED;
2516
- if (peg$silentFails === 0) { peg$fail(peg$e67); }
2539
+ if (peg$silentFails === 0) { peg$fail(peg$e68); }
2517
2540
  }
2518
2541
 
2519
2542
  return s0;
@@ -2526,11 +2549,11 @@ function peg$parse(input, options) {
2526
2549
  s1 = peg$currPos;
2527
2550
  peg$silentFails++;
2528
2551
  if (input.charCodeAt(peg$currPos) === 39) {
2529
- s2 = peg$c32;
2552
+ s2 = peg$c33;
2530
2553
  peg$currPos++;
2531
2554
  } else {
2532
2555
  s2 = peg$FAILED;
2533
- if (peg$silentFails === 0) { peg$fail(peg$e68); }
2556
+ if (peg$silentFails === 0) { peg$fail(peg$e69); }
2534
2557
  }
2535
2558
  if (s2 === peg$FAILED) {
2536
2559
  s2 = peg$parsenewLine();
@@ -2621,7 +2644,7 @@ function peg$parse(input, options) {
2621
2644
  peg$silentFails--;
2622
2645
  if (s0 === peg$FAILED) {
2623
2646
  s1 = peg$FAILED;
2624
- if (peg$silentFails === 0) { peg$fail(peg$e69); }
2647
+ if (peg$silentFails === 0) { peg$fail(peg$e70); }
2625
2648
  }
2626
2649
 
2627
2650
  return s0;
@@ -2634,11 +2657,11 @@ function peg$parse(input, options) {
2634
2657
  s0 = peg$currPos;
2635
2658
  s1 = peg$parsetemplateDocumentContents();
2636
2659
  peg$savedPos = s0;
2637
- s1 = peg$f34(s1);
2660
+ s1 = peg$f35(s1);
2638
2661
  s0 = s1;
2639
2662
  peg$silentFails--;
2640
2663
  s1 = peg$FAILED;
2641
- if (peg$silentFails === 0) { peg$fail(peg$e70); }
2664
+ if (peg$silentFails === 0) { peg$fail(peg$e71); }
2642
2665
 
2643
2666
  return s0;
2644
2667
  }
@@ -2649,21 +2672,12 @@ function peg$parse(input, options) {
2649
2672
  s0 = peg$currPos;
2650
2673
  s1 = peg$currPos;
2651
2674
  peg$silentFails++;
2652
- if (input.substr(peg$currPos, 2) === peg$c33) {
2653
- s2 = peg$c33;
2675
+ if (input.substr(peg$currPos, 2) === peg$c34) {
2676
+ s2 = peg$c34;
2654
2677
  peg$currPos += 2;
2655
2678
  } else {
2656
2679
  s2 = peg$FAILED;
2657
- if (peg$silentFails === 0) { peg$fail(peg$e71); }
2658
- }
2659
- if (s2 === peg$FAILED) {
2660
- if (input.substr(peg$currPos, 2) === peg$c34) {
2661
- s2 = peg$c34;
2662
- peg$currPos += 2;
2663
- } else {
2664
- s2 = peg$FAILED;
2665
- if (peg$silentFails === 0) { peg$fail(peg$e72); }
2666
- }
2680
+ if (peg$silentFails === 0) { peg$fail(peg$e72); }
2667
2681
  }
2668
2682
  peg$silentFails--;
2669
2683
  if (s2 === peg$FAILED) {
@@ -2705,7 +2719,7 @@ function peg$parse(input, options) {
2705
2719
  }
2706
2720
  }
2707
2721
  peg$savedPos = s0;
2708
- s1 = peg$f35(s1);
2722
+ s1 = peg$f36(s1);
2709
2723
  s0 = s1;
2710
2724
 
2711
2725
  return s0;
@@ -2728,7 +2742,7 @@ function peg$parse(input, options) {
2728
2742
  }
2729
2743
  if (s1 !== peg$FAILED) {
2730
2744
  peg$savedPos = s0;
2731
- s1 = peg$f36(s1);
2745
+ s1 = peg$f37(s1);
2732
2746
  }
2733
2747
  s0 = s1;
2734
2748
  peg$silentFails--;
@@ -2794,21 +2808,12 @@ function peg$parse(input, options) {
2794
2808
  if (peg$silentFails === 0) { peg$fail(peg$e75); }
2795
2809
  }
2796
2810
  if (s2 === peg$FAILED) {
2797
- if (input.substr(peg$currPos, 2) === peg$c33) {
2798
- s2 = peg$c33;
2811
+ if (input.substr(peg$currPos, 2) === peg$c34) {
2812
+ s2 = peg$c34;
2799
2813
  peg$currPos += 2;
2800
2814
  } else {
2801
2815
  s2 = peg$FAILED;
2802
- if (peg$silentFails === 0) { peg$fail(peg$e71); }
2803
- }
2804
- if (s2 === peg$FAILED) {
2805
- if (input.substr(peg$currPos, 2) === peg$c34) {
2806
- s2 = peg$c34;
2807
- peg$currPos += 2;
2808
- } else {
2809
- s2 = peg$FAILED;
2810
- if (peg$silentFails === 0) { peg$fail(peg$e72); }
2811
- }
2816
+ if (peg$silentFails === 0) { peg$fail(peg$e72); }
2812
2817
  }
2813
2818
  }
2814
2819
  peg$silentFails--;
@@ -2851,7 +2856,7 @@ function peg$parse(input, options) {
2851
2856
  }
2852
2857
  }
2853
2858
  peg$savedPos = s0;
2854
- s1 = peg$f37(s1);
2859
+ s1 = peg$f38(s1);
2855
2860
  s0 = s1;
2856
2861
 
2857
2862
  return s0;
@@ -2873,7 +2878,7 @@ function peg$parse(input, options) {
2873
2878
  }
2874
2879
  if (s1 !== peg$FAILED) {
2875
2880
  peg$savedPos = s0;
2876
- s1 = peg$f38(s1);
2881
+ s1 = peg$f39(s1);
2877
2882
  }
2878
2883
  s0 = s1;
2879
2884
 
@@ -2885,22 +2890,22 @@ function peg$parse(input, options) {
2885
2890
 
2886
2891
  peg$silentFails++;
2887
2892
  s0 = peg$currPos;
2888
- if (input.substr(peg$currPos, 2) === peg$c33) {
2889
- s1 = peg$c33;
2893
+ if (input.substr(peg$currPos, 2) === peg$c34) {
2894
+ s1 = peg$c34;
2890
2895
  peg$currPos += 2;
2891
2896
  } else {
2892
2897
  s1 = peg$FAILED;
2893
- if (peg$silentFails === 0) { peg$fail(peg$e71); }
2898
+ if (peg$silentFails === 0) { peg$fail(peg$e72); }
2894
2899
  }
2895
2900
  if (s1 !== peg$FAILED) {
2896
2901
  s2 = peg$parseexpression();
2897
2902
  if (s2 !== peg$FAILED) {
2898
- if (input.substr(peg$currPos, 2) === peg$c36) {
2899
- s3 = peg$c36;
2900
- peg$currPos += 2;
2903
+ if (input.charCodeAt(peg$currPos) === 125) {
2904
+ s3 = peg$c3;
2905
+ peg$currPos++;
2901
2906
  } else {
2902
2907
  s3 = peg$FAILED;
2903
- if (peg$silentFails === 0) { peg$fail(peg$e77); }
2908
+ if (peg$silentFails === 0) { peg$fail(peg$e8); }
2904
2909
  }
2905
2910
  if (s3 !== peg$FAILED) {
2906
2911
  s0 = s2;
@@ -2916,40 +2921,6 @@ function peg$parse(input, options) {
2916
2921
  peg$currPos = s0;
2917
2922
  s0 = peg$FAILED;
2918
2923
  }
2919
- if (s0 === peg$FAILED) {
2920
- s0 = peg$currPos;
2921
- if (input.substr(peg$currPos, 2) === peg$c34) {
2922
- s1 = peg$c34;
2923
- peg$currPos += 2;
2924
- } else {
2925
- s1 = peg$FAILED;
2926
- if (peg$silentFails === 0) { peg$fail(peg$e72); }
2927
- }
2928
- if (s1 !== peg$FAILED) {
2929
- s2 = peg$parseexpression();
2930
- if (s2 !== peg$FAILED) {
2931
- if (input.charCodeAt(peg$currPos) === 125) {
2932
- s3 = peg$c3;
2933
- peg$currPos++;
2934
- } else {
2935
- s3 = peg$FAILED;
2936
- if (peg$silentFails === 0) { peg$fail(peg$e8); }
2937
- }
2938
- if (s3 !== peg$FAILED) {
2939
- s0 = s2;
2940
- } else {
2941
- peg$currPos = s0;
2942
- s0 = peg$FAILED;
2943
- }
2944
- } else {
2945
- peg$currPos = s0;
2946
- s0 = peg$FAILED;
2947
- }
2948
- } else {
2949
- peg$currPos = s0;
2950
- s0 = peg$FAILED;
2951
- }
2952
- }
2953
2924
  peg$silentFails--;
2954
2925
  if (s0 === peg$FAILED) {
2955
2926
  s1 = peg$FAILED;
@@ -2998,7 +2969,7 @@ function peg$parse(input, options) {
2998
2969
  s5 = peg$parseclosingBrace();
2999
2970
  if (s5 !== peg$FAILED) {
3000
2971
  peg$savedPos = s0;
3001
- s0 = peg$f39(s3);
2972
+ s0 = peg$f40(s3);
3002
2973
  } else {
3003
2974
  peg$currPos = s0;
3004
2975
  s0 = peg$FAILED;
@@ -3010,7 +2981,7 @@ function peg$parse(input, options) {
3010
2981
  peg$silentFails--;
3011
2982
  if (s0 === peg$FAILED) {
3012
2983
  s1 = peg$FAILED;
3013
- if (peg$silentFails === 0) { peg$fail(peg$e78); }
2984
+ if (peg$silentFails === 0) { peg$fail(peg$e77); }
3014
2985
  }
3015
2986
 
3016
2987
  return s0;
@@ -3163,6 +3134,7 @@ const peg$allowedStartRules = [
3163
3134
  "reservedProtocol",
3164
3135
  "scopeReference",
3165
3136
  "separator",
3137
+ "shebang",
3166
3138
  "sign",
3167
3139
  "singleArrow",
3168
3140
  "singleLineComment",
@@ -37,8 +37,8 @@ async function getText(value, scope) {
37
37
  // Convert to text, preferring .toString but avoiding dumb Object.toString.
38
38
  // Exception: if the result is an array, we'll concatenate the values.
39
39
  let text;
40
- if (!value) {
41
- // Treat falsy values as the empty string.
40
+ if (value == null || value === false) {
41
+ // Treat falsy values (but not zero) as the empty string.
42
42
  text = "";
43
43
  } else if (typeof value === "string") {
44
44
  text = value;
@@ -47,7 +47,7 @@ async function getText(value, scope) {
47
47
  text = textDecoder.decode(value);
48
48
  } else if (
49
49
  !(value instanceof Array) &&
50
- value.toString !== getRealmObjectPrototype(value).toString
50
+ value.toString !== getRealmObjectPrototype(value)?.toString
51
51
  ) {
52
52
  text = value.toString();
53
53
  } else {
@@ -14,7 +14,7 @@ export default function extname(path) {
14
14
  // We want at least one character before the dot, then a dot, then a non-empty
15
15
  // sequence of characters after the dot that aren't slahes or dots.
16
16
  const extnameRegex = /[^/](?<ext>\.[^/\.]+)$/;
17
- const match = path.match(extnameRegex);
17
+ const match = String(path).match(extnameRegex);
18
18
  const extension = match?.groups?.ext.toLowerCase() ?? "";
19
19
  return extension;
20
20
  }
@@ -4,11 +4,9 @@ import Scope from "./Scope.js";
4
4
  /**
5
5
  * When using `get` to retrieve a value from a tree, if the value is a
6
6
  * function, invoke it and return the result.
7
- *
8
- * @type {import("@weborigami/async-tree").TreeTransform}
9
7
  */
10
- export default function functionResultsMap(tree) {
11
- return map({
8
+ export default function functionResultsMap(treelike) {
9
+ return map(treelike, {
12
10
  description: "functionResultsMap",
13
11
 
14
12
  value: async (sourceValue, sourceKey, tree) => {
@@ -24,5 +22,5 @@ export default function functionResultsMap(tree) {
24
22
  }
25
23
  return resultValue;
26
24
  },
27
- })(tree);
25
+ });
28
26
  }
@@ -58,6 +58,20 @@ function constructHref(protocol, host, ...keys) {
58
58
  return href;
59
59
  }
60
60
 
61
+ /**
62
+ * Find the indicated constructor in scope, then return a function which invokes
63
+ * it with `new`.
64
+ *
65
+ * @this {AsyncTree}
66
+ * @param {...any} keys
67
+ */
68
+ export async function constructor(...keys) {
69
+ const scope = this;
70
+ const constructor = await Tree.traverseOrThrow(scope, ...keys);
71
+ return (...args) => new constructor(...args);
72
+ }
73
+ constructor.toString = () => "«ops.constructor»";
74
+
61
75
  /**
62
76
  * Fetch the resource at the given href.
63
77
  *
@@ -273,6 +273,13 @@ describe("Origami parser", () => {
273
273
  assertParse("multiLineComment", "/*\nHello, world!\n*/", null);
274
274
  });
275
275
 
276
+ test("new", () => {
277
+ assertParse("expression", "new:@js/Date('2025-01-01')", [
278
+ [ops.constructor, "@js", "Date"],
279
+ "2025-01-01",
280
+ ]);
281
+ });
282
+
276
283
  test("number", () => {
277
284
  assertParse("number", "123", 123);
278
285
  assertParse("number", "-456", -456);
@@ -385,11 +392,17 @@ describe("Origami parser", () => {
385
392
  ]);
386
393
  });
387
394
 
388
- test("singleLineComment", () => {
389
- assertParse("singleLineComment", "# Hello, world!", null);
395
+ test("shebang", () => {
396
+ assertParse(
397
+ "expression",
398
+ `#!/usr/bin/env ori @invoke
399
+ 'Hello'
400
+ `,
401
+ "Hello"
402
+ );
390
403
  });
391
404
 
392
- test("singleLineComment (JS)", () => {
405
+ test("singleLineComment", () => {
393
406
  assertParse("singleLineComment", "// Hello, world!", null);
394
407
  });
395
408
 
@@ -421,14 +434,14 @@ describe("Origami parser", () => {
421
434
 
422
435
  test("templateLiteral", () => {
423
436
  assertParse("templateLiteral", "`Hello, world.`", "Hello, world.");
424
- assertParse("templateLiteral", "`foo {{x}} bar`", [
437
+ assertParse("templateLiteral", "`foo ${x} bar`", [
425
438
  ops.concat,
426
439
  "foo ",
427
440
  [ops.scope, "x"],
428
441
  " bar",
429
442
  ]);
430
- assertParse("templateLiteral", "`{{`nested`}}`", "nested");
431
- assertParse("templateLiteral", "`{{map(people, =`{{name}}`)}}`", [
443
+ assertParse("templateLiteral", "`${`nested`}`", "nested");
444
+ assertParse("templateLiteral", "`${map(people, =`${name}`)}`", [
432
445
  ops.concat,
433
446
  [
434
447
  [ops.scope, "map"],
@@ -457,11 +470,7 @@ describe("Origami parser", () => {
457
470
  ]);
458
471
  });
459
472
 
460
- test("templateSubstitution", () => {
461
- assertParse("templateSubstitution", "{{foo}}", [ops.scope, "foo"]);
462
- });
463
-
464
- test("templateSubtitution (JS)", () => {
473
+ test("templateSubtitution", () => {
465
474
  assertParse("templateSubstitution", "${foo}", [ops.scope, "foo"]);
466
475
  });
467
476
 
@@ -482,8 +491,8 @@ describe("Origami parser", () => {
482
491
  assertParse(
483
492
  "__",
484
493
  `
485
- # First line of comment
486
- # Second line of comment
494
+ // First comment
495
+ // Second comment
487
496
  `,
488
497
  ""
489
498
  );
@@ -121,4 +121,16 @@ describe("ops", () => {
121
121
  const result = await evaluate.call(b.scope, code);
122
122
  assert.equal(result, 1);
123
123
  });
124
+
125
+ test("returns a constructor", async () => {
126
+ const scope = new ObjectTree({
127
+ "@js": {
128
+ Number: Number,
129
+ },
130
+ });
131
+ const fn = await ops.constructor.call(scope, "@js", "Number");
132
+ const number = fn("1");
133
+ assert(number instanceof Number);
134
+ assert.equal(number, 1);
135
+ });
124
136
  });