@scinorandex/sparse
Version:
Yet another parser generator
206 lines • 9.59 kB
JavaScript
;
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