namespace tp { template bool UnifiedGrammarTranslator::strEq(const char* a, const char* b) { while (*a && *b) { if (*a != *b) return false; a++; b++; } return *a == *b; } template String UnifiedGrammarTranslator::lexeme(const tAlphabetType* text, const ContextFreeCompiler::SymbolVal& sym) { char buf[512]; ualni n = sym.len < 511 ? sym.len : 511; for (ualni i = 0; i < n; i++) buf[i] = (char) text[sym.start + i]; buf[n] = 0; return String(buf); } template char UnifiedGrammarTranslator::decodeEscape(char e) { switch (e) { case 'b': return '\b'; case 'f': return '\f'; case 'n': return '\n'; case 'r': return '\r'; case 't': return '\t'; default: return e; } } template String UnifiedGrammarTranslator::stripQuotes(const String& text) { ualni len = text.size() - 2; char* buf = new char[len + 1]; for (ualni i = 0; i < len; i++) buf[i] = text.read()[i + 1]; buf[len] = 0; String out(buf); delete[] buf; return out; } template String UnifiedGrammarTranslator::nodeName(AstNode node) { return mGrammarParser->mAstNames.get(node->symbol); } template String UnifiedGrammarTranslator::regexNodeName(AstNode node) { return mRegexParser->mAstNames.get(node->symbol); } template void UnifiedGrammarTranslator::translate( AstNode root, const tAlphabetType* text, ContextFreeGrammar& cf, UserRegexGrammar& rg, Map& terminals, Buffer& actions, GrammarParser& grammarParser, RegexParser& regexParser ) { mGrammarParser = &grammarParser; mRegexParser = ®exParser; BuildCtx ctx; ctx.text = text; ctx.cf = &cf; ctx.rg = &rg; ctx.terminals = &terminals; ctx.actions = &actions; ctx.nextToken = 1; walkGrammar(root, ctx); cf.setStart(ctx.start); } template void UnifiedGrammarTranslator::walkGrammar(AstNode node, BuildCtx& ctx) { walkBody(node->leafs[2], ctx); // whitespace owns token 0 (the ignore token), compiled before productions if (ctx.hasWhitespace) { const UserRegexNode* wsNode = compileRegexString(ctx.whitespace, ctx); if (wsNode) ctx.rg->addRule(wsNode, 0); } for (auto prod : ctx.productionNodes) { buildProduction(prod.data(), ctx); } } template void UnifiedGrammarTranslator::walkBody(AstNode node, BuildCtx& ctx) { if (node->leafs.size() == 0) return; walkItem(node->leafs[0], ctx); walkBody(node->leafs[1], ctx); } template void UnifiedGrammarTranslator::walkItem(AstNode node, BuildCtx& ctx) { AstNode child = node->leafs[0]; String name = nodeName(child); if (strEq(name.read(), "Directive")) { walkDirective(child, ctx); } else { collectProduction(child, ctx); } } template void UnifiedGrammarTranslator::walkDirective(AstNode node, BuildCtx& ctx) { String word = lexeme(ctx.text, node->leafs[1]->symbol); AstNode args = node->leafs[2]; Buffer argTexts; collectArgTexts(args, ctx, argTexts); if (strEq(word.read(), "start")) { if (argTexts.size() > 0) { ctx.start = argTexts[0]; ctx.hasStart = true; } return; } if (strEq(word.read(), "whitespace")) { if (argTexts.size() > 0) { ctx.whitespace = argTexts[0]; ctx.hasWhitespace = true; } return; } alni assoc = -1; if (strEq(word.read(), "left")) assoc = 0; else if (strEq(word.read(), "right")) assoc = 1; for (auto sym : argTexts) { ctx.cf->setTerminalPrecedence(sym.data(), ctx.nextPrecedence, assoc); } ctx.nextPrecedence++; } template void UnifiedGrammarTranslator::collectArgTexts(AstNode args, BuildCtx& ctx, Buffer& out) { if (args->leafs.size() == 0) return; AstNode arg = args->leafs[0]; String text = lexeme(ctx.text, arg->leafs[0]->symbol); if (text.read()[0] == '\'' || text.read()[0] == '"') { out.append(stripQuotes(text)); } else { out.append(text); } collectArgTexts(args->leafs[1], ctx, out); } template void UnifiedGrammarTranslator::collectProduction(AstNode node, BuildCtx& ctx) { String lhs = lexeme(ctx.text, node->leafs[0]->symbol); if (!ctx.hasStart) { ctx.start = lhs; ctx.hasStart = true; } AstNode alts = node->leafs[2]; Buffer alternatives; collectAlts(alts, alternatives); // named terminal: single regex symbol, no action, single alternative if (alternatives.size() == 1) { AstNode altNode = alternatives[0]; AstNode symbols = altNode->leafs[0]; AstNode optAction = altNode->leafs[1]; Buffer symbolNodes; collectSymbolNodes(symbols, symbolNodes); if (symbolNodes.size() == 1 && isRegexSymbol(symbolNodes[0], ctx) && optAction->leafs.size() == 0) { ctx.namedTerminals.put(lhs, true); } } ctx.productionNodes.append(node); } template bool UnifiedGrammarTranslator::isRegexSymbol(AstNode sym, BuildCtx& ctx) { AstNode leaf = sym->leafs[0]; String text = lexeme(ctx.text, leaf->symbol); return text.read()[0] == '"'; } template String UnifiedGrammarTranslator::altAction(AstNode altNode, BuildCtx& ctx) { AstNode optAction = altNode->leafs[1]; if (optAction->leafs.size() == 0) return String(""); // OptAction := Action ('[name]') String text = lexeme(ctx.text, optAction->leafs[0]->symbol); return stripQuotes(text); // strips the surrounding [ ] } template void UnifiedGrammarTranslator::buildProduction(AstNode node, BuildCtx& ctx) { String lhs = lexeme(ctx.text, node->leafs[0]->symbol); AstNode alts = node->leafs[2]; Buffer alternatives; collectAlts(alts, alternatives); for (auto alt : alternatives) { AstNode altNode = alt.data(); AstNode symbols = altNode->leafs[0]; AstNode optAction = altNode->leafs[1]; Buffer symbolNodes; collectSymbolNodes(symbols, symbolNodes); // named terminal if (symbolNodes.size() == 1 && isRegexSymbol(symbolNodes[0], ctx) && optAction->leafs.size() == 0 && alternatives.size() == 1) { String regexText = lexeme(ctx.text, symbolNodes[0]->leafs[0]->symbol); String inner = stripQuotes(regexText); const UserRegexNode* rgNode = compileRegexString(inner, ctx); if (rgNode) { ctx.rg->addRule(rgNode, ctx.nextToken); ctx.terminals->put(lhs, ctx.nextToken); ctx.nextToken++; } continue; } // production auto rule = ctx.cf->addRule(lhs); if (ctx.actions) ctx.actions->append(altAction(altNode, ctx)); // empty alternative -> epsilon production if (symbolNodes.size() == 0) { rule->addArg(ContextFreeGrammar::Arg(String(""), false, true)); continue; } for (auto sym : symbolNodes) { AstNode leaf = sym.data()->leafs[0]; String text = lexeme(ctx.text, leaf->symbol); if (text.read()[0] == '\'') { String lit = stripQuotes(text); if (!ctx.terminals->presents(lit)) { ctx.terminals->put(lit, ctx.nextToken); ctx.rg->addRule(charSeq(lit, ctx), ctx.nextToken); ctx.nextToken++; } rule->addArg(ContextFreeGrammar::Arg(lit, true)); } else if (text.read()[0] == '"') { String inner = stripQuotes(text); const UserRegexNode* rgNode = compileRegexString(inner, ctx); if (rgNode) { ctx.rg->addRule(rgNode, ctx.nextToken); ctx.terminals->put(inner, ctx.nextToken); ctx.nextToken++; } rule->addArg(ContextFreeGrammar::Arg(inner, true)); } else if (ctx.namedTerminals.presents(text)) { rule->addArg(ContextFreeGrammar::Arg(text, true)); } else { rule->addArg(ContextFreeGrammar::Arg(text, false)); } } } } template void UnifiedGrammarTranslator::collectAlts(AstNode alts, Buffer& out) { out.append(alts->leafs[0]); collectAltsRest(alts->leafs[1], out); } template void UnifiedGrammarTranslator::collectAltsRest(AstNode rest, Buffer& out) { if (rest->leafs.size() == 0) return; out.append(rest->leafs[1]); collectAltsRest(rest->leafs[2], out); } template void UnifiedGrammarTranslator::collectSymbolNodes(AstNode symbols, Buffer& out) { if (symbols->leafs.size() == 0) return; out.append(symbols->leafs[0]); collectSymbolNodes(symbols->leafs[1], out); } template auto UnifiedGrammarTranslator::compileRegexString(const String& regex, BuildCtx& ctx) -> const UserRegexNode* { mRegexText = regex; auto parsed = mRegexParser->parse(regex.read(), regex.size()); if (!parsed.accepted || !parsed.ast) { return nullptr; } return compileAlt(parsed.ast, ctx); } template String UnifiedGrammarTranslator::rLexeme(const ContextFreeCompiler::SymbolVal& sym) { char buf[256]; ualni n = sym.len < 255 ? sym.len : 255; for (ualni i = 0; i < n; i++) buf[i] = (char) mRegexText.read()[sym.start + i]; buf[n] = 0; return String(buf); } template auto UnifiedGrammarTranslator::compileAlt(AstNode alt, BuildCtx& ctx) -> const UserRegexNode* { const UserRegexNode* node = compileConcat(alt->leafs[0], ctx); return compileAltRest(alt->leafs[1], node, ctx); } template auto UnifiedGrammarTranslator::compileAltRest(AstNode rest, const UserRegexNode* left, BuildCtx& ctx) -> const UserRegexNode* { if (rest->leafs.size() == 0) return left; const UserRegexNode* right = compileConcat(rest->leafs[1], ctx); left = ctx.rg->alt(left, right); return compileAltRest(rest->leafs[2], left, ctx); } template auto UnifiedGrammarTranslator::compileConcat(AstNode concat, BuildCtx& ctx) -> const UserRegexNode* { const UserRegexNode* node = compileRepeat(concat->leafs[0], ctx); return compileConcatRest(concat->leafs[1], node, ctx); } template auto UnifiedGrammarTranslator::compileConcatRest(AstNode rest, const UserRegexNode* left, BuildCtx& ctx) -> const UserRegexNode* { if (rest->leafs.size() == 0) return left; const UserRegexNode* right = compileRepeat(rest->leafs[0], ctx); left = ctx.rg->seq({ left, right }); return compileConcatRest(rest->leafs[1], left, ctx); } template auto UnifiedGrammarTranslator::compileRepeat(AstNode repeat, BuildCtx& ctx) -> const UserRegexNode* { const UserRegexNode* node = compileAtom(repeat->leafs[0], ctx); AstNode suffix = repeat->leafs[1]; if (suffix->leafs.size() == 0) return node; String s = rLexeme(suffix->leafs[0]->symbol); if (s.read()[0] == '*') return ctx.rg->rep(node); if (s.read()[0] == '+') return ctx.rg->rep(node, true); if (s.read()[0] == '?') return ctx.rg->may(node); return node; } template auto UnifiedGrammarTranslator::compileAtom(AstNode atom, BuildCtx& ctx) -> const UserRegexNode* { AstNode child = atom->leafs[0]; if (child->leafs.size() > 0) { String name = regexNodeName(child); if (strEq(name.read(), "Group")) { return compileAlt(child->leafs[1], ctx); } if (strEq(name.read(), "Class")) { return compileClass(child, ctx); } } // terminal leaf (Char, Escape, String, Minus, Caret) String text = rLexeme(child->symbol); if (text.read()[0] == '\\') { return ctx.rg->val((tAlphabetType) decodeEscape(text.read()[1])); } if (text.read()[0] == '"') { String inner = stripQuotes(text); return charSeq(inner, ctx); } return charSeq(text, ctx); } template auto UnifiedGrammarTranslator::charSeq(const String& text, BuildCtx& ctx) -> const UserRegexNode* { const UserRegexNode* node = nullptr; for (const char* p = text.read(); *p; p++) { const UserRegexNode* ch = ctx.rg->val((tAlphabetType) *p); node = node ? ctx.rg->seq({ node, ch }) : ch; } if (!node) node = ctx.rg->val((tAlphabetType) 0); return node; } template auto UnifiedGrammarTranslator::compileClass(AstNode cls, BuildCtx& ctx) -> const UserRegexNode* { bool exclude = cls->leafs[1]->leafs.size() > 0; // CaretOpt AstNode items = cls->leafs[2]; Buffer> ranges; collectClassItems(items, ranges, ctx); return ctx.rg->ranges(ranges, exclude); } template char UnifiedGrammarTranslator::classCharValue(AstNode classChar, BuildCtx& ctx) { String s = rLexeme(classChar->leafs[0]->symbol); if (s.read()[0] == '\\') return decodeEscape(s.read()[1]); return s.read()[0]; } template void UnifiedGrammarTranslator::collectClassItems(AstNode items, Buffer>& out, BuildCtx& ctx) { if (items->leafs.size() == 0) return; AstNode item = items->leafs[0]; if (item->leafs.size() == 3) { char lo = classCharValue(item->leafs[0], ctx); char hi = classCharValue(item->leafs[2], ctx); out.append(IterRange((tAlphabetType) lo, (tAlphabetType) (hi + 1))); } else { char c = classCharValue(item->leafs[0], ctx); out.append(IterRange((tAlphabetType) c, (tAlphabetType) (c + 1))); } collectClassItems(items->leafs[1], out, ctx); } }