parser-lr 0.2.0 → 0.2.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 +92 -28
- package/bin/parser-lr.js +1 -1
- package/docs/grammar.md +170 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,49 +1,113 @@
|
|
|
1
1
|
# parser-lr
|
|
2
2
|
|
|
3
|
-
Shift-reduce parser
|
|
3
|
+
Shift-reduce parser for EBNF grammars. Write a `.grammar` file, build an LR parse table, then lex and parse source into a concrete syntax tree or AST.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Grammar file syntax is documented in [`docs/grammar.md`](docs/grammar.md).
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|------|------|
|
|
9
|
-
| `grammars/` | EBNF grammar specs (`grammar.grammar` meta-grammar, plus sample grammars) |
|
|
10
|
-
| `src/lib/` | Library API (Node and browser) — see [`src/lib/README.md`](src/lib/README.md) |
|
|
11
|
-
| `src/cli/` | Node CLI — see [`src/cli/README.md`](src/cli/README.md) |
|
|
7
|
+
## Install
|
|
12
8
|
|
|
13
|
-
|
|
9
|
+
From npm:
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
| `AstSchema` | AST types from the `ast` section |
|
|
19
|
-
| `TransformSchema` | CST-to-AST rules from the `transform` section |
|
|
20
|
-
| `AstNode` | Parse tree node (interior symbols and terminal leaves) |
|
|
21
|
-
| `Token` | Lexeme from tokenization (name, text, source span) |
|
|
22
|
-
| `ParseTable` | Serializable LR table metadata with token inventory |
|
|
23
|
-
| `ParserLr` | Shift-reduce parser (table build and parse) |
|
|
11
|
+
```bash
|
|
12
|
+
npm install parser-lr
|
|
13
|
+
```
|
|
24
14
|
|
|
25
|
-
|
|
15
|
+
From a clone of this repository:
|
|
26
16
|
|
|
27
17
|
```bash
|
|
28
|
-
npm
|
|
29
|
-
npm
|
|
18
|
+
npm install
|
|
19
|
+
npm run build
|
|
30
20
|
```
|
|
31
21
|
|
|
32
|
-
##
|
|
22
|
+
## CLI
|
|
33
23
|
|
|
34
|
-
|
|
24
|
+
The `parser-lr` command is published as the package `bin` entry. After a local build, link it globally:
|
|
35
25
|
|
|
36
26
|
```bash
|
|
37
|
-
npm
|
|
27
|
+
npm link
|
|
38
28
|
```
|
|
39
29
|
|
|
40
|
-
|
|
30
|
+
You can then run `parser-lr` from any directory. Without linking, use `npx parser-lr` (published install) or `node node_modules/parser-lr/bin/parser-lr.js`.
|
|
41
31
|
|
|
42
|
-
|
|
32
|
+
### Build a parse table
|
|
33
|
+
|
|
34
|
+
Generate a serialized LR table from a grammar file:
|
|
43
35
|
|
|
44
36
|
```bash
|
|
45
|
-
parser-lr table generate -g
|
|
46
|
-
parser-lr parse -i source.txt -g grammars/lisp.grammar
|
|
37
|
+
parser-lr table generate -g mylang.grammar -o mylang.table.json
|
|
47
38
|
```
|
|
48
39
|
|
|
49
|
-
|
|
40
|
+
Options:
|
|
41
|
+
|
|
42
|
+
| Option | Description |
|
|
43
|
+
|--------|-------------|
|
|
44
|
+
| `-g, --grammar <path>` | `.grammar` file (required) |
|
|
45
|
+
| `-o, --output <path>` | Output path (default: stdout) |
|
|
46
|
+
| `-a, --algorithm <name>` | `lr0`, `slr`, `lalr`, or `lr1` (default: `lr1`) |
|
|
47
|
+
|
|
48
|
+
The JSON table includes lexer token rules, skip rules, and the full ACTION/GOTO table. You can ship this file without the original grammar.
|
|
49
|
+
|
|
50
|
+
### Parse a source file
|
|
51
|
+
|
|
52
|
+
Parse input using either the grammar (table built in memory) or a saved table:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
parser-lr parse -i program.txt -g mylang.grammar
|
|
56
|
+
parser-lr parse -i program.txt -t mylang.table.json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Options:
|
|
60
|
+
|
|
61
|
+
| Option | Description |
|
|
62
|
+
|--------|-------------|
|
|
63
|
+
| `-i, --input <path>` | Source file to parse (required) |
|
|
64
|
+
| `-g, --grammar <path>` | `.grammar` file (one of grammar or table) |
|
|
65
|
+
| `-t, --table <path>` | Serialized table JSON from `table generate` |
|
|
66
|
+
| `-o, --output <path>` | Output path (default: stdout) |
|
|
67
|
+
| `--format <name>` | Output format (default: `json`) |
|
|
68
|
+
|
|
69
|
+
Output is a JSON object `{ "ast": … }`. On a syntax error, `ast` is `null`.
|
|
70
|
+
|
|
71
|
+
When the grammar defines `ast` and `transform` sections, the CLI applies CST-to-AST transforms and returns the abstract tree.
|
|
72
|
+
|
|
73
|
+
## Library
|
|
74
|
+
|
|
75
|
+
Import from `parser-lr` in Node or bundler projects (ESM). The API is browser-safe; the CLI is Node-only.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { readFile } from 'node:fs/promises';
|
|
79
|
+
import { ParseContext } from 'parser-lr';
|
|
80
|
+
|
|
81
|
+
const grammarSource = await readFile('mylang.grammar', 'utf8');
|
|
82
|
+
const context = ParseContext.fromGrammar(grammarSource, 'lr1');
|
|
83
|
+
|
|
84
|
+
const source = await readFile('program.txt', 'utf8');
|
|
85
|
+
const ast = context.parseSource(source);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Load a pre-built table instead of a grammar file:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { readFile } from 'node:fs/promises';
|
|
92
|
+
import { ParseContext } from 'parser-lr';
|
|
93
|
+
|
|
94
|
+
const tableJson = await readFile('mylang.table.json', 'utf8');
|
|
95
|
+
const context = ParseContext.fromTableJson(tableJson);
|
|
96
|
+
|
|
97
|
+
const source = await readFile('program.txt', 'utf8');
|
|
98
|
+
const ast = context.parse(context.lex(source));
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`ParseContext` exposes `lex`, `parse`, and `createLexer` for finer control. See [`src/lib/README.md`](src/lib/README.md) for the main types.
|
|
102
|
+
|
|
103
|
+
## Example grammars
|
|
104
|
+
|
|
105
|
+
Sample `.grammar` files ship in [`grammars/`](grammars/) (`calc.grammar`, `lisp.grammar`, `6502.grammar`, and the meta-grammar `grammar.grammar`). Use them as templates when writing your own language.
|
|
106
|
+
|
|
107
|
+
## Developing this package
|
|
108
|
+
|
|
109
|
+
Contributors who change the meta-grammar should regenerate the bootstrapped table:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm run bootstrap
|
|
113
|
+
```
|
package/bin/parser-lr.js
CHANGED
package/docs/grammar.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# `.grammar` file syntax
|
|
2
|
+
|
|
3
|
+
A `.grammar` file describes a language: lexer rules, parser productions, and optional AST shape and transform rules. Files are plain text with `//` line comments.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
A grammar file contains these sections in order:
|
|
8
|
+
|
|
9
|
+
1. `name` — grammar name
|
|
10
|
+
2. Zero or more of: `tokens`, `skip`, `states`
|
|
11
|
+
3. `start` — entry non-terminal
|
|
12
|
+
4. `grammar` — parse productions
|
|
13
|
+
5. Optional `ast` — AST type definitions
|
|
14
|
+
6. Optional `transform` — CST-to-AST mapping rules
|
|
15
|
+
|
|
16
|
+
Whitespace between sections is ignored. Skip rules discard matched text before tokenization.
|
|
17
|
+
|
|
18
|
+
## `name`
|
|
19
|
+
|
|
20
|
+
```ebnf
|
|
21
|
+
name "mylang" ;
|
|
22
|
+
name mylang ;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The name is an identifier or double-quoted string.
|
|
26
|
+
|
|
27
|
+
## `tokens`
|
|
28
|
+
|
|
29
|
+
Declares lexer tokens as regular expressions:
|
|
30
|
+
|
|
31
|
+
```ebnf
|
|
32
|
+
tokens
|
|
33
|
+
identifier = /[A-Za-z_][A-Za-z0-9_]*/ ;
|
|
34
|
+
number = /[0-9]+/ ;
|
|
35
|
+
plus = /\+/ ;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Each rule is `name = /pattern/ flags ;`. The pattern is a JavaScript regular expression body between slashes. Optional flag letters (`g`, `i`, `m`, `s`, `u`, `y`) may follow the closing slash.
|
|
39
|
+
|
|
40
|
+
## `skip`
|
|
41
|
+
|
|
42
|
+
Declares patterns to discard (whitespace, comments):
|
|
43
|
+
|
|
44
|
+
```ebnf
|
|
45
|
+
skip
|
|
46
|
+
whitespace = /[ \t\r\n]+/ ;
|
|
47
|
+
comment = /\/\/[^\n\r]*/ ;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Syntax matches `tokens`.
|
|
51
|
+
|
|
52
|
+
## `states`
|
|
53
|
+
|
|
54
|
+
Optional lexer start states (for multi-mode lexing):
|
|
55
|
+
|
|
56
|
+
```ebnf
|
|
57
|
+
states default, string ;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## `start`
|
|
61
|
+
|
|
62
|
+
Names the parser entry non-terminal:
|
|
63
|
+
|
|
64
|
+
```ebnf
|
|
65
|
+
start program ;
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## `grammar`
|
|
69
|
+
|
|
70
|
+
Parse productions use EBNF expression syntax:
|
|
71
|
+
|
|
72
|
+
```ebnf
|
|
73
|
+
grammar
|
|
74
|
+
program = statement { statement } ;
|
|
75
|
+
statement = identifier equal expression semicolon ;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Expressions
|
|
79
|
+
|
|
80
|
+
| Form | Syntax | Meaning |
|
|
81
|
+
|------|--------|---------|
|
|
82
|
+
| Sequence | `a b c` | Match in order |
|
|
83
|
+
| Choice | `a \| b \| c` | Match one alternative |
|
|
84
|
+
| Optional | `[ a ]` | Zero or one |
|
|
85
|
+
| Repeat | `{ a }` | Zero or more |
|
|
86
|
+
| Group | `( a )` | Precedence grouping |
|
|
87
|
+
| Terminal | `"while"` | Literal string in the input |
|
|
88
|
+
| Reference | `identifier` | Non-terminal or token name |
|
|
89
|
+
| Label | `#name` | Names an alternative for transforms |
|
|
90
|
+
| Binding | `[slot]:symbol` | Names a child slot for AST transforms |
|
|
91
|
+
|
|
92
|
+
Example with labels and bindings:
|
|
93
|
+
|
|
94
|
+
```ebnf
|
|
95
|
+
expr =
|
|
96
|
+
#add expr plus term
|
|
97
|
+
| #term term
|
|
98
|
+
;
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Production syntax
|
|
102
|
+
|
|
103
|
+
Each production ends with a semicolon:
|
|
104
|
+
|
|
105
|
+
```ebnf
|
|
106
|
+
identifier = expression ;
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## `ast`
|
|
110
|
+
|
|
111
|
+
Optional section declaring AST node shapes. Uses the same expression syntax as `grammar`, with `#` variants and `[slot]:` bindings naming child fields:
|
|
112
|
+
|
|
113
|
+
```ebnf
|
|
114
|
+
ast
|
|
115
|
+
expr =
|
|
116
|
+
#binary [left]:expr [operator]:operator [right]:expr
|
|
117
|
+
| #literal number
|
|
118
|
+
;
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
When present, transform rules map parse trees to these types.
|
|
122
|
+
|
|
123
|
+
## `transform`
|
|
124
|
+
|
|
125
|
+
Maps labeled parse alternatives to AST construction. One rule per parse production:
|
|
126
|
+
|
|
127
|
+
```ebnf
|
|
128
|
+
transform
|
|
129
|
+
expr ->
|
|
130
|
+
#add fold-left(expr.#binary, left, operator, right)
|
|
131
|
+
| #term pass(term)
|
|
132
|
+
;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Transform expressions
|
|
136
|
+
|
|
137
|
+
| Form | Syntax |
|
|
138
|
+
|------|--------|
|
|
139
|
+
| Drop | `drop` |
|
|
140
|
+
| Pass | `pass(reference)` |
|
|
141
|
+
| Build | `type.#variant` or `type.#variant(arg, …)` |
|
|
142
|
+
| Fold left | `fold-left(type.#variant, ref, …)` |
|
|
143
|
+
| Fold right | `fold-right(type.#variant, ref, …)` |
|
|
144
|
+
| Flatten | `flatten(type.#variant, head, tail)` |
|
|
145
|
+
|
|
146
|
+
`type.#variant` refers to an AST type and variant from the `ast` section. Arguments are binding names from the parse production.
|
|
147
|
+
|
|
148
|
+
Example (calculator):
|
|
149
|
+
|
|
150
|
+
```ebnf
|
|
151
|
+
transform
|
|
152
|
+
expr ->
|
|
153
|
+
#binary expr.#binary(left, operator, right)
|
|
154
|
+
| #literal expr.#literal(number)
|
|
155
|
+
;
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Complete example
|
|
159
|
+
|
|
160
|
+
See [`grammars/calc.grammar`](../grammars/calc.grammar) for a small working grammar with `grammar`, `ast`, and `transform` sections. [`grammars/lisp.grammar`](../grammars/lisp.grammar) and [`grammars/6502.grammar`](../grammars/6502.grammar) show larger languages.
|
|
161
|
+
|
|
162
|
+
## Building and using a grammar
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
parser-lr table generate -g mylang.grammar -o mylang.table.json
|
|
166
|
+
parser-lr parse -i source.txt -g mylang.grammar
|
|
167
|
+
parser-lr parse -i source.txt -t mylang.table.json
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
See the [project README](../README.md) for install and library usage.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "parser-lr",
|
|
3
3
|
"author": "Adam R Moss <adamrmoss@gmail.com>",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"description": "Shift-reduce parser library for EBNF grammars",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
24
|
"bin",
|
|
25
|
-
"dist"
|
|
25
|
+
"dist",
|
|
26
|
+
"docs"
|
|
26
27
|
],
|
|
27
28
|
"publishConfig": {
|
|
28
29
|
"access": "public",
|