@weborigami/language 0.0.68 → 0.0.70

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/main.js CHANGED
@@ -11,5 +11,7 @@ export { default as HandleExtensionsTransform } from "./src/runtime/HandleExtens
11
11
  export { default as ImportModulesMixin } from "./src/runtime/ImportModulesMixin.js";
12
12
  export { default as InvokeFunctionsTransform } from "./src/runtime/InvokeFunctionsTransform.js";
13
13
  export { default as OrigamiFiles } from "./src/runtime/OrigamiFiles.js";
14
+ export * as symbols from "./src/runtime/symbols.js";
15
+ export { default as taggedTemplate } from "./src/runtime/taggedTemplate.js";
14
16
  export { default as TreeEvent } from "./src/runtime/TreeEvent.js";
15
17
  export { default as WatchFilesMixin } from "./src/runtime/WatchFilesMixin.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/language",
3
- "version": "0.0.68",
3
+ "version": "0.0.70",
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.6.2"
12
12
  },
13
13
  "dependencies": {
14
- "@weborigami/async-tree": "0.0.68",
15
- "@weborigami/types": "0.0.68",
14
+ "@weborigami/async-tree": "0.0.70",
15
+ "@weborigami/types": "0.0.70",
16
16
  "watcher": "2.3.1"
17
17
  },
