479 lines
17 KiB
JavaScript
479 lines
17 KiB
JavaScript
"use strict";
|
|
/*!
|
|
* Copyright 2016 The ANTLR Project. All rights reserved.
|
|
* Licensed under the BSD-3-Clause license. See LICENSE file in the project root for license information.
|
|
*/
|
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
};
|
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.SemanticContext = void 0;
|
|
// ConvertTo-TS run at 2016-10-04T11:26:36.9521478-07:00
|
|
const Array2DHashSet_1 = require("../misc/Array2DHashSet");
|
|
const ArrayEqualityComparator_1 = require("../misc/ArrayEqualityComparator");
|
|
const MurmurHash_1 = require("../misc/MurmurHash");
|
|
const Decorators_1 = require("../Decorators");
|
|
const ObjectEqualityComparator_1 = require("../misc/ObjectEqualityComparator");
|
|
const Utils = require("../misc/Utils");
|
|
function max(items) {
|
|
let result;
|
|
for (let current of items) {
|
|
if (result === undefined) {
|
|
result = current;
|
|
continue;
|
|
}
|
|
let comparison = result.compareTo(current);
|
|
if (comparison < 0) {
|
|
result = current;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function min(items) {
|
|
let result;
|
|
for (let current of items) {
|
|
if (result === undefined) {
|
|
result = current;
|
|
continue;
|
|
}
|
|
let comparison = result.compareTo(current);
|
|
if (comparison > 0) {
|
|
result = current;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
/** A tree structure used to record the semantic context in which
|
|
* an ATN configuration is valid. It's either a single predicate,
|
|
* a conjunction `p1&&p2`, or a sum of products `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 {
|
|
/**
|
|
* The default {@link SemanticContext}, which is semantically equivalent to
|
|
* a predicate of the form `{true}?`.
|
|
*/
|
|
static get NONE() {
|
|
if (SemanticContext._NONE === undefined) {
|
|
SemanticContext._NONE = new SemanticContext.Predicate();
|
|
}
|
|
return SemanticContext._NONE;
|
|
}
|
|
/**
|
|
* Evaluate the precedence predicates for the context and reduce the result.
|
|
*
|
|
* @param parser The parser instance.
|
|
* @param parserCallStack
|
|
* @returns The simplified semantic context after precedence predicates are
|
|
* evaluated, which will be one of the following values.
|
|
*
|
|
* * {@link #NONE}: if the predicate simplifies to `true` after
|
|
* precedence predicates are evaluated.
|
|
* * `undefined`: if the predicate simplifies to `false` after
|
|
* precedence predicates are evaluated.
|
|
* * `this`: if the semantic context is not changed as a result of
|
|
* precedence predicate evaluation.
|
|
* * A non-`undefined` {@link SemanticContext}: the new simplified
|
|
* semantic context after precedence predicates are evaluated.
|
|
*/
|
|
evalPrecedence(parser, parserCallStack) {
|
|
return this;
|
|
}
|
|
static and(a, b) {
|
|
if (!a || a === SemanticContext.NONE) {
|
|
return b;
|
|
}
|
|
if (b === SemanticContext.NONE) {
|
|
return a;
|
|
}
|
|
let result = new SemanticContext.AND(a, b);
|
|
if (result.opnds.length === 1) {
|
|
return result.opnds[0];
|
|
}
|
|
return result;
|
|
}
|
|
/**
|
|
*
|
|
* @see ParserATNSimulator#getPredsForAmbigAlts
|
|
*/
|
|
static or(a, b) {
|
|
if (!a) {
|
|
return b;
|
|
}
|
|
if (a === SemanticContext.NONE || b === SemanticContext.NONE) {
|
|
return SemanticContext.NONE;
|
|
}
|
|
let result = new SemanticContext.OR(a, b);
|
|
if (result.opnds.length === 1) {
|
|
return result.opnds[0];
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
exports.SemanticContext = SemanticContext;
|
|
(function (SemanticContext) {
|
|
/**
|
|
* This random 30-bit prime represents the value of `AND.class.hashCode()`.
|
|
*/
|
|
const AND_HASHCODE = 40363613;
|
|
/**
|
|
* This random 30-bit prime represents the value of `OR.class.hashCode()`.
|
|
*/
|
|
const OR_HASHCODE = 486279973;
|
|
function filterPrecedencePredicates(collection) {
|
|
let result = [];
|
|
for (let i = 0; i < collection.length; i++) {
|
|
let context = collection[i];
|
|
if (context instanceof SemanticContext.PrecedencePredicate) {
|
|
result.push(context);
|
|
// Remove the item from 'collection' and move i back so we look at the same index again
|
|
collection.splice(i, 1);
|
|
i--;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
class Predicate extends SemanticContext {
|
|
constructor(ruleIndex = -1, predIndex = -1, isCtxDependent = false) {
|
|
super();
|
|
this.ruleIndex = ruleIndex;
|
|
this.predIndex = predIndex;
|
|
this.isCtxDependent = isCtxDependent;
|
|
}
|
|
eval(parser, parserCallStack) {
|
|
let localctx = this.isCtxDependent ? parserCallStack : undefined;
|
|
return parser.sempred(localctx, this.ruleIndex, this.predIndex);
|
|
}
|
|
hashCode() {
|
|
let hashCode = MurmurHash_1.MurmurHash.initialize();
|
|
hashCode = MurmurHash_1.MurmurHash.update(hashCode, this.ruleIndex);
|
|
hashCode = MurmurHash_1.MurmurHash.update(hashCode, this.predIndex);
|
|
hashCode = MurmurHash_1.MurmurHash.update(hashCode, this.isCtxDependent ? 1 : 0);
|
|
hashCode = MurmurHash_1.MurmurHash.finish(hashCode, 3);
|
|
return hashCode;
|
|
}
|
|
equals(obj) {
|
|
if (!(obj instanceof Predicate)) {
|
|
return false;
|
|
}
|
|
if (this === obj) {
|
|
return true;
|
|
}
|
|
return this.ruleIndex === obj.ruleIndex &&
|
|
this.predIndex === obj.predIndex &&
|
|
this.isCtxDependent === obj.isCtxDependent;
|
|
}
|
|
toString() {
|
|
return "{" + this.ruleIndex + ":" + this.predIndex + "}?";
|
|
}
|
|
}
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], Predicate.prototype, "eval", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], Predicate.prototype, "hashCode", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], Predicate.prototype, "equals", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], Predicate.prototype, "toString", null);
|
|
SemanticContext.Predicate = Predicate;
|
|
class PrecedencePredicate extends SemanticContext {
|
|
constructor(precedence) {
|
|
super();
|
|
this.precedence = precedence;
|
|
}
|
|
eval(parser, parserCallStack) {
|
|
return parser.precpred(parserCallStack, this.precedence);
|
|
}
|
|
evalPrecedence(parser, parserCallStack) {
|
|
if (parser.precpred(parserCallStack, this.precedence)) {
|
|
return SemanticContext.NONE;
|
|
}
|
|
else {
|
|
return undefined;
|
|
}
|
|
}
|
|
compareTo(o) {
|
|
return this.precedence - o.precedence;
|
|
}
|
|
hashCode() {
|
|
let hashCode = 1;
|
|
hashCode = 31 * hashCode + this.precedence;
|
|
return hashCode;
|
|
}
|
|
equals(obj) {
|
|
if (!(obj instanceof PrecedencePredicate)) {
|
|
return false;
|
|
}
|
|
if (this === obj) {
|
|
return true;
|
|
}
|
|
return this.precedence === obj.precedence;
|
|
}
|
|
toString() {
|
|
return "{" + this.precedence + ">=prec}?";
|
|
}
|
|
}
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], PrecedencePredicate.prototype, "eval", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], PrecedencePredicate.prototype, "evalPrecedence", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], PrecedencePredicate.prototype, "compareTo", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], PrecedencePredicate.prototype, "hashCode", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], PrecedencePredicate.prototype, "equals", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], PrecedencePredicate.prototype, "toString", null);
|
|
SemanticContext.PrecedencePredicate = PrecedencePredicate;
|
|
/**
|
|
* This is the base class for semantic context "operators", which operate on
|
|
* a collection of semantic context "operands".
|
|
*
|
|
* @since 4.3
|
|
*/
|
|
class Operator extends SemanticContext {
|
|
}
|
|
SemanticContext.Operator = Operator;
|
|
/**
|
|
* A semantic context which is true whenever none of the contained contexts
|
|
* is false.
|
|
*/
|
|
let AND = class AND extends Operator {
|
|
constructor(a, b) {
|
|
super();
|
|
let operands = new Array2DHashSet_1.Array2DHashSet(ObjectEqualityComparator_1.ObjectEqualityComparator.INSTANCE);
|
|
if (a instanceof AND) {
|
|
operands.addAll(a.opnds);
|
|
}
|
|
else {
|
|
operands.add(a);
|
|
}
|
|
if (b instanceof AND) {
|
|
operands.addAll(b.opnds);
|
|
}
|
|
else {
|
|
operands.add(b);
|
|
}
|
|
this.opnds = operands.toArray();
|
|
let precedencePredicates = filterPrecedencePredicates(this.opnds);
|
|
// interested in the transition with the lowest precedence
|
|
let reduced = min(precedencePredicates);
|
|
if (reduced) {
|
|
this.opnds.push(reduced);
|
|
}
|
|
}
|
|
get operands() {
|
|
return this.opnds;
|
|
}
|
|
equals(obj) {
|
|
if (this === obj) {
|
|
return true;
|
|
}
|
|
if (!(obj instanceof AND)) {
|
|
return false;
|
|
}
|
|
return ArrayEqualityComparator_1.ArrayEqualityComparator.INSTANCE.equals(this.opnds, obj.opnds);
|
|
}
|
|
hashCode() {
|
|
return MurmurHash_1.MurmurHash.hashCode(this.opnds, AND_HASHCODE);
|
|
}
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* The evaluation of predicates by this context is short-circuiting, but
|
|
* unordered.
|
|
*/
|
|
eval(parser, parserCallStack) {
|
|
for (let opnd of this.opnds) {
|
|
if (!opnd.eval(parser, parserCallStack)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
evalPrecedence(parser, parserCallStack) {
|
|
let differs = false;
|
|
let operands = [];
|
|
for (let context of this.opnds) {
|
|
let evaluated = context.evalPrecedence(parser, parserCallStack);
|
|
differs = differs || (evaluated !== context);
|
|
if (evaluated == null) {
|
|
// The AND context is false if any element is false
|
|
return undefined;
|
|
}
|
|
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 = operands[0];
|
|
for (let i = 1; i < operands.length; i++) {
|
|
result = SemanticContext.and(result, operands[i]);
|
|
}
|
|
return result;
|
|
}
|
|
toString() {
|
|
return Utils.join(this.opnds, "&&");
|
|
}
|
|
};
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], AND.prototype, "operands", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], AND.prototype, "equals", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], AND.prototype, "hashCode", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], AND.prototype, "eval", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], AND.prototype, "evalPrecedence", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], AND.prototype, "toString", null);
|
|
AND = __decorate([
|
|
__param(0, Decorators_1.NotNull), __param(1, Decorators_1.NotNull)
|
|
], AND);
|
|
SemanticContext.AND = AND;
|
|
/**
|
|
* A semantic context which is true whenever at least one of the contained
|
|
* contexts is true.
|
|
*/
|
|
let OR = class OR extends Operator {
|
|
constructor(a, b) {
|
|
super();
|
|
let operands = new Array2DHashSet_1.Array2DHashSet(ObjectEqualityComparator_1.ObjectEqualityComparator.INSTANCE);
|
|
if (a instanceof OR) {
|
|
operands.addAll(a.opnds);
|
|
}
|
|
else {
|
|
operands.add(a);
|
|
}
|
|
if (b instanceof OR) {
|
|
operands.addAll(b.opnds);
|
|
}
|
|
else {
|
|
operands.add(b);
|
|
}
|
|
this.opnds = operands.toArray();
|
|
let precedencePredicates = filterPrecedencePredicates(this.opnds);
|
|
// interested in the transition with the highest precedence
|
|
let reduced = max(precedencePredicates);
|
|
if (reduced) {
|
|
this.opnds.push(reduced);
|
|
}
|
|
}
|
|
get operands() {
|
|
return this.opnds;
|
|
}
|
|
equals(obj) {
|
|
if (this === obj) {
|
|
return true;
|
|
}
|
|
if (!(obj instanceof OR)) {
|
|
return false;
|
|
}
|
|
return ArrayEqualityComparator_1.ArrayEqualityComparator.INSTANCE.equals(this.opnds, obj.opnds);
|
|
}
|
|
hashCode() {
|
|
return MurmurHash_1.MurmurHash.hashCode(this.opnds, OR_HASHCODE);
|
|
}
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* The evaluation of predicates by this context is short-circuiting, but
|
|
* unordered.
|
|
*/
|
|
eval(parser, parserCallStack) {
|
|
for (let opnd of this.opnds) {
|
|
if (opnd.eval(parser, parserCallStack)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
evalPrecedence(parser, parserCallStack) {
|
|
let differs = false;
|
|
let operands = [];
|
|
for (let context of this.opnds) {
|
|
let evaluated = context.evalPrecedence(parser, parserCallStack);
|
|
differs = differs || (evaluated !== context);
|
|
if (evaluated === SemanticContext.NONE) {
|
|
// The OR context is true if any element is true
|
|
return SemanticContext.NONE;
|
|
}
|
|
else if (evaluated) {
|
|
// 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 undefined;
|
|
}
|
|
let result = operands[0];
|
|
for (let i = 1; i < operands.length; i++) {
|
|
result = SemanticContext.or(result, operands[i]);
|
|
}
|
|
return result;
|
|
}
|
|
toString() {
|
|
return Utils.join(this.opnds, "||");
|
|
}
|
|
};
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], OR.prototype, "operands", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], OR.prototype, "equals", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], OR.prototype, "hashCode", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], OR.prototype, "eval", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], OR.prototype, "evalPrecedence", null);
|
|
__decorate([
|
|
Decorators_1.Override
|
|
], OR.prototype, "toString", null);
|
|
OR = __decorate([
|
|
__param(0, Decorators_1.NotNull), __param(1, Decorators_1.NotNull)
|
|
], OR);
|
|
SemanticContext.OR = OR;
|
|
})(SemanticContext = exports.SemanticContext || (exports.SemanticContext = {}));
|
|
//# sourceMappingURL=SemanticContext.js.map
|