@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 +2 -0
- package/package.json +3 -3
- package/src/compiler/compile.js +1 -40
- package/src/compiler/origami.pegjs +41 -28
- package/src/compiler/parse.js +287 -193
- package/src/compiler/parserHelpers.js +51 -28
- package/src/runtime/WatchFilesMixin.js +1 -0
- package/src/runtime/evaluate.js +15 -15
- package/src/runtime/ops.js +54 -38
- package/src/runtime/symbols.js +3 -0
- package/src/runtime/taggedTemplate.js +9 -0
- package/test/compiler/compile.test.js +21 -0
- package/test/compiler/parse.test.js +160 -171
- package/test/runtime/taggedTemplate.test.js +10 -0
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.
|
|
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.
|
|
15
|
-
"@weborigami/types": "0.0.
|
|
14
|
+
"@weborigami/async-tree": "0.0.70",
|
|
15
|
+
"@weborigami/types": "0.0.70",
|
|
16
16
|
"watcher": "2.3.1"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
package/src/compiler/compile.js
CHANGED
|
@@ -5,10 +5,7 @@ function compile(source, startRule) {
|
|
|
5
5
|
if (typeof source === "string") {
|
|
6
6
|
source = { text: source };
|
|
7
7
|
}
|
|
8
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
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
|
-
=
|
|
431
|
-
return annotate(makeTemplate(
|
|
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
|
-
|
|
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
|
-
=
|
|
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
|
-
|
|
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
|
|
463
|
-
return annotate(expr, location());
|
|
464
|
-
}
|
|
477
|
+
= "${" __ @expr __ "}"
|
|
465
478
|
|
|
466
479
|
textChar
|
|
467
480
|
= escapedChar
|