@weborigami/language 0.0.38 → 0.0.40
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 +5 -1
- package/package.json +6 -6
- package/src/compiler/origami.pegjs +39 -37
- package/src/compiler/parse.js +404 -143
- package/src/runtime/FileLoadersTransform.js +1 -1
- package/src/runtime/concatTreeValues.js +21 -1
- package/src/runtime/ops.js +10 -1
- package/test/compiler/parse.test.js +2 -2
- package/test/runtime/InheritScopeMixin.test.js +2 -2
- package/test/runtime/{concatTreeValues.js → concatTreeValues.test.js} +14 -1
package/index.ts
CHANGED
|
@@ -14,7 +14,11 @@ export type Constructor<T> = new (...args: any[]) => T;
|
|
|
14
14
|
*/
|
|
15
15
|
export type FileUnpackFunction = (
|
|
16
16
|
input: StringLike,
|
|
17
|
-
options?: {
|
|
17
|
+
options?: {
|
|
18
|
+
compiler?: any,
|
|
19
|
+
key?: any,
|
|
20
|
+
parent?: AsyncTree | null
|
|
21
|
+
}
|
|
18
22
|
) => any;
|
|
19
23
|
|
|
20
24
|
/**
|
package/package.json
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.40",
|
|
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.
|
|
10
|
-
"typescript": "5.
|
|
9
|
+
"@types/node": "20.11.7",
|
|
10
|
+
"typescript": "5.3.3"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@weborigami/
|
|
14
|
-
"@weborigami/
|
|
13
|
+
"@weborigami/async-tree": "0.0.40",
|
|
14
|
+
"@weborigami/types": "0.0.40",
|
|
15
15
|
"peggy": "3.0.2",
|
|
16
16
|
"watcher": "2.3.0"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "peggy --allowed-start-rules=\"*\" --format es src/compiler/origami.pegjs --output src/compiler/parse.js",
|
|
20
20
|
"prepublishOnly": "npm run build",
|
|
21
|
-
"test": "node --test",
|
|
21
|
+
"test": "node --test --test-reporter=spec",
|
|
22
22
|
"typecheck": "tsc"
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -15,27 +15,27 @@ __
|
|
|
15
15
|
= (inlineSpace / newLine / comment)* { return ""; }
|
|
16
16
|
|
|
17
17
|
// A filesystem path that begins with a slash: `/foo/bar`
|
|
18
|
-
absoluteFilePath
|
|
18
|
+
absoluteFilePath "absolute file path"
|
|
19
19
|
= path:leadingSlashPath { return [[ops.filesRoot], ...path]; }
|
|
20
20
|
|
|
21
21
|
// A chain of arguments: `(arg1)(arg2)(arg3)`
|
|
22
|
-
argsChain
|
|
22
|
+
argsChain "function arguments"
|
|
23
23
|
= parts:(parensArgs / leadingSlashPath)+ { return parts; }
|
|
24
24
|
|
|
25
25
|
// An assignment statement: `foo = 1`
|
|
26
|
-
assignment
|
|
26
|
+
assignment "tree assignment"
|
|
27
27
|
= @identifier __ "=" __ @expr
|
|
28
28
|
|
|
29
29
|
assignmentOrShorthand
|
|
30
30
|
= assignment
|
|
31
31
|
/ key:identifier { return [key, [ops.inherited, key]]; }
|
|
32
32
|
|
|
33
|
-
array
|
|
33
|
+
array "array"
|
|
34
34
|
= "[" __ list:list? "]" { return [ops.array, ...(list ?? [])]; }
|
|
35
35
|
|
|
36
36
|
// Something that can be called. This is more restrictive than the `expr`
|
|
37
37
|
// parser; it doesn't accept regular function calls.
|
|
38
|
-
callTarget
|
|
38
|
+
callTarget "function call"
|
|
39
39
|
= absoluteFilePath
|
|
40
40
|
/ array
|
|
41
41
|
/ object
|
|
@@ -46,23 +46,23 @@ callTarget
|
|
|
46
46
|
/ scopeReference
|
|
47
47
|
|
|
48
48
|
// A single line comment
|
|
49
|
-
comment
|
|
49
|
+
comment "comment"
|
|
50
50
|
= "#" [^\n\r]*
|
|
51
51
|
|
|
52
52
|
digits
|
|
53
53
|
= @[0-9]+
|
|
54
54
|
|
|
55
|
-
doubleQuoteString
|
|
55
|
+
doubleQuoteString "double quote string"
|
|
56
56
|
= '"' chars:doubleQuoteStringChar* '"' { return chars.join(""); }
|
|
57
57
|
|
|
58
58
|
doubleQuoteStringChar
|
|
59
59
|
= !('"' / newLine) @textChar
|
|
60
60
|
|
|
61
|
-
escapedChar
|
|
61
|
+
escapedChar "backslash-escaped character"
|
|
62
62
|
= "\\" @.
|
|
63
63
|
|
|
64
64
|
// An Origami expression, no leading/trailing whitespace
|
|
65
|
-
expr
|
|
65
|
+
expr "expression"
|
|
66
66
|
// Try function calls first, as they can start with expression types that
|
|
67
67
|
// follow (array, object, etc.); we want to parse the largest thing first.
|
|
68
68
|
= implicitParensCall
|
|
@@ -87,21 +87,21 @@ expr
|
|
|
87
87
|
expression "Origami expression"
|
|
88
88
|
= __ @expr __
|
|
89
89
|
|
|
90
|
-
float
|
|
90
|
+
float "floating-point number"
|
|
91
91
|
= sign? digits? "." digits {
|
|
92
92
|
return parseFloat(text());
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// Parse a function and its arguments, e.g. `fn(arg)`, possibly part of a chain
|
|
96
96
|
// of function calls, like `fn(arg1)(arg2)(arg3)`.
|
|
97
|
-
functionComposition
|
|
97
|
+
functionComposition "function composition"
|
|
98
98
|
= target:callTarget chain:argsChain { return makeFunctionCall(target, chain); }
|
|
99
99
|
|
|
100
100
|
// An expression in parentheses: `(foo)`
|
|
101
|
-
group
|
|
101
|
+
group "parenthetical group"
|
|
102
102
|
= "(" __ @expr __ ")"
|
|
103
103
|
|
|
104
|
-
identifier
|
|
104
|
+
identifier "identifier"
|
|
105
105
|
= chars:identifierChar+ { return chars.join(""); }
|
|
106
106
|
|
|
107
107
|
identifierChar
|
|
@@ -109,7 +109,7 @@ identifierChar
|
|
|
109
109
|
/ escapedChar
|
|
110
110
|
|
|
111
111
|
// A function call with implicit parentheses: `fn 1, 2, 3`
|
|
112
|
-
implicitParensCall
|
|
112
|
+
implicitParensCall "function call with implicit parentheses"
|
|
113
113
|
= target:(functionComposition / callTarget) inlineSpace+ args:list {
|
|
114
114
|
return [target, ...args];
|
|
115
115
|
}
|
|
@@ -117,28 +117,29 @@ implicitParensCall
|
|
|
117
117
|
// A host identifier that may include a colon and port number: `example.com:80`.
|
|
118
118
|
// This is used as a special case at the head of a path, where we want to
|
|
119
119
|
// interpret a colon as part of a text identifier.
|
|
120
|
-
host
|
|
120
|
+
host "HTTP/HTTPS host"
|
|
121
121
|
= identifier (":" number)? { return text(); }
|
|
122
122
|
|
|
123
123
|
inlineSpace
|
|
124
124
|
= [ \t]
|
|
125
125
|
|
|
126
|
-
integer
|
|
126
|
+
integer "integer"
|
|
127
127
|
= sign? digits {
|
|
128
128
|
return parseInt(text());
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
// A lambda expression: `=foo()`
|
|
132
|
-
lambda
|
|
132
|
+
lambda "lambda function"
|
|
133
133
|
= "=" __ expr:expr { return [ops.lambda, expr]; }
|
|
134
134
|
|
|
135
135
|
// A path that begins with a slash: `/foo/bar`
|
|
136
|
-
leadingSlashPath
|
|
136
|
+
leadingSlashPath "path with a leading slash"
|
|
137
137
|
= "/" @path
|
|
138
138
|
/ "/" { return [""]; }
|
|
139
139
|
|
|
140
140
|
// A separated list of expressions
|
|
141
|
-
list
|
|
141
|
+
list "list"
|
|
142
|
+
= head:expr tail:(separator @expr)* separator? { return [head].concat(tail); }
|
|
142
143
|
|
|
143
144
|
newLine
|
|
144
145
|
= "\n"
|
|
@@ -146,7 +147,7 @@ newLine
|
|
|
146
147
|
/ "\r"
|
|
147
148
|
|
|
148
149
|
// A number
|
|
149
|
-
number
|
|
150
|
+
number "number"
|
|
150
151
|
= float
|
|
151
152
|
/ integer
|
|
152
153
|
|
|
@@ -154,7 +155,7 @@ number
|
|
|
154
155
|
//
|
|
155
156
|
// TODO: Use Object.fromEntries with array of key/value pairs
|
|
156
157
|
//
|
|
157
|
-
object
|
|
158
|
+
object "object literal"
|
|
158
159
|
= "{" __ properties:objectProperties? "}" { return [ops.object, ...(properties ?? [])]; }
|
|
159
160
|
|
|
160
161
|
// A separated list of object properties or shorthands
|
|
@@ -164,7 +165,7 @@ objectProperties
|
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
// A single object property with key and value: `x: 1`
|
|
167
|
-
objectProperty
|
|
168
|
+
objectProperty "object property"
|
|
168
169
|
= @identifier __ ":" __ @expr
|
|
169
170
|
|
|
170
171
|
objectPropertyOrShorthand
|
|
@@ -172,7 +173,7 @@ objectPropertyOrShorthand
|
|
|
172
173
|
/ key:identifier { return [key, [ops.scope, key]]; }
|
|
173
174
|
|
|
174
175
|
// Function arguments in parentheses
|
|
175
|
-
parensArgs
|
|
176
|
+
parensArgs "function arguments in parentheses"
|
|
176
177
|
= "(" __ list:list? ")" { return list ?? [undefined]; }
|
|
177
178
|
|
|
178
179
|
separator
|
|
@@ -183,36 +184,37 @@ sign
|
|
|
183
184
|
= [+\-]
|
|
184
185
|
|
|
185
186
|
// A slash-separated path of keys
|
|
186
|
-
path
|
|
187
|
+
path "slash-separated path"
|
|
187
188
|
= head:pathKey "/" tail:path { return [head].concat(tail); }
|
|
188
189
|
/ key:pathKey { return [key]; }
|
|
189
190
|
|
|
190
191
|
// A single key in a slash-separated path
|
|
191
|
-
pathKey
|
|
192
|
+
pathKey "path element"
|
|
192
193
|
= key:identifierChar* { return key.join(""); }
|
|
193
194
|
|
|
194
195
|
// Parse a protocol call like `fn://foo/bar`.
|
|
195
196
|
// There can be zero, one, or two slashes after the colon.
|
|
196
|
-
protocolCall
|
|
197
|
+
protocolCall "function call using protocol: syntax"
|
|
197
198
|
= protocol:protocol ":" "/"|0..2| host:host path:leadingSlashPath? {
|
|
198
199
|
return [protocol, host, ...(path ?? [])];
|
|
199
200
|
}
|
|
200
201
|
|
|
201
|
-
protocol
|
|
202
|
+
protocol "protocol"
|
|
202
203
|
= reservedProtocol
|
|
203
204
|
/ scopeReference
|
|
204
205
|
|
|
205
|
-
reservedProtocol
|
|
206
|
+
reservedProtocol "reserved protocol"
|
|
206
207
|
= "https" { return ops.https; }
|
|
207
208
|
/ "http" { return ops.http; }
|
|
209
|
+
/ "package" { return [ops.scope, "@package"] } // Alias
|
|
208
210
|
/ "treehttps" { return ops.treeHttps; }
|
|
209
211
|
/ "treehttp" { return ops.treeHttp; }
|
|
210
|
-
/ "tree" { return ops.treeHttps; } //
|
|
212
|
+
/ "tree" { return ops.treeHttps; } // Alias
|
|
211
213
|
|
|
212
|
-
scopeReference
|
|
214
|
+
scopeReference "scope reference"
|
|
213
215
|
= key:identifier { return [ops.scope, key]; }
|
|
214
216
|
|
|
215
|
-
singleQuoteString
|
|
217
|
+
singleQuoteString "single quote string"
|
|
216
218
|
= "'" chars:singleQuoteStringChar* "'" { return chars.join(""); }
|
|
217
219
|
|
|
218
220
|
singleQuoteStringChar
|
|
@@ -221,13 +223,13 @@ singleQuoteStringChar
|
|
|
221
223
|
start
|
|
222
224
|
= number
|
|
223
225
|
|
|
224
|
-
string
|
|
226
|
+
string "string"
|
|
225
227
|
= doubleQuoteString
|
|
226
228
|
/ singleQuoteString
|
|
227
229
|
|
|
228
230
|
// A top-level document defining a template. This is the same as a template
|
|
229
231
|
// literal, but can contain backticks at the top level.
|
|
230
|
-
templateDocument "
|
|
232
|
+
templateDocument "template"
|
|
231
233
|
= contents:templateDocumentContents { return [ops.lambda, contents]; }
|
|
232
234
|
|
|
233
235
|
// Template documents can contain backticks at the top level.
|
|
@@ -238,11 +240,11 @@ templateDocumentChar
|
|
|
238
240
|
templateDocumentContents
|
|
239
241
|
= parts:(templateDocumentText / templateSubstitution)* { return makeTemplate(parts); }
|
|
240
242
|
|
|
241
|
-
templateDocumentText
|
|
243
|
+
templateDocumentText "template text"
|
|
242
244
|
= chars:templateDocumentChar+ { return chars.join(""); }
|
|
243
245
|
|
|
244
246
|
// A backtick-quoted template literal
|
|
245
|
-
templateLiteral
|
|
247
|
+
templateLiteral "template literal"
|
|
246
248
|
= "`" @templateLiteralContents "`"
|
|
247
249
|
|
|
248
250
|
templateLiteralChar
|
|
@@ -257,14 +259,14 @@ templateLiteralText
|
|
|
257
259
|
= chars:templateLiteralChar+ { return chars.join(""); }
|
|
258
260
|
|
|
259
261
|
// A substitution in a template literal: `{{ fn() }}`
|
|
260
|
-
templateSubstitution
|
|
262
|
+
templateSubstitution "template substitution"
|
|
261
263
|
= "{{" @expression "}}"
|
|
262
264
|
|
|
263
265
|
textChar
|
|
264
266
|
= escapedChar / .
|
|
265
267
|
|
|
266
268
|
// A tree literal: `{ index.html = "Hello" }`
|
|
267
|
-
tree
|
|
269
|
+
tree "tree literal"
|
|
268
270
|
= "{" __ assignments:treeAssignments? "}" { return [ops.tree, ...(assignments ?? [])]; }
|
|
269
271
|
|
|
270
272
|
// A separated list of assignments or shorthands
|