@weborigami/language 0.2.0 → 0.2.2

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/language",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Web Origami expression language compiler and runtime",
5
5
  "type": "module",
6
6
  "main": "./main.js",
@@ -12,8 +12,8 @@
12
12
  "yaml": "2.5.1"
13
13
  },
14
14
  "dependencies": {
15
- "@weborigami/async-tree": "0.2.0",
16
- "@weborigami/types": "0.2.0",
15
+ "@weborigami/async-tree": "0.2.2",
16
+ "@weborigami/types": "0.2.2",
17
17
  "watcher": "2.3.1"
18
18
  },
19
19
  "scripts": {
@@ -16,7 +16,7 @@ import {
16
16
  annotate,
17
17
  downgradeReference,
18
18
  makeArray,
19
- makeBinaryOperatorChain,
19
+ makeBinaryOperation,
20
20
  makeCall,
21
21
  makeDeferredArguments,
22
22
  makeObject,
@@ -24,7 +24,7 @@ import {
24
24
  makeProperty,
25
25
  makeReference,
26
26
  makeTemplate,
27
- makeUnaryOperatorCall
27
+ makeUnaryOperation
28
28
  } from "./parserHelpers.js";
29
29
 
30
30
  }}
@@ -35,6 +35,15 @@ __
35
35
  return null;
36
36
  }
37
37
 
38
+ additiveExpression
39
+ = head:multiplicativeExpression tail:(__ @additiveOperator __ @multiplicativeExpression)* {
40
+ return annotate(tail.reduce(makeBinaryOperation, head), location());
41
+ }
42
+
43
+ additiveOperator
44
+ = "+"
45
+ / "-"
46
+
38
47
  arguments "function arguments"
39
48
  = parenthesesArguments
40
49
  / pathArguments
@@ -52,22 +61,51 @@ arrayEntries
52
61
  }
53
62
 
54
63
  arrayEntry
55
- = spread
64
+ = spreadElement
56
65
  / pipelineExpression
66
+ // JavaScript treats a missing value as `undefined`
67
+ / __ !"]" {
68
+ return annotate([ops.literal, undefined], location());
69
+ }
57
70
 
58
71
  arrowFunction
59
72
  = "(" __ parameters:identifierList? __ ")" __ doubleArrow __ pipeline:pipelineExpression {
60
73
  return annotate([ops.lambda, parameters ?? [], pipeline], location());
61
74
  }
75
+ / identifier:identifier __ doubleArrow __ pipeline:pipelineExpression {
76
+ return annotate([ops.lambda, [identifier], pipeline], location());
77
+ }
62
78
  / conditionalExpression
63
79
 
80
+ bitwiseAndExpression
81
+ = head:equalityExpression tail:(__ @bitwiseAndOperator __ @equalityExpression)* {
82
+ return annotate(tail.reduce(makeBinaryOperation, head), location());
83
+ }
84
+
85
+ bitwiseAndOperator
86
+ = @"&" !"&"
87
+
88
+ bitwiseOrExpression
89
+ = head:bitwiseXorExpression tail:(__ @bitwiseOrOperator __ @bitwiseXorExpression)* {
90
+ return annotate(tail.reduce(makeBinaryOperation, head), location());
91
+ }
92
+
93
+ bitwiseOrOperator
94
+ = @"|" !"|"
95
+
96
+ bitwiseXorExpression
97
+ = head:bitwiseAndExpression tail:(__ @bitwiseXorOperator __ @bitwiseAndExpression)* {
98
+ return annotate(tail.reduce(makeBinaryOperation, head), location());
99
+ }
100
+
101
+ bitwiseXorOperator
102
+ = "^"
103
+
64
104
  // A function call: `fn(arg)`, possibly part of a chain of function calls, like
65
105
  // `fn(arg1)(arg2)(arg3)`.
66
106
  callExpression "function call"
67
107
  = head:protocolExpression tail:arguments* {
68
- return tail.length === 0
69
- ? head
70
- : annotate(tail.reduce(makeCall, head), location());
108
+ return annotate(tail.reduce(makeCall, head), location());
71
109
  }
72
110
 
73
111
  // Required closing curly brace. We use this for the `object` term: if the
@@ -75,7 +113,7 @@ callExpression "function call"
75
113
  closingBrace
76
114
  = "}"
