@parser-generator/grammar-interpreter
Version:
A Parser Generator that supports LL,SLR,LR1,LALR
917 lines (915 loc) • 33.1 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const definition_1 = require("@parser-generator/definition");
const shim_1 = require("@light0x00/shim");
// import { cloneDeep, groupBy ,Dictionary} from "lodash";
const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
const groupBy_1 = __importDefault(require("lodash/groupBy"));
const core_1 = require("@parser-generator/core");
const definition_2 = require("@parser-generator/definition");
// import { } from "@parser-generator/definition";
/*************************************** Lexical Analysis ****************************************/
var Tag;
(function (Tag) {
Tag["BLANK"] = "BLANK";
Tag["CRLF"] = "CRLF";
Tag["EOF"] = "EOF";
Tag["NIL"] = "NIL";
Tag["HEAD"] = "HEAD";
Tag["SCRIPT"] = "SCRIPT";
Tag["STRING"] = "STRING";
Tag["STRING_SINGLE_QUOTES"] = "STRING_SINGLE_QUOTES";
Tag["STRING_DOUBLE_QUOTES"] = "STRING_DOUBLE_QUOTES";
Tag["NUMBER"] = "NUMBER";
Tag["WORD"] = "WORD";
Tag["SINGLE"] = "SINGLE";
Tag["ID"] = "ID";
Tag["KEYWORD"] = "KEYWORD";
Tag["ASSOC"] = "ASSOC";
})(Tag = exports.Tag || (exports.Tag = {}));
class Token {
constructor(tag) {
this._lineNo = -1;
this._colNo = -1;
this._tag = tag;
}
setPos(lineNo, colNo) {
this._lineNo = lineNo;
this._colNo = colNo;
}
get lineNo() {
return this._lineNo;
}
get colNo() {
return this._colNo;
}
get tag() {
return this._tag;
}
toString() {
return this.value + `(${this._lineNo},${this._colNo})`;
}
}
class Special extends Token {
constructor(tag) {
super(tag);
}
get value() {
return this._tag;
}
}
class Word extends Token {
constructor(tag, lexeme) {
super(tag);
this.lexeme = lexeme;
}
get value() {
return this.lexeme;
}
}
class Number extends Token {
constructor(lexeme) {
super(Tag.NUMBER);
this.lexeme = lexeme;
}
get value() {
return this.lexeme;
}
}
/("|')(?<value>(\\(\1)|[^\1])*)?(\1)/.exec(`'\\"\\'aa'`);
class GrammarLexer extends definition_2.CommonAbstractRegexpLexer {
constructor(text) {
super(text, [
{ regexp: /\n+/y, type: Tag.CRLF, },
{ regexp: /\s+/y, type: Tag.BLANK, },
{ regexp: /#(?<value>\w+)/y, type: Tag.HEAD },
{ regexp: /[A-Z_a-z]\w*/y, type: Tag.WORD },
{ regexp: /<%(?<value>(.|\s)+?)%>/y, type: Tag.SCRIPT },
{ regexp: /'(?<value>(\\(')|[^'])*)?'/y, type: Tag.STRING },
{ regexp: /"(?<value>(\\(")|[^"])*)?"/y, type: Tag.STRING },
{ regexp: /([1-9]\d*\.\d+)|(0\.\d+)/y, type: Tag.NUMBER },
{ regexp: /(0(?![0-9]))|([1-9]\d*(?!\.))/y, type: Tag.NUMBER },
{ regexp: /(.)/y, type: Tag.SINGLE }
]);
this.lineNo = 1;
this.colNo = 0;
this.reserves = new Map();
this.reserves.set("left", new Word(Tag.ASSOC, "left"));
this.reserves.set("right", new Word(Tag.ASSOC, "right"));
this.reserves.set("NIL", new Special(Tag.NIL));
}
getEOF() {
if (this.eofToken == undefined) {
this.eofToken = new Special(Tag.EOF);
this.eofToken.setPos(this.lineNo, this.colNo);
}
return this.eofToken;
}
createToken(lexeme, type, match) {
let token;
switch (type) {
case Tag.CRLF:
this.lineNo += lexeme.length;
this.colNo = 0;
break;
case Tag.BLANK:
break;
case Tag.WORD: {
let re = this.reserves.get(lexeme);
if (re == undefined)
token = new Word(Tag.ID, lexeme);
else
token = cloneDeep_1.default(re);
break;
}
case Tag.HEAD:
token = new Word(Tag.HEAD, match.groups["value"]);
break;
case Tag.SCRIPT: {
token = new Word(Tag.SCRIPT, match.groups["value"]);
token.setPos(this.lineNo, this.colNo);
//处理行列计数
let { rows, cols } = shit(lexeme);
this.lineNo += rows;
this.colNo = (rows > 0) ? cols : this.colNo += cols;
break;
}
case Tag.NUMBER:
token = new Number(parseFloat(lexeme));
break;
case Tag.STRING:
{
let lexeme;
if (match[0].startsWith("\"")) {
lexeme = match.groups["value"].replace(/\\"/g, "\"");
}
else {
lexeme = match.groups["value"].replace(/\\'/g, "'");
}
token = new Word(Tag.STRING, lexeme);
}
break;
case Tag.SINGLE:
token = new Word(Tag.SINGLE, lexeme);
break;
default:
throw new Error(`unknown type ${type}`);
}
if (type != Tag.SCRIPT) { //script 类型单独处理
if (token != undefined)
token.setPos(this.lineNo, this.colNo);
this.colNo += lexeme.length;
}
return token;
}
onMatchFaild(text, lastIndex) {
throw new Error(`unrecognized charactor(${this.lineNo},${this.colNo}):${text[lastIndex]} `);
}
}
exports.GrammarLexer = GrammarLexer;
//返回指定字符串的换行符数量,最后一行的列数
function shit(str) {
let rows = 0;
let cols = 0;
let isLastRow = true;
for (let i = str.length - 1; i >= 0; i--) {
if (str[i] == "\n") {
rows++;
isLastRow = false;
}
else if (isLastRow)
cols++;
}
return { rows, cols };
}
/*************************************** AST ****************************************/
class ASTree {
}
class ProgramNode extends ASTree {
constructor(gNode, paNode, blocks) {
super();
// traits: SymbolTraits;
this.traits = new Map();
this.grammarNode = gNode;
this.blocks = blocks;
// if (paNode == undefined)
// this.traits = new Map<SSymbol, SymbolTrait>();
// else
// this.traits = paNode.traits;
}
accpet(visitor) {
for (let c of this.blocks) {
c.accpet(visitor);
}
visitor.visitProgramNode(this);
}
}
exports.ProgramNode = ProgramNode;
class ScriptNode extends ASTree {
constructor(t) {
super();
this.script = t.value;
}
accpet(visitor) {
visitor.visitScriptNode(this);
}
}
class PrecAssocNode extends ASTree {
constructor(children) {
super();
this.children = children;
}
accpet(visitor) {
visitor.visitPrecAssocNode(this);
}
}
class PrecassocItemNode extends ASTree {
constructor(tokens) {
super();
shim_1.assert(tokens[0] != undefined);
this.symbol = tokens[0];
// this.leftAssoc = (tokens[1]?.value??"right") == "left";
// this.prec = (tokens[2]?.value??-1) as number;
console.log(tokens);
if (tokens[1] != undefined)
this.leftAssoc = tokens[1].value == "left";
else
this.leftAssoc = false;
if (tokens[2] != undefined)
this.prec = tokens[2].value;
else
this.prec = -1;
}
accpet(visitor) {
throw new Error("Method not implemented.");
}
}
class Env {
constructor() {
this.syms = new Map();
//反向索引
this.rsyms = new Map(); //记录变量对应的对象 的变量名
this.prods = new Map(); //记录Production对象对应的变量名
this.rprods = new Map(); //记录Production对象对应的变量名
this.registerSym("NIL", definition_1.NIL);
this.registerSym("EOF", definition_1.EOF);
}
//Symbol
registerSym(varName, sym) {
this.syms.set(varName, sym);
this.rsyms.set(sym, varName);
}
getSym(varName) {
return this.syms.get(varName);
}
hasSym(varName) {
return this.syms.has(varName);
}
getSymVarName(sym) {
return this.rsyms.get(sym);
}
//Production
registerProd(prod, varName) {
this.rprods.set(prod, varName);
this.prods.set(varName, prod);
}
getProdVarName(prod) {
shim_1.assert(this.rprods.has(prod), `Undeclared production ${prod}`);
return this.rprods.get(prod);
}
getProd(varName) {
shim_1.assert(this.prods.has(varName), `Undeclared productio ${varName}`);
return this.prods.get(varName);
}
}
class GrammarNode extends ASTree {
// Directory <ProductionNode[]>
constructor(tNodes, ntNodes, prodNodes) {
super();
this._env = new Env();
//属性
this.nts = [];
this.tpNodes = tNodes;
this.prodNodes = prodNodes;
this.ntNodes = ntNodes;
/* 初始化文法变量 */
//terminal proto
for (let { token: t } of tNodes) {
let v = t.value;
if (this.env.hasSym(v))
throw new Error(`Duplicate declared variable ${v} at (${t.lineNo},${t.colNo})`);
this.env.registerSym(v, new definition_1.TokenPro(v));
}
//non-terminal
for (let i = 0; i < ntNodes.length; i++) {
let nt = ntNodes[i].token;
let varName = ntNodes[i].varName;
if (this.env.hasSym(varName))
throw new Error(`Duplicate declared variable ${varName} at (${nt.lineNo},${nt.colNo})`);
let ntProto;
if (i == 0)
this.startSym = ntProto = new definition_1.NonTerminal(varName, true);
else
ntProto = new definition_1.NonTerminal(varName);
this.env.registerSym(varName, ntProto);
this.nts.push(ntProto);
}
//产生式按所属非终结符分组
this.prodNodeGroups = groupBy_1.default(this.prodNodes, (p) => p.ntVarName);
//为产生式分配id和变量名
let prodIdCount = 0;
for (let key in this.prodNodeGroups) {
let group = this.prodNodeGroups[key];
for (let pNode of group) {
pNode.pid = prodIdCount++;
pNode.varName = "p" + pNode.pid;
pNode.env = this.env;
}
}
}
get env() {
return this._env;
}
accpet(visitor) {
for (let tpNode of this.tpNodes) {
visitor.visitTokenProNode(tpNode);
}
for (let ntNode of this.ntNodes) {
visitor.visitNonterminalNode(ntNode);
}
for (let pNode of this.prodNodes) {
visitor.visitProductionNode(pNode);
}
visitor.visitGrammarNode(this);
}
}
class TokenProNode extends ASTree {
constructor(token) {
super();
this.token = token;
this.varName = token.value;
}
accpet(visitor) {
visitor.visitTokenProNode(this);
}
}
class ProductionNode extends ASTree {
constructor(nt, body) {
super();
this.left = nt;
this.ntVarName = nt.value;
//pre
if (body[0].tag == Tag.SCRIPT)
this.preAction = body[0];
//post
if (body[body.length - 1].tag == Tag.SCRIPT)
this.postAction = body[body.length - 1];
//body
this.rawBody = new Array();
let start = this.preAction == undefined ? 0 : 1;
let end = this.postAction == undefined ? body.length - 1 : body.length - 2;
for (let i = start; i <= end; i++) {
let symbol = body[i];
let action;
if (i + 1 <= end && body[i + 1].tag == Tag.SCRIPT) {
action = body[i + 1];
i++;
}
this.rawBody.push({ symToken: symbol, action: action });
}
}
accpet(visitor) {
visitor.visitProductionNode(this);
}
}
class NonTerminalNode extends ASTree {
constructor(token, prods) {
super();
this._varName = token.value;
this.token = token;
this.prods = prods;
let defaultPostAction;
for (let p of prods) {
if (defaultPostAction == undefined && p.postAction != undefined) {
defaultPostAction = p.postAction;
break;
}
}
// if (defaultPostAction == undefined)
// console.warn(`The nonterminal "${this.varName}" doesn't have post-action,it's required for parser!`);
//没有归约动作的产生式 优先向后寻找 如果后方没有 则使用默认的
for (let i = 0; i < prods.length; i++) {
if (prods[i].postAction == undefined) {
for (let k = i + 1; k < prods.length; k++) {
if (prods[k].postAction != undefined) {
prods[i].postAction = prods[k].postAction;
break;
}
}
if (prods[i].postAction == undefined)
prods[i].postAction = defaultPostAction;
}
}
}
get varName() {
return this._varName;
}
accpet(visitor) {
visitor.visitNonterminalNode(this);
}
}
function pos(t) {
return `(${t.lineNo},${t.colNo})`;
}
/*************************************** Syntax Analysis ****************************************/
const TOKEN_KEY = "TOKEN_PROTOTYPES";
const GRAMMAR_KEY = "GRAMMAR";
const SPA_KEY = "SYMBOL_ASSOC_PREC";
function parse(lexer) {
/****************************************
文法草案
{} 重复0次多多次
[] 0次或一次
program -> SCRIPT | HEAD { SCRIPT | HEAD }
token -> ID {,ID}
precassoc -> precassoc_item { precassoc_item }
precassoc_item -> symbol ('left' | 'right') NUMBER
grammar -> nt { nt }
nt -> ID '-' '>' prod { | prod } ';'
prod -> [ SCRIPT ] symbol SCRIPT { symbol SCRIPT }
symbol -> ID | token | string
****************************************/
return program();
function misMatch(expection, actuality) {
throw new Error(`Expected token is ${expection}, but actually ${actuality}`);
}
function program() {
/*
Block:
- GRAMMAR (Required)
- TOKEN_PROTO
- SCRIPT
- PREC_ASSOC
*/
//如果生成代码时需要保证每个块的顺序 则用链表存储顺序
let blocks = new Array();
let grammarNode;
let precAssocNode;
let terminalProtoNodes;
blocks.push(...scriptBlock());
//token 声明
if (lexer.peek().value == TOKEN_KEY) {
lexer.next();
terminalProtoNodes = tokenDeclarationsBlock();
}
else {
terminalProtoNodes = [];
}
blocks.push(...scriptBlock());
//文法
if (lexer.peek().value == GRAMMAR_KEY) {
lexer.next();
grammarNode = grammarBlock(terminalProtoNodes);
blocks.push(grammarNode);
}
else {
misMatch(GRAMMAR_KEY, lexer.peek().value);
}
blocks.push(...scriptBlock());
//优先级 结合性
if (lexer.peek().value == SPA_KEY) {
lexer.next();
precAssocNode = precAssocBlock();
if (precAssocNode != undefined)
blocks.push(precAssocNode);
}
else {
precAssocNode = new PrecAssocNode([]);
}
blocks.push(...scriptBlock());
if (lexer.peek().tag != Tag.EOF) {
throw new Error(`Unexpected block "${lexer.peek().value}" at ` + pos(lexer.peek()));
}
return new ProgramNode(grammarNode, precAssocNode, blocks);
}
function scriptBlock() {
let scriptNodes = [];
while (lexer.peek().tag == Tag.SCRIPT)
scriptNodes.push(new ScriptNode(lexer.next()));
return scriptNodes;
}
function tokenDeclarationsBlock() {
let protos = [];
while (1) {
if (lexer.peek().tag != Tag.ID)
break;
protos.push(new TokenProNode(lexer.next()));
if (lexer.peek().value != ",")
break;
lexer.next();
}
return protos;
}
function precAssocBlock() {
let items = new Array();
while (isSymbol(lexer.peek())) {
let sym = lexer.next();
let assoc, prec;
if (lexer.peek().tag == Tag.ASSOC) {
assoc = lexer.next();
// throw new Error(`expect left or right at (${pos(lexer.peek())})`);
}
if (lexer.peek().tag == Tag.NUMBER) {
prec = lexer.next();
// throw new Error(`expect a number at (${pos(lexer.peek())})`);
}
if (assoc === undefined && prec === undefined) {
throw new Error(`expect a left/right or number at (${pos(lexer.peek())})`);
}
items.push(new PrecassocItemNode([sym, assoc, prec]));
}
if (items.length > 0)
return new PrecAssocNode(items);
}
function isSymbol(token) {
return token.tag == Tag.STRING || token.tag == Tag.ID;
}
function grammarBlock(terminalProtoNodes) {
let rawGrammar = new Array();
let ntDecls = new Array(); //文法中声明的所有非终结符(左侧)
let nt;
while ((nt = nonTerminal()) != undefined) {
rawGrammar.push(nt);
ntDecls.push(nt.key);
}
shim_1.assert(rawGrammar.length > 0, "The grammar should have at least one non-terminal ");
let ntNodes = new Array();
let prodNodes = new Array();
//每一个非终结符
for (let { key, prods } of rawGrammar) {
let ntPNodes = new Array();
//非终结符的每一个产生式
for (let p of prods) {
ntPNodes.push(new ProductionNode(key, p));
}
ntNodes.push(new NonTerminalNode(key, ntPNodes));
prodNodes.push(...ntPNodes);
}
return new GrammarNode(terminalProtoNodes, ntNodes, prodNodes);
}
function matchNext(tag, val) {
let look = lexer.next();
let tagMis = look.tag != tag;
let valMis = val != undefined && look.value != val;
if (tagMis) {
let msg = valMis ? `expect ${tag} ${val} at ${pos(look)}` : `expect ${tag} at ${pos(look)}`;
throw new Error(msg);
}
}
function nonTerminal() {
if (lexer.peek().tag != Tag.ID)
return undefined;
//左侧
let nt = lexer.next();
//箭头
matchNext(Tag.SINGLE, "-");
matchNext(Tag.SINGLE, ">");
//右侧
let prods = [];
while (1) {
let curProd = production();
//下一个是产生式
if (lexer.peek().value == "|") {
prods.push(curProd);
curProd = [];
lexer.next();
}
//已经到达当前非终结符末尾
else if (lexer.peek().value == ";") {
prods.push(curProd);
lexer.next();
break;
}
else {
throw new Error(`Unexpected symbol "${lexer.peek().value}" at ` + pos(lexer.peek()));
}
}
return { key: nt, prods };
}
function production() {
let body = new Array();
let isScript = false;
let isNil = false; //是否是空产生式体
while (1) {
if (lexer.peek().tag == Tag.ID || lexer.peek().tag == Tag.STRING) {
if (isNil) {
throw new Error("The NIL production can't have any symbol,expected \";\" at" + pos(lexer.peek()));
}
isScript = false;
body.push(lexer.next());
}
else if (lexer.peek().tag == Tag.NIL) {
isScript = false;
isNil = true;
body.push(lexer.next());
}
else if (lexer.peek().tag == Tag.SCRIPT) {
if (isScript)
throw new Error("The production body cannot have multiple continuous script," + pos(lexer.peek()));
body.push(lexer.next());
isScript = true;
}
else {
break;
}
}
return body;
}
}
exports.parse = parse;
class EvalVisitor {
constructor(context) {
this.traits = new Map();
this.nts = [];
this.prods = [];
this.context = context;
}
get grammar() {
if (this._grammar == undefined) {
let { parser } = this.context;
if (parser == "LL")
this._grammar = new definition_1.Grammar(this.nts, this.prods, this.startSym, this.traits); //根据配置决定初始化
else
this._grammar = new definition_1.AugmentedGrammar(this.nts, this.prods, this.startSym, this.traits); //根据配置决定初始化
}
return this._grammar;
}
visitProgramNode(node) {
//
}
visitScriptNode(node) {
//
}
visitGrammarNode(node) {
this.startSym = node.startSym;
this.nts = node.nts;
this.env = node.env;
}
visitTokenProNode(node) {
//
}
visitNonterminalNode(node) {
//
}
visitProductionNode(node) {
let nt = node.env.getSym(node.ntVarName);
let body = new Array();
for (let { symToken } of node.rawBody) {
if (symToken.tag == Tag.STRING) {
body.push(new definition_1.SymbolWrapper(symToken.value));
}
else if (symToken.tag == Tag.ID || symToken.tag == Tag.NIL) {
let proto = node.env.getSym(symToken.value);
shim_1.assert(proto != undefined, `Undeclared identifier "${symToken.value}" at ` + pos(symToken));
body.push(new definition_1.SymbolWrapper(proto));
}
else {
throw new Error(`Unknow token ${symToken}!`);
}
}
let preAction = node.preAction == undefined ? undefined : eval(node.preAction.value);
let postAction = node.postAction == undefined ? undefined : eval(node.postAction.value);
shim_1.assert(node.pid != undefined);
let prod = new definition_1.Production(node.pid, nt, body, preAction, postAction);
node.env.registerProd(prod, node.varName); //对象到变量名的反向索引(用于代码生成)
nt.prods.push(prod); //追加到所属的非终结符
this.prods.push(prod);
}
visitPrecAssocNode(node) {
for (let { symbol, prec, leftAssoc } of node.children) {
if (symbol.tag == Tag.STRING) {
this.traits.set(symbol.value, new definition_1.SymbolTrait(prec, leftAssoc));
}
else if (symbol.tag == Tag.ID) {
this.traits.set(this.env.getSym(symbol.value), new definition_1.SymbolTrait(prec, leftAssoc));
}
}
}
}
exports.EvalVisitor = EvalVisitor;
const DEFINITION_PACKAGE = "@parser-generator/definition";
// const LL_PARSER_PACKAGE = "@parser-generator/core";
// const LR_PARSER_PACKAGE = "@parser-generator/core";
const LR_PARSING_TABLE_VAR = "table";
const FIRST_SET_VAR = "first";
const FOLLOW_SET_VAR = "follow";
const SYMBOL_TRAITS_VAR = "traits";
//ILexer, IToken, ASTElement
class TSCodegenVisitor {
constructor(context) {
this._code = "";
this.context = context;
this.emit(`import { ${definition_1.NonTerminal.name}, ${definition_1.TokenPro.name}, ${definition_1.Production.name}, ${definition_1.SymbolWrapper.name}, SSymbol ,${definition_1.SymbolTrait.name}, NIL, Terminal, EOF `);
if (context.parser == "LL") {
this.emit("}");
}
else {
this.emit(`,${definition_1.ParsingTable.name}, ${definition_1.Goto.name}, ${definition_1.Shift.name}, ${definition_1.Accept.name}, ${definition_1.Reduce.name} }`);
}
this.emitln(` from "${DEFINITION_PACKAGE}";`);
}
emit(code) {
this._code += code;
}
emitln(code) {
this.emit(code + "\n");
}
get code() {
return this._code;
}
visitProgramNode(node) {
// let env = node.grammarNode.env;
// let { firstTable, followTable } = this.context;
//parser
if (this.context.parser == "LL") {
// assert(firstTable != undefined && followTable != undefined);
// this.emitln(`import {${LLParser.name}} from "${LL_PARSER_PACKAGE}";`);
// this.emitln(`export let parser = new ${LLParser.name}(${env.getSymVarName(node.grammarNode.startSym)},${FIRST_SET_VAR},${FOLLOW_SET_VAR});`);
this.emitln(`export { ${FIRST_SET_VAR},${FOLLOW_SET_VAR} }`);
}
else {
// this.emitln(`import {${LRParser.name}} from "${LR_PARSER_PACKAGE}";`);
// this.emitln(`export let parser = new ${LRParser.name}(${LR_PARSING_TABLE_VAR});`);
this.emitln(`export default ${LR_PARSING_TABLE_VAR};`);
}
}
visitScriptNode(node) {
this.emit(node.script + "\n");
}
visitGrammarNode(node) {
for (let key in node.prodNodeGroups) {
let group = node.prodNodeGroups[key];
let pnames = new Array();
for (let pNode of group) {
pnames.push(pNode.varName);
}
this.emitln(`${key}.prods=[${pnames.join(",")}];`);
}
let env = node.env;
if (this.context.parser == "LL") {
//first table
let { firstTable, followTable } = this.context;
shim_1.assert(firstTable != undefined && followTable != undefined, "First-table and follow-table is required for lr-parser code generator");
this.emitln("/* first set */");
this.emitln(`let ${FIRST_SET_VAR} = new Map<${definition_1.NonTerminal.name},Map<Terminal,${definition_1.Production.name}>>();`);
for (let [nt, fset] of firstTable) {
let stmt = `${FIRST_SET_VAR}.set(${env.getSymVarName(nt)},`;
stmt += `new Map<Terminal,${definition_1.Production.name}>([`;
for (let [sym, prod] of fset) {
let k = typeof sym === "string" ? `"${sym}"` : env.getSymVarName(sym);
let v = env.getProdVarName(prod);
stmt += `[${k},${v}],`;
}
stmt = stmt.replace(/,$/, "");
stmt += "])";
stmt += ");";
this.emitln(stmt);
}
//follow table
this.emitln("/* follow set */");
this.emitln(`let ${FOLLOW_SET_VAR} = new Map<${definition_1.NonTerminal.name},Set<Terminal>>();`);
for (let [nt, fset] of followTable) {
let stmt = `${FOLLOW_SET_VAR}.set(${env.getSymVarName(nt)},`;
stmt += "new Set<Terminal>([";
for (let a of fset) {
if (typeof a === "string")
stmt += `"${a}",`;
else
stmt += env.getSymVarName(a) + ",";
}
stmt = stmt.replace(/,$/, "");
stmt += "])";
stmt += ");";
this.emitln(stmt);
}
}
else {
//lr table
let { lrTable: lrParsingTable } = this.context;
shim_1.assert(lrParsingTable != undefined, "LR parsing table is required for lr-parser code generator");
this.emitln(`let ${LR_PARSING_TABLE_VAR} = new ${definition_1.ParsingTable.name}();`);
for (let [stateId, ops] of lrParsingTable.table) {
for (let [sym, op] of ops) {
if (typeof sym === "string") {
this.emit(`${LR_PARSING_TABLE_VAR}.put(${stateId},"${sym}",`);
}
else {
this.emit(`${LR_PARSING_TABLE_VAR}.put(${stateId},${env.getSymVarName(sym)},`);
}
if (op.isShift()) {
this.emit(`new ${definition_1.Shift.name}(${op.nextStateId})`);
}
else if (op.isReduce()) {
this.emit(`new ${definition_1.Reduce.name}(${env.getProdVarName(op.prod)})`);
}
else if (op.isGoto()) {
this.emit(`new ${definition_1.Goto.name}(${op.nextStateId})`);
}
else if (op.isAccept()) {
this.emit(`new ${definition_1.Accept.name}()`);
}
this.emitln(");");
}
}
}
}
visitTokenProNode(node) {
this.emitln(`export let ${node.varName} = new ${definition_1.TokenPro.name}("${node.varName}");`);
}
visitNonterminalNode(node) {
this.emitln(`let ${node.varName} = new ${definition_1.NonTerminal.name}("${node.varName}");`);
}
visitProductionNode(node) {
this.emitln(`/* ${node.env.getProd(node.varName).toString()} */`);
let code = `let ${node.varName} = new ${definition_1.Production.name}(${node.pid},${node.ntVarName}`;
code += ",[";
for (let { symToken: symbol, action } of node.rawBody) {
let symbolStr = symbol.tag == Tag.STRING ? `"${symbol.value}"` : symbol.value;
let actionStr = (action == undefined ? "" : "," + action.value);
code += `\n\tnew ${definition_1.SymbolWrapper.name}(${symbolStr}${actionStr}),`;
}
code = code.replace(/,$/, "");
code += "]";
if (node.preAction != undefined)
code += "," + node.preAction.value;
else
code += ",undefined";
if (node.postAction != undefined)
code += "," + node.postAction.value;
else
code += ",undefined";
code += ");";
this.emit(code);
this.emit("\n");
}
visitPrecAssocNode(node) {
// this.emit(`let ${SYMBOL_TRAITS_VAR} = new Map<SSymbol, ${SymbolTrait.name}>();`);
// for (let item of node.children) {
// this.emit("\n");
// let symStr = item.symbol.tag == Tag.STRING ? `"${item.symbol.value}"` : item.symbol.value;//区别字符串字面量与变量名
// this.emit(`${SYMBOL_TRAITS_VAR}.set(${symStr},new ${SymbolTrait.name}(${item.prec},${item.leftAssoc ? "true" : "false"}));`);
// }
// this.emit("\n");
}
}
exports.TSCodegenVisitor = TSCodegenVisitor;
function genParser(rawGrammar, { parser = "LR1", lang = "TS" }) {
let pragram = parse(new GrammarLexer(rawGrammar));
//1. 拿到grammar对象
let evalVisitor = new EvalVisitor({ parser });
pragram.accpet(evalVisitor);
let grammar = evalVisitor.grammar;
//2. 拿到分析表
let firstTable, followTable, lrTable, lrAutomata;
//2.1 first
let fir = new core_1.FirstCalculator(grammar);
firstTable = fir.getFirstTable();
//2.2 follow
let fol = new core_1.FollowCalculator(grammar, fir);
followTable = fol.getFollowTable();
if (parser == "LL") {
//for nothing
}
else {
//2.3 automata & parsing table
shim_1.assert(grammar instanceof definition_1.AugmentedGrammar);
if (parser == "SLR") {
lrAutomata = core_1.getSLRAutomata(grammar);
lrTable = core_1.getSLRParsingTable(grammar, lrAutomata, (i) => fol.getFollowSet(i));
}
else if (parser == "LR1") {
lrAutomata = core_1.getLR1Automata(grammar, (i) => fir.getFirstSet(i));
lrTable = core_1.getLR1ParsingTable(grammar, lrAutomata);
}
else if (parser == "LALR") {
throw new Error("LALR parsing table is unimplemented!");
}
}
//3. 生成代码
let codegenVisitor;
if (lang == "TS") {
codegenVisitor = new TSCodegenVisitor({ parser, firstTable, followTable, lrTable: lrTable });
}
else {
throw new Error("JS code generator is unimplemented");
}
pragram.accpet(codegenVisitor);
let code = codegenVisitor.code;
//4. 返回结果
return { code, grammar, firstTable, followTable, lrTable, lrAutomata };
}
exports.genParser = genParser;
//# sourceMappingURL=interp.js.map