@cirru/parser.ts 0.0.9 → 0.0.10
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/.yarn/install-state.gz +0 -0
- package/Agents.md +13 -13
- package/lib/index.js +22 -6
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +22 -6
- package/src/parser.test.ts +27 -0
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
package/Agents.md
CHANGED
|
@@ -16,7 +16,7 @@ Main exports: `parse(code)` and `parseOneLiner(code)` from `src/index.ts`.
|
|
|
16
16
|
## Quick Commands
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
yarn test # run all
|
|
19
|
+
yarn test # run all Jest tests
|
|
20
20
|
yarn bench # benchmark on bundled test fixtures (×20, ~16 KB)
|
|
21
21
|
BENCH_FILE=/absolute/path/to/large.cirru yarn bench # benchmark on a real file
|
|
22
22
|
yarn compile # emit lib/ (tsc -p tsconfig-compile.json)
|
|
@@ -184,15 +184,15 @@ Interpretation:
|
|
|
184
184
|
|
|
185
185
|
## File Map
|
|
186
186
|
|
|
187
|
-
| File | Purpose
|
|
188
|
-
| ----------------------- |
|
|
189
|
-
| `src/index.ts` | Parser entry point; `lexAndBuild`, `parse`, `parseOneLiner`
|
|
190
|
-
| `src/tree.ts` | Tree helpers: `resolveDollar`, `resolveComma`, utilities
|
|
191
|
-
| `src/types.ts` | `ELexState`, `ELexControl`, `ICirruNode`
|
|
192
|
-
| `src/parser.test.ts` | All 22 tests
|
|
193
|
-
| `src/bench.ts` | Benchmark script
|
|
194
|
-
| `tsconfig.bench.json` | Separate tsconfig for bench (es2022, node types)
|
|
195
|
-
| `tsconfig-compile.json` | Library build config
|
|
196
|
-
| `test/cirru/*.cirru` | Test input fixtures
|
|
197
|
-
| `test/ast/*.json` | Expected AST outputs
|
|
198
|
-
| `lib/` | Compiled library output (committed)
|
|
187
|
+
| File | Purpose |
|
|
188
|
+
| ----------------------- | ----------------------------------------------------------------------- |
|
|
189
|
+
| `src/index.ts` | Parser entry point; `lexAndBuild`, `parse`, `parseOneLiner` |
|
|
190
|
+
| `src/tree.ts` | Tree helpers: `resolveDollar`, `resolveComma`, utilities |
|
|
191
|
+
| `src/types.ts` | `ELexState`, `ELexControl`, `ICirruNode` |
|
|
192
|
+
| `src/parser.test.ts` | All 22 tests |
|
|
193
|
+
| `src/bench.ts` | Benchmark script |
|
|
194
|
+
| `tsconfig.bench.json` | Separate tsconfig for bench (es2022, node types) |
|
|
195
|
+
| `tsconfig-compile.json` | Library build config |
|
|
196
|
+
| `test/cirru/*.cirru` | Test input fixtures |
|
|
197
|
+
| `test/ast/*.json` | Expected AST outputs |
|
|
198
|
+
| `lib/` | Compiled library output (generated; not committed—run `yarn compile`) |
|
package/lib/index.js
CHANGED
|
@@ -39,6 +39,8 @@ let lexAndBuild = (code) => {
|
|
|
39
39
|
let state = types_1.ELexState.indent;
|
|
40
40
|
let buffer = ""; // string content when escape sequences appear
|
|
41
41
|
let level = 0;
|
|
42
|
+
// Track explicit balance separately from indentation-generated frames.
|
|
43
|
+
let parenDepth = 0;
|
|
42
44
|
let indentCount = 0;
|
|
43
45
|
let tokenStart = 0;
|
|
44
46
|
let stringStart = 0;
|
|
@@ -69,6 +71,16 @@ let lexAndBuild = (code) => {
|
|
|
69
71
|
else if (tok === ",")
|
|
70
72
|
hasComma = true;
|
|
71
73
|
};
|
|
74
|
+
const openParen = () => {
|
|
75
|
+
parenDepth++;
|
|
76
|
+
emitOpen();
|
|
77
|
+
};
|
|
78
|
+
const closeParen = () => {
|
|
79
|
+
if (parenDepth === 0)
|
|
80
|
+
throw new Error("Unexpected closing parenthesis ')'");
|
|
81
|
+
parenDepth--;
|
|
82
|
+
emitClose();
|
|
83
|
+
};
|
|
72
84
|
// Called whenever a new line's first non-whitespace character is encountered.
|
|
73
85
|
// newLevel = indentCount >> 1 (already validated as even before calling).
|
|
74
86
|
// On the very first call: lazily emit the outer open, then any extra opens for depth.
|
|
@@ -112,10 +124,10 @@ let lexAndBuild = (code) => {
|
|
|
112
124
|
indentCount = 0;
|
|
113
125
|
break;
|
|
114
126
|
case CHAR_LPAREN:
|
|
115
|
-
|
|
127
|
+
openParen();
|
|
116
128
|
break;
|
|
117
129
|
case CHAR_RPAREN:
|
|
118
|
-
|
|
130
|
+
closeParen();
|
|
119
131
|
break;
|
|
120
132
|
case CHAR_DQUOTE:
|
|
121
133
|
state = types_1.ELexState.string;
|
|
@@ -149,12 +161,12 @@ let lexAndBuild = (code) => {
|
|
|
149
161
|
break;
|
|
150
162
|
case CHAR_LPAREN:
|
|
151
163
|
emitToken(code.slice(tokenStart, pointer - 1));
|
|
152
|
-
|
|
164
|
+
openParen();
|
|
153
165
|
state = types_1.ELexState.space;
|
|
154
166
|
break;
|
|
155
167
|
case CHAR_RPAREN:
|
|
156
168
|
emitToken(code.slice(tokenStart, pointer - 1));
|
|
157
|
-
|
|
169
|
+
closeParen();
|
|
158
170
|
state = types_1.ELexState.space;
|
|
159
171
|
break;
|
|
160
172
|
default:
|
|
@@ -175,7 +187,7 @@ let lexAndBuild = (code) => {
|
|
|
175
187
|
state = types_1.ELexState.escape;
|
|
176
188
|
break;
|
|
177
189
|
case CHAR_NEWLINE:
|
|
178
|
-
throw new Error("
|
|
190
|
+
throw new Error("Unexpected newline in string");
|
|
179
191
|
default:
|
|
180
192
|
if (stringHasEscape)
|
|
181
193
|
buffer = buffer + code[pointer - 1];
|
|
@@ -233,10 +245,12 @@ let lexAndBuild = (code) => {
|
|
|
233
245
|
if ((0, tree_1.isOdd)(indentCount))
|
|
234
246
|
throw new Error(`Invalid indentation size ${indentCount}`);
|
|
235
247
|
flushIndent(indentCount >> 1);
|
|
236
|
-
|
|
248
|
+
openParen();
|
|
237
249
|
state = types_1.ELexState.space;
|
|
238
250
|
indentCount = 0;
|
|
239
251
|
break;
|
|
252
|
+
case CHAR_RPAREN:
|
|
253
|
+
throw new Error("Unexpected closing parenthesis ')' at line start");
|
|
240
254
|
default:
|
|
241
255
|
if ((0, tree_1.isOdd)(indentCount))
|
|
242
256
|
throw new Error(`Invalid indentation size ${indentCount}`);
|
|
@@ -261,6 +275,8 @@ let lexAndBuild = (code) => {
|
|
|
261
275
|
}
|
|
262
276
|
if (first)
|
|
263
277
|
return []; // no content was seen
|
|
278
|
+
if (parenDepth !== 0)
|
|
279
|
+
throw new Error("Unclosed parenthesis at end of input");
|
|
264
280
|
// Close remaining open levels (mirrors the original: level closes + 1 final close)
|
|
265
281
|
for (let i = 0; i < level; i++)
|
|
266
282
|
emitClose();
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -42,6 +42,8 @@ let lexAndBuild = (code: string): ICirruNode[] => {
|
|
|
42
42
|
let state = ELexState.indent as ELexState;
|
|
43
43
|
let buffer = ""; // string content when escape sequences appear
|
|
44
44
|
let level = 0;
|
|
45
|
+
// Track explicit balance separately from indentation-generated frames.
|
|
46
|
+
let parenDepth = 0;
|
|
45
47
|
let indentCount = 0;
|
|
46
48
|
let tokenStart = 0;
|
|
47
49
|
let stringStart = 0;
|
|
@@ -73,6 +75,17 @@ let lexAndBuild = (code: string): ICirruNode[] => {
|
|
|
73
75
|
else if (tok === ",") hasComma = true;
|
|
74
76
|
};
|
|
75
77
|
|
|
78
|
+
const openParen = () => {
|
|
79
|
+
parenDepth++;
|
|
80
|
+
emitOpen();
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const closeParen = () => {
|
|
84
|
+
if (parenDepth === 0) throw new Error("Unexpected closing parenthesis ')'");
|
|
85
|
+
parenDepth--;
|
|
86
|
+
emitClose();
|
|
87
|
+
};
|
|
88
|
+
|
|
76
89
|
// Called whenever a new line's first non-whitespace character is encountered.
|
|
77
90
|
// newLevel = indentCount >> 1 (already validated as even before calling).
|
|
78
91
|
// On the very first call: lazily emit the outer open, then any extra opens for depth.
|
|
@@ -113,10 +126,10 @@ let lexAndBuild = (code: string): ICirruNode[] => {
|
|
|
113
126
|
indentCount = 0;
|
|
114
127
|
break;
|
|
115
128
|
case CHAR_LPAREN:
|
|
116
|
-
|
|
129
|
+
openParen();
|
|
117
130
|
break;
|
|
118
131
|
case CHAR_RPAREN:
|
|
119
|
-
|
|
132
|
+
closeParen();
|
|
120
133
|
break;
|
|
121
134
|
case CHAR_DQUOTE:
|
|
122
135
|
state = ELexState.string;
|
|
@@ -150,12 +163,12 @@ let lexAndBuild = (code: string): ICirruNode[] => {
|
|
|
150
163
|
break;
|
|
151
164
|
case CHAR_LPAREN:
|
|
152
165
|
emitToken(code.slice(tokenStart, pointer - 1));
|
|
153
|
-
|
|
166
|
+
openParen();
|
|
154
167
|
state = ELexState.space;
|
|
155
168
|
break;
|
|
156
169
|
case CHAR_RPAREN:
|
|
157
170
|
emitToken(code.slice(tokenStart, pointer - 1));
|
|
158
|
-
|
|
171
|
+
closeParen();
|
|
159
172
|
state = ELexState.space;
|
|
160
173
|
break;
|
|
161
174
|
default:
|
|
@@ -176,7 +189,7 @@ let lexAndBuild = (code: string): ICirruNode[] => {
|
|
|
176
189
|
state = ELexState.escape;
|
|
177
190
|
break;
|
|
178
191
|
case CHAR_NEWLINE:
|
|
179
|
-
throw new Error("
|
|
192
|
+
throw new Error("Unexpected newline in string");
|
|
180
193
|
default:
|
|
181
194
|
if (stringHasEscape) buffer = buffer + code[pointer - 1];
|
|
182
195
|
break;
|
|
@@ -231,10 +244,12 @@ let lexAndBuild = (code: string): ICirruNode[] => {
|
|
|
231
244
|
case CHAR_LPAREN:
|
|
232
245
|
if (isOdd(indentCount)) throw new Error(`Invalid indentation size ${indentCount}`);
|
|
233
246
|
flushIndent(indentCount >> 1);
|
|
234
|
-
|
|
247
|
+
openParen();
|
|
235
248
|
state = ELexState.space;
|
|
236
249
|
indentCount = 0;
|
|
237
250
|
break;
|
|
251
|
+
case CHAR_RPAREN:
|
|
252
|
+
throw new Error("Unexpected closing parenthesis ')' at line start");
|
|
238
253
|
default:
|
|
239
254
|
if (isOdd(indentCount)) throw new Error(`Invalid indentation size ${indentCount}`);
|
|
240
255
|
flushIndent(indentCount >> 1);
|
|
@@ -257,6 +272,7 @@ let lexAndBuild = (code: string): ICirruNode[] => {
|
|
|
257
272
|
}
|
|
258
273
|
|
|
259
274
|
if (first) return []; // no content was seen
|
|
275
|
+
if (parenDepth !== 0) throw new Error("Unclosed parenthesis at end of input");
|
|
260
276
|
|
|
261
277
|
// Close remaining open levels (mirrors the original: level closes + 1 final close)
|
|
262
278
|
for (let i = 0; i < level; i++) emitClose();
|
package/src/parser.test.ts
CHANGED
|
@@ -5,6 +5,33 @@ let { resolveDollar, resolveComma, resolveDollarComma } = require("./tree");
|
|
|
5
5
|
|
|
6
6
|
test("single quote", () => expect(parse('a "\\\'"')).toEqual([["a", "'"]]));
|
|
7
7
|
|
|
8
|
+
describe("parenthesis syntax errors", () => {
|
|
9
|
+
test.each([")", " )", "\n)", "a)", "a )", "a (b))", "a (b) )", "a\n )"])("rejects unmatched closing parenthesis: %j", (code) => {
|
|
10
|
+
expect(() => parse(code)).toThrow(/Unexpected closing parenthesis/);
|
|
11
|
+
expect(() => parseOneLiner(code)).toThrow(/Unexpected closing parenthesis/);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test.each(["(", "a (b", "a ((b)", "a (b\n c"])("rejects unfinished parentheses: %j", (code) => {
|
|
15
|
+
expect(() => parse(code)).toThrow(/Unclosed parenthesis/);
|
|
16
|
+
expect(() => parseOneLiner(code)).toThrow(/Unclosed parenthesis/);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("keeps quoted parentheses, balanced nesting and empty input", () => {
|
|
20
|
+
expect(parse('a "(" ")"')).toEqual([["a", "(", ")"]]);
|
|
21
|
+
expect(parse('a "\\\"(\\\""')).toEqual([["a", '"("']]);
|
|
22
|
+
expect(parse("a (b (c))")).toEqual([["a", ["b", ["c"]]]]);
|
|
23
|
+
expect(parse("a (b\n c)")).toEqual([["a", ["b", ["c"]]]]);
|
|
24
|
+
expect(parse("\n ")).toEqual([]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("preserves Cirru multiline frame closure and following tokens", () => {
|
|
28
|
+
expect(parse("a (b\n c) d")).toEqual([["a", ["b", ["c"], "d"]]]);
|
|
29
|
+
expect(parse("a (b\n c)\n d")).toEqual([["a", ["b", ["c"]], ["d"]]]);
|
|
30
|
+
expect(parse("a (b\n c) d (e)\nf")).toEqual([["a", ["b", ["c"], "d", ["e"]]], ["f"]]);
|
|
31
|
+
expect(parse("a (b\nc)")).toEqual([["a", ["b"], ["c"]]]);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
8
35
|
test("demo", () => {
|
|
9
36
|
let code = fs.readFileSync(path.join(__dirname, "../test/cirru/demo.cirru"), "utf8");
|
|
10
37
|
let data = require(path.join(__dirname, "../test/ast/demo.json"));
|