UNPKG

chevrotain

Version:

Chevrotain is a high performance fault tolerant javascript parsing DSL for building recursive decent parsers

179 lines (155 loc) 5.43 kB
import { compact, contains, forEach, isArray, isEmpty, isFunction, isUndefined, keys, map } from "../../utils/utils" import { defineNameProp, functionName } from "../../lang/lang_extensions" import { ICstVisitor } from "../../../api" export function defaultVisit<IN, OUT>(ctx: any, param: IN): OUT { let childrenNames = keys(ctx) let childrenNamesLength = childrenNames.length for (let i = 0; i < childrenNamesLength; i++) { let currChildName = childrenNames[i] let currChildArray = ctx[currChildName] let currChildArrayLength = currChildArray.length for (let j = 0; j < currChildArrayLength; j++) { let currChild: any = currChildArray[j] // distinction between Tokens Children and CstNode children if (currChild.tokenTypeIdx === undefined) { this[currChild.name](currChild.children, param) } } } // defaultVisit does not support generic out param return undefined } export function createBaseSemanticVisitorConstructor( grammarName: string, ruleNames: string[] ): { new (...args: any[]): ICstVisitor<any, any> } { let derivedConstructor: any = function () {} // can be overwritten according to: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/ // name?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fname defineNameProp(derivedConstructor, grammarName + "BaseSemantics") let semanticProto = { visit: function (cstNode, param) { // enables writing more concise visitor methods when CstNode has only a single child if (isArray(cstNode)) { // A CST Node's children dictionary can never have empty arrays as values // If a key is defined there will be at least one element in the corresponding value array. cstNode = cstNode[0] } // enables passing optional CstNodes concisely. if (isUndefined(cstNode)) { return undefined } return this[cstNode.name](cstNode.children, param) }, validateVisitor: function () { let semanticDefinitionErrors = validateVisitor(this, ruleNames) if (!isEmpty(semanticDefinitionErrors)) { let errorMessages = map( semanticDefinitionErrors, (currDefError) => currDefError.msg ) throw Error( `Errors Detected in CST Visitor <${functionName( this.constructor )}>:\n\t` + `${errorMessages.join("\n\n").replace(/\n/g, "\n\t")}` ) } } } derivedConstructor.prototype = semanticProto derivedConstructor.prototype.constructor = derivedConstructor derivedConstructor._RULE_NAMES = ruleNames return derivedConstructor } export function createBaseVisitorConstructorWithDefaults( grammarName: string, ruleNames: string[], baseConstructor: Function ): { new (...args: any[]): ICstVisitor<any, any> } { let derivedConstructor: any = function () {} // can be overwritten according to: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/ // name?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fname defineNameProp(derivedConstructor, grammarName + "BaseSemanticsWithDefaults") let withDefaultsProto = Object.create(baseConstructor.prototype) forEach(ruleNames, (ruleName) => { withDefaultsProto[ruleName] = defaultVisit }) derivedConstructor.prototype = withDefaultsProto derivedConstructor.prototype.constructor = derivedConstructor return derivedConstructor } export enum CstVisitorDefinitionError { REDUNDANT_METHOD, MISSING_METHOD } export interface IVisitorDefinitionError { msg: string type: CstVisitorDefinitionError methodName: string } export function validateVisitor( visitorInstance: Function, ruleNames: string[] ): IVisitorDefinitionError[] { let missingErrors = validateMissingCstMethods(visitorInstance, ruleNames) let redundantErrors = validateRedundantMethods(visitorInstance, ruleNames) return missingErrors.concat(redundantErrors) } export function validateMissingCstMethods( visitorInstance: Function, ruleNames: string[] ): IVisitorDefinitionError[] { let errors: IVisitorDefinitionError[] = map(ruleNames, (currRuleName) => { if (!isFunction(visitorInstance[currRuleName])) { return { msg: `Missing visitor method: <${currRuleName}> on ${functionName( <any>visitorInstance.constructor )} CST Visitor.`, type: CstVisitorDefinitionError.MISSING_METHOD, methodName: currRuleName } } }) return compact<IVisitorDefinitionError>(errors) } const VALID_PROP_NAMES = ["constructor", "visit", "validateVisitor"] export function validateRedundantMethods( visitorInstance: Function, ruleNames: string[] ): IVisitorDefinitionError[] { let errors = [] for (let prop in visitorInstance) { if ( isFunction(visitorInstance[prop]) && !contains(VALID_PROP_NAMES, prop) && !contains(ruleNames, prop) ) { errors.push({ msg: `Redundant visitor method: <${prop}> on ${functionName( <any>visitorInstance.constructor )} CST Visitor\n` + `There is no Grammar Rule corresponding to this method's name.\n`, type: CstVisitorDefinitionError.REDUNDANT_METHOD, methodName: prop }) } } return errors }