UNPKG

chevrotain

Version:

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

188 lines (187 loc) 8.32 kB
import { addNoneTerminalToCst, addTerminalToCst, setNodeLocationFull, setNodeLocationOnlyOffset, } from "../../cst/cst.js"; import { createBaseSemanticVisitorConstructor, createBaseVisitorConstructorWithDefaults, } from "../../cst/cst_visitor.js"; import { DEFAULT_PARSER_CONFIG } from "../parser.js"; /** * This trait is responsible for the CST building logic. */ export class TreeBuilder { initTreeBuilder(config) { this.CST_STACK = []; // outputCst is no longer exposed/defined in the pubic API this.outputCst = config.outputCst; this.nodeLocationTracking = Object.hasOwn(config, "nodeLocationTracking") ? config.nodeLocationTracking // assumes end user provides the correct config value/type : DEFAULT_PARSER_CONFIG.nodeLocationTracking; if (!this.outputCst) { this.cstInvocationStateUpdate = () => { }; this.cstFinallyStateUpdate = () => { }; this.cstPostTerminal = () => { }; this.cstPostNonTerminal = () => { }; this.cstPostRule = () => { }; } else { if (/full/i.test(this.nodeLocationTracking)) { if (this.recoveryEnabled) { this.setNodeLocationFromToken = setNodeLocationFull; this.setNodeLocationFromNode = setNodeLocationFull; this.cstPostRule = () => { }; this.setInitialNodeLocation = this.setInitialNodeLocationFullRecovery; } else { this.setNodeLocationFromToken = () => { }; this.setNodeLocationFromNode = () => { }; this.cstPostRule = this.cstPostRuleFull; this.setInitialNodeLocation = this.setInitialNodeLocationFullRegular; } } else if (/onlyOffset/i.test(this.nodeLocationTracking)) { if (this.recoveryEnabled) { this.setNodeLocationFromToken = setNodeLocationOnlyOffset; this.setNodeLocationFromNode = setNodeLocationOnlyOffset; this.cstPostRule = () => { }; this.setInitialNodeLocation = this.setInitialNodeLocationOnlyOffsetRecovery; } else { this.setNodeLocationFromToken = () => { }; this.setNodeLocationFromNode = () => { }; this.cstPostRule = this.cstPostRuleOnlyOffset; this.setInitialNodeLocation = this.setInitialNodeLocationOnlyOffsetRegular; } } else if (/none/i.test(this.nodeLocationTracking)) { this.setNodeLocationFromToken = () => { }; this.setNodeLocationFromNode = () => { }; this.cstPostRule = () => { }; this.setInitialNodeLocation = () => { }; } else { throw Error(`Invalid <nodeLocationTracking> config option: "${config.nodeLocationTracking}"`); } } } setInitialNodeLocationOnlyOffsetRecovery(cstNode) { cstNode.location = { startOffset: NaN, endOffset: NaN, }; } setInitialNodeLocationOnlyOffsetRegular(cstNode) { cstNode.location = { // without error recovery the starting Location of a new CstNode is guaranteed // To be the next Token's startOffset (for valid inputs). // For invalid inputs there won't be any CSTOutput so this potential // inaccuracy does not matter startOffset: this.LA_FAST(1).startOffset, endOffset: NaN, }; } setInitialNodeLocationFullRecovery(cstNode) { cstNode.location = { startOffset: NaN, startLine: NaN, startColumn: NaN, endOffset: NaN, endLine: NaN, endColumn: NaN, }; } /** * @see setInitialNodeLocationOnlyOffsetRegular for explanation why this work * @param cstNode */ setInitialNodeLocationFullRegular(cstNode) { const nextToken = this.LA_FAST(1); cstNode.location = { startOffset: nextToken.startOffset, startLine: nextToken.startLine, startColumn: nextToken.startColumn, endOffset: NaN, endLine: NaN, endColumn: NaN, }; } cstInvocationStateUpdate(fullRuleName) { const cstNode = { name: fullRuleName, children: Object.create(null), }; this.setInitialNodeLocation(cstNode); this.CST_STACK.push(cstNode); } cstFinallyStateUpdate() { this.CST_STACK.pop(); } cstPostRuleFull(ruleCstNode) { // casts to `required<CstNodeLocation>` are safe because `cstPostRuleFull` should only be invoked when full location is enabled // TODO(perf): can we replace this with LA_FAST? // edge case is the empty CstNode on first rule invocation. // perhaps create a test case to verify correctness of LA vs LA_FAST in this scenario? const prevToken = this.LA(0); const loc = ruleCstNode.location; // If this condition is true it means we consumed at least one Token // In this CstNode. if (loc.startOffset <= prevToken.startOffset === true) { loc.endOffset = prevToken.endOffset; loc.endLine = prevToken.endLine; loc.endColumn = prevToken.endColumn; } // "empty" CstNode edge case else { loc.startOffset = NaN; loc.startLine = NaN; loc.startColumn = NaN; } } cstPostRuleOnlyOffset(ruleCstNode) { // TODO: can we replace this with LA_FAST? see comment in `cstPostRuleFull()` const prevToken = this.LA(0); // `location' is not null because `cstPostRuleOnlyOffset` will only be invoked when location tracking is enabled. const loc = ruleCstNode.location; // If this condition is true it means we consumed at least one Token // In this CstNode. if (loc.startOffset <= prevToken.startOffset === true) { loc.endOffset = prevToken.endOffset; } // "empty" CstNode edge case else { loc.startOffset = NaN; } } cstPostTerminal(key, consumedToken) { const rootCst = this.CST_STACK[this.CST_STACK.length - 1]; addTerminalToCst(rootCst, consumedToken, key); // This is only used when **both** error recovery and CST Output are enabled. this.setNodeLocationFromToken(rootCst.location, consumedToken); } cstPostNonTerminal(ruleCstResult, ruleName) { const preCstNode = this.CST_STACK[this.CST_STACK.length - 1]; addNoneTerminalToCst(preCstNode, ruleName, ruleCstResult); // This is only used when **both** error recovery and CST Output are enabled. this.setNodeLocationFromNode(preCstNode.location, ruleCstResult.location); } getBaseCstVisitorConstructor() { if (this.baseCstVisitorConstructor === undefined) { const newBaseCstVisitorConstructor = createBaseSemanticVisitorConstructor(this.className, Object.keys(this.gastProductionsCache)); this.baseCstVisitorConstructor = newBaseCstVisitorConstructor; return newBaseCstVisitorConstructor; } return this.baseCstVisitorConstructor; } getBaseCstVisitorConstructorWithDefaults() { if (this.baseCstVisitorWithDefaultsConstructor === undefined) { const newConstructor = createBaseVisitorConstructorWithDefaults(this.className, Object.keys(this.gastProductionsCache), this.getBaseCstVisitorConstructor()); this.baseCstVisitorWithDefaultsConstructor = newConstructor; return newConstructor; } return this.baseCstVisitorWithDefaultsConstructor; } getPreviousExplicitRuleShortName() { return this.RULE_STACK[this.RULE_STACK_IDX - 1]; } getLastExplicitRuleOccurrenceIndex() { return this.RULE_OCCURRENCE_STACK[this.RULE_OCCURRENCE_STACK_IDX]; } } //# sourceMappingURL=tree_builder.js.map