antlr4-runtime
Version:
JavaScript runtime for ANTLR4
215 lines (194 loc) • 5.48 kB
JavaScript
/* Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
import RuleContext from './RuleContext.js';
import TerminalNode from '../tree/TerminalNode.js';
import TerminalNodeImpl from '../tree/TerminalNodeImpl.js';
import ErrorNodeImpl from '../tree/ErrorNodeImpl.js';
import Interval from "../misc/Interval.js";
/**
* A rule invocation record for parsing.
*
* Contains all of the information about the current rule not stored in the
* RuleContext. It handles parse tree children list, Any ATN state
* tracing, and the default values available for rule indications:
* start, stop, rule index, current alt number, current
* ATN state.
*
* Subclasses made for each rule and grammar track the parameters,
* return values, locals, and labels specific to that rule. These
* are the objects that are returned from rules.
*
* Note text is not an actual field of a rule return value; it is computed
* from start and stop using the input stream's toString() method. I
* could add a ctor to this so that we can pass in and store the input
* stream, but I'm not sure we want to do that. It would seem to be undefined
* to get the .text property anyway if the rule matches tokens from multiple
* input streams.
*
* I do not use getters for fields of objects that are used simply to
* group values such as this aggregate. The getters/setters are there to
* satisfy the superclass interface.
*/
export default class ParserRuleContext extends RuleContext {
constructor(parent, invokingStateNumber) {
super(parent, invokingStateNumber);
/**
* If we are debugging or building a parse tree for a visitor,
* we need to track all of the tokens and rule invocations associated
* with this rule's context. This is empty for parsing w/o tree constr.
* operation because we don't the need to track the details about
* how we parse this rule.
*/
this.children = null;
this.start = null;
this.stop = null;
/**
* The exception that forced this rule to return. If the rule successfully
* completed, this is {@code null}.
*/
this.exception = null;
}
// COPY a ctx (I'm deliberately not using copy constructor)
copyFrom(ctx) {
// from RuleContext
this.parentCtx = ctx.parentCtx;
this.invokingState = ctx.invokingState;
this.children = null;
this.start = ctx.start;
this.stop = ctx.stop;
// copy any error nodes to alt label node
if(ctx.children) {
this.children = [];
// reset parent pointer for any error nodes
ctx.children.map(function(child) {
if (child instanceof ErrorNodeImpl) {
this.children.push(child);
child.parentCtx = this;
}
}, this);
}
}
// Double dispatch methods for listeners
enterRule(listener) {
}
exitRule(listener) {
}
// Does not set parent link; other add methods do that
addChild(child) {
if (this.children === null) {
this.children = [];
}
this.children.push(child);
return child;
}
/** Used by enterOuterAlt to toss out a RuleContext previously added as
* we entered a rule. If we have // label, we will need to remove
* generic ruleContext object.
*/
removeLastChild() {
if (this.children !== null) {
this.children.pop();
}
}
addTokenNode(token) {
const node = new TerminalNodeImpl(token);
this.addChild(node);
node.parentCtx = this;
return node;
}
addErrorNode(badToken) {
const node = new ErrorNodeImpl(badToken);
this.addChild(node);
node.parentCtx = this;
return node;
}
getChild(i, type) {
type = type || null;
if (this.children === null || i < 0 || i >= this.children.length) {
return null;
}
if (type === null) {
return this.children[i];
} else {
for(let j=0; j<this.children.length; j++) {
const child = this.children[j];
if(child instanceof type) {
if(i===0) {
return child;
} else {
i -= 1;
}
}
}
return null;
}
}
getToken(ttype, i) {
if (this.children === null || i < 0 || i >= this.children.length) {
return null;
}
for(let j=0; j<this.children.length; j++) {
const child = this.children[j];
if (child instanceof TerminalNode) {
if (child.symbol.type === ttype) {
if(i===0) {
return child;
} else {
i -= 1;
}
}
}
}
return null;
}
getTokens(ttype ) {
if (this.children=== null) {
return [];
} else {
const tokens = [];
for(let j=0; j<this.children.length; j++) {
const child = this.children[j];
if (child instanceof TerminalNode) {
if (child.symbol.type === ttype) {
tokens.push(child);
}
}
}
return tokens;
}
}
getTypedRuleContext(ctxType, i) {
return this.getChild(i, ctxType);
}
getTypedRuleContexts(ctxType) {
if (this.children=== null) {
return [];
} else {
const contexts = [];
for(let j=0; j<this.children.length; j++) {
const child = this.children[j];
if (child instanceof ctxType) {
contexts.push(child);
}
}
return contexts;
}
}
getChildCount() {
if (this.children=== null) {
return 0;
} else {
return this.children.length;
}
}
getSourceInterval() {
if( this.start === null || this.stop === null) {
return Interval.INVALID_INTERVAL;
} else {
return new Interval(this.start.tokenIndex, this.stop.tokenIndex);
}
}
}
RuleContext.EMPTY = new ParserRuleContext();