Skip to content

Commit 3c989ce

Browse files
committed
Finish readme
1 parent 95b6adc commit 3c989ce

2 files changed

Lines changed: 63 additions & 52 deletions

File tree

README.md

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,33 @@
1-
# sub<sub><sup> ✦ </sup></sub>script [![build](https://github.com/dy/subscript/actions/workflows/node.js.yml/badge.svg)](https://github.com/dy/subscript/actions/workflows/node.js.yml) [![npm](https://img.shields.io/npm/v/subscript)](http://npmjs.org/subscript) [![size](https://img.shields.io/bundlephobia/minzip/subscript?label=size)](https://bundlephobia.com/package/subscript) [![demo](https://img.shields.io/badge/play-%F0%9F%9A%80-white)](https://dy.github.io/subscript/)
1+
# sub<sub><sup> ✦ </sup></sub>script [![build](https://github.com/dy/subscript/actions/workflows/node.js.yml/badge.svg)](https://github.com/dy/subscript/actions/workflows/node.js.yml) [![npm](https://img.shields.io/npm/v/subscript)](http://npmjs.org/subscript) [![size](https://img.shields.io/bundlephobia/minzip/subscript?label=size)](https://bundlephobia.com/package/subscript) [![demo](https://img.shields.io/badge/play-%F0%9F%9A%80-white)](https://dy.github.io/subscript/) [![microjs](https://img.shields.io/badge/microjs-subscript-blue?color=darkslateblue)](http://microjs.com/#subscript)
22

