@scinorandex/sparse 0.0.1 → 0.0.3

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 (62) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.js +2 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/generator.d.ts +4 -1
  5. package/dist/generator.js +47 -32
  6. package/dist/generator.js.map +1 -1
  7. package/dist/index.d.ts +3 -2
  8. package/dist/index.js +5 -5
  9. package/dist/index.js.map +1 -1
  10. package/dist/meta/common.d.ts +57 -0
  11. package/dist/meta/common.js +62 -0
  12. package/dist/meta/common.js.map +1 -0
  13. package/dist/meta/handwritten.d.ts +12 -0
  14. package/dist/{grammarParser.js → meta/handwritten.js} +24 -24
  15. package/dist/meta/handwritten.js.map +1 -0
  16. package/dist/meta/selfhosted.d.ts +23 -0
  17. package/dist/meta/selfhosted.js +206 -0
  18. package/dist/meta/selfhosted.js.map +1 -0
  19. package/dist/meta/states.d.ts +1580 -0
  20. package/dist/meta/states.js +1493 -0
  21. package/dist/meta/states.js.map +1 -0
  22. package/dist/parser.d.ts +16 -5
  23. package/dist/parser.js +25 -7
  24. package/dist/parser.js.map +1 -1
  25. package/dist/{tableParser.d.ts → table/handwritten.d.ts} +1 -1
  26. package/dist/{tableParser.js → table/handwritten.js} +2 -2
  27. package/dist/table/handwritten.js.map +1 -0
  28. package/dist/table/selfhosted.d.ts +2 -0
  29. package/dist/table/selfhosted.js +65 -0
  30. package/dist/table/selfhosted.js.map +1 -0
  31. package/dist/table/states.d.ts +592 -0
  32. package/dist/table/states.js +593 -0
  33. package/dist/table/states.js.map +1 -0
  34. package/dist/utils/Result.d.ts +1 -1
  35. package/dist/utils/errorWindowBuilder.d.ts +1 -1
  36. package/example/LoLang/example.ts +14 -7
  37. package/example/complicated/grammar.txt +37 -0
  38. package/example/math/example.ts +1 -4
  39. package/example/selfhosted/01-meta-grammar.txt +10 -0
  40. package/example/selfhosted/02-table-grammar.txt +6 -0
  41. package/example/selfhosted/codegen-meta.ts +1489 -0
  42. package/example/selfhosted/codegen-table.ts +589 -0
  43. package/example/selfhosted/example.ts +39 -0
  44. package/package.json +4 -4
  45. package/src/cli.ts +1 -1
  46. package/src/generator.ts +52 -34
  47. package/src/index.ts +3 -2
  48. package/src/meta/common.ts +74 -0
  49. package/src/{grammarParser.ts → meta/handwritten.ts} +17 -21
  50. package/src/meta/selfhosted.ts +272 -0
  51. package/src/meta/states.ts +1489 -0
  52. package/src/parser.ts +53 -19
  53. package/src/{tableParser.ts → table/handwritten.ts} +5 -5
  54. package/src/table/selfhosted.ts +67 -0
  55. package/src/table/states.ts +589 -0
  56. package/src/utils/Result.ts +1 -1
  57. package/src/utils/errorWindowBuilder.ts +1 -1
  58. package/tsconfig.build.json +30 -0
  59. package/tsconfig.json +6 -6
  60. package/dist/grammarParser.d.ts +0 -27
  61. package/dist/grammarParser.js.map +0 -1
  62. package/dist/tableParser.js.map +0 -1
package/src/parser.ts CHANGED
@@ -1,16 +1,16 @@
1
1
  import { Slex, Token } from "@scinorandex/slex";
2
2
  import { generateStates } from "./generator";
3
- import { Production } from "./grammarParser";
4
3
  import { Stack } from "./utils/Stack";
5
4
  import { Result } from "./utils/Result";
5
+ import { Production } from "./meta/common";
6
6
 
7
7
  export type TableAction = { type: "shift" | "reduce" | "goto"; value: number };
8
8
 
