@panagos/jojoscript 0.9.0 → 0.9.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/README.md +21 -1
- package/package.json +1 -1
- package/src/index.js +33 -33
- package/src/pipeline-parser.js +36 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# JojoScript 0.9.
|
|
1
|
+
# JojoScript 0.9.1
|
|
2
2
|
|
|
3
3
|
JojoScript is a small JavaScript-compatible language and runtime for
|
|
4
4
|
expressive, composable pipelines. It adds lightweight syntax for declarations,
|
|
@@ -237,6 +237,26 @@ becomes:
|
|
|
237
237
|
divide(100, value)
|
|
238
238
|
```
|
|
239
239
|
|
|
240
|
+
#### Property access stages
|
|
241
|
+
|
|
242
|
+
A pipeline stage may also start with a dot to read a field or invoke a method
|
|
243
|
+
on the piped value directly:
|
|
244
|
+
|
|
245
|
+
```jojo
|
|
246
|
+
users |> .length
|
|
247
|
+
name |> .trim() |> .toUpperCase()
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
becomes:
|
|
251
|
+
|
|
252
|
+
```js
|
|
253
|
+
users.length
|
|
254
|
+
name.trim().toUpperCase()
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
This is handy for compact pipelines where the previous value is the receiver of
|
|
258
|
+
an ordinary JavaScript property or method call.
|
|
259
|
+
|
|
240
260
|
#### `catch` / `orElse` control stages
|
|
241
261
|
|
|
242
262
|
`catch` and `orElse` provide inline error handling without leaving pipeline
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
export {
|
|
2
|
-
compile,
|
|
3
|
-
compileFile,
|
|
4
|
-
compileDirectory
|
|
5
|
-
} from './compiler.js'
|
|
6
|
-
|
|
7
|
-
export {
|
|
8
|
-
transformPipelines,
|
|
9
|
-
emitPipeline,
|
|
10
|
-
findPipelineStdlibFunctions
|
|
11
|
-
} from './pipeline-parser.js'
|
|
12
|
-
|
|
13
|
-
export {
|
|
14
|
-
transformMatchExpressions
|
|
15
|
-
} from './match-parser.js'
|
|
16
|
-
|
|
17
|
-
export {
|
|
18
|
-
transformTypeAnnotations
|
|
19
|
-
} from './type-annotations.js'
|
|
20
|
-
|
|
21
|
-
export {
|
|
22
|
-
analyzePipelines,
|
|
23
|
-
buildGraph,
|
|
24
|
-
renderGraphText,
|
|
25
|
-
renderGraphDot
|
|
26
|
-
} from './pipeline-ir.js'
|
|
27
|
-
|
|
28
|
-
export {
|
|
29
|
-
transformEffectDeclarations,
|
|
30
|
-
transformHandleBlocks,
|
|
31
|
-
findEffectDeclarations,
|
|
32
|
-
findHandleBlocks
|
|
33
|
-
} from './effect-parser.js'
|
|
1
|
+
export {
|
|
2
|
+
compile,
|
|
3
|
+
compileFile,
|
|
4
|
+
compileDirectory
|
|
5
|
+
} from './compiler.js'
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
transformPipelines,
|
|
9
|
+
emitPipeline,
|
|
10
|
+
findPipelineStdlibFunctions
|
|
11
|
+
} from './pipeline-parser.js'
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
transformMatchExpressions
|
|
15
|
+
} from './match-parser.js'
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
transformTypeAnnotations
|
|
19
|
+
} from './type-annotations.js'
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
analyzePipelines,
|
|
23
|
+
buildGraph,
|
|
24
|
+
renderGraphText,
|
|
25
|
+
renderGraphDot
|
|
26
|
+
} from './pipeline-ir.js'
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
transformEffectDeclarations,
|
|
30
|
+
transformHandleBlocks,
|
|
31
|
+
findEffectDeclarations,
|
|
32
|
+
findHandleBlocks
|
|
33
|
+
} from './effect-parser.js'
|
package/src/pipeline-parser.js
CHANGED
|
@@ -112,9 +112,18 @@ function splitArguments(tokens) {
|
|
|
112
112
|
return result
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
function isPropertyAccessName(name) {
|
|
116
|
+
return /^\.[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*$/.test(name)
|
|
117
|
+
}
|
|
118
|
+
|
|
115
119
|
function parseStage(source, tokens) {
|
|
116
120
|
if (!tokens.length) throw new SyntaxError('Empty pipeline stage')
|
|
117
121
|
|
|
122
|
+
const bareName = text(source, tokens).trim()
|
|
123
|
+
if (isPropertyAccessName(bareName)) {
|
|
124
|
+
return { name: bareName, args: [], propertyCall: false }
|
|
125
|
+
}
|
|
126
|
+
|
|
118
127
|
if (tokens.every((token, index) =>
|
|
119
128
|
index % 2 === 0
|
|
120
129
|
? token.type === 'identifier'
|
|
@@ -145,6 +154,14 @@ function parseStage(source, tokens) {
|
|
|
145
154
|
|
|
146
155
|
const name = text(source, tokens.slice(0, openIndex)).trim()
|
|
147
156
|
|
|
157
|
+
if (isPropertyAccessName(name)) {
|
|
158
|
+
const args = splitArguments(
|
|
159
|
+
tokens.slice(openIndex + 1, closeIndex)
|
|
160
|
+
).map(argument => text(source, argument).trim())
|
|
161
|
+
|
|
162
|
+
return { name, args, propertyCall: true }
|
|
163
|
+
}
|
|
164
|
+
|
|
148
165
|
if (!/^[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*$/.test(name)) {
|
|
149
166
|
throw new SyntaxError(`Invalid pipeline function: ${name}`)
|
|
150
167
|
}
|
|
@@ -310,6 +327,25 @@ export function emitPipeline(pipeline, options = {}) {
|
|
|
310
327
|
continue
|
|
311
328
|
}
|
|
312
329
|
|
|
330
|
+
if (stage.name.startsWith('.')) {
|
|
331
|
+
const args = stage.args.slice()
|
|
332
|
+
const placeholderIndex = args.indexOf('_')
|
|
333
|
+
if (placeholderIndex !== -1) {
|
|
334
|
+
args[placeholderIndex] = expression
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (stage.propertyCall || args.length > 0) {
|
|
338
|
+
expression = `${expression}${stage.name}(${args.join(', ')})`
|
|
339
|
+
} else {
|
|
340
|
+
expression = `${expression}${stage.name}`
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (options.profile) {
|
|
344
|
+
expression = `traceNode(${expression}, ${metaLiteral(pipelineId, stage.nodeId, stage.name)})`
|
|
345
|
+
}
|
|
346
|
+
continue
|
|
347
|
+
}
|
|
348
|
+
|
|
313
349
|
if (stage.name === 'retry' && stage.standaloneRetry) {
|
|
314
350
|
const meta = metaLiteral(pipelineId, stage.nodeId, 'retry')
|
|
315
351
|
expression = `(await retry(async () => (${expression}), ${stage.args[0]}, ${meta})())`
|