199 lines
5.6 KiB
TypeScript
199 lines
5.6 KiB
TypeScript
import { forEach, map } from "../utils/utils"
|
|
import {
|
|
RepetitionMandatory,
|
|
Option,
|
|
RepetitionMandatoryWithSeparator,
|
|
RepetitionWithSeparator,
|
|
Terminal,
|
|
NonTerminal,
|
|
Alternation,
|
|
Alternative,
|
|
Repetition
|
|
} from "../parse/grammar/gast/gast_public"
|
|
import { IProduction, TokenType, Rule } from "../../api"
|
|
|
|
/**
|
|
* Missing features
|
|
* 1. Rule arguments
|
|
* 2. Gates
|
|
* 3. embedded actions
|
|
*/
|
|
|
|
const NL = "\n"
|
|
|
|
export function genUmdModule(options: { name: string; rules: Rule[] }): string {
|
|
return `
|
|
(function (root, factory) {
|
|
if (typeof define === 'function' && define.amd) {
|
|
// AMD. Register as an anonymous module.
|
|
define(['chevrotain'], factory);
|
|
} else if (typeof module === 'object' && module.exports) {
|
|
// Node. Does not work with strict CommonJS, but
|
|
// only CommonJS-like environments that support module.exports,
|
|
// like Node.
|
|
module.exports = factory(require('chevrotain'));
|
|
} else {
|
|
// Browser globals (root is window)
|
|
root.returnExports = factory(root.b);
|
|
}
|
|
}(typeof self !== 'undefined' ? self : this, function (chevrotain) {
|
|
|
|
${genClass(options)}
|
|
|
|
return {
|
|
${options.name}: ${options.name}
|
|
}
|
|
}));
|
|
`
|
|
}
|
|
|
|
export function genWrapperFunction(options: {
|
|
name: string
|
|
rules: Rule[]
|
|
}): string {
|
|
return `
|
|
${genClass(options)}
|
|
return new ${options.name}(tokenVocabulary, config)
|
|
`
|
|
}
|
|
|
|
export function genClass(options: { name: string; rules: Rule[] }): string {
|
|
// TODO: how to pass the token vocabulary? Constructor? other?
|
|
let result = `
|
|
function ${options.name}(tokenVocabulary, config) {
|
|
// invoke super constructor
|
|
// No support for embedded actions currently, so we can 'hardcode'
|
|
// The use of CstParser.
|
|
chevrotain.CstParser.call(this, tokenVocabulary, config)
|
|
|
|
const $ = this
|
|
|
|
${genAllRules(options.rules)}
|
|
|
|
// very important to call this after all the rules have been defined.
|
|
// otherwise the parser may not work correctly as it will lack information
|
|
// derived during the self analysis phase.
|
|
this.performSelfAnalysis(this)
|
|
}
|
|
|
|
// inheritance as implemented in javascript in the previous decade... :(
|
|
${options.name}.prototype = Object.create(chevrotain.CstParser.prototype)
|
|
${options.name}.prototype.constructor = ${options.name}
|
|
`
|
|
|
|
return result
|
|
}
|
|
|
|
export function genAllRules(rules: Rule[]): string {
|
|
let rulesText = map(rules, (currRule) => {
|
|
return genRule(currRule, 1)
|
|
})
|
|
|
|
return rulesText.join("\n")
|
|
}
|
|
|
|
export function genRule(prod: Rule, n: number): string {
|
|
let result = indent(n, `$.RULE("${prod.name}", function() {`) + NL
|
|
result += genDefinition(prod.definition, n + 1)
|
|
result += indent(n + 1, `})`) + NL
|
|
return result
|
|
}
|
|
|
|
export function genTerminal(prod: Terminal, n: number): string {
|
|
const name = prod.terminalType.name
|
|
// TODO: potential performance optimization, avoid tokenMap Dictionary access
|
|
return indent(n, `$.CONSUME${prod.idx}(this.tokensMap.${name})` + NL)
|
|
}
|
|
|
|
export function genNonTerminal(prod: NonTerminal, n: number): string {
|
|
return indent(n, `$.SUBRULE${prod.idx}($.${prod.nonTerminalName})` + NL)
|
|
}
|
|
|
|
export function genAlternation(prod: Alternation, n: number): string {
|
|
let result = indent(n, `$.OR${prod.idx}([`) + NL
|
|
const alts = map(prod.definition, (altDef) => genSingleAlt(altDef, n + 1))
|
|
result += alts.join("," + NL)
|
|
result += NL + indent(n, `])` + NL)
|
|
return result
|
|
}
|
|
|
|
export function genSingleAlt(prod: Alternative, n: number): string {
|
|
let result = indent(n, `{`) + NL
|
|
|
|
result += indent(n + 1, "ALT: function() {") + NL
|
|
result += genDefinition(prod.definition, n + 1)
|
|
result += indent(n + 1, `}`) + NL
|
|
result += indent(n, `}`)
|
|
|
|
return result
|
|
}
|
|
|
|
function genProd(prod: IProduction, n: number): string {
|
|
/* istanbul ignore else */
|
|
if (prod instanceof NonTerminal) {
|
|
return genNonTerminal(prod, n)
|
|
} else if (prod instanceof Option) {
|
|
return genDSLRule("OPTION", prod, n)
|
|
} else if (prod instanceof RepetitionMandatory) {
|
|
return genDSLRule("AT_LEAST_ONE", prod, n)
|
|
} else if (prod instanceof RepetitionMandatoryWithSeparator) {
|
|
return genDSLRule("AT_LEAST_ONE_SEP", prod, n)
|
|
} else if (prod instanceof RepetitionWithSeparator) {
|
|
return genDSLRule("MANY_SEP", prod, n)
|
|
} else if (prod instanceof Repetition) {
|
|
return genDSLRule("MANY", prod, n)
|
|
} else if (prod instanceof Alternation) {
|
|
return genAlternation(prod, n)
|
|
} else if (prod instanceof Terminal) {
|
|
return genTerminal(prod, n)
|
|
} else if (prod instanceof Alternative) {
|
|
return genDefinition(prod.definition, n)
|
|
} else {
|
|
throw Error("non exhaustive match")
|
|
}
|
|
}
|
|
|
|
function genDSLRule(
|
|
dslName,
|
|
prod: {
|
|
definition: IProduction[]
|
|
idx: number
|
|
separator?: TokenType
|
|
},
|
|
n: number
|
|
): string {
|
|
let result = indent(n, `$.${dslName + prod.idx}(`)
|
|
|
|
if (prod.separator) {
|
|
result += "{" + NL
|
|
result +=
|
|
indent(n + 1, `SEP: this.tokensMap.${prod.separator.name}`) + "," + NL
|
|
result += `DEF: ${genDefFunction(prod.definition, n + 2)}` + NL
|
|
result += indent(n, "}") + NL
|
|
} else {
|
|
result += genDefFunction(prod.definition, n + 1)
|
|
}
|
|
|
|
result += indent(n, `)`) + NL
|
|
return result
|
|
}
|
|
|
|
function genDefFunction(definition: IProduction[], n: number): string {
|
|
let def = "function() {" + NL
|
|
def += genDefinition(definition, n)
|
|
def += indent(n, `}`) + NL
|
|
return def
|
|
}
|
|
|
|
function genDefinition(def: IProduction[], n: number): string {
|
|
let result = ""
|
|
forEach(def, (prod) => {
|
|
result += genProd(prod, n + 1)
|
|
})
|
|
return result
|
|
}
|
|
|
|
function indent(howMuch: number, text: string): string {
|
|
const spaces = Array(howMuch * 4 + 1).join(" ")
|
|
return spaces + text
|
|
}
|