9
9
  export class TableState {
10
10
  public readonly actions: Map<string, TableAction>;
11
11
 
12
- constructor() {
13
- this.actions = new Map();
12
+ constructor(actions = new Map<string, TableAction>()) {
13
+ this.actions = actions;
14
14
  }
15
15
 
16
16
  public getTerminalAction(terminal: string): TableAction | undefined {
@@ -20,8 +20,21 @@ export class TableState {
20
20
  public getVariableAction(variable: string): TableAction | undefined {
21
21
  return this.actions.get(variable);
22
22
  }
23
+
24
+ toJSObject() {
25
+ return Object.fromEntries(this.actions);
26
+ }
27
+
28
+ static fromJSObject(json: ReturnType<TableState["toJSObject"]>) {
29
+ return new TableState(new Map(Object.entries(json)));
30
+ }
23
31
  }
24
32
 
33
+ type ReducerType<TokenType, Metadata, Node> = (
34
+ newInput: { bag: any; name: string | null },
35
+ oldInput: { input: LR1StackSymbol<TokenType, Metadata, Node>[]; index: number },
36
+ ) => Node;
37
+
25
38
  export type LR1StackSymbol<TokenType, Metadata, Node> =
26
39
  | { type: "token"; token: Token<TokenType, Metadata> }
27
40
  | { type: "node"; node: Node };
@@ -32,7 +45,7 @@ export class Sparse<TokenType, Metadata, Node> {
32
45
  productions: Production[];
33
46
  states: TableState[];
34
47
  toStringifiedTokenType: (tokenType: TokenType) => string;
35
- }
48
+ },
36
49
  ) {}
37
50
 
38
51
  public static tryFromProductions<TokenType, Metadata, Node>(options: {
@@ -65,9 +78,9 @@ export class Sparse<TokenType, Metadata, Node> {
65
78
  public generate(
66
79
  lexer: ReturnType<Slex<TokenType, Metadata>["generate"]>,
67
80
  options: {
68
- reducer: (input: LR1StackSymbol<TokenType, Metadata, Node>[], productionIndex: number) => Node;
81
+ reducer: ReducerType<TokenType, Metadata, Node>;
69
82
  recover?: ParserRecoveryFunction<TokenType, Metadata, Node>;
70
- }
83
+ },
71
84
  ) {
72
85
  return new LR1Parser<TokenType, Metadata, Node>(this.options, options.reducer, options.recover ?? null, lexer);
73
86
  }
@@ -104,9 +117,9 @@ class LR1Parser<TokenType, Metadata, Node> {
104
117
  states: TableState[];
105
118
  toStringifiedTokenType: (tokenType: TokenType) => string;
106
119
  },
107
- public readonly reducer: (input: LR1StackSymbol<TokenType, Metadata, Node>[], productionIndex: number) => Node,
120
+ public readonly reducer: ReducerType<TokenType, Metadata, Node>,
108
121
  public readonly recover: ParserRecoveryFunction<TokenType, Metadata, Node> | null,
109
- public readonly lexer: ReturnType<Slex<TokenType, Metadata>["generate"]>
122
+ public readonly lexer: ReturnType<Slex<TokenType, Metadata>["generate"]>,
110
123
  ) {}
111
124
 
112
125
  public parse(): ParserResult<TokenType, Metadata, Node> {
@@ -121,9 +134,9 @@ class LR1Parser<TokenType, Metadata, Node> {
121
134
  if (this.recover === null)
122
135
  throw new LR1ParserGraveError(
123
136
  `Invalid syntax. Expected ${[...states[currentState].actions.keys()].join(
124
- ","
137
+ ",",
125
138
  )} but got ${toStringifiedTokenType(token.type)}`,
126
- token
139
+ token,
127
140
  );
128
141
 
129
142
  const recoveryResult = this.recover({
@@ -166,29 +179,46 @@ class LR1Parser<TokenType, Metadata, Node> {
166
179
  }
167
180
 
168
181
  // try to perform the reduction
182
+ const productionIndex = production.originalProductionIndex;
183
+
169
184
  try {
170
- this.symbolsStack.push({ type: "node", node: this.reducer(popped, fixedAction.value) });
185
+ // prepare the object that you're going to send to the reducer function
186
+ const bag = {} as { [key: string]: Node | Token<TokenType, Metadata> };
187
+
188
+ for (let i = 0; i < production.rhs.length; i++) {
189
+ const rhsItem = production.rhs[i];
190
+ const poppedItem = popped[i];
191
+ if (rhsItem.name != null)
192
+ bag[rhsItem.name] = poppedItem.type === "token" ? poppedItem.token : poppedItem.node;
193
+ }
194
+
195
+ const newInput = { bag, name: production.name };
196
+ const oldInput = { input: popped, index: productionIndex };
197
+
198
+ const node = this.reducer(newInput, oldInput);
199
+ this.symbolsStack.push({ type: "node", node });
171
200
  } catch (err) {
172
201
  const input = JSON.stringify(popped, null, 2);
202
+ console.log(err);
173
203
  throw new LR1ParserGraveError(
174
- `Error while performing reduction for production: ${fixedAction.value}. Reduction input: ${input}`,
175
- token
204
+ `Error while performing reduction for production: ${productionIndex}. Reduction input: ${input}`,
205
+ token,
176
206
  );
177
207
  }
178
208
 
179
209
  // get the top node and figure out what state to add to state stack
180
210
  const topNode = this.statesStack.peek();
181
- const gotoAction = states[topNode].getVariableAction(production.lhs.lexeme);
211
+ const gotoAction = states[topNode].getVariableAction(production.identifier);
182
212
 
183
213
  if (gotoAction == undefined)
184
214
  throw new LR1ParserGraveError(
185
- `No GOTO action for for state: ${topNode} and production: ${production.lhs}. Actions: Reduce by production ${fixedAction.value}`,
186
- token
215
+ `No GOTO action for for state: ${topNode} and production: ${production.identifier}. Actions: Reduce by production ${productionIndex}`,
216
+ token,
187
217
  );
188
218
  else if (gotoAction.type !== "goto")
189
219
  throw new LR1ParserGraveError(
190
- `Expected GOTO action for state: ${topNode} and production: ${production.lhs}. Actions: Reduce by production ${fixedAction.value}. Received: ${gotoAction.type}`,
191
- token
220
+ `Expected GOTO action for state: ${topNode} and production: ${production.identifier}. Actions: Reduce by production ${productionIndex}. Received: ${gotoAction.type}`,
221
+ token,
192
222
  );
193
223
 
194
224
  this.statesStack.push(gotoAction.value);
@@ -201,7 +231,11 @@ class LR1Parser<TokenType, Metadata, Node> {
201
231
  }
202
232
 
203
233
  export class LR1ParserGraveError<TokenType, Metadata> extends Error {
204
- constructor(public readonly reason: string, public readonly currentToken: Token<TokenType, Metadata>, err?: Error) {
234
+ constructor(
235
+ public readonly reason: string,
236
+ public readonly currentToken: Token<TokenType, Metadata>,
237
+ err?: Error,
238
+ ) {
205
239
  super(reason);
206
240
  }
207
241
  }
@@ -1,5 +1,5 @@
1
1
  import { Slex } from "@scinorandex/slex";
2
- import { TableAction, TableState } from "./parser";
2
+ import { TableAction, TableState } from "../parser";
3
3
 
4
4
  type TableTokenMetadata = {};
5
5
  export enum TableTokenType {
@@ -20,11 +20,11 @@ export const tableLexerGenerator = new Slex<TableTokenType, TableTokenMetadata>(
20
20
 
21
21
  tableLexerGenerator.addRule(
22
22
  "lowercase",
23
- "a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v | w | x | y | z"
23
+ "a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v | w | x | y | z",
24
24
  );
25
25
  tableLexerGenerator.addRule(
26
26
  "uppercase",
27
- "A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z"
27
+ "A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z",
28
28
  );
29
29
  tableLexerGenerator.addRule("letter", "${lowercase} | ${uppercase}");
30
30
  tableLexerGenerator.addRule("digit", "0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9");
@@ -58,8 +58,8 @@ export const buildStates = (grammar: string) =>
58
58
  token.type === TableTokenType.PRODUCTION_NAME
59
59
  ? { type: "goto", value: parseInt(value.lexeme) }
60
60
  : value.type === TableTokenType.REDUCE_ACTION
61
- ? { type: "reduce", value: parseInt(value.lexeme.substring(1)) }
62
- : { type: "shift", value: parseInt(value.lexeme.substring(1)) };
61
+ ? { type: "reduce", value: parseInt(value.lexeme.substring(1)) }
62
+ : { type: "shift", value: parseInt(value.lexeme.substring(1)) };
63
63
 
64
64
  state.actions.set(token.lexeme, setting);
65
65
  if (lexer.peekNextToken().type == TableTokenType.COMMA) lexer.getNextToken();
@@ -0,0 +1,67 @@
1
+ import { GrammarToken } from "../meta/common";
2
+ import { BaseNode, getSelfHostedParserGenerator, grammarLexerGenerator, ListNode } from "../meta/selfhosted";
3
+ import { TableAction, TableState } from "../parser";
4
+ import { selfhosted } from "./states";
5
+
6
+ class StateNode extends BaseNode {
7
+ constructor(public readonly items: ListNode<ItemNode>) {
8
+ super();
9
+ }
10
+
11
+ toTableState(): TableState {
12
+ const state = new TableState();
13
+ for (const item of this.items.getItems()) {
14
+ const key = item.type === "terminal" ? `[${item.name.lexeme}]` : `<${item.name.lexeme}>`;
15
+ state.actions.set(key, item.action);
16
+ }
17
+ return state;
18
+ }
19
+ }
20
+
21
+ class ItemNode extends BaseNode {
22
+ constructor(
23
+ public readonly type: "terminal" | "variable",
24
+ public readonly name: GrammarToken,
25
+ public readonly action: TableAction,
26
+ ) {
27
+ super();
28
+ }
29
+ }
30
+
31
+ type Reducer = (bag: any) => BaseNode;
32
+ const reducers: { [key: string]: Reducer } = {
33
+ program: (bag: { states: ListNode<StateNode> }) => bag.states,
34
+ states: (bag: { state: ListNode<ItemNode>; rest?: ListNode<StateNode> }) => {
35
+ if (bag.rest == null) return new ListNode<StateNode>([new StateNode(bag.state)]);
36
+ else return bag.rest.add(new StateNode(bag.state));
37
+ },
38
+
39
+ items: (bag: { item: ItemNode; rest?: ListNode<ItemNode> }) => {
40
+ if (bag.rest == null) return new ListNode<ItemNode>([bag.item]);
41
+ else return bag.rest.add(bag.item);
42
+ },
43
+
44
+ terminal: (bag: { name: GrammarToken; action: GrammarToken }) => {
45
+ const action: TableAction = {
46
+ type: bag.action.lexeme.startsWith("s") ? "shift" : "reduce",
47
+ value: parseInt(bag.action.lexeme.substring(1)),
48
+ };
49
+ return new ItemNode("terminal", bag.name, action);
50
+ },
51
+
52
+ variable: (bag: { name: GrammarToken; state: GrammarToken }) => {
53
+ const action: TableAction = { type: "goto", value: parseInt(bag.state.lexeme) };
54
+ return new ItemNode("variable", bag.name, action);
55
+ },
56
+ };
57
+
58
+ export const buildStates = (grammar: string): TableState[] => {
59
+ const lexer = grammarLexerGenerator.generate(grammar, () => ({}));
60
+ const parserGenerator = getSelfHostedParserGenerator(selfhosted as any);
61
+ const parser = parserGenerator.generate(lexer, {
62
+ reducer: ({ bag, name }) => reducers[name ?? ""](bag),
63
+ });
64
+
65
+ const parsingResult = (parser.parse().result as ListNode<StateNode>).getItemsReversed();
66
+ return parsingResult.map((state) => state.toTableState());
67
+ };