@scinorandex/sparse 0.0.8 → 0.1.2

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.
Files changed (65) hide show
  1. package/.github/workflows/ci.yml +41 -0
  2. package/AGENTS.md +60 -0
  3. package/README.md +286 -30
  4. package/dist/cli.js +113 -24
  5. package/dist/cli.js.map +1 -1
  6. package/dist/generator.d.ts +9 -2
  7. package/dist/generator.js +179 -50
  8. package/dist/generator.js.map +1 -1
  9. package/dist/index.d.ts +17 -4
  10. package/dist/index.js +34 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/meta/common.d.ts +17 -0
  13. package/dist/meta/common.js +66 -1
  14. package/dist/meta/common.js.map +1 -1
  15. package/dist/meta/selfhosted.d.ts +4 -5
  16. package/dist/meta/selfhosted.js +51 -38
  17. package/dist/meta/selfhosted.js.map +1 -1
  18. package/dist/parser.d.ts +56 -33
  19. package/dist/parser.js +146 -18
  20. package/dist/parser.js.map +1 -1
  21. package/dist/table/selfhosted.d.ts +10 -1
  22. package/dist/table/selfhosted.js +32 -12
  23. package/dist/table/selfhosted.js.map +1 -1
  24. package/dist/table/validate.d.ts +17 -0
  25. package/dist/table/validate.js +84 -0
  26. package/dist/table/validate.js.map +1 -0
  27. package/dist/utils/Stack.d.ts +4 -0
  28. package/dist/utils/Stack.js +16 -1
  29. package/dist/utils/Stack.js.map +1 -1
  30. package/dist/utils/errorWindowBuilder.js +11 -11
  31. package/dist/utils/errorWindowBuilder.js.map +1 -1
  32. package/dist/utils/loadFiles.d.ts +8 -0
  33. package/dist/utils/loadFiles.js +47 -0
  34. package/dist/utils/loadFiles.js.map +1 -0
  35. package/dist/utils/reducers.d.ts +9 -0
  36. package/dist/utils/reducers.js +56 -0
  37. package/dist/utils/reducers.js.map +1 -0
  38. package/example/LoLang/example.ts +42 -18
  39. package/example/LoLang/tablelalr.txt +381 -0
  40. package/example/kleene-test/example.ts +35 -17
  41. package/example/math/example.ts +12 -7
  42. package/example/selfhosted/example.ts +30 -24
  43. package/opencode.json +18 -0
  44. package/package.json +9 -3
  45. package/src/cli.ts +132 -23
  46. package/src/generator.ts +237 -67
  47. package/src/index.ts +56 -3
  48. package/src/meta/common.ts +116 -0
  49. package/src/meta/selfhosted.ts +42 -11
  50. package/src/parser.ts +259 -47
  51. package/src/table/selfhosted.ts +48 -12
  52. package/src/table/validate.ts +143 -0
  53. package/src/utils/Stack.ts +22 -2
  54. package/src/utils/errorWindowBuilder.ts +15 -13
  55. package/src/utils/loadFiles.ts +54 -0
  56. package/src/utils/reducers.ts +93 -0
  57. package/test/cli.test.ts +144 -0
  58. package/test/codegen.test.ts +75 -0
  59. package/test/grammar.test.ts +243 -0
  60. package/test/helpers.ts +59 -0
  61. package/test/lalr.test.ts +114 -0
  62. package/test/parser.test.ts +392 -0
  63. package/test/table.test.ts +144 -0
  64. package/tsconfig.json +1 -1
  65. package/example/complicated/grammar.txt +0 -37
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadTable = exports.loadGrammar = void 0;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const common_1 = require("../meta/common");
9
+ const selfhosted_1 = require("../table/selfhosted");
10
+ const selfhosted_2 = require("../meta/selfhosted");
11
+ const loadGrammar = async (grammarPath, options = {}) => {
12
+ let source;
13
+ try {
14
+ source = await promises_1.default.readFile(grammarPath, "utf8");
15
+ }
16
+ catch (err) {
17
+ return {
18
+ success: false,
19
+ reason: `Could not read the grammar file: ${err instanceof Error ? err.message : String(err)}`,
20
+ token: (0, common_1.syntheticGrammarToken)(),
21
+ };
22
+ }
23
+ const result = (0, selfhosted_2.tryBuildProductions)(source, options);
24
+ if (result.success === false)
25
+ return Object.assign(Object.assign({}, result), { reason: `${result.reason} (in ${grammarPath})` });
26
+ return result;
27
+ };
28
+ exports.loadGrammar = loadGrammar;
29
+ const loadTable = async (tablePath, options = {}) => {
30
+ let source;
31
+ try {
32
+ source = await promises_1.default.readFile(tablePath, "utf8");
33
+ }
34
+ catch (err) {
35
+ return {
36
+ success: false,
37
+ reason: `Could not read the parsing table file: ${err instanceof Error ? err.message : String(err)}`,
38
+ token: (0, common_1.syntheticGrammarToken)(),
39
+ };
40
+ }
41
+ const result = (0, selfhosted_1.tryBuildStates)(source, Object.assign(Object.assign({}, options), { source: tablePath }));
42
+ if (result.success === false)
43
+ return Object.assign(Object.assign({}, result), { reason: `${result.reason} (in ${tablePath})` });
44
+ return result;
45
+ };
46
+ exports.loadTable = loadTable;
47
+ //# sourceMappingURL=loadFiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadFiles.js","sourceRoot":"","sources":["../../src/utils/loadFiles.ts"],"names":[],"mappings":";;;;;;AAAA,2DAA6B;AAC7B,2CAA+F;AAE/F,oDAAyE;AACzE,mDAAyD;AAMlD,MAAM,WAAW,GAAG,KAAK,EAC9B,WAAmB,EACnB,UAA8B,EAAE,EACD,EAAE;IACjC,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,oCAAoC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;YAC9F,KAAK,EAAE,IAAA,8BAAqB,GAAE;SAC/B,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,gCAAmB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpD,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK;QAC1B,uCAAY,MAAM,KAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,QAAQ,WAAW,GAAG,IAAG;IACvE,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAnBW,QAAA,WAAW,eAmBtB;AAKK,MAAM,SAAS,GAAG,KAAK,EAC5B,SAAiB,EACjB,UAA4B,EAAE,EACC,EAAE;IACjC,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,0CAA0C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;YACpG,KAAK,EAAE,IAAA,8BAAqB,GAAE;SAC/B,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,2BAAc,EAAC,MAAM,kCAAO,OAAO,KAAE,MAAM,EAAE,SAAS,IAAG,CAAC;IACzE,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK;QAC1B,uCAAY,MAAM,KAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,QAAQ,SAAS,GAAG,IAAG;IACrE,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAnBW,QAAA,SAAS,aAmBpB"}
@@ -0,0 +1,9 @@
1
+ import { Production } from "../meta/common";
2
+ export declare const KLEENE_REDUCER_NAME = "autogenerated-kleene";
3
+ export declare const namedProductions: (productions: Production[]) => string[];
4
+ export declare const missingReducerNames: (productions: Production[], reducers: Record<string, unknown>) => string[];
5
+ export declare const missingReducerMessage: (productions: Production[], reducers: Record<string, unknown>) => string;
6
+ export type ReducerMap<Node> = Record<string, (newInput: any, oldInput: any) => Node>;
7
+ export declare const defineReducers: <Reducers extends Record<string, (...args: never[]) => unknown>>(reducers: Reducers) => (newInput: any, oldInput: any) => any;
8
+ export declare const assertReducersCoverGrammar: (productions: Production[], reducers: ReducerMap<unknown>) => void;
9
+ export declare const enumToString: <TokenType = unknown>(tokenTypes: object) => (tokenType: TokenType) => string;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enumToString = exports.assertReducersCoverGrammar = exports.defineReducers = exports.missingReducerMessage = exports.missingReducerNames = exports.namedProductions = exports.KLEENE_REDUCER_NAME = void 0;
4
+ exports.KLEENE_REDUCER_NAME = "autogenerated-kleene";
5
+ const namedProductions = (productions) => {
6
+ const names = new Set();
7
+ for (const production of productions)
8
+ if (production.name != null)
9
+ names.add(production.name);
10
+ return [...names].sort();
11
+ };
12
+ exports.namedProductions = namedProductions;
13
+ const missingReducerNames = (productions, reducers) => (0, exports.namedProductions)(productions).filter((name) => reducers[name] == null);
14
+ exports.missingReducerNames = missingReducerNames;
15
+ const describeKleene = (productions) => {
16
+ const needed = productions.filter((production) => production.name === exports.KLEENE_REDUCER_NAME ||
17
+ production.identifier.startsWith("<autogen-"));
18
+ if (needed.length === 0)
19
+ return "";
20
+ return `\nNote: this grammar uses "*" or "+", so it has ${needed.length} autogenerated list production(s) named "${exports.KLEENE_REDUCER_NAME}". That reducer has to flatten the list it is handed (see README, "Repetition with * and +").`;
21
+ };
22
+ const missingReducerMessage = (productions, reducers) => {
23
+ const missing = (0, exports.missingReducerNames)(productions, reducers);
24
+ if (missing.length === 0)
25
+ return "";
26
+ const has = (0, exports.namedProductions)(productions).filter((name) => reducers[name] != null);
27
+ const kleene = missing.includes(exports.KLEENE_REDUCER_NAME) ? describeKleene(productions) : "";
28
+ return (`No reducer for ${missing.length === 1 ? "production" : "productions"} ${missing
29
+ .map((name) => `"${name}"`)
30
+ .join(", ")}.` +
31
+ `\nReducers are keyed by the name given in the grammar, e.g. "<PROGRAM: program>" is reduced by reducers.program.` +
32
+ (has.length === 0 ? "\nYour reducer map is empty." : `\nYou have reducers for: ${has.join(", ")}.`) +
33
+ kleene);
34
+ };
35
+ exports.missingReducerMessage = missingReducerMessage;
36
+ const defineReducers = (reducers) => function sparseReducer(newInput, oldInput) {
37
+ const name = newInput === null || newInput === void 0 ? void 0 : newInput.name;
38
+ const lookup = reducers;
39
+ const reducer = name == null ? undefined : lookup[name];
40
+ if (reducer == null)
41
+ throw new Error(`No reducer for production "${name !== null && name !== void 0 ? name : ""}" (production ${oldInput === null || oldInput === void 0 ? void 0 : oldInput.index}). Reducers are keyed by the name given in the grammar.`);
42
+ return reducer(newInput, oldInput);
43
+ };
44
+ exports.defineReducers = defineReducers;
45
+ const assertReducersCoverGrammar = (productions, reducers) => {
46
+ const message = (0, exports.missingReducerMessage)(productions, reducers);
47
+ if (message !== "")
48
+ throw new Error(message);
49
+ };
50
+ exports.assertReducersCoverGrammar = assertReducersCoverGrammar;
51
+ const enumToString = (tokenTypes) => (tokenType) => {
52
+ const name = tokenTypes[tokenType];
53
+ return `${name !== null && name !== void 0 ? name : tokenType}`;
54
+ };
55
+ exports.enumToString = enumToString;
56
+ //# sourceMappingURL=reducers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducers.js","sourceRoot":"","sources":["../../src/utils/reducers.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import fs from "fs/promises";
2
- import { buildProductions, LR1StackSymbol, Sparse } from "../../src/index";
2
+ import { LR1StackSymbol, Sparse, buildProductions, buildStates } from "../../src/index";
3
3
  import { ColumnAndRow, Slex, Token } from "@scinorandex/slex";
