refactored RuleContext.js to use es6 classes

refactored ParserRuleContext.js to use es6 classes
fix: dont wrap export in object
use const/let for better scoping
use jsdoc
This commit is contained in:
Camilo Roca 2020-03-15 23:55:05 +01:00
parent 4de9e80109
commit ee9474b194
5 changed files with 336 additions and 332 deletions

View File

@ -3,223 +3,223 @@
* can be found in the LICENSE.txt file in the project root.
*/
//* 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.
const RuleContext = require('./RuleContext');
const Tree = require('./tree/Tree');
const INVALID_INTERVAL = Tree.INVALID_INTERVAL;
const TerminalNode = Tree.TerminalNode;
const TerminalNodeImpl = Tree.TerminalNodeImpl;
const ErrorNodeImpl = Tree.ErrorNodeImpl;
const Interval = require("./IntervalSet").Interval;
var RuleContext = require('./RuleContext');
var Tree = require('./tree/Tree');
var INVALID_INTERVAL = Tree.INVALID_INTERVAL;
var TerminalNode = Tree.TerminalNode;
var TerminalNodeImpl = Tree.TerminalNodeImpl;
var ErrorNodeImpl = Tree.ErrorNodeImpl;
var Interval = require("./IntervalSet").Interval;
function ParserRuleContext(parent, invokingStateNumber) {
parent = parent || null;
invokingStateNumber = invokingStateNumber || null;
RuleContext.call(this, parent, invokingStateNumber);
this.ruleIndex = -1;
// * 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;
}
ParserRuleContext.prototype = Object.create(RuleContext.prototype);
ParserRuleContext.prototype.constructor = ParserRuleContext;
// * COPY a ctx (I'm deliberately not using copy constructor)///
ParserRuleContext.prototype.copyFrom = function(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);
/**
* 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.
*/
class ParserRuleContext extends RuleContext {
constructor(parent, invokingStateNumber) {
parent = parent || null;
invokingStateNumber = invokingStateNumber || null;
super(parent, invokingStateNumber);
this.ruleIndex = -1;
/**
* 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;
}
};
// Double dispatch methods for listeners
ParserRuleContext.prototype.enterRule = function(listener) {
};
ParserRuleContext.prototype.exitRule = function(listener) {
};
// * Does not set parent link; other add methods do that///
ParserRuleContext.prototype.addChild = function(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.
// /
ParserRuleContext.prototype.removeLastChild = function() {
if (this.children !== null) {
this.children.pop();
}
};
ParserRuleContext.prototype.addTokenNode = function(token) {
var node = new TerminalNodeImpl(token);
this.addChild(node);
node.parentCtx = this;
return node;
};
ParserRuleContext.prototype.addErrorNode = function(badToken) {
var node = new ErrorNodeImpl(badToken);
this.addChild(node);
node.parentCtx = this;
return node;
};
ParserRuleContext.prototype.getChild = function(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(var j=0; j<this.children.length; j++) {
var child = this.children[j];
if(child instanceof type) {
if(i===0) {
return child;
} else {
i -= 1;
// 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);
}
return null;
}
};
ParserRuleContext.prototype.getToken = function(ttype, i) {
if (this.children === null || i < 0 || i >= this.children.length) {
return null;
}
for(var j=0; j<this.children.length; j++) {
var child = this.children[j];
if (child instanceof TerminalNode) {
if (child.symbol.type === ttype) {
if(i===0) {
return child;
} else {
i -= 1;
// 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;
}
}
return null;
};
ParserRuleContext.prototype.getTokens = function(ttype ) {
if (this.children=== null) {
return [];
} else {
var tokens = [];
for(var j=0; j<this.children.length; j++) {
var child = this.children[j];
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) {
tokens.push(child);
if(i===0) {
return child;
} else {
i -= 1;
}
}
}
}
return tokens;
}
};
return null;
}
ParserRuleContext.prototype.getTypedRuleContext = function(ctxType, i) {
return this.getChild(i, ctxType);
};
ParserRuleContext.prototype.getTypedRuleContexts = function(ctxType) {
if (this.children=== null) {
return [];
} else {
var contexts = [];
for(var j=0; j<this.children.length; j++) {
var child = this.children[j];
if (child instanceof ctxType) {
contexts.push(child);
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;
}
return contexts;
}
};
ParserRuleContext.prototype.getChildCount = function() {
if (this.children=== null) {
return 0;
} else {
return this.children.length;
getTypedRuleContext(ctxType, i) {
return this.getChild(i, ctxType);
}
};
ParserRuleContext.prototype.getSourceInterval = function() {
if( this.start === null || this.stop === null) {
return INVALID_INTERVAL;
} else {
return new Interval(this.start.tokenIndex, this.stop.tokenIndex);
}
};
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 INVALID_INTERVAL;
} else {
return new Interval(this.start.tokenIndex, this.stop.tokenIndex);
}
}
}
RuleContext.EMPTY = new ParserRuleContext();
function InterpreterRuleContext(parent, invokingStateNumber, ruleIndex) {
ParserRuleContext.call(parent, invokingStateNumber);
this.ruleIndex = ruleIndex;
return this;
class InterpreterRuleContext extends ParserRuleContext {
constructor(parent, invokingStateNumber, ruleIndex) {
super(parent, invokingStateNumber);
this.ruleIndex = ruleIndex;
}
}
InterpreterRuleContext.prototype = Object.create(ParserRuleContext.prototype);
InterpreterRuleContext.prototype.constructor = InterpreterRuleContext;
exports.ParserRuleContext = ParserRuleContext;
module.exports = ParserRuleContext;

View File

@ -2,155 +2,159 @@
* 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.
*/
///
// A rule context is a record of a single rule invocation. It knows
// which context invoked it, if any. If there is no parent context, then
// naturally the invoking state is not valid. The parent link
// provides a chain upwards from the current rule invocation to the root
// of the invocation tree, forming a stack. We actually carry no
// information about the rule associated with this context (except
// when parsing). We keep only the state number of the invoking state from
// the ATN submachine that invoked this. Contrast this with the s
// pointer inside ParserRuleContext that tracks the current state
// being "executed" for the current rule.
//
// The parent contexts are useful for computing lookahead sets and
// getting error information.
//
// These objects are used during parsing and prediction.
// For the special case of parsers, we use the subclass
// ParserRuleContext.
//
// @see ParserRuleContext
///
const {RuleNode} = require('./tree/Tree');
const {INVALID_INTERVAL} = require('./tree/Tree');
const INVALID_ALT_NUMBER = require('./atn/ATN').INVALID_ALT_NUMBER || 0; // TODO: solve cyclic dependency to avoid || 0
var RuleNode = require('./tree/Tree').RuleNode;
var INVALID_INTERVAL = require('./tree/Tree').INVALID_INTERVAL;
var INVALID_ALT_NUMBER = require('./atn/ATN').INVALID_ALT_NUMBER || 0; // TODO: solve cyclic dependency to avoid || 0
function RuleContext(parent, invokingState) {
// What context invoked this rule?
this.parentCtx = parent || null;
// What state invoked the rule associated with this context?
// The "return address" is the followState of invokingState
// If parent is null, this should be -1.
this.invokingState = invokingState || -1;
return this;
}
RuleContext.prototype = Object.create(RuleNode.prototype);
RuleContext.prototype.constructor = RuleContext;
RuleContext.prototype.depth = function() {
var n = 0;
var p = this;
while (p !== null) {
p = p.parentCtx;
n += 1;
class RuleContext extends RuleNode {
/** A rule context is a record of a single rule invocation. It knows
* which context invoked it, if any. If there is no parent context, then
* naturally the invoking state is not valid. The parent link
* provides a chain upwards from the current rule invocation to the root
* of the invocation tree, forming a stack. We actually carry no
* information about the rule associated with this context (except
* when parsing). We keep only the state number of the invoking state from
* the ATN submachine that invoked this. Contrast this with the s
* pointer inside ParserRuleContext that tracks the current state
* being "executed" for the current rule.
*
* The parent contexts are useful for computing lookahead sets and
* getting error information.
*
* These objects are used during parsing and prediction.
* For the special case of parsers, we use the subclass
* ParserRuleContext.
*
* @see ParserRuleContext
*/
constructor(parent, invokingState) {
// What context invoked this rule?
super();
this.parentCtx = parent || null;
/**
* What state invoked the rule associated with this context?
* The "return address" is the followState of invokingState
* If parent is null, this should be -1.
*/
this.invokingState = invokingState || -1;
}
return n;
};
// A context is empty if there is no invoking state; meaning nobody call
// current context.
RuleContext.prototype.isEmpty = function() {
return this.invokingState === -1;
};
depth() {
let n = 0;
let p = this;
while (p !== null) {
p = p.parentCtx;
n += 1;
}
return n;
}
/**
* A context is empty if there is no invoking state; meaning nobody call
* current context.
*/
isEmpty() {
return this.invokingState === -1;
}
// satisfy the ParseTree / SyntaxTree interface
RuleContext.prototype.getSourceInterval = function() {
return INVALID_INTERVAL;
};
RuleContext.prototype.getRuleContext = function() {
return this;
};
RuleContext.prototype.getPayload = function() {
return this;
};
// Return the combined text of all child nodes. This method only considers
// tokens which have been added to the parse tree.
// <p>
// Since tokens on hidden channels (e.g. whitespace or comments) are not
// added to the parse trees, they will not appear in the output of this
// method.
// /
RuleContext.prototype.getText = function() {
if (this.getChildCount() === 0) {
return "";
} else {
return this.children.map(function(child) {
return child.getText();
}).join("");
getSourceInterval() {
return INVALID_INTERVAL;
}
};
// For rule associated with this parse tree internal node, return
// the outer alternative number used to match the input. Default
// implementation does not compute nor store this alt num. Create
// a subclass of ParserRuleContext with backing field and set
// option contextSuperClass.
// to set it.
RuleContext.prototype.getAltNumber = function() { return INVALID_ALT_NUMBER; }
getRuleContext() {
return this;
}
// Set the outer alternative number for this context node. Default
// implementation does nothing to avoid backing field overhead for
// trees that don't need it. Create
// a subclass of ParserRuleContext with backing field and set
// option contextSuperClass.
RuleContext.prototype.setAltNumber = function(altNumber) { }
getPayload() {
return this;
}
RuleContext.prototype.getChild = function(i) {
return null;
};
/**
* Return the combined text of all child nodes. This method only considers
* tokens which have been added to the parse tree.
* <p>
* Since tokens on hidden channels (e.g. whitespace or comments) are not
* added to the parse trees, they will not appear in the output of this
* method.
*/
getText() {
if (this.getChildCount() === 0) {
return "";
} else {
return this.children.map(function(child) {
return child.getText();
}).join("");
}
}
RuleContext.prototype.getChildCount = function() {
return 0;
};
/**
* For rule associated with this parse tree internal node, return
* the outer alternative number used to match the input. Default
* implementation does not compute nor store this alt num. Create
* a subclass of ParserRuleContext with backing field and set
* option contextSuperClass.
* to set it.
*/
getAltNumber() { return INVALID_ALT_NUMBER; }
RuleContext.prototype.accept = function(visitor) {
return visitor.visitChildren(this);
};
/**
* Set the outer alternative number for this context node. Default
* implementation does nothing to avoid backing field overhead for
* trees that don't need it. Create
* a subclass of ParserRuleContext with backing field and set
* option contextSuperClass.
*/
setAltNumber(altNumber) { }
getChild(i) {
return null;
}
getChildCount() {
return 0;
}
accept(visitor) {
return visitor.visitChildren(this);
}
/**
* Print out a whole tree, not just a node, in LISP format
* (root child1 .. childN). Print just a node if this is a leaf.
*/
toStringTree(ruleNames, recog) {
return Trees.toStringTree(this, ruleNames, recog);
}
toString(ruleNames, stop) {
ruleNames = ruleNames || null;
stop = stop || null;
let p = this;
let s = "[";
while (p !== null && p !== stop) {
if (ruleNames === null) {
if (!p.isEmpty()) {
s += p.invokingState;
}
} else {
const ri = p.ruleIndex;
const ruleName = (ri >= 0 && ri < ruleNames.length) ? ruleNames[ri]
: "" + ri;
s += ruleName;
}
if (p.parentCtx !== null && (ruleNames !== null || !p.parentCtx.isEmpty())) {
s += " ";
}
p = p.parentCtx;
}
s += "]";
return s;
}
}
//need to manage circular dependencies, so export now
module.exports = RuleContext;
var Trees = require('./tree/Trees');
// Print out a whole tree, not just a node, in LISP format
// (root child1 .. childN). Print just a node if this is a leaf.
//
RuleContext.prototype.toStringTree = function(ruleNames, recog) {
return Trees.toStringTree(this, ruleNames, recog);
};
RuleContext.prototype.toString = function(ruleNames, stop) {
ruleNames = ruleNames || null;
stop = stop || null;
var p = this;
var s = "[";
while (p !== null && p !== stop) {
if (ruleNames === null) {
if (!p.isEmpty()) {
s += p.invokingState;
}
} else {
var ri = p.ruleIndex;
var ruleName = (ri >= 0 && ri < ruleNames.length) ? ruleNames[ri]
: "" + ri;
s += ruleName;
}
if (p.parentCtx !== null && (ruleNames !== null || !p.parentCtx.isEmpty())) {
s += " ";
}
p = p.parentCtx;
}
s += "]";
return s;
};
const Trees = require('./tree/Trees');

View File

@ -16,7 +16,7 @@ const {DFAState, PredPrediction} = require('./../dfa/DFAState');
const ATNSimulator = require('./ATNSimulator');
const PredictionMode = require('./PredictionMode');
const RuleContext = require('./../RuleContext');
const {ParserRuleContext} = require('./../ParserRuleContext');
const ParserRuleContext = require('./../ParserRuleContext');
const {SemanticContext} = require('./SemanticContext');
const PredictionContext = require('./../PredictionContext');
const {Interval} = require('./../IntervalSet');

View File

@ -18,6 +18,6 @@ exports.Lexer = require('./Lexer');
exports.Parser = require('./Parser');
var pc = require('./PredictionContext');
exports.PredictionContextCache = pc.PredictionContextCache;
exports.ParserRuleContext = require('./ParserRuleContext').ParserRuleContext;
exports.ParserRuleContext = require('./ParserRuleContext');
exports.Interval = require('./IntervalSet').Interval;
exports.Utils = require('./Utils');

View File

@ -6,7 +6,7 @@
const Utils = require('./../Utils');
const {Token} = require('./../Token');
const {ErrorNode, TerminalNode} = require('./Tree');
const {ParserRuleContext} = require('./../ParserRuleContext');
const ParserRuleContext = require('./../ParserRuleContext');
const RuleContext = require('./../RuleContext');
const ATN = require('./../atn/ATN');