@weborigami/language 0.0.64-beta.2 → 0.0.65-beta.1
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/index.ts +20 -9
- package/package.json +5 -5
- package/src/compiler/compile.js +2 -2
- package/src/compiler/origami.pegjs +73 -40
- package/src/compiler/parse.d.ts +2 -2
- package/src/compiler/parse.js +566 -390
- package/src/compiler/parserHelpers.js +42 -4
- package/src/runtime/evaluate.js +1 -1
- package/src/runtime/expressionFunction.js +6 -5
- package/test/compiler/parse.test.js +20 -4
package/index.ts
CHANGED
|
@@ -3,13 +3,16 @@ import { Packed } from "@weborigami/async-tree";
|
|
|
3
3
|
export * from "./main.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* A chunk of compiled Origami code. This is just an
|
|
7
|
-
* `
|
|
6
|
+
* A chunk of compiled Origami code. This is just an array with an additional
|
|
7
|
+
* `location` property.
|
|
8
8
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
export type Code = Array<any> & {
|
|
10
|
+
location: {
|
|
11
|
+
source: Source;
|
|
12
|
+
start: Position;
|
|
13
|
+
end: Position;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* A class constructor is an object with a `new` method that returns an
|
|
@@ -26,6 +29,14 @@ export type ExtensionHandler = {
|
|
|
26
29
|
unpack?: UnpackFunction;
|
|
27
30
|
}
|
|
28
31
|
|
|
32
|
+
export type ParseResult = Code | string | number;
|
|
33
|
+
|
|
34
|
+
export type Position = {
|
|
35
|
+
column: number;
|
|
36
|
+
line: number;
|
|
37
|
+
offset: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
29
40
|
/**
|
|
30
41
|
* A mixin is a function that takes an existing class and returns a new class.
|
|
31
42
|
*
|
|
@@ -40,11 +51,11 @@ export type Mixin<MixinMembers> = <T>(
|
|
|
40
51
|
|
|
41
52
|
/**
|
|
42
53
|
* Source code representation used by the parser.
|
|
43
|
-
*/
|
|
54
|
+
*/
|
|
44
55
|
export type Source = {
|
|
45
|
-
name
|
|
56
|
+
name?: string;
|
|
46
57
|
text: string;
|
|
47
|
-
url
|
|
58
|
+
url?: URL;
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
/**
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.65-beta.1",
|
|
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": "
|
|
9
|
+
"@types/node": "22.5.4",
|
|
10
10
|
"peggy": "4.0.3",
|
|
11
|
-
"typescript": "5.5.
|
|
11
|
+
"typescript": "5.5.4"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@weborigami/async-tree": "
|
|
15
|
-
"@weborigami/types": "0.0.
|
|
14
|
+
"@weborigami/async-tree": "https://gitpkg.vercel.app/WebOrigami/origami/async-tree?b5d8eebf",
|
|
15
|
+
"@weborigami/types": "0.0.65-beta.1",
|
|
16
16
|
"watcher": "2.3.1"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
package/src/compiler/compile.js
CHANGED
|
@@ -8,11 +8,11 @@ function compile(source, startRule) {
|
|
|
8
8
|
// Trim whitespace from template blocks before we begin lexing, as our
|
|
9
9
|
// heuristics are non-local and hard to implement in our parser.
|
|
10
10
|
const preprocessed = trimTemplateWhitespace(source.text);
|
|
11
|
-
const
|
|
11
|
+
const parseResult = parse(preprocessed, {
|
|
12
12
|
grammarSource: source,
|
|
13
13
|
startRule,
|
|
14
14
|
});
|
|
15
|
-
const fn = createExpressionFunction(
|
|
15
|
+
const fn = createExpressionFunction(parseResult);
|
|
16
16
|
return fn;
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -7,16 +7,14 @@
|
|
|
7
7
|
//
|
|
8
8
|
|
|
9
9
|
import * as ops from "../runtime/ops.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return parseResult;
|
|
19
|
-
}
|
|
10
|
+
import {
|
|
11
|
+
annotate,
|
|
12
|
+
makeArray,
|
|
13
|
+
makeFunctionCall,
|
|
14
|
+
makeObject,
|
|
15
|
+
makePipeline,
|
|
16
|
+
makeTemplate
|
|
17
|
+
} from "./parserHelpers.js";
|
|
20
18
|
|
|
21
19
|
}}
|
|
22
20
|
|
|
@@ -45,7 +43,9 @@ array "array"
|
|
|
45
43
|
|
|
46
44
|
// A separated list of array entries
|
|
47
45
|
arrayEntries
|
|
48
|
-
=
|
|
46
|
+
= entries:arrayEntry|1.., separator| separator? {
|
|
47
|
+
return annotate(entries, location());
|
|
48
|
+
}
|
|
49
49
|
|
|
50
50
|
arrayEntry
|
|
51
51
|
= spread
|
|
@@ -68,15 +68,15 @@ callTarget "function call"
|
|
|
68
68
|
closingBrace
|
|
69
69
|
= "}"
|
|
70
70
|
/ .? {
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
error("Expected right curly brace");
|
|
72
|
+
}
|
|
73
73
|
|
|
74
74
|
// Required closing bracket
|
|
75
75
|
closingBracket
|
|
76
76
|
= "]"
|
|
77
77
|
/ .? {
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
error("Expected right bracket");
|
|
79
|
+
}
|
|
80
80
|
|
|
81
81
|
// Required closing parenthesis. We use this for the `group` term: it's the last
|
|
82
82
|
// term in the `step` parser that starts with a parenthesis, so if that parser
|
|
@@ -84,8 +84,8 @@ closingBracket
|
|
|
84
84
|
closingParen
|
|
85
85
|
= ")"
|
|
86
86
|
/ .? {
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
error("Expected right parenthesis");
|
|
88
|
+
}
|
|
89
89
|
|
|
90
90
|
// A single line comment
|
|
91
91
|
comment "comment"
|
|
@@ -106,7 +106,14 @@ doubleQuoteStringChar
|
|
|
106
106
|
ellipsis = "..." / "…" // Unicode ellipsis
|
|
107
107
|
|
|
108
108
|
escapedChar "backslash-escaped character"
|
|
109
|
-
= "\\"
|
|
109
|
+
= "\\0" { return "\0"; }
|
|
110
|
+
/ "\\b" { return "\b"; }
|
|
111
|
+
/ "\\f" { return "\f"; }
|
|
112
|
+
/ "\\n" { return "\n"; }
|
|
113
|
+
/ "\\r" { return "\r"; }
|
|
114
|
+
/ "\\t" { return "\t"; }
|
|
115
|
+
/ "\\v" { return "\v"; }
|
|
116
|
+
/ "\\" @.
|
|
110
117
|
|
|
111
118
|
// An Origami expression, no leading/trailing whitespace
|
|
112
119
|
expr
|
|
@@ -126,15 +133,17 @@ float "floating-point number"
|
|
|
126
133
|
// of function calls, like `fn(arg1)(arg2)(arg3)`.
|
|
127
134
|
functionComposition "function composition"
|
|
128
135
|
= target:callTarget chain:args* end:implicitParensArgs? {
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
if (end) {
|
|
137
|
+
chain.push(end);
|
|
138
|
+
}
|
|
139
|
+
return annotate(makeFunctionCall(target, chain), location());
|
|
131
140
|
}
|
|
132
|
-
return annotate(makeFunctionCall(target, chain), location());
|
|
133
|
-
}
|
|
134
141
|
|
|
135
142
|
// An expression in parentheses: `(foo)`
|
|
136
143
|
group "parenthetical group"
|
|
137
|
-
= "(" __
|
|
144
|
+
= "(" __ expr:expr __ closingParen {
|
|
145
|
+
return annotate(expr, location());
|
|
146
|
+
}
|
|
138
147
|
|
|
139
148
|
guillemetString "guillemet string"
|
|
140
149
|
= '«' chars:guillemetStringChar* '»' { return chars.join(""); }
|
|
@@ -157,7 +166,9 @@ identifierChar
|
|
|
157
166
|
/ escapedChar
|
|
158
167
|
|
|
159
168
|
identifierList
|
|
160
|
-
=
|
|
169
|
+
= list:identifier|1.., separator| separator? {
|
|
170
|
+
return annotate(list, location());
|
|
171
|
+
}
|
|
161
172
|
|
|
162
173
|
identifierOrString
|
|
163
174
|
= identifier
|
|
@@ -166,7 +177,10 @@ identifierOrString
|
|
|
166
177
|
implicitParensArgs "arguments with implicit parentheses"
|
|
167
178
|
// Implicit parens args are a separate list of `step`, not `expr`, because
|
|
168
179
|
// they can't contain a pipeline.
|
|
169
|
-
= inlineSpace+
|
|
180
|
+
= inlineSpace+ args:step|1.., separator| separator? {
|
|
181
|
+
/* Stuff */
|
|
182
|
+
return annotate(args, location());
|
|
183
|
+
}
|
|
170
184
|
|
|
171
185
|
inlineSpace
|
|
172
186
|
= [ \t]
|
|
@@ -184,12 +198,18 @@ lambda "lambda function"
|
|
|
184
198
|
|
|
185
199
|
// A path that begins with a slash: `/foo/bar`
|
|
186
200
|
leadingSlashPath "path with a leading slash"
|
|
187
|
-
= "/"
|
|
188
|
-
|
|
201
|
+
= "/" path:path {
|
|
202
|
+
return annotate(path, location());
|
|
203
|
+
}
|
|
204
|
+
/ "/" {
|
|
205
|
+
return annotate([""], location());
|
|
206
|
+
}
|
|
189
207
|
|
|
190
208
|
// A separated list of expressions
|
|
191
209
|
list "list"
|
|
192
|
-
=
|
|
210
|
+
= list:expr|1.., separator| separator? {
|
|
211
|
+
return annotate(list, location());
|
|
212
|
+
}
|
|
193
213
|
|
|
194
214
|
multiLineComment
|
|
195
215
|
= "/*" (!"*/" .)* "*/" { return null; }
|
|
@@ -212,7 +232,9 @@ object "object literal"
|
|
|
212
232
|
|
|
213
233
|
// A separated list of object entries
|
|
214
234
|
objectEntries
|
|
215
|
-
=
|
|
235
|
+
= entries:objectEntry|1.., separator| separator? {
|
|
236
|
+
return annotate(entries, location());
|
|
237
|
+
}
|
|
216
238
|
|
|
217
239
|
objectEntry
|
|
218
240
|
= spread
|
|
@@ -223,8 +245,8 @@ objectEntry
|
|
|
223
245
|
// A getter definition inside an object literal: `foo = 1`
|
|
224
246
|
objectGetter "object getter"
|
|
225
247
|
= key:objectKey __ "=" __ value:expr {
|
|
226
|
-
|
|
227
|
-
|
|
248
|
+
return annotate([key, [ops.getter, value]], location());
|
|
249
|
+
}
|
|
228
250
|
|
|
229
251
|
// A standalone reference inside an object literal: `foo`
|
|
230
252
|
objectIdentifier "object identifier"
|
|
@@ -238,17 +260,19 @@ objectKey "object key"
|
|
|
238
260
|
|
|
239
261
|
// A property definition in an object literal: `x: 1`
|
|
240
262
|
objectProperty "object property"
|
|
241
|
-
=
|
|
263
|
+
= key:objectKey __ ":" __ value:expr {
|
|
264
|
+
return annotate([key, value], location());
|
|
265
|
+
}
|
|
242
266
|
|
|
243
267
|
parameterizedLambda
|
|
244
268
|
= "(" __ parameters:identifierList? __ ")" __ doubleArrow __ expr:expr {
|
|
245
|
-
|
|
246
|
-
|
|
269
|
+
return annotate([ops.lambda, parameters ?? [], expr], location());
|
|
270
|
+
}
|
|
247
271
|
|
|
248
272
|
// Function arguments in parentheses
|
|
249
273
|
parensArgs "function arguments in parentheses"
|
|
250
274
|
= "(" __ list:list? __ ")" {
|
|
251
|
-
return list ??
|
|
275
|
+
return annotate(list ?? [undefined], location());
|
|
252
276
|
}
|
|
253
277
|
|
|
254
278
|
pipeline
|
|
@@ -258,7 +282,9 @@ pipeline
|
|
|
258
282
|
|
|
259
283
|
// A slash-separated path of keys
|
|
260
284
|
path "slash-separated path"
|
|
261
|
-
= pathKey|1.., "/"|
|
|
285
|
+
= path:pathKey|1.., "/"| {
|
|
286
|
+
return annotate(path, location());
|
|
287
|
+
}
|
|
262
288
|
|
|
263
289
|
// A single key in a slash-separated path
|
|
264
290
|
pathKey "path element"
|
|
@@ -318,7 +344,9 @@ singleQuoteStringChar
|
|
|
318
344
|
= !("'" / newLine) @textChar
|
|
319
345
|
|
|
320
346
|
spread
|
|
321
|
-
= ellipsis expr:expr {
|
|
347
|
+
= ellipsis expr:expr {
|
|
348
|
+
return annotate([ops.spread, expr], location());
|
|
349
|
+
}
|
|
322
350
|
|
|
323
351
|
// A single step in a pipeline, or a top-level expression
|
|
324
352
|
step
|
|
@@ -372,7 +400,9 @@ templateDocumentText "template text"
|
|
|
372
400
|
|
|
373
401
|
// A backtick-quoted template literal
|
|
374
402
|
templateLiteral "template literal"
|
|
375
|
-
= "`"
|
|
403
|
+
= "`" contents:templateLiteralContents "`" {
|
|
404
|
+
return annotate(contents, location());
|
|
405
|
+
}
|
|
376
406
|
|
|
377
407
|
templateLiteralChar
|
|
378
408
|
= !("`" / "${") @textChar
|
|
@@ -389,10 +419,13 @@ templateLiteralText
|
|
|
389
419
|
|
|
390
420
|
// A substitution in a template literal: `${x}`
|
|
391
421
|
templateSubstitution "template substitution"
|
|
392
|
-
= "${" __
|
|
422
|
+
= "${" __ expr:expr __ "}" {
|
|
423
|
+
return annotate(expr, location());
|
|
424
|
+
}
|
|
393
425
|
|
|
394
426
|
textChar
|
|
395
|
-
= escapedChar
|
|
427
|
+
= escapedChar
|
|
428
|
+
/ .
|
|
396
429
|
|
|
397
430
|
whitespaceWithNewLine
|
|
398
431
|
= inlineSpace* comment? newLine __
|
package/src/compiler/parse.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ParseResult } from "../../index.ts";
|
|
2
2
|
|
|
3
|
-
export function parse(input: string, options: any):
|
|
3
|
+
export function parse(input: string, options: any): ParseResult;
|