chevrotain
Version:
Chevrotain is a high performance fault tolerant javascript parsing DSL for building recursive decent parsers
188 lines (187 loc) • 8.32 kB
JavaScript
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