3-
> Tiny expression compiler.
3+
> Tiny expression parser & evaluator.
44
5-
```js
6-
import subscript from 'subscript'
7-
8-
let fn = subscript('a + b * 2')
9-
fn({ a: 1, b: 3 }) // 7
10-
```
11-
12-
13-
* **Minimal** – common expressions < JSON + expressions < JS subset
145
* **Safe** — sandboxed, blocks `__proto__`, `constructor`, no global access
156
* **Fast** — Pratt parser engine, see [benchmarks](#performance)
167
* **Portable** — universal expression format, any compile target
178
* **Metacircular** — can parse and compile itself
189
* **Extensible** — pluggable syntax for building custom DSL
1910

11+
## Usage
2012

13+
```js
14+
import subscript from 'subscript'
15+
16+
let fn = subscript('a + b * 2')
17+
fn({ a: 1, b: 3 }) // 7
18+
```
2119

2220
## Presets
2321

24-
**subscript** — common expressions (~4kb gzip):
22+
**subscript** — common expressions:<br>
2523
`a.b a[b] a(b) + - * / % < > <= >= == != ! && || ~ & | ^ << >> ++ -- = += -= *= /=`
2624
```js
2725
import subscript from 'subscript'
2826

2927
subscript('a.b + c * 2')({ a: { b: 1 }, c: 3 }) // 7
3028
```
3129

32-
**justin** — + JSON, arrows, templates (~6kb gzip):
30+
**justin** — + JSON, arrows, templates:<br>
3331
`` 'str' 0x 0b === !== ** ?? >>> ?. ? : => ... [] {} ` // /**/ true false null ``
3432
```js
3533
import justin from 'subscript/justin.js'
@@ -38,7 +36,7 @@ justin('{ x: a?.b ?? 0, y: [1, ...rest] }')({ a: null, rest: [2, 3] })
3836
// { x: 0, y: [1, 2, 3] }
3937
```
4038

41-
**jessie** — + statements, functions (~8kb gzip):
39+
**jessie** — + statements, functions:<br>
4240
`if else for while do let const var function class return throw try catch switch import export /regex/`
4341
```js
4442
import jessie from 'subscript/jessie.js'
@@ -55,6 +53,23 @@ fn({}) // 120
5553

5654
Jessie can parse and compile its own source.
5755

56+
57+
## Parse / Compile
58+
59+
Subscript exposes `parse` to build AST and `compile` to create evaluators.
60+
61+
```js
62+
import { parse, compile } from 'subscript'
63+
64+
// parse expression
65+
let tree = parse('a.b + c - 1')
66+
tree // ['-', ['+', ['.', 'a', 'b'], 'c'], [,1]]
67+
68+
// compile tree to evaluable function
69+
fn = compile(tree)
70+
fn({ a: {b: 1}, c: 2 }) // 2
71+
```
72+
5873
## Extension
5974

6075
```js
@@ -73,7 +88,7 @@ import justin from 'subscript/justin.js'
7388
justin('[1,2,3] ∩ [2,3,4]')({}) // [2, 3]
7489
```
7590

76-
See [docs.md](./docs.md) for full API: `binary`, `unary`, `nary`, `group`, `access`, `literal`, `token`.
91+
See [docs.md](./docs.md) for full API.
7792

7893

7994
## Syntax Tree
@@ -87,6 +102,14 @@ parse('a + b * 2')
87102
// ['+', 'a', ['*', 'b', [, 2]]]
88103
```
89104

105+
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):
106+
107+
* not limited to particular language (JS), can be compiled to different targets;
108+
* reflects execution sequence, rather than code layout;
109+
* has minimal overhead, directly maps to operators;
110+
* simplifies manual evaluation and debugging;
111+
* has conventional form and one-liner docs:
112+
90113
Three forms:
91114

92115
```js
@@ -95,7 +118,7 @@ Three forms:
95118
[op, ...args] // operation — apply operator
96119
```
97120

98-
Portable to any language. See [spec.md](./spec.md).
121+
See [spec.md](./spec.md).
99122

100123

101124
## Safety
@@ -117,6 +140,30 @@ Parse 30k: subscript 150ms · justin 183ms · jsep 270ms · expr-eval 480ms ·
117140
Eval 30k: new Function 7ms · subscript 15ms · jsep+eval 30ms · expr-eval 72ms
118141
```
119142

143+
## Utils
144+
145+
### Stringify
146+
147+
To convert tree back to code, there's codegenerator function:
148+
149+
```js
150+
import { stringify } from 'subscript.js'
151+
152+
stringify(['+', ['*', 'min', [,60]], [,'sec']])
153+
// 'min * 60 + "sec"'
154+
```
155+
156+
## Bundle
157+
158+
Create custom dialect as single file:
159+
160+
```js
161+
import { bundle } from 'subscript/util/bundle.js'
162+
163+
const code = await bundle('subscript/jessie.js')
164+
// → self-contained ES module
165+
```
166+
120167

121168
## Used by
122169

docs.md

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ subscript('a * b')({ a: 3, b: 4 }) // 12
1414
```
1515

1616

17-
1817
## Evaluate Code
1918

2019
### Function Call
@@ -309,41 +308,6 @@ import {
309308
```
310309

311310

312-
313-
## Syntax Tree
314-
315-
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):
316-
317-
* portable to any language, not limited to JS;
318-
* reflects execution sequence, rather than code layout;
319-
* has minimal overhead, directly maps to operators;
320-
* simplifies manual evaluation and debugging;
321-
* has conventional form and one-liner docs:
322-
323-
```js
324-
'x' // identifier
325-
[, 1] // literal
326-
['+', 'a', 'b'] // binary
327-
['-', 'a'] // unary prefix
328-
['++', 'a', null] // unary postfix
329-
['?', a, b, c] // ternary
330-
['if', cond, then, else] // control flow
331-
```
332-
333-
See full [spec](./spec.md)
334-
335-
336-
## Bundle
337-
338-
Create custom dialect as single file:
339-
340-
```js
341-
import { bundle } from 'subscript/util/bundle.js'
342-
343-
const code = await bundle('subscript/jessie.js')
344-
// → self-contained ES module
345-
```
346-
347311
[**Playground →**](https://dy.github.io/subscript/) — interactive DSL builder
348312

349313
<p align="center">🕉</p>

0 commit comments

Comments
 (0)