77
115
  / .? {
78
- error("Expected right curly brace");
116
+ error(`An object ended without a closing brace, or contained something that wasn't expected.\nThe top level of an object can only contain definitions ("a: b" or "a = b") or spreads ("...a").`);
79
117
  }
80
118
 
81
119
  // Required closing bracket
@@ -94,24 +132,35 @@ closingParenthesis
94
132
  error("Expected right parenthesis");
95
133
  }
96
134
 
135
+ // A comma-separated list of expressions: `x, y, z`
136
+ commaExpression
137
+ // The commas are required, but the list can have a single item.
138
+ = list:pipelineExpression|1.., __ "," __ | {
139
+ return list.length === 1
140
+ ? list[0]
141
+ : annotate([ops.comma, ...list], location());
142
+ }
143
+
97
144
  // A single line comment
98
145
  comment "comment"
99
146
  = multiLineComment
100
147
  / singleLineComment
101
148
 
102
149
  conditionalExpression
103
- = condition:logicalOrExpression __
104
- "?" __ truthy:pipelineExpression __
105
- ":" __ falsy:pipelineExpression
150
+ = condition:logicalOrExpression tail:(__
151
+ "?" __ @pipelineExpression __
152
+ ":" __ @pipelineExpression)?
106
153
  {
154
+ if (!tail) {
155
+ return condition;
156
+ }
107
157
  return annotate([
108
158
  ops.conditional,
109
159
  downgradeReference(condition),
110
- [ops.lambda, [], downgradeReference(truthy)],
111
- [ops.lambda, [], downgradeReference(falsy)]
160
+ [ops.lambda, [], downgradeReference(tail[0])],
161
+ [ops.lambda, [], downgradeReference(tail[1])]
112
162
  ], location());
113
163
  }
114
- / logicalOrExpression
115
164
 
116
165
  digits
117
166
  = @[0-9]+
@@ -129,10 +178,8 @@ doubleQuoteStringChar
129
178
  ellipsis = "..." / "…" // Unicode ellipsis
130
179
 
131
180
  equalityExpression
