diff --git a/runtime/JavaScript/src/antlr4/atn/SemanticContext.js b/runtime/JavaScript/src/antlr4/atn/SemanticContext.js index 992183588..4c7e64bd1 100644 --- a/runtime/JavaScript/src/antlr4/atn/SemanticContext.js +++ b/runtime/JavaScript/src/antlr4/atn/SemanticContext.js @@ -1,404 +1,400 @@ -// /* Copyright (c) 2012-2017 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. */ -// -// A tree structure used to record the semantic context in which -// an ATN configuration is valid. It's either a single predicate, -// a conjunction {@code p1&&p2}, or a sum of products {@code p1||p2}. -// -//
I have scoped the {@link AND}, {@link OR}, and {@link Predicate} subclasses of -// {@link SemanticContext} within the scope of this outer class.
-// +const {Set, Hash} = require('./../Utils'); -var Set = require('./../Utils').Set; -var Hash = require('./../Utils').Hash; +/** + * A tree structure used to record the semantic context in which + * an ATN configuration is valid. It's either a single predicate, + * a conjunction {@code p1&&p2}, or a sum of products {@code p1||p2}. + * + *I have scoped the {@link AND}, {@link OR}, and {@link Predicate} subclasses of + * {@link SemanticContext} within the scope of this outer class.
+ */ +class SemanticContext { + hashCode() { + const hash = new Hash(); + this.updateHashCode(hash); + return hash.finish(); + } -function SemanticContext() { - return this; + /** + * For context independent predicates, we evaluate them without a local + * context (i.e., null context). That way, we can evaluate them without + * having to create proper rule-specific context during prediction (as + * opposed to the parser, which creates them naturally). In a practical + * sense, this avoids a cast exception from RuleContext to myruleContext. + * + *For context dependent predicates, we must pass in a local context so that + * references such as $arg evaluate properly as _localctx.arg. We only + * capture context dependent predicates in the context in which we begin + * prediction, so we passed in the outer context here in case of context + * dependent predicate evaluation.
+ */ + evaluate(parser, outerContext) {} + + /** + * Evaluate the precedence predicates for the context and reduce the result. + * + * @param parser The parser instance. + * @param outerContext The current parser context object. + * @return The simplified semantic context after precedence predicates are + * evaluated, which will be one of the following values. + *For context dependent predicates, we must pass in a local context so that -// references such as $arg evaluate properly as _localctx.arg. We only -// capture context dependent predicates in the context in which we begin -// prediction, so we passed in the outer context here in case of context -// dependent predicate evaluation.
-// -SemanticContext.prototype.evaluate = function(parser, outerContext) { -}; +class Predicate extends SemanticContext { + constructor(ruleIndex, predIndex, isCtxDependent) { + super(); + this.ruleIndex = ruleIndex === undefined ? -1 : ruleIndex; + this.predIndex = predIndex === undefined ? -1 : predIndex; + this.isCtxDependent = isCtxDependent === undefined ? false : isCtxDependent; // e.g., $i ref in pred + return this; + } -// -// Evaluate the precedence predicates for the context and reduce the result. -// -// @param parser The parser instance. -// @param outerContext The current parser context object. -// @return The simplified semantic context after precedence predicates are -// evaluated, which will be one of the following values. -//-// The evaluation of predicates by this context is short-circuiting, but -// unordered.
-// -AND.prototype.evaluate = function(parser, outerContext) { - for (var i = 0; i < this.opnds.length; i++) { - if (!this.opnds[i].evaluate(parser, outerContext)) { - return false; +class AND extends SemanticContext { + /** + * A semantic context which is true whenever none of the contained contexts + * is false + */ + constructor(a, b) { + super(); + const operands = new Set(); + if (a instanceof AND) { + a.opnds.map(function(o) { + operands.add(o); + }); + } else { + operands.add(a); } - } - return true; -}; - -AND.prototype.evalPrecedence = function(parser, outerContext) { - var differs = false; - var operands = []; - for (var i = 0; i < this.opnds.length; i++) { - var context = this.opnds[i]; - var evaluated = context.evalPrecedence(parser, outerContext); - differs |= (evaluated !== context); - if (evaluated === null) { - // The AND context is false if any element is false - return null; - } else if (evaluated !== SemanticContext.NONE) { - // Reduce the result by skipping true elements - operands.push(evaluated); + if (b instanceof AND) { + b.opnds.map(function(o) { + operands.add(o); + }); + } else { + operands.add(b); } - } - if (!differs) { + const precedencePredicates = PrecedencePredicate.filterPrecedencePredicates(operands); + if (precedencePredicates.length > 0) { + // interested in the transition with the lowest precedence + let reduced = null; + precedencePredicates.map( function(p) { + if(reduced===null || p.precedence-// The evaluation of predicates by this context is short-circuiting, but -// unordered.
-// -OR.prototype.evaluate = function(parser, outerContext) { - for (var i = 0; i < this.opnds.length; i++) { - if (this.opnds[i].evaluate(parser, outerContext)) { + equals(other) { + if (this === other) { return true; + } else if (!(other instanceof AND)) { + return false; + } else { + return this.opnds === other.opnds; } } - return false; -}; -OR.prototype.evalPrecedence = function(parser, outerContext) { - var differs = false; - var operands = []; - for (var i = 0; i < this.opnds.length; i++) { - var context = this.opnds[i]; - var evaluated = context.evalPrecedence(parser, outerContext); - differs |= (evaluated !== context); - if (evaluated === SemanticContext.NONE) { - // The OR context is true if any element is true - return SemanticContext.NONE; - } else if (evaluated !== null) { - // Reduce the result by skipping false elements - operands.push(evaluated); - } + updateHashCode(hash) { + hash.update(this.opnds, "AND"); } - if (!differs) { + + /** + * {@inheritDoc} + * + *+ * The evaluation of predicates by this context is short-circuiting, but + * unordered.
+ */ + evaluate(parser, outerContext) { + for (let i = 0; i < this.opnds.length; i++) { + if (!this.opnds[i].evaluate(parser, outerContext)) { + return false; + } + } + return true; + } + + evalPrecedence(parser, outerContext) { + let differs = false; + const operands = []; + for (let i = 0; i < this.opnds.length; i++) { + const context = this.opnds[i]; + const evaluated = context.evalPrecedence(parser, outerContext); + differs |= (evaluated !== context); + if (evaluated === null) { + // The AND context is false if any element is false + return null; + } else if (evaluated !== SemanticContext.NONE) { + // Reduce the result by skipping true elements + operands.push(evaluated); + } + } + if (!differs) { + return this; + } + if (operands.length === 0) { + // all elements were true, so the AND context is true + return SemanticContext.NONE; + } + let result = null; + operands.map(function(o) { + result = result === null ? o : SemanticContext.andContext(result, o); + }); + return result; + } + + toString() { + let s = ""; + this.opnds.map(function(o) { + s += "&& " + o.toString(); + }); + return s.length > 3 ? s.slice(3) : s; + } +} + + +class OR extends SemanticContext { + /** + * A semantic context which is true whenever at least one of the contained + * contexts is true + */ + constructor(a, b) { + super(); + const operands = new Set(); + if (a instanceof OR) { + a.opnds.map(function(o) { + operands.add(o); + }); + } else { + operands.add(a); + } + if (b instanceof OR) { + b.opnds.map(function(o) { + operands.add(o); + }); + } else { + operands.add(b); + } + + const precedencePredicates = PrecedencePredicate.filterPrecedencePredicates(operands); + if (precedencePredicates.length > 0) { + // interested in the transition with the highest precedence + const s = precedencePredicates.sort(function(a, b) { + return a.compareTo(b); + }); + const reduced = s[s.length-1]; + operands.add(reduced); + } + this.opnds = operands.values(); return this; } - if (operands.length === 0) { - // all elements were false, so the OR context is false - return null; + + equals(other) { + if (this === other) { + return true; + } else if (!(other instanceof OR)) { + return false; + } else { + return this.opnds === other.opnds; + } } - var result = null; - operands.map(function(o) { - return result === null ? o : SemanticContext.orContext(result, o); - }); - return result; -}; -OR.prototype.toString = function() { - var s = ""; - this.opnds.map(function(o) { - s += "|| " + o.toString(); - }); - return s.length > 3 ? s.slice(3) : s; -}; + updateHashCode(hash) { + hash.update(this.opnds, "OR"); + } -exports.SemanticContext = SemanticContext; -exports.PrecedencePredicate = PrecedencePredicate; -exports.Predicate = Predicate; + /** + *+ * The evaluation of predicates by this context is short-circuiting, but + * unordered.
+ */ + evaluate(parser, outerContext) { + for (let i = 0; i < this.opnds.length; i++) { + if (this.opnds[i].evaluate(parser, outerContext)) { + return true; + } + } + return false; + } + + evalPrecedence(parser, outerContext) { + let differs = false; + const operands = []; + for (let i = 0; i < this.opnds.length; i++) { + const context = this.opnds[i]; + const evaluated = context.evalPrecedence(parser, outerContext); + differs |= (evaluated !== context); + if (evaluated === SemanticContext.NONE) { + // The OR context is true if any element is true + return SemanticContext.NONE; + } else if (evaluated !== null) { + // Reduce the result by skipping false elements + operands.push(evaluated); + } + } + if (!differs) { + return this; + } + if (operands.length === 0) { + // all elements were false, so the OR context is false + return null; + } + const result = null; + operands.map(function(o) { + return result === null ? o : SemanticContext.orContext(result, o); + }); + return result; + } + + toString() { + let s = ""; + this.opnds.map(function(o) { + s += "|| " + o.toString(); + }); + return s.length > 3 ? s.slice(3) : s; + } +} + +module.exports = { + SemanticContext, + PrecedencePredicate, + Predicate +}