Skip to main content
Module

x/ohm_js/src/Builder.js

A library and language for building parsers, interpreters, compilers, etc.
Latest
File
import {Grammar} from './Grammar.js';import {GrammarDecl} from './GrammarDecl.js';import * as pexprs from './pexprs.js';
// --------------------------------------------------------------------// Private stuff// --------------------------------------------------------------------
export class Builder { constructor() { this.currentDecl = null; this.currentRuleName = null; }
newGrammar(name) { return new GrammarDecl(name); }
grammar(metaInfo, name, superGrammar, defaultStartRule, rules) { const gDecl = new GrammarDecl(name); if (superGrammar) { // `superGrammar` may be a recipe (i.e. an Array), or an actual grammar instance. gDecl.withSuperGrammar( superGrammar instanceof Grammar ? superGrammar : this.fromRecipe(superGrammar), ); } if (defaultStartRule) { gDecl.withDefaultStartRule(defaultStartRule); } if (metaInfo && metaInfo.source) { gDecl.withSource(metaInfo.source); }
this.currentDecl = gDecl; Object.keys(rules).forEach(ruleName => { this.currentRuleName = ruleName; const ruleRecipe = rules[ruleName];
const action = ruleRecipe[0]; // define/extend/override const metaInfo = ruleRecipe[1]; const description = ruleRecipe[2]; const formals = ruleRecipe[3]; const body = this.fromRecipe(ruleRecipe[4]);
let source; if (gDecl.source && metaInfo && metaInfo.sourceInterval) { source = gDecl.source.subInterval( metaInfo.sourceInterval[0], metaInfo.sourceInterval[1] - metaInfo.sourceInterval[0], ); } gDecl[action](ruleName, formals, body, description, source); }); this.currentRuleName = this.currentDecl = null; return gDecl.build(); }
terminal(x) { return new pexprs.Terminal(x); }
range(from, to) { return new pexprs.Range(from, to); }
param(index) { return new pexprs.Param(index); }
alt(...termArgs) { let terms = []; for (let arg of termArgs) { if (!(arg instanceof pexprs.PExpr)) { arg = this.fromRecipe(arg); } if (arg instanceof pexprs.Alt) { terms = terms.concat(arg.terms); } else { terms.push(arg); } } return terms.length === 1 ? terms[0] : new pexprs.Alt(terms); }
seq(...factorArgs) { let factors = []; for (let arg of factorArgs) { if (!(arg instanceof pexprs.PExpr)) { arg = this.fromRecipe(arg); } if (arg instanceof pexprs.Seq) { factors = factors.concat(arg.factors); } else { factors.push(arg); } } return factors.length === 1 ? factors[0] : new pexprs.Seq(factors); }
star(expr) { if (!(expr instanceof pexprs.PExpr)) { expr = this.fromRecipe(expr); } return new pexprs.Star(expr); }
plus(expr) { if (!(expr instanceof pexprs.PExpr)) { expr = this.fromRecipe(expr); } return new pexprs.Plus(expr); }
opt(expr) { if (!(expr instanceof pexprs.PExpr)) { expr = this.fromRecipe(expr); } return new pexprs.Opt(expr); }
not(expr) { if (!(expr instanceof pexprs.PExpr)) { expr = this.fromRecipe(expr); } return new pexprs.Not(expr); }
lookahead(expr) { if (!(expr instanceof pexprs.PExpr)) { expr = this.fromRecipe(expr); } return new pexprs.Lookahead(expr); }
lex(expr) { if (!(expr instanceof pexprs.PExpr)) { expr = this.fromRecipe(expr); } return new pexprs.Lex(expr); }
app(ruleName, optParams) { if (optParams && optParams.length > 0) { optParams = optParams.map(function(param) { return param instanceof pexprs.PExpr ? param : this.fromRecipe(param); }, this); } return new pexprs.Apply(ruleName, optParams); }
// Note that unlike other methods in this class, this method cannot be used as a // convenience constructor. It only works with recipes, because it relies on // `this.currentDecl` and `this.currentRuleName` being set. splice(beforeTerms, afterTerms) { return new pexprs.Splice( this.currentDecl.superGrammar, this.currentRuleName, beforeTerms.map(term => this.fromRecipe(term)), afterTerms.map(term => this.fromRecipe(term)), ); }
fromRecipe(recipe) { // the meta-info of 'grammar' is processed in Builder.grammar const args = recipe[0] === 'grammar' ? recipe.slice(1) : recipe.slice(2); const result = this[recipe[0]](...args);
const metaInfo = recipe[1]; if (metaInfo) { if (metaInfo.sourceInterval && this.currentDecl) { result.withSource(this.currentDecl.sourceInterval(...metaInfo.sourceInterval)); } } return result; }}