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 CHANGED
@@ -1,49 +1,113 @@
1
1
  # parser-lr
2
2
 
3
- Shift-reduce parser library for EBNF grammars. Build an LR parse table from a grammar, then parse source into an AST.
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
- ## Layout
5
+ Grammar file syntax is documented in [`docs/grammar.md`](docs/grammar.md).
6
6
 
7
- | Path | Role |
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
- ## Library API
9
+ From npm:
14
10
 
15
- | Type | Purpose |
16
- |------|---------|
17
- | `Grammar` | Parsed `.grammar` file (lexer, parse productions, optional `AstSchema`) |
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
- ## Build and test
15
+ From a clone of this repository:
26
16
 
27
17
  ```bash
28
- npm run build # tsc → dist/lib/, Rollup → bin/parser-lr.js
29
- npm test
18
+ npm install
19
+ npm run build
30
20
  ```
31
21
 
32
- ## Bootstrap
22
+ ## CLI
33
23
 
34
- Grammar files are lexed and parsed with a bootstrapped meta-grammar table checked in at `src/lib/grammar/grammar.json`. Regenerate it after changing `grammars/grammar.grammar`:
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 run bootstrap
27
+ npm link
38
28
  ```
39
29
 
40
- The meta-grammar LR parser builds the `Grammar` model used to bootstrap table generation.
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
- ## CLI
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 grammars/lisp.grammar -o table.json
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
- The `parse` command outputs a concrete syntax tree as JSON (`{ "ast": … }`).
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
@@ -3781,7 +3781,7 @@ const {
3781
3781
  Help,
3782
3782
  } = commander;
3783
3783
 
3784
- var version$1 = "0.2.0";
3784
+ var version$1 = "0.2.1";
3785
3785
  var packageDefinition = {
3786
3786
  version: version$1};
3787
3787
 
@@ -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.0",
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",