4
4
 
5
5
  // prettier-ignore
6
- enum LoLangTokenType {
6
+ export enum LoLangTokenType {
7
7
  EOF,
8
8
 
9
9
  IMPORT,
@@ -42,7 +42,7 @@ enum LoLangTokenType {
42
42
  SINGLE_LINE_COMMENT, MULTI_LINE_COMMENT
43
43
  }
44
44
 
45
- const lexerGenerator = new Slex<LoLangTokenType, {}>({
45
+ export const lexerGenerator = new Slex<LoLangTokenType, {}>({
46
46
  EOF_TYPE: LoLangTokenType.EOF,
47
47
  isHigherPrecedence: ({ current, next }) => current === LoLangTokenType.IDENTIFIER,
48
48
  // Specifies that SINGLE_LINE_COMMENT and MULTI_LINE_COMMENT tokens should be ignored.
@@ -165,17 +165,18 @@ class Node {
165
165
  }
166
166
  }
167
167
 
168
- async function main() {
168
+ async function parse(tablePath: string) {
169
169
  const exampleSourceCode = await fs.readFile("./example/LoLang/Features_Array_Methods.lol", "utf8");
170
170
  const lexer = lexerGenerator.generate(exampleSourceCode, () => ({}));
171
171
 
172
172
  const toStringifiedTokenType = (type: LoLangTokenType) => LoLangTokenType[type];
173
173
  const productions = buildProductions(await fs.readFile("./example/LoLang/grammar.txt", "utf8"));
174
- const parserGenerator = Sparse.fromProductions<LoLangTokenType, {}, Node>({ productions, toStringifiedTokenType });
174
+ const states = buildStates(await fs.readFile(tablePath, "utf8"));
175
+ const parserGenerator = new Sparse<LoLangTokenType, {}, Node>({ productions, toStringifiedTokenType, states });
175
176
 
176
177
  const parser = parserGenerator.generate(lexer, {
177
178
  reducer: (_, { input }) => new Node(input),
178
- recover({ lexer, statesStack, symbolsStack, isSafe, addError, crash, finish, states }) {
179
+ recover({ lexer, statesStack, symbolsStack, isSafe, addError, crash, finish, insertToken, states }) {
179
180
  let token = lexer.peekNextToken();
180
181
  const currentState = statesStack.peek();
181
182
 
@@ -188,25 +189,26 @@ async function main() {
188
189
  token = lexer.peekNextToken();
189
190
 
190
191
  // check for lacking semicolons
191
- if (states[currentState].actions.has("[SEMICOLON]")) {
192
- const action = states[currentState].getTerminalAction("SEMICOLON");
192
+ const semicolonAction = states[currentState].getTerminalAction("SEMICOLON");
193
+ if (semicolonAction != null) {
194
+ // A semicolon the author forgot. It gets the same position as the token we choked on.
193
195
  const newToken = new Token<LoLangTokenType, {}>(
194
196
  LoLangTokenType.SEMICOLON,
195
197
  ";",
196
- new ColumnAndRow(token.column, token.line),
198
+ new ColumnAndRow(token.line, token.column),
197
199
  token.metadata,
198
200
  );
199
201
 
200
- if (action) {
201
- if (action.type === "reduce") return finish({ newToken });
202
+ // If the parser only wanted a semicolon to reduce, hand it back as the next token.
203
+ if (semicolonAction.type === "reduce") return finish({ newToken });
202
204
 
203
- statesStack.push(action.value);
204
- symbolsStack.push({ type: "token", token: newToken });
205
- addError(
206
- `Expected SEMICOLON but received ${LoLangTokenType[token.type]}. SEMICOLON was automatically inserted.`,
207
- );
208
- }
205
+ // Otherwise shift the synthesized semicolon onto the stacks and carry on from there.
206
+ const inserted = insertToken(newToken);
207
+ if (inserted != null) return crash(inserted.reason);
209
208
 
209
+ addError(
210
+ `Expected SEMICOLON but received ${LoLangTokenType[token.type]}. SEMICOLON was automatically inserted.`,
211
+ );
210
212
  if (isSafe()) return finish();
211
213
  }
212
214
 
@@ -215,7 +217,29 @@ async function main() {
215
217
  },
216
218
  });
217
219
 
218
- console.log(parser.parse());
220
+ const parsingResult = parser.parse();
221
+ return JSON.stringify(parsingResult.result?.toObject());
222
+ }
223
+
224
+ async function main() {
225
+ let nsTime = Date.now();
226
+ const lr1Result = await parse("./example/LoLang/table.txt");
227
+ const lr1Time = Date.now() - nsTime;
228
+
229
+ nsTime = Date.now();
230
+ const lalrResult = await parse("./example/LoLang/tablelalr.txt");
231
+ const lalrTime = Date.now() - nsTime;
232
+
233
+ console.log(`LR(1) time: ${lr1Time}ms`);
234
+ console.log(`LALR(1) time: ${lalrTime}ms`);
235
+ const delta = Math.abs(lalrTime - lr1Time);
236
+ console.log("LR(1) is " + (lr1Time < lalrTime ? "faster" : "slower") + " than LALR(1)" + ` (${delta}ms)`);
237
+
238
+ console.log("==============================");
239
+ console.log(`LR(1) result: ${lr1Result}`);
240
+ console.log(`LALR(1) result: ${lalrResult}`);
241
+ console.log("==============================");
242
+ console.log(lr1Result === lalrResult ? "Equal output" : "Not equal output");
219
243
  }
220
244
 
221
245
  main();