18
18
  "scripts": {
@@ -5,10 +5,7 @@ function compile(source, startRule) {
5
5
  if (typeof source === "string") {
6
6
  source = { text: source };
7
7
  }
8
- // Trim whitespace from template blocks before we begin lexing, as our
9
- // heuristics are non-local and hard to implement in our parser.
10
- const preprocessed = trimTemplateWhitespace(source.text);
11
- const parseResult = parse(preprocessed, {
8
+ const parseResult = parse(source.text, {
12
9
  grammarSource: source,
13
10
  startRule,
14
11
  });
@@ -23,39 +20,3 @@ export function expression(source) {
23
20
  export function templateDocument(source) {
24
21
  return compile(source, "templateDocument");
25
22
  }
26
-
27
- // Trim the whitespace around and in substitution blocks in a template. There's
28
- // no explicit syntax for blocks, but we infer them as any place where a
29
- // substitution itself contains a multi-line template literal.
30
- //
31
- // Example:
32
- //
33
- // ${ if `
34
- // true text
35
- // `, `
36
- // false text
37
- // ` }
38
- //
39
- // Case 1: a substitution that starts the text or starts a line (there's only
40
- // whitespace before the `${`), and has the line end with the start of a
41
- // template literal (there's only whitespace after the backtick) marks the start
42
- // of a block.
43
- //
44
- // Case 2: a line in the middle that ends one template literal and starts
45
- // another is an internal break in the block. Edge case: three backticks in a
46
- // row, like ```, are common in markdown and are not treated as a break.
47
- //
48
- // Case 3: a line that ends a template literal and ends with `}` or ends the
49
- // text marks the end of the block.
50
- //
51
- // In all three cases, we trim spaces and tabs from the start and end of the
52
- // line. In case 1, we also remove the preceding newline.
53
- function trimTemplateWhitespace(text) {
54
- const regex1 = /(^|\n)[ \t]*((?:{{|\${).*?`)[ \t]*\n/g;
55
- const regex2 = /\n[ \t]*(`(?!`).*?`)[ \t]*\n/g;
56
- const regex3js = /\n[ \t]*(`(?!`).*?(?:}}|[^\\]}))[ \t]*(?:\n|$)/g;
57
- const trimBlockStarts = text.replace(regex1, "$1$2");
58
- const trimBlockBreaks = trimBlockStarts.replace(regex2, "\n$1");
59
- const trimBlockEnds = trimBlockBreaks.replace(regex3js, "\n$1");
60
- return trimBlockEnds;
61
- }
@@ -13,6 +13,7 @@ import {
13
13
  makeFunctionCall,
14
14
  makeObject,
15
15
  makePipeline,
16
+ makeProperty,
16
17
  makeTemplate
17
18
  } from "./parserHelpers.js";
18
19
 
@@ -102,7 +103,7 @@ doubleArrow = "⇒" / "=>"
102
103
 
103
104
  doubleQuoteString "double quote string"
104
105
  = '"' chars:doubleQuoteStringChar* '"' {
105
- return annotate([ops.primitive, chars.join("")], location());
106
+ return annotate([ops.literal, chars.join("")], location());
106
107
  }
107
108
 
108
109
  doubleQuoteStringChar
@@ -131,13 +132,21 @@ expression "Origami expression"
131
132
 
132
133
  float "floating-point number"
133
134
  = sign? digits? "." digits {
134
- return annotate([ops.primitive, parseFloat(text())], location());
135
+ return annotate([ops.literal, parseFloat(text())], location());
135
136
  }
136
137
 
137
138
  // Parse a function and its arguments, e.g. `fn(arg)`, possibly part of a chain
138
139
  // of function calls, like `fn(arg1)(arg2)(arg3)`.
139
140
  functionComposition "function composition"
140
- = target:callTarget chain:args* end:implicitParensArgs? {
141
+ // Function with at least one argument and maybe implicit parens arguments
142
+ = target:callTarget chain:args+ end:implicitParensArgs? {
143
+ if (end) {
144
+ chain.push(end);
145
+ }
146
+ return annotate(makeFunctionCall(target, chain, location()), location());
147
+ }
148
+ // Function with implicit parens arguments after maybe other arguments
149
+ / target:callTarget chain:args* end:implicitParensArgs {
141
150
  if (end) {
142
151
  chain.push(end);
143
152
  }
@@ -152,7 +161,7 @@ group "parenthetical group"
152
161
 
153
162
  guillemetString "guillemet string"
154
163
  = '«' chars:guillemetStringChar* '»' {
155
- return annotate([ops.primitive, chars.join("")], location());
164
+ return annotate([ops.literal, chars.join("")], location());
156
165
  }
157
166
 
158
167
  guillemetStringChar
@@ -165,7 +174,7 @@ host "HTTP/HTTPS host"
165
174
  = identifier:identifier port:(":" @number)? {
166
175
  const portText = port ? `:${port[1]}` : "";
167
176
  const hostText = identifier + portText;
168
- return annotate([ops.primitive, hostText], location());
177
+ return annotate([ops.literal, hostText], location());
169
178
  }
170
179
 
171
180
  identifier "identifier"
@@ -194,7 +203,7 @@ inlineSpace
194
203
 
195
204
  integer "integer"
196
205
  = sign? digits {
197
- return annotate([ops.primitive, parseInt(text())], location());
206
+ return annotate([ops.literal, parseInt(text())], location());
198
207
  }
199
208
 
200
209
  // A lambda expression: `=foo()`
@@ -249,7 +258,7 @@ objectEntry
249
258
  // A getter definition inside an object literal: `foo = 1`
250
259
  objectGetter "object getter"
251
260
  = key:objectKey __ "=" __ value:expr {
252
- return annotate([key, [ops.getter, value]], location());
261
+ return annotate(makeProperty(key, [ops.getter, value]), location());
253
262
  }
254
263
 
255
264
  objectHiddenKey
@@ -262,7 +271,7 @@ objectKey "object key"
262
271
  // A property definition in an object literal: `x: 1`
263
272
  objectProperty "object property"
264
273
  = key:objectKey __ ":" __ value:expr {
265
- return annotate([key, value], location());
274
+ return annotate(makeProperty(key, value), location());
266
275
  }
267
276
 
268
277
  // A shorthand reference inside an object literal: `foo`
@@ -276,7 +285,7 @@ objectPublicKey
276
285
  return identifier + (slash ?? "");
277
286
  }
278
287
  / string:string {
279
- // Remove `ops.primitive` from the string code
288
+ // Remove `ops.literal` from the string code
280
289
  return string[1];
281
290
  }
282
291
 
@@ -308,7 +317,7 @@ path "slash-separated path"
308
317
  // A path key followed by a slash
309
318
  pathElement
310
319
  = chars:pathKeyChar* "/" {
311
- return annotate([ops.primitive, chars.join("") + "/"], location());
320
+ return annotate([ops.literal, chars.join("") + "/"], location());
312
321
  }
313
322
 
314
323
  // A single character in a slash-separated path.
@@ -321,7 +330,7 @@ pathKeyChar
321
330
  // A path key without a slash
322
331
  pathTail
323
332
  = chars:pathKeyChar+ {
324
- return annotate([ops.primitive, chars.join("")], location());
333
+ return annotate([ops.literal, chars.join("")], location());
325
334
  }
326
335
 
327
336
  // Parse a protocol call like `fn://foo/bar`.
@@ -353,6 +362,7 @@ scopeReference "scope reference"
353
362
  scopeTraverse
354
363
  = ref:scopeReference "/" path:path {
355
364
  const head = [ops.scope, `${ ref[1] }/`];
365
+ head.location = ref.location;
356
366
  return annotate([ops.traverse, head, ...path], location());
357
367
  }
358
368
 
@@ -373,7 +383,7 @@ singleLineComment
373
383
 
374
384
  singleQuoteString "single quote string"
375
385
  = "'" chars:singleQuoteStringChar* "'" {
376
- return annotate([ops.primitive, chars.join("")], location());
386
+ return annotate([ops.literal, chars.join("")], location());
377
387
  }
378
388
 
379
389
  singleQuoteStringChar
@@ -401,8 +411,10 @@ step
401
411
  / templateLiteral
402
412
  / string
403
413
  / group
404
- // Protocol calls are distinguished by a colon, but it's not at the start.
414
+ // Things that have a distinctive character, but not at the start
405
415
  / protocolCall
416
+ / taggedTemplate
417
+ / scopeTraverse
406
418
  // Least distinctive option is a simple scope reference, so it comes last.
407
419
  / scopeReference
408
420
 
@@ -414,6 +426,11 @@ string "string"
414
426
  / singleQuoteString
415
427
  / guillemetString
416
428
 
429
+ taggedTemplate
430
+ = tag:callTarget "`" contents:templateLiteralContents "`" {
431
+ return annotate(makeTemplate(tag, contents[0], contents[1]), location());
432
+ }
433
+
417
434
  // A top-level document defining a template. This is the same as a template
418
435
  // literal, but can contain backticks at the top level.
419
436
  templateDocument "template"
@@ -427,19 +444,19 @@ templateDocumentChar
427
444
 
428
445
  // The contents of a template document containing plain text and substitutions
429
446
  templateDocumentContents
430
- = parts:(templateDocumentText / templateSubstitution)* {
431
- return annotate(makeTemplate(parts), location());
447
+ = head:templateDocumentText tail:(templateSubstitution templateDocumentText)* {
448
+ return annotate(makeTemplate(ops.template, head, tail), location());
432
449
  }
433
450
 
434
451
  templateDocumentText "template text"
435
- = chars:templateDocumentChar+ {
436
- return annotate([ops.primitive, chars.join("")], location());
437
- }
452
+ = chars:templateDocumentChar* {
453
+ return chars.join("");
454
+ }
438
455
 
439
456
  // A backtick-quoted template literal
440
457
  templateLiteral "template literal"
441
458
  = "`" contents:templateLiteralContents "`" {
442
- return annotate(contents, location());
459
+ return annotate(makeTemplate(ops.template, contents[0], contents[1]), location());
443
460
  }
444
461
 
445
462
  templateLiteralChar
@@ -447,21 +464,17 @@ templateLiteralChar
447
464
 
448
465
  // The contents of a template literal containing plain text and substitutions
449
466
  templateLiteralContents
450
- = parts:(templateLiteralText / templateSubstitution)* {
451
- return annotate(makeTemplate(parts), location());
452
- }
467
+ = head:templateLiteralText tail:(templateSubstitution templateLiteralText)*
453
468
 
454
469
  // Plain text in a template literal
455
470
  templateLiteralText
456
- = chars:templateLiteralChar+ {
457
- return annotate([ops.primitive, chars.join("")], location());
458
- }
471
+ = chars:templateLiteralChar* {
472
+ return chars.join("");
473
+ }
459
474
 
460
475
  // A substitution in a template literal: `${x}`
461
476
  templateSubstitution "template substitution"
462
- = "${" __ expr:expr __ "}" {
463
- return annotate(expr, location());
464
- }
477
+ = "${" __ @expr __ "}"
465
478
 
466
479
  textChar
467
480
  = escapedChar