UNPKG

@scinorandex/sparse

Version:

Yet another parser generator

206 lines 9.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildProductions = exports.tryBuildProductions = exports.getSelfHostedParserGenerator = exports.ListNode = exports.BaseNode = exports.grammarLexerGenerator = void 0; const slex_1 = require("@scinorandex/slex"); const index_1 = require("../index"); const states_1 = require("./states"); const parser_1 = require("../parser"); const common_1 = require("./common"); exports.grammarLexerGenerator = new slex_1.Slex({ EOF_TYPE: common_1.GrammarTokenType.EOF, isHigherPrecedence: () => false, }); exports.grammarLexerGenerator.addRule("lowercase", "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"); exports.grammarLexerGenerator.addRule("uppercase", "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"); exports.grammarLexerGenerator.addRule("letter", "${lowercase} | ${uppercase}"); exports.grammarLexerGenerator.addRule("digit", "0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9"); exports.grammarLexerGenerator.addRule("alphanumeric", "${letter} | ${digit}"); exports.grammarLexerGenerator.addRule("identifier", "(${letter} | $_) (${letter} | ${digit} | $_)*", common_1.GrammarTokenType.IDENTIFIER); exports.grammarLexerGenerator.addRule("colon", "$:", common_1.GrammarTokenType.COLON); exports.grammarLexerGenerator.addRule("semicolon", "$;", common_1.GrammarTokenType.SEMICOLON); exports.grammarLexerGenerator.addRule("langle", "$<", common_1.GrammarTokenType.L_ANGLE); exports.grammarLexerGenerator.addRule("rangle", "$>", common_1.GrammarTokenType.R_ANGLE); exports.grammarLexerGenerator.addRule("lbracket", "$[", common_1.GrammarTokenType.L_BRACKET); exports.grammarLexerGenerator.addRule("rbracket", "$]", common_1.GrammarTokenType.R_BRACKET); exports.grammarLexerGenerator.addRule("lparen", "$(", common_1.GrammarTokenType.L_PAREN); exports.grammarLexerGenerator.addRule("rparen", "$)", common_1.GrammarTokenType.R_PAREN); exports.grammarLexerGenerator.addRule("pipe", "$|", common_1.GrammarTokenType.PIPE); exports.grammarLexerGenerator.addRule("equals", "$=", common_1.GrammarTokenType.EQUALS); exports.grammarLexerGenerator.addRule("comma", "$,", common_1.GrammarTokenType.COMMA); exports.grammarLexerGenerator.addRule("question_mark", "$?", common_1.GrammarTokenType.QUESTION_MARK); exports.grammarLexerGenerator.addRule("number", "(${digit})+", common_1.GrammarTokenType.NUMBER); class BaseNode { } exports.BaseNode = BaseNode; class ListNode extends BaseNode { constructor(items) { super(); if (Array.isArray(items)) this.items = items; else this.items = [items]; } add(item) { this.items.push(item); return this; } getItems() { return this.items; } getItemsReversed() { return this.items.toReversed(); } } exports.ListNode = ListNode; class ProductionNode extends BaseNode { constructor(lhs, rhs, originalProductionIndex, name) { super(); this.lhs = lhs; this.rhs = rhs; this.originalProductionIndex = originalProductionIndex; this.name = name; } toStruct() { var _a; if (this.originalProductionIndex === -1) throw new Error("ProductionNode has no originalProductionIndex"); const hasGrouped = this.rhs.getItemsReversed().some((node) => node instanceof GroupedTokenNode); if (hasGrouped) throw new Error("Need to unroll before serializing"); return { lhs: this.lhs, identifier: `<${this.lhs.lexeme}>`, name: (_a = this.name) !== null && _a !== void 0 ? _a : null, originalProductionIndex: this.originalProductionIndex, rhs: this.rhs.getItemsReversed().map((node) => node.toStruct()), }; } setOriginalProductionIndex(index) { this.originalProductionIndex = index; } } function expandProduction(production) { const rhs = production.rhs.getItemsReversed(); for (let i = 0; i < rhs.length; i++) { const item = rhs[i]; const beforeItems = rhs.slice(0, i); const afterItems = rhs.slice(i + 1); if (item instanceof GroupedTokenNode) { const withoutCurrentItem = new ProductionNode(production.lhs, new ListNode([...beforeItems, ...afterItems].toReversed()), production.originalProductionIndex, production.name); const withCurrentItem = new ProductionNode(production.lhs, new ListNode([...beforeItems, ...item.inside.getItemsReversed(), ...afterItems].toReversed()), production.originalProductionIndex, production.name); const newProductions = [withoutCurrentItem, withCurrentItem]; return newProductions.flatMap((prod) => expandProduction(prod)); } else if (item instanceof TokenNode) { if (item.variables.getItems().length > 1) { const variables = item.variables.getItemsReversed(); const newProductions = variables.map((variable) => { return new ProductionNode(production.lhs, new ListNode([ ...beforeItems, new TokenNode("variable", new ListNode([variable]), item.name), ]), production.originalProductionIndex, production.name); }); return newProductions.flatMap((prod) => expandProduction(prod)); } } else { throw new Error("shoudn't reach here, ProductionNode RHS is neither GroupedTokenNode nor TokenNode"); } } return [production]; } class TokenNode extends BaseNode { constructor(type, variables, name) { super(); this.type = type; this.variables = variables; this.name = name; } toStruct() { var _a; const items = this.variables.getItems(); if (items.length > 1) throw new Error("Not yet unrolled"); const lexeme = items[0].lexeme; const identifier = this.type === "variable" ? `<${lexeme}>` : `[${lexeme}]`; const name = (_a = this.name) !== null && _a !== void 0 ? _a : null; return { type: this.type, token: items[0], identifier, name }; } } class GroupedTokenNode extends BaseNode { constructor(inside) { super(); this.inside = inside; } } class ProgramNode extends BaseNode { constructor(productions) { super(); this.productions = productions; } unrollProductions() { const productions = this.productions.getItemsReversed(); for (let i = 0; i < productions.length; i++) productions[i].setOriginalProductionIndex(i); return productions.flatMap((node) => expandProduction(node)); } getProductions() { return this.unrollProductions().map((node) => node.toStruct()); } } function getSelfHostedParserGenerator(e = states_1.selfhosted) { return new index_1.Sparse({ productions: e.productions.map((production) => (0, common_1.hydrateProduction)(production)), states: e.states.map((state) => parser_1.TableState.fromJSObject(state)), toStringifiedTokenType: (type) => common_1.GrammarTokenType[type], }); } exports.getSelfHostedParserGenerator = getSelfHostedParserGenerator; const reducers = { program: (bag) => { var _a; return new ProgramNode((_a = bag.productions) !== null && _a !== void 0 ? _a : new ListNode([])); }, productions: (bag) => { if (bag.rest == null) return new ListNode([bag.production]); else return bag.rest.add(bag.production); }, production: (bag) => { var _a; return new ProductionNode(bag.production_name, bag.tokens, -1, (_a = bag.uuid) === null || _a === void 0 ? void 0 : _a.lexeme); }, tokens: (bag) => { if (bag.rest == null) return new ListNode([bag.token]); else return bag.rest.add(bag.token); }, token: (bag) => bag.token, grouped_token: (bag) => new GroupedTokenNode(bag.tokens), variable: (bag) => { var _a; return new TokenNode("variable", bag.inside, (_a = bag.token_name) === null || _a === void 0 ? void 0 : _a.lexeme); }, terminal: (bag) => { var _a; return new TokenNode("terminal", bag.inside, (_a = bag.token_name) === null || _a === void 0 ? void 0 : _a.lexeme); }, inside: (bag) => { if (bag.rest == null) return new ListNode([bag.identifier]); else return bag.rest.add(bag.identifier); }, }; const tryBuildProductions = (lexer) => { if (typeof lexer === "string") lexer = exports.grammarLexerGenerator.generate(lexer, () => ({})); const parserGenerator = getSelfHostedParserGenerator(); const parser = parserGenerator.generate(lexer, { reducer: ({ bag, name }) => { return reducers[name !== null && name !== void 0 ? name : ""](bag); }, }); const parsingResult = parser.parse().result; const productions = parsingResult.getProductions(); return { success: true, value: productions }; }; exports.tryBuildProductions = tryBuildProductions; const buildProductions = (lexer) => { const result = (0, exports.tryBuildProductions)(lexer); if (result.success === false) throw new Error(`Encountered error "${result.reason}" while parsing grammar at ${result.token.line}:${result.token.column}`); return result.value; }; exports.buildProductions = buildProductions; //# sourceMappingURL=selfhosted.js.map