132
- = head:unaryExpression tail:(__ @equalityOperator __ @unaryExpression)* {
133
- return tail.length === 0
134
- ? head
135
- : annotate(makeBinaryOperatorChain(head, tail), location());
181
+ = head:relationalExpression tail:(__ @equalityOperator __ @relationalExpression)* {
182
+ return annotate(tail.reduce(makeBinaryOperation, head), location());
136
183
  }
137
184
 
138
185
  equalityOperator
@@ -151,12 +198,17 @@ escapedChar "backslash-escaped character"
151
198
  / "\\v" { return "\v"; }
152
199
  / "\\" @.
153
200
 
201
+ exponentiationExpression
202
+ = left:unaryExpression right:(__ "**" __ @exponentiationExpression)? {
203
+ return right ? annotate([ops.exponentiation, left, right], location()) : left;
204
+ }
205
+
154
206
  // A top-level expression, possibly with leading/trailing whitespace
155
207
  expression
156
- = __ @pipelineExpression __
208
+ = __ @commaExpression __
157
209
 
158
210
  floatLiteral "floating-point number"
159
- = sign? digits? "." digits {
211
+ = digits? "." digits {
160
212
  return annotate([ops.literal, parseFloat(text())], location());
161
213
  }
162
214
 
@@ -195,7 +247,7 @@ identifier "identifier"
195
247
  = chars:identifierChar+ { return chars.join(""); }
196
248
 
197
249
  identifierChar
198
- = [^(){}\[\]<>\?!&\|\-=,/:\`"'«»\\ →⇒\t\n\r] // No unescaped whitespace or special chars
250
+ = [^(){}\[\]<>\?!\|\-=,/:\`"'«»\\→⇒… \t\n\r] // No unescaped whitespace or special chars
199
251
  / @'-' !'>' // Accept a hyphen but not in a single arrow combination
200
252
  / escapedChar
201
253
 
@@ -220,7 +272,7 @@ inlineSpace
220
272
  = [ \t]
221
273
 
222
274
  integerLiteral "integer"
223
- = sign? digits {
275
+ = digits {
224
276
  return annotate([ops.literal, parseInt(text())], location());
225
277
  }
226
278
 
@@ -235,7 +287,7 @@ literal
235
287
  / stringLiteral
236
288
 
237
289
  logicalAndExpression
238
- = head:equalityExpression tail:(__ "&&" __ @equalityExpression)* {
290
+ = head:bitwiseOrExpression tail:(__ "&&" __ @bitwiseOrExpression)* {
239
291
  return tail.length === 0
240
292
  ? head
241
293
  : annotate(
@@ -257,6 +309,16 @@ logicalOrExpression
257
309
  multiLineComment
258
310
  = "/*" (!"*/" .)* "*/" { return null; }
259
311
 
312
+ multiplicativeExpression
313
+ = head:exponentiationExpression tail:(__ @multiplicativeOperator __ @exponentiationExpression)* {
314
+ return annotate(tail.reduce(makeBinaryOperation, head), location());
315
+ }
316
+
317
+ multiplicativeOperator
318
+ = "*"
319
+ / "/"
320
+ / "%"
321
+
260
322
  // A namespace reference is a string of letters only, followed by a colon.
261
323
  // For the time being, we also allow a leading `@`, which is deprecated.
262
324
  namespace
@@ -297,7 +359,7 @@ objectEntries
297
359
  }
298
360
 
299
361
  objectEntry
300
- = spread
362
+ = spreadElement
301
363
  / objectProperty
302
364
  / objectGetter
303
365
  / objectShorthandProperty
@@ -382,7 +444,10 @@ pathSegmentChar
382
444
  // functions to it.
383
445
  pipelineExpression
384
446
  = head:shorthandFunction tail:(__ singleArrow __ @shorthandFunction)* {
385
- return tail.reduce(makePipeline, downgradeReference(head));
447
+ return annotate(
448
+ tail.reduce(makePipeline, downgradeReference(head)),
449
+ location()
450
+ );
386
451
  }
387
452
 
388
453
  primary
@@ -419,6 +484,17 @@ reference
419
484
  / namespace
420
485
  / scopeReference
421
486
 
487
+ relationalExpression
488
+ = head:shiftExpression tail:(__ @relationalOperator __ @shiftExpression)* {
489
+ return annotate(tail.reduce(makeBinaryOperation, head), location());
490
+ }
491
+
492
+ relationalOperator
493
+ = "<="
494
+ / "<"
495
+ / ">="
496
+ / ">"
497
+
422
498
  // A top-level folder below the root: `/foo`
423
499
  // or the root folder itself: `/`
424
500
  rootDirectory
@@ -449,6 +525,16 @@ slashFollows
449
525
  shebang
450
526
  = "#!" [^\n\r]* { return null; }
451
527
 
528
+ shiftExpression
529
+ = head:additiveExpression tail:(__ @shiftOperator __ @additiveExpression)* {
530
+ return annotate(tail.reduce(makeBinaryOperation, head), location());
531
+ }
532
+
533
+ shiftOperator
534
+ = "<<"
535
+ / ">>>"
536
+ / ">>"
537
+
452
538
  // A shorthand lambda expression: `=foo(_)`
453
539
  shorthandFunction "lambda function"
454
540
  // Avoid a following equal sign (for an equality)
@@ -457,9 +543,6 @@ shorthandFunction "lambda function"
457
543
  }
458
544
  / implicitParenthesesCallExpression
459
545
 
460
- sign
461
- = [+\-]
462
-
463
546
  singleArrow
464
547
  = "→"
465
548
  / "->"
@@ -475,8 +558,8 @@ singleQuoteString "single quote string"
475
558
  singleQuoteStringChar
476
559
  = !("'" / newLine) @textChar
477
560
 
478
- spread
479
- = ellipsis __ value:conditionalExpression {
561
+ spreadElement
562
+ = ellipsis __ value:pipelineExpression {
480
563
  return annotate([ops.spread, value], location());
481
564
  }
482
565
 
@@ -539,12 +622,15 @@ textChar
539
622
  // A unary prefix operator: `!x`
540
623
  unaryExpression
541
624
  = operator:unaryOperator __ expression:unaryExpression {
542
- return annotate(makeUnaryOperatorCall(operator, expression), location());
625
+ return annotate(makeUnaryOperation(operator, expression), location());
543
626
  }
544
627
  / callExpression
545
628
 
546
629
  unaryOperator
547
630
  = "!"
631
+ / "+"
632
+ / "-"
633
+ / "~"
548
634
 
549
635
  whitespaceWithNewLine
550
636
  = inlineSpace* comment? newLine __