subscript 8.0.0 → 8.1.0
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 +30 -12
- package/feature/access.js +11 -0
- package/feature/add.js +22 -0
- package/feature/array.js +11 -0
- package/feature/arrow.js +23 -0
- package/feature/assign.js +11 -0
- package/feature/bitwise.js +15 -0
- package/feature/bool.js +5 -0
- package/feature/call.js +15 -0
- package/feature/comment.js +7 -0
- package/feature/compare.js +11 -0
- package/feature/group.js +12 -0
- package/feature/in.js +6 -0
- package/feature/increment.js +16 -0
- package/feature/logic.js +11 -0
- package/feature/mult.js +25 -0
- package/feature/number.js +11 -0
- package/feature/object.js +16 -0
- package/feature/optional.js +30 -0
- package/feature/pow.js +5 -0
- package/feature/string.js +19 -0
- package/feature/ternary.js +7 -0
- package/justin.js +1 -35
- package/justin.min.js +1 -1
- package/package.json +5 -5
- package/src/compile.d.ts +17 -0
- package/src/compile.js +26 -0
- package/src/const.js +40 -0
- package/src/parse.d.ts +19 -0
- package/src/parse.js +100 -0
- package/subscript.min.js +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# <
|
|
1
|
+
# sub<em>script</em> <a href="https://github.com/spectjs/subscript/actions/workflows/node.js.yml"><img src="https://github.com/spectjs/subscript/actions/workflows/node.js.yml/badge.svg"/></a> <a href="https://bundlephobia.com/package/subscript"><img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/subscript/latest?color=brightgreen&label=gzip"/></a> <a href="http://npmjs.org/subscript"><img src="https://img.shields.io/npm/v/subscript"/></a> <a href="http://microjs.com/#subscript"><img src="https://img.shields.io/badge/microjs-subscript-blue?color=darkslateblue"/></a>
|
|
2
2
|
|
|
3
3
|
> _Subscript_ is fast, tiny & extensible expression evaluator / microlanguage.
|
|
4
4
|
|
|
5
|
-
Used for:
|
|
5
|
+
#### Used for:
|
|
6
6
|
|
|
7
7
|
* templates (eg. [sprae](https://github.com/dy/sprae), [templize](https://github.com/dy/templize))
|
|
8
8
|
* expressions evaluators, calculators
|
|
@@ -82,7 +82,7 @@ fn = compile(tree)
|
|
|
82
82
|
fn({ a: {b: 1}, c: 2 }) // 3
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
### Syntax Tree
|
|
86
86
|
|
|
87
87
|
AST has simplified lispy tree structure (inspired by [frisk](https://ghub.io/frisk) / [nisp](https://github.com/ysmood/nisp)), opposed to [ESTree](https://github.com/estree/estree):
|
|
88
88
|
|
|
@@ -90,34 +90,52 @@ AST has simplified lispy tree structure (inspired by [frisk](https://ghub.io/fri
|
|
|
90
90
|
* reflects execution sequence, rather than code layout;
|
|
91
91
|
* has minimal overhead, directly maps to operators;
|
|
92
92
|
* simplifies manual evaluation and debugging;
|
|
93
|
-
* has conventional form and one-
|
|
93
|
+
* has conventional form and one-liner docs:
|
|
94
94
|
|
|
95
95
|
```js
|
|
96
96
|
import { compile } from 'subscript.js'
|
|
97
97
|
|
|
98
98
|
const fn = compile(['+', ['*', 'min', [,60]], [,'sec']])
|
|
99
|
-
|
|
100
99
|
fn({min: 5}) // min*60 + "sec" == "300sec"
|
|
100
|
+
|
|
101
|
+
// node kinds
|
|
102
|
+
['+', a]; // unary prefix or postfix operator `+a`
|
|
103
|
+
['+', a, b]; // binary operator `a + b`
|
|
104
|
+
['+', a, b, c]; // n-ary operator `a + b + c`
|
|
105
|
+
['()', a]; // group operator `(a)`
|
|
106
|
+
['(', a, b]; // access operator `a(b)`
|
|
107
|
+
[, a]; // literal value `'a'`
|
|
108
|
+
a; // variable (from scope)
|
|
101
109
|
```
|
|
102
110
|
|
|
103
111
|
## Extending
|
|
104
112
|
|
|
105
|
-
_Subscript_ provides API to customize
|
|
113
|
+
_Subscript_ provides pluggable language [features](./features) and API to customize syntax:
|
|
106
114
|
|
|
107
|
-
* `unary(str, precedence, postfix=false)` − register unary operator, either prefix or postfix
|
|
108
|
-
* `binary(str, precedence,
|
|
109
|
-
* `nary(str, precedence
|
|
110
|
-
* `
|
|
111
|
-
* `
|
|
115
|
+
* `unary(str, precedence, postfix=false)` − register unary operator, either prefix `⚬a` or postfix `a⚬`.
|
|
116
|
+
* `binary(str, precedence, rassoc=false)` − register binary operator `a ⚬ b`, optionally right-associative.
|
|
117
|
+
* `nary(str, precedence)` − register n-ary (sequence) operator like `a; b;` or `a, b`, allows missing args.
|
|
118
|
+
* `group(str, precedence)` - register group, like `[a]`, `{a}`, `(a)` etc.
|
|
119
|
+
* `access(str, precedence)` - register access operator, like `a[b]`, `a(b)` etc.
|
|
120
|
+
* `token(str, precedence, lnode => node)` − register custom token or literal. Callback takes left-side node and returns complete expression node.
|
|
121
|
+
* `operator(str, (a, b) => ctx => value)` − register evaluator for an operator. Callback takes node arguments and returns evaluator function.
|
|
112
122
|
|
|
113
123
|
```js
|
|
114
124
|
import script, { compile, operator, unary, binary, token } from './subscript.js'
|
|
115
125
|
|
|
116
|
-
//
|
|
126
|
+
// enable objects/arrays syntax
|
|
127
|
+
import 'subscript/feature/array.js';
|
|
128
|
+
import 'subscript/feature/object.js';
|
|
129
|
+
|
|
130
|
+
// add identity operators (precedence of comparison)
|
|
117
131
|
binary('===', 9), binary('!==', 9)
|
|
118
132
|
operator('===', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx)===b(ctx)))
|
|
119
133
|
operator('===', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx)!==b(ctx)))
|
|
120
134
|
|
|
135
|
+
// add nullish coalescing (precedence of logical or)
|
|
136
|
+
binary('??', 3)
|
|
137
|
+
operator('??', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) ?? b(ctx)))
|
|
138
|
+
|
|
121
139
|
// add JS literals
|
|
122
140
|
token('undefined', 20, a => a ? err() : [, undefined])
|
|
123
141
|
token('NaN', 20, a => a ? err() : [, NaN])
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { access, binary, group } from '../src/parse.js'
|
|
2
|
+
import { operator, compile } from '../src/compile.js'
|
|
3
|
+
import { CBRACK, PREC_ACCESS } from '../src/const.js'
|
|
4
|
+
|
|
5
|
+
// a[b]
|
|
6
|
+
access('[]', PREC_ACCESS)
|
|
7
|
+
operator('[', (a, b) => !b ? err() : (a = compile(a), b = compile(b), ctx => a(ctx)[b(ctx)]))
|
|
8
|
+
|
|
9
|
+
// a.b
|
|
10
|
+
binary('.', PREC_ACCESS)
|
|
11
|
+
operator('.', (a, b) => (a = compile(a), b = !b[0] ? b[1] : b, ctx => a(ctx)[b])) // a.true, a.1 → needs to work fine
|
package/feature/add.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
import { binary, unary } from '../src/parse.js'
|
|
3
|
+
import { PREC_ADD, PREC_PREFIX, PREC_ASSIGN } from '../src/const.js'
|
|
4
|
+
import { compile, prop, operator } from '../src/compile.js'
|
|
5
|
+
|
|
6
|
+
unary('+', PREC_PREFIX), operator('+', (a, b) => !b && (a = compile(a), ctx => +a(ctx)))
|
|
7
|
+
unary('-', PREC_PREFIX), operator('-', (a, b) => !b && (a = compile(a), ctx => -a(ctx)))
|
|
8
|
+
|
|
9
|
+
binary('+', PREC_ADD), operator('+', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) + b(ctx)))
|
|
10
|
+
binary('-', PREC_ADD), operator('-', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) - b(ctx)))
|
|
11
|
+
|
|
12
|
+
binary('+=', PREC_ASSIGN, true)
|
|
13
|
+
operator('+=', (a, b) => (
|
|
14
|
+
b = compile(b),
|
|
15
|
+
prop(a, (container, path, ctx) => container(ctx)[path(ctx)] += b(ctx))
|
|
16
|
+
))
|
|
17
|
+
|
|
18
|
+
binary('-=', PREC_ASSIGN, true)
|
|
19
|
+
operator('-=', (a, b) => (
|
|
20
|
+
b = compile(b),
|
|
21
|
+
prop(a, (container, path, ctx) => (container(ctx)[path(ctx)] -= b(ctx)))
|
|
22
|
+
))
|
package/feature/array.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { token, expr, group } from '../src/parse.js'
|
|
2
|
+
import { operator, compile } from '../src/compile.js'
|
|
3
|
+
import { PREC_TOKEN } from '../src/const.js'
|
|
4
|
+
|
|
5
|
+
// [a,b,c]
|
|
6
|
+
group('[]', PREC_TOKEN)
|
|
7
|
+
operator('[]', (a, b) => (
|
|
8
|
+
!a ? () => [] : // []
|
|
9
|
+
a[0] === ',' ? (a = a.slice(1).map(compile), ctx => a.map(a => a(ctx))) : // [a,b,c]
|
|
10
|
+
(a = compile(a), ctx => [a(ctx)]) // [a]
|
|
11
|
+
))
|
package/feature/arrow.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { binary, group } from "../src/parse.js"
|
|
2
|
+
import { compile, operator } from "../src/compile.js"
|
|
3
|
+
import { PREC_ASSIGN, PREC_TOKEN } from "../src/const.js"
|
|
4
|
+
|
|
5
|
+
// arrow functions (useful for array methods)
|
|
6
|
+
binary('=>', PREC_ASSIGN)
|
|
7
|
+
operator('=>',
|
|
8
|
+
(a, b) => (
|
|
9
|
+
a = a[0] === '()' ? a[1] : a,
|
|
10
|
+
a = !a ? [] : // () =>
|
|
11
|
+
a[0] === ',' ? (a = a.slice(1)) : // (a,c) =>
|
|
12
|
+
(a = [a]), // a =>
|
|
13
|
+
|
|
14
|
+
b = compile(b[0] === '{}' ? b[1] : b), // `=> {x}` -> `=> x`
|
|
15
|
+
|
|
16
|
+
(ctx = null) => (
|
|
17
|
+
ctx = Object.create(ctx),
|
|
18
|
+
(...args) => (a.map((a, i) => ctx[a] = args[i]), b(ctx))
|
|
19
|
+
)
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
binary('')
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { binary, err } from "../src/parse.js";
|
|
2
|
+
import { compile, operator, operators, prop } from "../src/compile.js";
|
|
3
|
+
import { PREC_ASSIGN } from "../src/const.js";
|
|
4
|
+
|
|
5
|
+
// assignments
|
|
6
|
+
binary('=', PREC_ASSIGN, true)
|
|
7
|
+
operator('=', (a, b) => (
|
|
8
|
+
b = compile(b),
|
|
9
|
+
// a = x, ((a)) = x, a.b = x, a['b'] = x
|
|
10
|
+
prop(a, (container, path, ctx) => container(ctx)[path(ctx)] = b(ctx))
|
|
11
|
+
))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PREC_OR, PREC_AND, PREC_SHIFT, PREC_XOR, PREC_PREFIX } from "../src/const.js"
|
|
2
|
+
import { unary, binary } from "../src/parse.js"
|
|
3
|
+
import { operator, compile } from "../src/compile.js"
|
|
4
|
+
|
|
5
|
+
unary('~', PREC_PREFIX), operator('~', (a, b) => !b && (a = compile(a), ctx => ~a(ctx)))
|
|
6
|
+
|
|
7
|
+
binary('|', PREC_OR), operator('|', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) | b(ctx)))
|
|
8
|
+
|
|
9
|
+
binary('&', PREC_AND), operator('&', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) & b(ctx)))
|
|
10
|
+
|
|
11
|
+
binary('^', PREC_XOR), operator('^', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) ^ b(ctx)))
|
|
12
|
+
|
|
13
|
+
binary('>>', PREC_SHIFT), operator('>>', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) >> b(ctx)))
|
|
14
|
+
|
|
15
|
+
binary('<<', PREC_SHIFT), operator('<<', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) << b(ctx)))
|
package/feature/bool.js
ADDED
package/feature/call.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { access } from '../src/parse.js'
|
|
2
|
+
import { operator, compile, prop } from '../src/compile.js'
|
|
3
|
+
import { PREC_ACCESS } from '../src/const.js'
|
|
4
|
+
|
|
5
|
+
// a(b,c,d), a()
|
|
6
|
+
access('()', PREC_ACCESS)
|
|
7
|
+
operator('(', (a, b, args) => (
|
|
8
|
+
args = !b ? () => [] : // a()
|
|
9
|
+
b[0] === ',' ? (b = b.slice(1).map(b => !b ? err() : compile(b)), ctx => b.map(arg => arg(ctx))) : // a(b,c)
|
|
10
|
+
(b = compile(b), ctx => [b(ctx)]), // a(b)
|
|
11
|
+
|
|
12
|
+
// a(...args), a.b(...args), a[b](...args)
|
|
13
|
+
prop(a, (obj, path, ctx) => obj(ctx)[path(ctx)](...args(ctx)), true)
|
|
14
|
+
)
|
|
15
|
+
)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SPACE, STAR, PREC_TOKEN } from "../src/const.js"
|
|
2
|
+
import { token, skip, cur, idx, expr } from "../src/parse.js"
|
|
3
|
+
|
|
4
|
+
// /**/, //
|
|
5
|
+
// FIXME: try replacing with group
|
|
6
|
+
token('/*', PREC_TOKEN, (a, prec) => (skip(c => c !== STAR && cur.charCodeAt(idx + 1) !== 47), skip(2), a || expr(prec) || []))
|
|
7
|
+
token('//', PREC_TOKEN, (a, prec) => (skip(c => c >= SPACE), a || expr(prec) || ['']))
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PREC_EQ } from '../src/const.js'
|
|
2
|
+
import { unary, binary } from "../src/parse.js"
|
|
3
|
+
import { operator, compile } from "../src/compile.js"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
binary('==', PREC_EQ), operator('==', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) == b(ctx)))
|
|
7
|
+
binary('!=', PREC_EQ), operator('!=', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) != b(ctx)))
|
|
8
|
+
binary('>', PREC_EQ), operator('>', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) > b(ctx)))
|
|
9
|
+
binary('<', PREC_EQ), operator('<', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) < b(ctx)))
|
|
10
|
+
binary('>=', PREC_EQ), operator('>=', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) >= b(ctx)))
|
|
11
|
+
binary('<=', PREC_EQ), operator('<=', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) <= b(ctx)))
|
package/feature/group.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { err, nary, group } from '../src/parse.js'
|
|
2
|
+
import { compile, operator } from '../src/compile.js'
|
|
3
|
+
import { PREC_ACCESS, PREC_GROUP, PREC_SEQ } from '../src/const.js'
|
|
4
|
+
|
|
5
|
+
// (a,b,c), (a)
|
|
6
|
+
// FIXME: try raising group precedence (it causes conflict in ?. though)
|
|
7
|
+
group('()', PREC_ACCESS)
|
|
8
|
+
operator('()', (a) => (!a && err('Empty ()'), compile(a)))
|
|
9
|
+
|
|
10
|
+
const last = (...args) => (args = args.map(compile), ctx => args.map(arg => arg(ctx)).pop())
|
|
11
|
+
nary(',', PREC_SEQ), operator(',', last)
|
|
12
|
+
nary(';', PREC_SEQ, true), operator(';', last)
|
package/feature/in.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { binary } from "../src/parse.js";
|
|
2
|
+
import { compile, operator } from "../src/compile.js";
|
|
3
|
+
import { PREC_COMP } from "../src/const.js";
|
|
4
|
+
|
|
5
|
+
// a in b
|
|
6
|
+
binary('in', PREC_COMP), operator('in', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) in b(ctx)))
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { token, expr } from "../src/parse.js"
|
|
2
|
+
import { operator, compile, prop } from "../src/compile.js"
|
|
3
|
+
import { PREC_POSTFIX } from "../src/const.js"
|
|
4
|
+
|
|
5
|
+
let inc, dec
|
|
6
|
+
token('++', PREC_POSTFIX, a => a ? ['-', ['++', a], [, 1]] : ['++', expr(PREC_POSTFIX - 1)])
|
|
7
|
+
operator('++', inc = (a) =>
|
|
8
|
+
// ++a, ++((a)), ++a.b, ++a[b]
|
|
9
|
+
prop(a, (obj, path, ctx) => ++obj(ctx)[path(ctx)])
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
token('--', PREC_POSTFIX, a => a ? ['+', ['--', a], [, 1]] : ['--', expr(PREC_POSTFIX - 1)])
|
|
13
|
+
operator('--', dec = (a) => (
|
|
14
|
+
// --a, --a.b, --a[b]
|
|
15
|
+
prop(a, (obj, path, ctx) => --obj(ctx)[path(ctx)])
|
|
16
|
+
))
|
package/feature/logic.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PREC_LOR, PREC_LAND, PREC_PREFIX } from '../src/const.js';
|
|
2
|
+
import { unary, binary } from "../src/parse.js"
|
|
3
|
+
import { operator, compile } from "../src/compile.js"
|
|
4
|
+
|
|
5
|
+
unary('!', PREC_PREFIX), operator('!', (a, b) => !b && (a = compile(a), ctx => !a(ctx)))
|
|
6
|
+
|
|
7
|
+
binary('||', PREC_LOR)
|
|
8
|
+
operator('||', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) || b(ctx)))
|
|
9
|
+
|
|
10
|
+
binary('&&', PREC_LAND)
|
|
11
|
+
operator('&&', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) && b(ctx)))
|
package/feature/mult.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { binary } from '../src/parse.js'
|
|
2
|
+
import { operator, compile, prop } from '../src/compile.js'
|
|
3
|
+
import { PREC_MULT, PREC_ASSIGN } from '../src/const.js'
|
|
4
|
+
|
|
5
|
+
binary('*', PREC_MULT), operator('*', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) * b(ctx)))
|
|
6
|
+
binary('/', PREC_MULT), operator('/', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) / b(ctx)))
|
|
7
|
+
binary('%', PREC_MULT), operator('%', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) % b(ctx)))
|
|
8
|
+
|
|
9
|
+
binary('*=', PREC_ASSIGN, true)
|
|
10
|
+
operator('*=', (a, b) => (
|
|
11
|
+
b = compile(b),
|
|
12
|
+
prop(a, (container, path, ctx) => container(ctx)[path(ctx)] *= b(ctx))
|
|
13
|
+
))
|
|
14
|
+
|
|
15
|
+
binary('/=', PREC_ASSIGN, true)
|
|
16
|
+
operator('/=', (a, b) => (
|
|
17
|
+
b = compile(b),
|
|
18
|
+
prop(a, (container, path, ctx) => container(ctx)[path(ctx)] /= b(ctx))
|
|
19
|
+
))
|
|
20
|
+
|
|
21
|
+
binary('%=', PREC_ASSIGN, true)
|
|
22
|
+
operator('%=', (a, b) => (
|
|
23
|
+
b = compile(b),
|
|
24
|
+
prop(a, (container, path, ctx) => container(ctx)[path(ctx)] %= b(ctx))
|
|
25
|
+
))
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { lookup, skip, err } from "../src/parse.js"
|
|
2
|
+
import { PERIOD, _0, _E, _e, _9 } from "../src/const.js"
|
|
3
|
+
|
|
4
|
+
// parse number
|
|
5
|
+
const num = a => a ? err() : [, (a = +skip(c => c === PERIOD || (c >= _0 && c <= _9) || (c === _E || c === _e ? 2 : 0))) != a ? err() : a]
|
|
6
|
+
|
|
7
|
+
// .1
|
|
8
|
+
lookup[PERIOD] = a => (!a && num())
|
|
9
|
+
|
|
10
|
+
// 0-9
|
|
11
|
+
for (let i = _0; i <= _9; i++) lookup[i] = num
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { token, expr, group, binary } from '../src/parse.js'
|
|
2
|
+
import { operator, compile } from '../src/compile.js'
|
|
3
|
+
import { PREC_ASSIGN, PREC_SEQ, PREC_TOKEN } from '../src/const.js'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// {a:1, b:2, c:3}
|
|
7
|
+
group('{}', PREC_TOKEN)
|
|
8
|
+
operator('{}', (a, b) => (
|
|
9
|
+
!a ? () => ({}) : // {}
|
|
10
|
+
a[0] === ',' ? (a = a.slice(1).map(compile), ctx => Object.fromEntries(a.map(a => a(ctx)))) : // {a:1,b:2}
|
|
11
|
+
a[0] === ':' ? (a = compile(a), ctx => Object.fromEntries([a(ctx)])) : // {a:1}
|
|
12
|
+
(b = compile(a), ctx => ({ [a]: b(ctx) }))
|
|
13
|
+
))
|
|
14
|
+
|
|
15
|
+
binary(':', PREC_ASSIGN, true)
|
|
16
|
+
operator(':', (a, b) => (b = compile(b), a = Array.isArray(a) ? compile(a) : (a => a).bind(0, a), ctx => [a(ctx), b(ctx)]))
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { token, expr } from '../src/parse.js'
|
|
2
|
+
import { operator, compile } from '../src/compile.js'
|
|
3
|
+
import { PREC_ACCESS } from '../src/const.js'
|
|
4
|
+
|
|
5
|
+
// a?.[, a?.( - postfix operator
|
|
6
|
+
token('?.', PREC_ACCESS, a => a && ['?.', a])
|
|
7
|
+
// a ?.
|
|
8
|
+
operator('?.', a => (a = compile(a), ctx => a(ctx) || (() => { })))
|
|
9
|
+
|
|
10
|
+
// a?.b, a?.() - optional chain operator
|
|
11
|
+
token('?.', PREC_ACCESS, (a, b) => a && (b = expr(PREC_ACCESS), !b?.map) && ['?.', a, b])
|
|
12
|
+
// a ?. b
|
|
13
|
+
operator('?.', (a, b) => b && (a = compile(a), ctx => a(ctx)?.[b]))
|
|
14
|
+
|
|
15
|
+
// a?.x() - keep context, but watch out a?.()
|
|
16
|
+
operator('(', (a, b, container, args, path, optional) => (a[0] === '?.') && (a[2] || Array.isArray(a[1])) && (
|
|
17
|
+
args = !b ? () => [] : // a()
|
|
18
|
+
b[0] === ',' ? (b = b.slice(1).map(compile), ctx => b.map(a => a(ctx))) : // a(b,c)
|
|
19
|
+
(b = compile(b), ctx => [b(ctx)]), // a(b)
|
|
20
|
+
|
|
21
|
+
// a?.()
|
|
22
|
+
!a[2] && (optional = true, a = a[1]),
|
|
23
|
+
|
|
24
|
+
// a?.['x']?.()
|
|
25
|
+
a[0] === '[' ? (path = compile(a[2])) : (path = () => a[2]),
|
|
26
|
+
(container = compile(a[1]), optional ?
|
|
27
|
+
ctx => (container(ctx)?.[path(ctx)]?.(...args(ctx))) :
|
|
28
|
+
ctx => (container(ctx)?.[path(ctx)](...args(ctx)))
|
|
29
|
+
)
|
|
30
|
+
))
|
package/feature/pow.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { skip, err, cur, idx, lookup } from '../src/parse.js'
|
|
2
|
+
import { DQUOTE, QUOTE, BSLASH } from '../src/const.js'
|
|
3
|
+
|
|
4
|
+
const escape = { n: '\n', r: '\r', t: '\t', b: '\b', f: '\f', v: '\v' },
|
|
5
|
+
string = q => (qc, c, str = '') => {
|
|
6
|
+
qc && err('Unexpected string') // must not follow another token
|
|
7
|
+
skip() // first quote
|
|
8
|
+
while (c = cur.charCodeAt(idx), c - q) {
|
|
9
|
+
if (c === BSLASH) skip(), c = skip(), str += escape[c] || c
|
|
10
|
+
else str += skip()
|
|
11
|
+
}
|
|
12
|
+
skip() || err('Bad string')
|
|
13
|
+
return [, str]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
// "' with /
|
|
18
|
+
lookup[DQUOTE] = string(DQUOTE)
|
|
19
|
+
lookup[QUOTE] = string(QUOTE)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { token, expr } from '../src/parse.js'
|
|
2
|
+
import { operator, compile } from '../src/compile.js'
|
|
3
|
+
import { PREC_ASSIGN, COLON } from '../src/const.js'
|
|
4
|
+
|
|
5
|
+
// ?:
|
|
6
|
+
token('?', PREC_ASSIGN, (a, b, c) => a && (b = expr(PREC_ASSIGN, COLON)) && (c = expr(PREC_ASSIGN + 1), ['?', a, b, c]))
|
|
7
|
+
operator('?', (a, b, c) => (a = compile(a), b = compile(b), c = compile(c), ctx => a(ctx) ? b(ctx) : c(ctx)))
|
package/justin.js
CHANGED
|
@@ -12,43 +12,9 @@ import './feature/ternary.js'
|
|
|
12
12
|
import './feature/bool.js'
|
|
13
13
|
import './feature/array.js'
|
|
14
14
|
import './feature/object.js'
|
|
15
|
+
import './feature/optional.js'
|
|
15
16
|
|
|
16
|
-
// operators
|
|
17
|
-
// set('===', PREC_EQ, (a, b) => a === b)
|
|
18
|
-
// set('!==', PREC_EQ, (a, b) => a !== b)
|
|
19
|
-
binary('??', PREC_OR), operator('??', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) ?? b(ctx)))
|
|
20
|
-
|
|
21
|
-
// a?.[, a?.( - postfix operator
|
|
22
|
-
token('?.', PREC_ACCESS, a => a && ['?.', a])
|
|
23
|
-
// a ?.
|
|
24
|
-
operator('?.', a => (a = compile(a), ctx => a(ctx) || (() => { })))
|
|
25
|
-
|
|
26
|
-
// a?.b, a?.() - optional chain operator
|
|
27
|
-
token('?.', PREC_ACCESS, (a, b) => a && (b = expr(PREC_ACCESS), !b?.map) && ['?.', a, b])
|
|
28
|
-
// a ?. b
|
|
29
|
-
operator('?.', (a, b) => b && (a = compile(a), ctx => a(ctx)?.[b]))
|
|
30
|
-
|
|
31
|
-
// a?.x() - keep context, but watch out a?.()
|
|
32
|
-
operator('(', (a, b, container, args, path, optional) => (b != null) && (a[0] === '?.') && (a[2] || Array.isArray(a[1])) && (
|
|
33
|
-
args = b == '' ? () => [] : // a()
|
|
34
|
-
b[0] === ',' ? (b = b.slice(1).map(compile), ctx => b.map(a => a(ctx))) : // a(b,c)
|
|
35
|
-
(b = compile(b), ctx => [b(ctx)]), // a(b)
|
|
36
|
-
|
|
37
|
-
// a?.()
|
|
38
|
-
!a[2] && (optional = true, a = a[1]),
|
|
39
|
-
|
|
40
|
-
// a?.['x']?.()
|
|
41
|
-
a[0] === '[' ? (path = compile(a[2])) : (path = () => a[2]),
|
|
42
|
-
(container = compile(a[1]), optional ?
|
|
43
|
-
ctx => (container(ctx)?.[path(ctx)]?.(...args(ctx))) :
|
|
44
|
-
ctx => (container(ctx)?.[path(ctx)](...args(ctx)))
|
|
45
|
-
)
|
|
46
|
-
))
|
|
47
|
-
|
|
48
|
-
// literals
|
|
49
17
|
token('null', 20, a => a ? err() : [, null])
|
|
50
|
-
// token('undefined', 20, a => a ? err() : [, undefined])
|
|
51
|
-
// token('NaN', 20, a => a ? err() : [, NaN])
|
|
52
18
|
|
|
53
19
|
export default subscript
|
|
54
20
|
export * from './subscript.js'
|
package/justin.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let r,e,t=t=>(r=0,e=t,t=
|
|
1
|
+
let r,e,t=t=>(r=0,e=t,t=n(),e[r]?a():t||""),a=(t="Bad syntax",a=e.slice(0,r).split("\n"),s=a.pop())=>{let n=e.slice(r-108,r).split("\n").pop(),i=e.slice(r,r+108).split("\n").shift();throw EvalError(`${t} at ${a.length}:${s.length} \`${r>=108?"…":""}${n}▶${i}\``,"font-weight: bold")},s=(t=1,a=r,s)=>{if("number"==typeof t)r+=t;else for(;s=t(e.charCodeAt(r));)r+=s;return e.slice(a,r)},n=(e=0,s,n,i,p,l)=>{for(;(n=t.space())&&(p=((l=o[n])&&l(i,e))??(!i&&t.id()));)i=p;return s&&(n==s?r++:a()),i},i=r=>r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||36==r||95==r||r>=192&&215!=r&&247!=r,p=t.id=()=>s(i),l=t.space=t=>{for(;(t=e.charCodeAt(r))<=32;)r++;return t},o=[],c=(t,a=32,s,n=t.charCodeAt(0),p=t.length,l=o[n],c=t.toUpperCase()!==t)=>o[n]=(n,o,d=r)=>o<a&&(p<2||e.substr(r,p)==t)&&(!c||!i(e.charCodeAt(r+p)))&&(r+=p,s(n,o))||(r=d,l?.(n,o)),d=(r,e,t=!1)=>c(r,e,((a,s)=>a&&(s=n(e-(t?.5:0)))&&[r,a,s])),f=(r,e,t)=>c(r,e,(a=>t?a&&[r,a]:!a&&(a=n(e-.5))&&[r,a])),h=(r,e)=>{c(r,e,((t,a)=>(a=n(e),(!t||t[0]!==r)&&(t=[r,t]),t.push(a),t)))},m=(r,e)=>c(r[0],e,(e=>!e&&[r,n(0,r.charCodeAt(1))])),A=(r,e)=>c(r[0],e,(e=>e&&[r[0],e,n(0,r.charCodeAt(1))]));const b=r=>Array.isArray(r)?r[0]?u[r[0]](...r.slice(1)):()=>r[1]:e=>e?.[r],u={},y=(r,e,t=u[r])=>u[r]=(...r)=>e(...r)||t&&t(...r),C=(r,e,t)=>"()"===r[0]?C(r[1],e,t):"string"==typeof r?e.bind(0,(r=>r),(()=>r)):"."===r[0]?e.bind(0,b(r[1]),(r=r[2],()=>r)):"["===r[0]?e.bind(0,b(r[1]),b(r[2])):t?(r=b(r),e.bind(0,(e=>[r(e)]),(()=>0))):()=>a("Bad left value"),g=r=>r?a():[,(r=+s((r=>46===r||r>=48&&r<=57||(69===r||101===r?2:0))))!=r?a():r];o[46]=r=>!r&&g();for(let r=48;r<=57;r++)o[r]=g;const $={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},v=t=>(n,i,p="")=>{for(n&&a("Unexpected string"),s();(i=e.charCodeAt(r))-t;)92===i?(s(),i=s(),p+=$[i]||i):p+=s();return s()||a("Bad string"),[,p]};o[34]=v(34),o[39]=v(39),A("()",17),y("(",((r,e,t)=>(t=e?","===e[0]?(e=e.slice(1).map((r=>r?b(r):err())),r=>e.map((e=>e(r)))):(e=b(e),r=>[e(r)]):()=>[],C(r,((r,e,a)=>r(a)[e(a)](...t(a))),!0)))),A("[]",17),y("[",((r,e)=>e?(r=b(r),e=b(e),t=>r(t)[e(t)]):err())),d(".",17),y(".",((r,e)=>(r=b(r),e=e[0]?e:e[1],t=>r(t)[e]))),m("()",17),y("()",(r=>(!r&&a("Empty ()"),b(r))));const E=(...r)=>(r=r.map(b),e=>r.map((r=>r(e))).pop());h(",",1),y(",",E),h(";",1),y(";",E),d("*",12),y("*",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)*e(t)))),d("/",12),y("/",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)/e(t)))),d("%",12),y("%",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)%e(t)))),d("*=",2,!0),y("*=",((r,e)=>(e=b(e),C(r,((r,t,a)=>r(a)[t(a)]*=e(a)))))),d("/=",2,!0),y("/=",((r,e)=>(e=b(e),C(r,((r,t,a)=>r(a)[t(a)]/=e(a)))))),d("%=",2,!0),y("%=",((r,e)=>(e=b(e),C(r,((r,t,a)=>r(a)[t(a)]%=e(a)))))),f("+",14),y("+",((r,e)=>!e&&(r=b(r),e=>+r(e)))),f("-",14),y("-",((r,e)=>!e&&(r=b(r),e=>-r(e)))),d("+",11),y("+",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)+e(t)))),d("-",11),y("-",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)-e(t)))),d("+=",2,!0),y("+=",((r,e)=>(e=b(e),C(r,((r,t,a)=>r(a)[t(a)]+=e(a)))))),d("-=",2,!0),y("-=",((r,e)=>(e=b(e),C(r,((r,t,a)=>r(a)[t(a)]-=e(a)))))),c("++",15,(r=>r?["-",["++",r],[,1]]:["++",n(14)])),y("++",(r=>C(r,((r,e,t)=>++r(t)[e(t)])))),c("--",15,(r=>r?["+",["--",r],[,1]]:["--",n(14)])),y("--",(r=>C(r,((r,e,t)=>--r(t)[e(t)])))),f("~",14),y("~",((r,e)=>!e&&(r=b(r),e=>~r(e)))),d("|",5),y("|",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)|e(t)))),d("&",7),y("&",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)&e(t)))),d("^",6),y("^",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)^e(t)))),d(">>",10),y(">>",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)>>e(t)))),d("<<",10),y("<<",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)<<e(t)))),d("==",8),y("==",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)==e(t)))),d("!=",8),y("!=",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)!=e(t)))),d(">",8),y(">",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)>e(t)))),d("<",8),y("<",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)<e(t)))),d(">=",8),y(">=",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)>=e(t)))),d("<=",8),y("<=",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)<=e(t)))),f("!",14),y("!",((r,e)=>!e&&(r=b(r),e=>!r(e)))),d("||",3),y("||",((r,e)=>(r=b(r),e=b(e),t=>r(t)||e(t)))),d("&&",4),y("&&",((r,e)=>(r=b(r),e=b(e),t=>r(t)&&e(t)))),d("=",2,!0),y("=",((r,e)=>(e=b(e),C(r,((r,t,a)=>r(a)[t(a)]=e(a))))));var x=r=>b(t(r));c("/*",20,((t,a)=>(s((t=>42!==t&&47!==e.charCodeAt(r+1))),s(2),t||n(a)||[]))),c("//",20,((r,e)=>(s((r=>r>=32)),r||n(e)||[""]))),d("**",13,!0),y("**",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)**e(t)))),d("in",9),y("in",((r,e)=>e&&(r=b(r),e=b(e),t=>r(t)in e(t)))),c("?",2,((r,e,t)=>r&&(e=n(2,58))&&["?",r,e,n(3)])),y("?",((r,e,t)=>(r=b(r),e=b(e),t=b(t),a=>r(a)?e(a):t(a)))),c("true",20,(r=>r?err():[,!0])),c("false",20,(r=>r?err():[,!1])),m("[]",20),y("[]",((r,e)=>r?","===r[0]?(r=r.slice(1).map(b),e=>r.map((r=>r(e)))):(r=b(r),e=>[r(e)]):()=>[])),m("{}",20),y("{}",((r,e)=>r?","===r[0]?(r=r.slice(1).map(b),e=>Object.fromEntries(r.map((r=>r(e))))):":"===r[0]?(r=b(r),e=>Object.fromEntries([r(e)])):(e=b(r),t=>({[r]:e(t)})):()=>({}))),d(":",2,!0),y(":",((r,e)=>(e=b(e),r=Array.isArray(r)?b(r):(r=>r).bind(0,r),t=>[r(t),e(t)]))),c("?.",17,(r=>r&&["?.",r])),y("?.",(r=>(r=b(r),e=>r(e)||(()=>{})))),c("?.",17,((r,e)=>r&&!(e=n(17))?.map&&["?.",r,e])),y("?.",((r,e)=>e&&(r=b(r),t=>r(t)?.[e]))),y("(",((r,e,t,a,s,n)=>"?."===r[0]&&(r[2]||Array.isArray(r[1]))&&(a=e?","===e[0]?(e=e.slice(1).map(b),r=>e.map((e=>e(r)))):(e=b(e),r=>[e(r)]):()=>[],!r[2]&&(r=r[1]),s="["===r[0]?b(r[2]):()=>r[2],t=b(r[1]),r=>t(r)?.[s(r)]?.(...a(r))))),c("null",20,(r=>r?a():[,null]));export{A as access,d as binary,b as compile,e as cur,x as default,a as err,n as expr,m as group,p as id,r as idx,i as isId,o as lookup,h as nary,y as operator,u as operators,t as parse,C as prop,s as skip,l as space,c as token,f as unary};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "subscript",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"description": "Fast and tiny expression evaluator with minimal syntax.",
|
|
5
5
|
"main": "subscript.js",
|
|
6
6
|
"module": "subscript.js",
|
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
"types": "./subscript.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": "./subscript.js",
|
|
11
|
-
"./parse.js": "./parse.js",
|
|
12
|
-
"./compile.js": "./compile.js",
|
|
11
|
+
"./parse.js": "./src/parse.js",
|
|
12
|
+
"./compile.js": "./src/compile.js",
|
|
13
13
|
"./subscript.js": "./subscript.js",
|
|
14
14
|
"./justin.js": "./justin.js"
|
|
15
15
|
},
|
|
16
16
|
"type": "module",
|
|
17
17
|
"files": [
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"src",
|
|
19
|
+
"feature",
|
|
20
20
|
"subscript.js",
|
|
21
21
|
"subscript.min.js",
|
|
22
22
|
"justin.js",
|
package/src/compile.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type OperatorFunction = (...args: any[]) => any;
|
|
2
|
+
export type OperatorMap = {
|
|
3
|
+
[key: string]: OperatorFunction;
|
|
4
|
+
};
|
|
5
|
+
export type Node = string | [string | undefined, ...Node[]];
|
|
6
|
+
export function compile(node: Node): ((ctx?: any) => any) | OperatorFunction;
|
|
7
|
+
export const operators: OperatorMap;
|
|
8
|
+
export function operator(op: string, fn: OperatorFunction): void;
|
|
9
|
+
|
|
10
|
+
type AccessorFunction = (ctx: any) => any;
|
|
11
|
+
export function access(
|
|
12
|
+
a: [string, ...any[]] | string,
|
|
13
|
+
fn: Function,
|
|
14
|
+
generic: boolean
|
|
15
|
+
): AccessorFunction;
|
|
16
|
+
|
|
17
|
+
export default compile;
|
package/src/compile.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { err } from "./parse.js"
|
|
2
|
+
|
|
3
|
+
// build optimized evaluator for the tree
|
|
4
|
+
export const compile = (node) => !Array.isArray(node) ? ctx => ctx?.[node] : !node[0] ? () => node[1] : operators[node[0]](...node.slice(1)),
|
|
5
|
+
|
|
6
|
+
// registered operators
|
|
7
|
+
operators = {},
|
|
8
|
+
|
|
9
|
+
// register an operator
|
|
10
|
+
operator = (op, fn, prev = operators[op]) => (operators[op] = (...args) => fn(...args) || prev && prev(...args)),
|
|
11
|
+
|
|
12
|
+
// takes node and returns evaluator depending on the case with passed params (container, path, ctx) =>
|
|
13
|
+
prop = (a, fn, generic) => (
|
|
14
|
+
// (((x))) => x
|
|
15
|
+
a[0] === '()' ? prop(a[1], fn, generic) :
|
|
16
|
+
// (_, name, ctx) => ctx[path]
|
|
17
|
+
typeof a === 'string' ? fn.bind(0, ctx => ctx, () => a) :
|
|
18
|
+
// (container, path, ctx) => container(ctx)[path]
|
|
19
|
+
a[0] === '.' ? fn.bind(0, compile(a[1]), (a = a[2], () => a)) :
|
|
20
|
+
// (container, path, ctx) => container(ctx)[path(ctx)]
|
|
21
|
+
a[0] === '[' ? fn.bind(0, compile(a[1]), compile(a[2])) :
|
|
22
|
+
// (src, _, ctx) => src(ctx)
|
|
23
|
+
generic ? (a = compile(a), fn.bind(0, ctx => [a(ctx)], () => 0)) : () => err('Bad left value')
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
export default compile
|
package/src/const.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const
|
|
2
|
+
PERIOD = 46,
|
|
3
|
+
OPAREN = 40,
|
|
4
|
+
CPAREN = 41,
|
|
5
|
+
OBRACK = 91,
|
|
6
|
+
CBRACK = 93,
|
|
7
|
+
OBRACE = 123,
|
|
8
|
+
CBRACE = 125,
|
|
9
|
+
SPACE = 32,
|
|
10
|
+
COLON = 58,
|
|
11
|
+
DQUOTE = 34,
|
|
12
|
+
QUOTE = 39,
|
|
13
|
+
_0 = 48,
|
|
14
|
+
_9 = 57,
|
|
15
|
+
_E = 69,
|
|
16
|
+
_e = 101,
|
|
17
|
+
BSLASH = 92,
|
|
18
|
+
SLASH = 47,
|
|
19
|
+
STAR = 42
|
|
20
|
+
|
|
21
|
+
// ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence
|
|
22
|
+
export const
|
|
23
|
+
PREC_SEQ = 1,
|
|
24
|
+
PREC_ASSIGN = 2,
|
|
25
|
+
PREC_LOR = 3,
|
|
26
|
+
PREC_LAND = 4,
|
|
27
|
+
PREC_OR = 5,
|
|
28
|
+
PREC_XOR = 6,
|
|
29
|
+
PREC_AND = 7,
|
|
30
|
+
PREC_EQ = 8,
|
|
31
|
+
PREC_COMP = 9,
|
|
32
|
+
PREC_SHIFT = 10,
|
|
33
|
+
PREC_ADD = 11,
|
|
34
|
+
PREC_MULT = 12,
|
|
35
|
+
PREC_EXP = 13,
|
|
36
|
+
PREC_PREFIX = 14,
|
|
37
|
+
PREC_POSTFIX = 15,
|
|
38
|
+
PREC_ACCESS = 17,
|
|
39
|
+
PREC_GROUP = 18,
|
|
40
|
+
PREC_TOKEN = 20
|
package/src/parse.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export let idx: any;
|
|
2
|
+
export let cur: any;
|
|
3
|
+
export function parse(s: string): any;
|
|
4
|
+
export namespace parse {
|
|
5
|
+
function space(cc: any): any;
|
|
6
|
+
function id(n: any): any;
|
|
7
|
+
}
|
|
8
|
+
export function err(msg?: string, frag?: string): never;
|
|
9
|
+
export function skip(is: number | ((c: number) => number)): string;
|
|
10
|
+
export const lookup: ((a: any, b: any) => any)[];
|
|
11
|
+
export function token(op: string, prec: number, map: (a: any, curPrec: number, from: number) => any): (a: any, curPrec: number, from?: any) => any;
|
|
12
|
+
export function binary(op: string, prec: number, right?: boolean | undefined): (a: any, curPrec: number, from?: any) => any;
|
|
13
|
+
export function unary(op: string, prec: number, post?: boolean | undefined): (a: any, curPrec: number, from?: any) => any;
|
|
14
|
+
export function nary(op: string, prec: number, skips?: boolean | undefined): (a: any, curPrec: number, from?: any) => any;
|
|
15
|
+
export function expr(prec: number, end?: string | undefined): any;
|
|
16
|
+
export function isId(c: number): boolean;
|
|
17
|
+
export function space(): number;
|
|
18
|
+
export function id(): string;
|
|
19
|
+
export default parse;
|
package/src/parse.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { SPACE } from "./const.js"
|
|
2
|
+
|
|
3
|
+
// current string, index and collected ids
|
|
4
|
+
export let idx, cur,
|
|
5
|
+
|
|
6
|
+
// no handling tagged literals since easily done on user side with cache, if needed
|
|
7
|
+
parse = s => (idx = 0, cur = s, s = expr(), cur[idx] ? err() : s || ''),
|
|
8
|
+
|
|
9
|
+
err = (msg = 'Bad syntax',
|
|
10
|
+
lines = cur.slice(0, idx).split('\n'),
|
|
11
|
+
last = lines.pop()
|
|
12
|
+
) => {
|
|
13
|
+
let before = cur.slice(idx - 108, idx).split('\n').pop()
|
|
14
|
+
let after = cur.slice(idx, idx + 108).split('\n').shift()
|
|
15
|
+
throw EvalError(`${msg} at ${lines.length}:${last.length} \`${idx >= 108 ? '…' : ''}${before}▶${after}\``, 'font-weight: bold')
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
skip = (is = 1, from = idx, l) => {
|
|
19
|
+
if (typeof is == 'number') idx += is
|
|
20
|
+
else while (l = is(cur.charCodeAt(idx))) idx += l
|
|
21
|
+
return cur.slice(from, idx)
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
// a + b - c
|
|
25
|
+
expr = (prec = 0, end, cc, token, newNode, fn) => {
|
|
26
|
+
// chunk/token parser
|
|
27
|
+
while (
|
|
28
|
+
(cc = parse.space()) && // till not end
|
|
29
|
+
// FIXME: extra work is happening here, when lookup bails out due to lower precedence -
|
|
30
|
+
// it makes extra `space` call for parent exprs on the same character to check precedence again
|
|
31
|
+
(newNode =
|
|
32
|
+
((fn = lookup[cc]) && fn(token, prec)) ?? // if operator with higher precedence isn't found
|
|
33
|
+
(!token && parse.id()) // parse literal or quit. token seqs are forbidden: `a b`, `a "b"`, `1.32 a`
|
|
34
|
+
)
|
|
35
|
+
) token = newNode;
|
|
36
|
+
|
|
37
|
+
// check end character
|
|
38
|
+
if (end) cc == end ? idx++ : err()
|
|
39
|
+
|
|
40
|
+
return token
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
isId = c =>
|
|
44
|
+
(c >= 48 && c <= 57) || // 0..9
|
|
45
|
+
(c >= 65 && c <= 90) || // A...Z
|
|
46
|
+
(c >= 97 && c <= 122) || // a...z
|
|
47
|
+
c == 36 || c == 95 || // $, _,
|
|
48
|
+
(c >= 192 && c != 215 && c != 247), // any non-ASCII
|
|
49
|
+
|
|
50
|
+
// parse identifier (configurable)
|
|
51
|
+
id = parse.id = () => skip(isId),
|
|
52
|
+
|
|
53
|
+
// skip space chars, return first non-space character
|
|
54
|
+
space = parse.space = cc => { while ((cc = cur.charCodeAt(idx)) <= SPACE) idx++; return cc },
|
|
55
|
+
|
|
56
|
+
// operator/token lookup table
|
|
57
|
+
// lookup[0] is id parser to let configs redefine it
|
|
58
|
+
lookup = [],
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
// create operator checker/mapper (see examples)
|
|
62
|
+
token = (
|
|
63
|
+
op,
|
|
64
|
+
prec = SPACE,
|
|
65
|
+
map,
|
|
66
|
+
c = op.charCodeAt(0),
|
|
67
|
+
l = op.length,
|
|
68
|
+
prev = lookup[c],
|
|
69
|
+
word = op.toUpperCase() !== op // make sure word boundary comes after word operator
|
|
70
|
+
) => lookup[c] = (a, curPrec, from = idx) =>
|
|
71
|
+
(curPrec < prec && (l < 2 || cur.substr(idx, l) == op) && (!word || !isId(cur.charCodeAt(idx + l))) && (idx += l, map(a, curPrec))) ||
|
|
72
|
+
(idx = from, prev?.(a, curPrec)),
|
|
73
|
+
|
|
74
|
+
// right assoc is indicated by negative precedence (meaning go from right to left)
|
|
75
|
+
binary = (op, prec, right = false) => token(op, prec, (a, b) => a && (b = expr(prec - (right ? .5 : 0))) && [op, a, b]),
|
|
76
|
+
|
|
77
|
+
// post indicates postfix rather than prefix operator
|
|
78
|
+
unary = (op, prec, post) => token(op, prec, a => post ? (a && [op, a]) : (!a && (a = expr(prec - .5)) && [op, a])),
|
|
79
|
+
|
|
80
|
+
// skips means ,,, ;;; are allowed
|
|
81
|
+
nary = (op, prec) => {
|
|
82
|
+
token(op, prec, (a, b) => (
|
|
83
|
+
(b = expr(prec)),
|
|
84
|
+
(
|
|
85
|
+
(!a || a[0] !== op) && (a = [op, a]), // if beginning of sequence - init node
|
|
86
|
+
a.push(b),
|
|
87
|
+
a
|
|
88
|
+
))
|
|
89
|
+
)
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// register (a), [b], {c} etc groups
|
|
93
|
+
// FIXME: add "Unclosed paren" error
|
|
94
|
+
group = (op, prec) => token(op[0], prec, a => (!a && ([op, expr(0, op.charCodeAt(1))]))),
|
|
95
|
+
|
|
96
|
+
// register a(b), a[b], a<b> etc,
|
|
97
|
+
access = (op, prec) => token(op[0], prec, a => (a && [op[0], a, expr(0, op.charCodeAt(1))]))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
export default parse
|
package/subscript.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let t,
|
|
1
|
+
let r,t,e=e=>(r=0,t=e,e=n(),t[r]?s():e||""),s=(e="Bad syntax",s=t.slice(0,r).split("\n"),a=s.pop())=>{let n=t.slice(r-108,r).split("\n").pop(),o=t.slice(r,r+108).split("\n").shift();throw EvalError(`${e} at ${s.length}:${a.length} \`${r>=108?"…":""}${n}▶${o}\``,"font-weight: bold")},a=(e=1,s=r,a)=>{if("number"==typeof e)r+=e;else for(;a=e(t.charCodeAt(r));)r+=a;return t.slice(s,r)},n=(t=0,a,n,o,p,i)=>{for(;(n=e.space())&&(p=((i=l[n])&&i(o,t))??(!o&&e.id()));)o=p;return a&&(n==a?r++:s()),o},o=r=>r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||36==r||95==r||r>=192&&215!=r&&247!=r,p=e.id=()=>a(o),i=e.space=e=>{for(;(e=t.charCodeAt(r))<=32;)r++;return e},l=[],c=(e,s=32,a,n=e.charCodeAt(0),p=e.length,i=l[n],c=e.toUpperCase()!==e)=>l[n]=(n,l,d=r)=>l<s&&(p<2||t.substr(r,p)==e)&&(!c||!o(t.charCodeAt(r+p)))&&(r+=p,a(n,l))||(r=d,i?.(n,l)),d=(r,t,e=!1)=>c(r,t,((s,a)=>s&&(a=n(t-(e?.5:0)))&&[r,s,a])),f=(r,t,e)=>c(r,t,(s=>e?s&&[r,s]:!s&&(s=n(t-.5))&&[r,s])),h=(r,t)=>{c(r,t,((e,s)=>(s=n(t),(!e||e[0]!==r)&&(e=[r,e]),e.push(s),e)))},b=(r,t)=>c(r[0],t,(t=>!t&&[r,n(0,r.charCodeAt(1))])),u=(r,t)=>c(r[0],t,(t=>t&&[r[0],t,n(0,r.charCodeAt(1))]));const A=r=>r?s():[,(r=+a((r=>46===r||r>=48&&r<=57||(69===r||101===r?2:0))))!=r?s():r];l[46]=r=>!r&&A();for(let r=48;r<=57;r++)l[r]=A;const C={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},g=e=>(n,o,p="")=>{for(n&&s("Unexpected string"),a();(o=t.charCodeAt(r))-e;)92===o?(a(),o=a(),p+=C[o]||o):p+=a();return a()||s("Bad string"),[,p]};l[34]=g(34),l[39]=g(39);const m=r=>Array.isArray(r)?r[0]?y[r[0]](...r.slice(1)):()=>r[1]:t=>t?.[r],y={},$=(r,t,e=y[r])=>y[r]=(...r)=>t(...r)||e&&e(...r),v=(r,t,e)=>"()"===r[0]?v(r[1],t,e):"string"==typeof r?t.bind(0,(r=>r),(()=>r)):"."===r[0]?t.bind(0,m(r[1]),(r=r[2],()=>r)):"["===r[0]?t.bind(0,m(r[1]),m(r[2])):e?(r=m(r),t.bind(0,(t=>[r(t)]),(()=>0))):()=>s("Bad left value");u("()",17),$("(",((r,t,e)=>(e=t?","===t[0]?(t=t.slice(1).map((r=>r?m(r):err())),r=>t.map((t=>t(r)))):(t=m(t),r=>[t(r)]):()=>[],v(r,((r,t,s)=>r(s)[t(s)](...e(s))),!0)))),u("[]",17),$("[",((r,t)=>t?(r=m(r),t=m(t),e=>r(e)[t(e)]):err())),d(".",17),$(".",((r,t)=>(r=m(r),t=t[0]?t:t[1],e=>r(e)[t]))),b("()",17),$("()",(r=>(!r&&s("Empty ()"),m(r))));const x=(...r)=>(r=r.map(m),t=>r.map((r=>r(t))).pop());h(",",1),$(",",x),h(";",1),$(";",x),d("*",12),$("*",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)*t(e)))),d("/",12),$("/",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)/t(e)))),d("%",12),$("%",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)%t(e)))),d("*=",2,!0),$("*=",((r,t)=>(t=m(t),v(r,((r,e,s)=>r(s)[e(s)]*=t(s)))))),d("/=",2,!0),$("/=",((r,t)=>(t=m(t),v(r,((r,e,s)=>r(s)[e(s)]/=t(s)))))),d("%=",2,!0),$("%=",((r,t)=>(t=m(t),v(r,((r,e,s)=>r(s)[e(s)]%=t(s)))))),f("+",14),$("+",((r,t)=>!t&&(r=m(r),t=>+r(t)))),f("-",14),$("-",((r,t)=>!t&&(r=m(r),t=>-r(t)))),d("+",11),$("+",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)+t(e)))),d("-",11),$("-",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)-t(e)))),d("+=",2,!0),$("+=",((r,t)=>(t=m(t),v(r,((r,e,s)=>r(s)[e(s)]+=t(s)))))),d("-=",2,!0),$("-=",((r,t)=>(t=m(t),v(r,((r,e,s)=>r(s)[e(s)]-=t(s)))))),c("++",15,(r=>r?["-",["++",r],[,1]]:["++",n(14)])),$("++",(r=>v(r,((r,t,e)=>++r(e)[t(e)])))),c("--",15,(r=>r?["+",["--",r],[,1]]:["--",n(14)])),$("--",(r=>v(r,((r,t,e)=>--r(e)[t(e)])))),f("~",14),$("~",((r,t)=>!t&&(r=m(r),t=>~r(t)))),d("|",5),$("|",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)|t(e)))),d("&",7),$("&",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)&t(e)))),d("^",6),$("^",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)^t(e)))),d(">>",10),$(">>",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)>>t(e)))),d("<<",10),$("<<",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)<<t(e)))),d("==",8),$("==",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)==t(e)))),d("!=",8),$("!=",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)!=t(e)))),d(">",8),$(">",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)>t(e)))),d("<",8),$("<",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)<t(e)))),d(">=",8),$(">=",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)>=t(e)))),d("<=",8),$("<=",((r,t)=>t&&(r=m(r),t=m(t),e=>r(e)<=t(e)))),f("!",14),$("!",((r,t)=>!t&&(r=m(r),t=>!r(t)))),d("||",3),$("||",((r,t)=>(r=m(r),t=m(t),e=>r(e)||t(e)))),d("&&",4),$("&&",((r,t)=>(r=m(r),t=m(t),e=>r(e)&&t(e)))),d("=",2,!0),$("=",((r,t)=>(t=m(t),v(r,((r,e,s)=>r(s)[e(s)]=t(s))))));var B=r=>m(e(r));export{u as access,d as binary,m as compile,t as cur,B as default,s as err,n as expr,b as group,p as id,r as idx,o as isId,l as lookup,h as nary,$ as operator,y as operators,e as parse,v as prop,a as skip,i as space,c as token,f as unary};
|