yukigo-mini-parser 0.1.0

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/src/grammar.ts ADDED
@@ -0,0 +1,167 @@
1
+ // Generated automatically by nearley, version 2.20.1
2
+ // http://github.com/Hardmath123/nearley
3
+ // Bypasses TS6133. Allow declared but unused functions.
4
+ // @ts-ignore
5
+ function id(d: any[]): any { return d[0]; }
6
+ declare var EOF: any;
7
+ declare var number: any;
8
+ declare var char: any;
9
+ declare var string: any;
10
+ declare var bool: any;
11
+ declare var variable: any;
12
+ declare var equal: any;
13
+ declare var notEqual: any;
14
+ declare var lt: any;
15
+ declare var lte: any;
16
+ declare var gt: any;
17
+ declare var gte: any;
18
+ declare var WS: any;
19
+
20
+ import { MiniLexer } from "./lexer.js"
21
+ import {
22
+ SimpleType,
23
+ Assignment,
24
+ Variable,
25
+ ListPrimitive,
26
+ ListType,
27
+ ArithmeticBinaryOperation,
28
+ SymbolPrimitive,
29
+ NumberPrimitive,
30
+ ComparisonOperation,
31
+ BooleanPrimitive,
32
+ StringPrimitive,
33
+ ParameterizedType,
34
+ Sequence,
35
+ UnguardedBody,
36
+ If,
37
+ While,
38
+ VariablePattern,
39
+ Equation,
40
+ Procedure,
41
+ TypeSignature,
42
+ CharPrimitive,
43
+ NilPrimitive,
44
+ Return
45
+ } from "yukigo-ast"
46
+
47
+ interface NearleyToken {
48
+ value: any;
49
+ [key: string]: any;
50
+ };
51
+
52
+ interface NearleyLexer {
53
+ reset: (chunk: string, info: any) => void;
54
+ next: () => NearleyToken | undefined;
55
+ save: () => any;
56
+ formatError: (token: never) => string;
57
+ has: (tokenType: string) => boolean;
58
+ };
59
+
60
+ interface NearleyRule {
61
+ name: string;
62
+ symbols: NearleySymbol[];
63
+ postprocess?: (d: any[], loc?: number, reject?: {}) => any;
64
+ };
65
+
66
+ type NearleySymbol = string | { literal: any } | { test: (token: any) => boolean };
67
+
68
+ interface Grammar {
69
+ Lexer: NearleyLexer | undefined;
70
+ ParserRules: NearleyRule[];
71
+ ParserStart: string;
72
+ };
73
+
74
+ const grammar: Grammar = {
75
+ Lexer: MiniLexer,
76
+ ParserRules: [
77
+ {"name": "program$ebnf$1", "symbols": ["statement"]},
78
+ {"name": "program$ebnf$1", "symbols": ["program$ebnf$1", "statement"], "postprocess": (d) => d[0].concat([d[1]])},
79
+ {"name": "program", "symbols": ["program$ebnf$1", "_", (MiniLexer.has("EOF") ? {type: "EOF"} : EOF)], "postprocess": (d) => d[0].flat(Infinity)},
80
+ {"name": "statement$subexpression$1", "symbols": ["assignment"]},
81
+ {"name": "statement$subexpression$1", "symbols": ["variable_statement"]},
82
+ {"name": "statement$subexpression$1", "symbols": ["function_statement"]},
83
+ {"name": "statement$subexpression$1", "symbols": ["return_statement"]},
84
+ {"name": "statement$subexpression$1", "symbols": ["if_statement"]},
85
+ {"name": "statement$subexpression$1", "symbols": ["while_statement"]},
86
+ {"name": "statement", "symbols": ["statement$subexpression$1", "_", {"literal":";"}, "_"], "postprocess": (d) => d[0][0]},
87
+ {"name": "variable_statement$ebnf$1$subexpression$1", "symbols": ["_", {"literal":":="}, "_", "expression"]},
88
+ {"name": "variable_statement$ebnf$1", "symbols": ["variable_statement$ebnf$1$subexpression$1"], "postprocess": id},
89
+ {"name": "variable_statement$ebnf$1", "symbols": [], "postprocess": () => null},
90
+ {"name": "variable_statement", "symbols": ["type", "__", "variable", "variable_statement$ebnf$1"], "postprocess": (d) => new Variable(d[2], d[3] ? d[3][3] : new NilPrimitive(null), d[0])},
91
+ {"name": "return_statement", "symbols": [{"literal":"return"}, "_", "expression"], "postprocess": (d) => new Return(d[2])},
92
+ {"name": "while_statement", "symbols": [{"literal":"while"}, "_", "condition", "_", "statement_list"], "postprocess": d => new While(d[2], d[4])},
93
+ {"name": "if_statement", "symbols": [{"literal":"if"}, "_", "condition", "_", "statement_list", "_", {"literal":"else"}, "_", "statement_list"], "postprocess": d => new If(d[2], d[4], d[8])},
94
+ {"name": "condition", "symbols": [{"literal":"("}, "_", "expression", "_", {"literal":")"}], "postprocess": (d) => d[2]},
95
+ {"name": "statement_list$ebnf$1", "symbols": []},
96
+ {"name": "statement_list$ebnf$1", "symbols": ["statement_list$ebnf$1", "statement"], "postprocess": (d) => d[0].concat([d[1]])},
97
+ {"name": "statement_list", "symbols": [{"literal":"{"}, "_", "statement_list$ebnf$1", "_", {"literal":"}"}], "postprocess": d => new Sequence(d[2])},
98
+ {"name": "assignment", "symbols": ["variable", "_", {"literal":":="}, "_", "expression"], "postprocess": (d) => new Assignment(d[0], d[4])},
99
+ {"name": "function_statement$subexpression$1$ebnf$1", "symbols": ["param_list"], "postprocess": id},
100
+ {"name": "function_statement$subexpression$1$ebnf$1", "symbols": [], "postprocess": () => null},
101
+ {"name": "function_statement$subexpression$1", "symbols": [{"literal":"("}, "_", "function_statement$subexpression$1$ebnf$1", "_", {"literal":")"}, "_", {"literal":"{"}, "_", "body", "_", {"literal":"}"}]},
102
+ {"name": "function_statement", "symbols": ["type", "__", "variable", "function_statement$subexpression$1"], "postprocess": (d) => {
103
+ const paramTypeList = []
104
+ const patternList = []
105
+ if(d[3][2]) {
106
+ for(const [paramType, paramPattern] of d[3][2]) {
107
+ paramTypeList.push(paramType);
108
+ patternList.push(paramPattern);
109
+ }
110
+ }
111
+ const signatureType = new ParameterizedType(paramTypeList, d[0], [])
112
+
113
+ const signature = new TypeSignature(d[2], signatureType);
114
+ const procedure = new Procedure(d[2], [new Equation(patternList, d[3][8])])
115
+ return [signature, procedure]
116
+ }},
117
+ {"name": "param_list$ebnf$1", "symbols": []},
118
+ {"name": "param_list$ebnf$1$subexpression$1", "symbols": ["_", {"literal":","}, "_", "param"]},
119
+ {"name": "param_list$ebnf$1", "symbols": ["param_list$ebnf$1", "param_list$ebnf$1$subexpression$1"], "postprocess": (d) => d[0].concat([d[1]])},
120
+ {"name": "param_list", "symbols": ["param", "param_list$ebnf$1"], "postprocess": d => [d[0], ...d[1].map(x => x[3])]},
121
+ {"name": "param", "symbols": ["type", "__", "variable"], "postprocess": d => [d[0], new VariablePattern(d[2])]},
122
+ {"name": "body$ebnf$1", "symbols": []},
123
+ {"name": "body$ebnf$1", "symbols": ["body$ebnf$1", "statement"], "postprocess": (d) => d[0].concat([d[1]])},
124
+ {"name": "body", "symbols": ["body$ebnf$1"], "postprocess": (d) => new UnguardedBody(new Sequence(d[0]))},
125
+ {"name": "expression", "symbols": ["comparison"], "postprocess": (d) => d[0]},
126
+ {"name": "comparison", "symbols": ["addition", "_", "comparison_operator", "_", "addition"], "postprocess": (d) => new ComparisonOperation(d[2], d[0], d[4])},
127
+ {"name": "comparison", "symbols": ["addition"], "postprocess": (d) => d[0]},
128
+ {"name": "addition", "symbols": ["addition", "_", {"literal":"+"}, "_", "multiplication"], "postprocess": (d) => new ArithmeticBinaryOperation("Plus", d[0], d[4])},
129
+ {"name": "addition", "symbols": ["addition", "_", {"literal":"-"}, "_", "multiplication"], "postprocess": (d) => new ArithmeticBinaryOperation("Minus", d[0], d[4])},
130
+ {"name": "addition", "symbols": ["multiplication"], "postprocess": (d) => d[0]},
131
+ {"name": "multiplication", "symbols": ["multiplication", "_", {"literal":"*"}, "_", "primary"], "postprocess": (d) => new ArithmeticBinaryOperation("Multiply", d[0], d[4])},
132
+ {"name": "multiplication", "symbols": ["multiplication", "_", {"literal":"/"}, "_", "primary"], "postprocess": (d) => new ArithmeticBinaryOperation("Divide", d[0], d[4])},
133
+ {"name": "multiplication", "symbols": ["primary"], "postprocess": (d) => d[0]},
134
+ {"name": "primary", "symbols": ["variable"], "postprocess": (d) => d[0]},
135
+ {"name": "primary", "symbols": [{"literal":"("}, "_", "expression", "_", {"literal":")"}], "postprocess": (d) => d[2]},
136
+ {"name": "primary", "symbols": ["primitive"], "postprocess": (d) => d[0]},
137
+ {"name": "primitive", "symbols": [(MiniLexer.has("number") ? {type: "number"} : number)], "postprocess": (d) => new NumberPrimitive(Number(d[0].value))},
138
+ {"name": "primitive", "symbols": [(MiniLexer.has("char") ? {type: "char"} : char)], "postprocess": (d) => new CharPrimitive(d[0].value)},
139
+ {"name": "primitive", "symbols": [(MiniLexer.has("string") ? {type: "string"} : string)], "postprocess": (d) => new StringPrimitive(d[0].value)},
140
+ {"name": "primitive", "symbols": [(MiniLexer.has("bool") ? {type: "bool"} : bool)], "postprocess": (d) => new BooleanPrimitive(d[0].value)},
141
+ {"name": "primitive", "symbols": ["variable"], "postprocess": (d) => d[0]},
142
+ {"name": "primitive", "symbols": ["list_primitive"], "postprocess": d => d[0]},
143
+ {"name": "type", "symbols": ["variable"], "postprocess": (d) => new SimpleType(d[0].value, [])},
144
+ {"name": "type", "symbols": ["type", {"literal":"["}, {"literal":"]"}], "postprocess": (d) => new ListType(d[0], [])},
145
+ {"name": "list_primitive", "symbols": [{"literal":"["}, "_", "expression_list", "_", {"literal":"]"}], "postprocess": (d) => new ListPrimitive(d[2])},
146
+ {"name": "expression_list$ebnf$1", "symbols": []},
147
+ {"name": "expression_list$ebnf$1$subexpression$1", "symbols": [{"literal":","}, "_", "expression", "_"]},
148
+ {"name": "expression_list$ebnf$1", "symbols": ["expression_list$ebnf$1", "expression_list$ebnf$1$subexpression$1"], "postprocess": (d) => d[0].concat([d[1]])},
149
+ {"name": "expression_list", "symbols": ["expression", "_", "expression_list$ebnf$1"], "postprocess": (d) => ([d[0], ...d[2].map(x => x[2])])},
150
+ {"name": "variable", "symbols": [(MiniLexer.has("variable") ? {type: "variable"} : variable)], "postprocess": (d) => new SymbolPrimitive(d[0].value)},
151
+ {"name": "comparison_operator", "symbols": [(MiniLexer.has("equal") ? {type: "equal"} : equal)], "postprocess": d => "Equal"},
152
+ {"name": "comparison_operator", "symbols": [(MiniLexer.has("notEqual") ? {type: "notEqual"} : notEqual)], "postprocess": d => "NotEqual"},
153
+ {"name": "comparison_operator", "symbols": [(MiniLexer.has("lt") ? {type: "lt"} : lt)], "postprocess": d => "LessThan"},
154
+ {"name": "comparison_operator", "symbols": [(MiniLexer.has("lte") ? {type: "lte"} : lte)], "postprocess": d => "LessOrEqualThan"},
155
+ {"name": "comparison_operator", "symbols": [(MiniLexer.has("gt") ? {type: "gt"} : gt)], "postprocess": d => "GreaterThan"},
156
+ {"name": "comparison_operator", "symbols": [(MiniLexer.has("gte") ? {type: "gte"} : gte)], "postprocess": d => "GreaterOrEqualThan"},
157
+ {"name": "_$ebnf$1", "symbols": []},
158
+ {"name": "_$ebnf$1", "symbols": ["_$ebnf$1", (MiniLexer.has("WS") ? {type: "WS"} : WS)], "postprocess": (d) => d[0].concat([d[1]])},
159
+ {"name": "_", "symbols": ["_$ebnf$1"], "postprocess": d => null},
160
+ {"name": "__$ebnf$1", "symbols": [(MiniLexer.has("WS") ? {type: "WS"} : WS)]},
161
+ {"name": "__$ebnf$1", "symbols": ["__$ebnf$1", (MiniLexer.has("WS") ? {type: "WS"} : WS)], "postprocess": (d) => d[0].concat([d[1]])},
162
+ {"name": "__", "symbols": ["__$ebnf$1"], "postprocess": d => null}
163
+ ],
164
+ ParserStart: "program",
165
+ };
166
+
167
+ export default grammar;
package/src/index.ts ADDED
@@ -0,0 +1,24 @@
1
+ import { YukigoParser } from "yukigo-ast";
2
+ import nearley from "nearley";
3
+ import grammar from "./grammar.js";
4
+
5
+ export class YukigoMiniParser implements YukigoParser {
6
+ public errors: string[] = [];
7
+
8
+ public parse(code: string) {
9
+ const parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar));
10
+ try {
11
+ parser.feed(code);
12
+ parser.finish();
13
+ } catch (error) {
14
+ const token = error.token;
15
+ const message = `Unexpected '${token.type}' token '${token.value}' at line ${token.line} col ${token.col}.`;
16
+ this.errors.push(message)
17
+ }
18
+ const results = parser.results
19
+ if(results.length > 1)
20
+ throw Error(`Ambiguous grammar. The parser generated ${results.length} ASTs`)
21
+
22
+ return results[0]
23
+ }
24
+ }
package/src/lexer.ts ADDED
@@ -0,0 +1,49 @@
1
+ import moo from "moo";
2
+ import { makeLexer } from "moo-ignore";
3
+
4
+ const keywords = [
5
+ "return",
6
+ "if",
7
+ "else"
8
+ ]
9
+
10
+ export const MiniLexerConfig = {
11
+ EOF: "*__EOF__*",
12
+ wildcard: "_",
13
+ WS: /[ \t]+/,
14
+ comment: /--.*?$|{-[\s\S]*?-}/,
15
+ number:
16
+ /0[xX][0-9a-fA-F]+|0[bB][01]+|0[oO][0-7]+|(?:\d*\.\d+|\d+)(?:[eE][+-]?\d+)?/,
17
+ char: /'(?:\\['\\bfnrtv0]|\\u[0-9a-fA-F]{4}|[^'\\\n\r])?'/,
18
+ string: /"(?:\\["\\bfnrtv0]|\\u[0-9a-fA-F]{4}|[^"\\\n\r])*"/,
19
+ bool: {
20
+ match: ["True", "False"],
21
+ },
22
+ semicolon: ";",
23
+ assign: ":=",
24
+ notEqual: "!=",
25
+ equal: "==",
26
+ gte: ">=",
27
+ gt: ">",
28
+ lte: "<=",
29
+ lt: "<",
30
+ lparen: "(",
31
+ rparen: ")",
32
+ lsquare: "[",
33
+ rsquare: "]",
34
+ lbracket: "{",
35
+ rbracket: "}",
36
+ comma: ",",
37
+ operator: /\+|\*/,
38
+ variable: {
39
+ match: /[a-z_][a-zA-Z0-9_']*/,
40
+ type: moo.keywords({
41
+ keyword: keywords,
42
+ }),
43
+ },
44
+ NL: { match: /\r?\n/, lineBreaks: true },
45
+ };
46
+
47
+ export const MiniLexer = makeLexer(MiniLexerConfig, ["NL", "comment"], {
48
+ eof: true,
49
+ });
package/src/parse.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { YukigoMiniParser } from "./index.js";
2
+
3
+ const parser = new YukigoMiniParser()
4
+
5
+ const code = `int n
6
+ int result
7
+
8
+ n := 5
9
+ result := 1
10
+
11
+ while n > 0 do
12
+ result := result * n
13
+ n := n - 1
14
+ endwhile
15
+
16
+ print result # This should print 120`
17
+
18
+ console.log(parser.parse(code))
@@ -0,0 +1,158 @@
1
+ import { assert } from "chai";
2
+ import {
3
+ ArithmeticBinaryOperation,
4
+ Assignment,
5
+ ComparisonOperation,
6
+ Equation,
7
+ If,
8
+ ListPrimitive,
9
+ ListType,
10
+ NilPrimitive,
11
+ NumberPrimitive,
12
+ ParameterizedType,
13
+ Procedure,
14
+ Return,
15
+ Sequence,
16
+ SimpleType,
17
+ SymbolPrimitive,
18
+ TypeSignature,
19
+ UnguardedBody,
20
+ Variable,
21
+ VariablePattern,
22
+ While,
23
+ YukigoParser,
24
+ } from "yukigo-ast";
25
+ import { YukigoMiniParser } from "../src/index.js";
26
+
27
+ describe("Parser Tests", () => {
28
+ let parser: YukigoParser;
29
+ beforeEach(() => {
30
+ parser = new YukigoMiniParser();
31
+ });
32
+
33
+ it("should parse assignment", () => {
34
+ const code = `int n; int result;`;
35
+ assert.deepEqual(parser.parse(code), [
36
+ new Variable(
37
+ new SymbolPrimitive("n"),
38
+ new NilPrimitive(null),
39
+ new SimpleType("int", [])
40
+ ),
41
+ new Variable(
42
+ new SymbolPrimitive("result"),
43
+ new NilPrimitive(null),
44
+ new SimpleType("int", [])
45
+ ),
46
+ ]);
47
+ });
48
+ it("should parse function declaration", () => {
49
+ const code = `int add(int x, int y) {
50
+ int result := x + y;
51
+ return result;
52
+ };`;
53
+ assert.deepEqual(parser.parse(code), [
54
+ new TypeSignature(
55
+ new SymbolPrimitive("add"),
56
+ new ParameterizedType(
57
+ [new SimpleType("int", []), new SimpleType("int", [])],
58
+ new SimpleType("int", []),
59
+ []
60
+ )
61
+ ),
62
+ new Procedure(new SymbolPrimitive("add"), [
63
+ new Equation(
64
+ [
65
+ new VariablePattern(new SymbolPrimitive("x")),
66
+ new VariablePattern(new SymbolPrimitive("y")),
67
+ ],
68
+ new UnguardedBody(
69
+ new Sequence([
70
+ new Variable(
71
+ new SymbolPrimitive("result"),
72
+ new ArithmeticBinaryOperation(
73
+ "Plus",
74
+ new SymbolPrimitive("x"),
75
+ new SymbolPrimitive("y")
76
+ ),
77
+ new SimpleType("int", [])
78
+ ),
79
+ new Return(new SymbolPrimitive("result")),
80
+ ])
81
+ )
82
+ ),
83
+ ]),
84
+ ]);
85
+ });
86
+ it("should parse list primitive", () => {
87
+ const code = `int[] numberList := [1, 2, 3 + 4];`;
88
+ assert.deepEqual(parser.parse(code), [
89
+ new Variable(
90
+ new SymbolPrimitive("numberList"),
91
+ new ListPrimitive([
92
+ new NumberPrimitive(1),
93
+ new NumberPrimitive(2),
94
+ new ArithmeticBinaryOperation(
95
+ "Plus",
96
+ new NumberPrimitive(3),
97
+ new NumberPrimitive(4)
98
+ ),
99
+ ]),
100
+ new ListType(new SimpleType("int", []), [])
101
+ ),
102
+ ]);
103
+ });
104
+ it("should parse if statement", () => {
105
+ const code = `if(a != b) { c := a + b; } else { c := a * 2; };`;
106
+ assert.deepEqual(parser.parse(code), [
107
+ new If(
108
+ new ComparisonOperation(
109
+ "NotEqual",
110
+ new SymbolPrimitive("a"),
111
+ new SymbolPrimitive("b")
112
+ ),
113
+ new Sequence([
114
+ new Assignment(
115
+ new SymbolPrimitive("c"),
116
+ new ArithmeticBinaryOperation(
117
+ "Plus",
118
+ new SymbolPrimitive("a"),
119
+ new SymbolPrimitive("b")
120
+ )
121
+ ),
122
+ ]),
123
+ new Sequence([
124
+ new Assignment(
125
+ new SymbolPrimitive("c"),
126
+ new ArithmeticBinaryOperation(
127
+ "Multiply",
128
+ new SymbolPrimitive("a"),
129
+ new NumberPrimitive(2)
130
+ )
131
+ ),
132
+ ]),
133
+ ),
134
+ ]);
135
+ });
136
+ it("should parse while loop statement", () => {
137
+ const code = `while(a < 10) { a := a + 1; };`;
138
+ assert.deepEqual(parser.parse(code), [
139
+ new While(
140
+ new ComparisonOperation(
141
+ "LessThan",
142
+ new SymbolPrimitive("a"),
143
+ new NumberPrimitive(10)
144
+ ),
145
+ new Sequence([
146
+ new Assignment(
147
+ new SymbolPrimitive("a"),
148
+ new ArithmeticBinaryOperation(
149
+ "Plus",
150
+ new SymbolPrimitive("a"),
151
+ new NumberPrimitive(1)
152
+ )
153
+ ),
154
+ ]),
155
+ ),
156
+ ]);
157
+ });
158
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2024",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2024"],
7
+ "outDir": "./dist",
8
+ "declaration": true,
9
+ "esModuleInterop": true,
10
+ "allowSyntheticDefaultImports": true,
11
+ "sourceMap": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "skipLibCheck": true
14
+ },
15
+ "include": ["src"],
16
+ "exclude": ["dist/**/*"]
17
+ }