antlr4ts
antlr4ts copied to clipboard
error -- assignment to const
I am using antlr4ts from an Angular app. It appears to work fine with Angular 6, but it does not work with Angular 7 or Angular 8 when building with the --prod flag. It appears as if the generated parser is assigning a const variable. The following is the error message that I am getting.
Uncaught TypeError: Assignment to constant variable. at Object.qKUm (PredictionContextCache.js.pre-build-optimizer.js:138) at f (bootstrap:78) at Object.Ia1M (PredictionContext.js.pre-build-optimizer.js:22) at f (bootstrap:78) at Object.WirZ (ATNConfig.js.pre-build-optimizer.js:22) at f (bootstrap:78) at Object.Pjyi (ATNConfigSet.js.pre-build-optimizer.js:18) at f (bootstrap:78) at Object.Nhs0 (DFA.js.pre-build-optimizer.js:18) at f (bootstrap:78)
I don't see any constant variables in PredictionContextCache.ts. Can you provide some additional information about how this is occurring?
I am having the same error. It happens if buildOptimizer
is turned on.
see https://angular.io/cli/build --buildOptimizer=true|false
option.
full file ATNState.js.pre-build-optimizer.js
class ATNState {
constructor() {
this.stateNumber = ATNState.INVALID_STATE_NUMBER;
this.ruleIndex = 0; // at runtime, we don't have Rule objects
this.epsilonOnlyTransitions = false;
/** Track the transitions emanating from this ATN state. */
this.transitions = [];
this.optimizedTransitions = this.transitions;
}
/**
* Gets the state number.
*
* @returns the state number
*/
getStateNumber() {
return this.stateNumber;
}
/**
* For all states except {@link RuleStopState}, this returns the state
* number. Returns -1 for stop states.
*
* @returns -1 for {@link RuleStopState}, otherwise the state number
*/
get nonStopStateNumber() {
return this.getStateNumber();
}
hashCode() {
return this.stateNumber;
}
equals(o) {
// are these states same object?
if (o instanceof ATNState) {
return this.stateNumber === o.stateNumber;
}
return false;
}
get isNonGreedyExitState() {
return false;
}
toString() {
return String(this.stateNumber);
}
getTransitions() {
return this.transitions.slice(0);
}
get numberOfTransitions() {
return this.transitions.length;
}
addTransition(e, index) {
if (this.transitions.length === 0) {
this.epsilonOnlyTransitions = e.isEpsilon;
}
else if (this.epsilonOnlyTransitions !== e.isEpsilon) {
this.epsilonOnlyTransitions = false;
throw new Error("ATN state " + this.stateNumber + " has both epsilon and non-epsilon transitions.");
}
this.transitions.splice(index !== undefined ? index : this.transitions.length, 0, e);
}
transition(i) {
return this.transitions[i];
}
setTransition(i, e) {
this.transitions[i] = e;
}
removeTransition(index) {
return this.transitions.splice(index, 1)[0];
}
get onlyHasEpsilonTransitions() {
return this.epsilonOnlyTransitions;
}
setRuleIndex(ruleIndex) {
this.ruleIndex = ruleIndex;
}
get isOptimized() {
return this.optimizedTransitions !== this.transitions;
}
get numberOfOptimizedTransitions() {
return this.optimizedTransitions.length;
}
getOptimizedTransition(i) {
return this.optimizedTransitions[i];
}
addOptimizedTransition(e) {
if (!this.isOptimized) {
this.optimizedTransitions = new Array();
}
this.optimizedTransitions.push(e);
}
setOptimizedTransition(i, e) {
if (!this.isOptimized) {
throw new Error("This ATNState is not optimized.");
}
this.optimizedTransitions[i] = e;
}
removeOptimizedTransition(i) {
if (!this.isOptimized) {
throw new Error("This ATNState is not optimized.");
}
this.optimizedTransitions.splice(i, 1);
}
}
__decorate([
Decorators_1.Override
], ATNState.prototype, "hashCode", null);
__decorate([
Decorators_1.Override
], ATNState.prototype, "equals", null);
__decorate([
Decorators_1.Override
], ATNState.prototype, "toString", null);
exports.ATNState = ATNState;
(function (ATNState) {
ATNState.INVALID_STATE_NUMBER = -1;
})(ATNState = exports.ATNState || (exports.ATNState = {}));
the error occurs ATNState = exports.ATNState || (exports.ATNState = {})
where ATNState
is already defined as a class.
@kingfolk is your snippet the generated code? ATNState
does not appear to be a const
so the error does not seem to be related. Is this the complete generated file? And what is the execution context of this file. Is it imported as a module or inside a bundle. Is it in strict mode or not?
I am able to reproduce this issue simply by
# create an angular project
ng new antlr4ts-test
cd antlr4ts-test
yarn add antlr4ts
and in the app.component.ts
import * as antlr4 from 'antlr4ts';
...
constructor() {
console.log('antlr4', antlr4);
}
...
then run in production mode
ng serve --prod
under angular 8
Thanks that makes it easier to debug. The problem is not in the source mapped code but in the actual generated code:
GXQA: function(e, t, r) {
var n = r("mrSG").__decorate;
Object.defineProperty(t, "__esModule", {
value: !0
});
const o = r("8Yb9")
, i = (()=>{
class e {
constructor() {
this.stateNumber = e.INVALID_STATE_NUMBER,
this.ruleIndex = 0,
this.epsilonOnlyTransitions = !1,
this.transitions = [],
this.optimizedTransitions = this.transitions
}
getStateNumber() {
return this.stateNumber
}
get nonStopStateNumber() {
return this.getStateNumber()
}
hashCode() {
return this.stateNumber
}
equals(t) {
return t instanceof e && this.stateNumber === t.stateNumber
}
get isNonGreedyExitState() {
return !1
}
toString() {
return String(this.stateNumber)
}
getTransitions() {
return this.transitions.slice(0)
}
get numberOfTransitions() {
return this.transitions.length
}
addTransition(e, t) {
if (0 === this.transitions.length)
this.epsilonOnlyTransitions = e.isEpsilon;
else if (this.epsilonOnlyTransitions !== e.isEpsilon)
throw this.epsilonOnlyTransitions = !1,
new Error("ATN state " + this.stateNumber + " has both epsilon and non-epsilon transitions.");
this.transitions.splice(void 0 !== t ? t : this.transitions.length, 0, e)
}
transition(e) {
return this.transitions[e]
}
setTransition(e, t) {
this.transitions[e] = t
}
removeTransition(e) {
return this.transitions.splice(e, 1)[0]
}
get onlyHasEpsilonTransitions() {
return this.epsilonOnlyTransitions
}
setRuleIndex(e) {
this.ruleIndex = e
}
get isOptimized() {
return this.optimizedTransitions !== this.transitions
}
get numberOfOptimizedTransitions() {
return this.optimizedTransitions.length
}
getOptimizedTransition(e) {
return this.optimizedTransitions[e]
}
addOptimizedTransition(e) {
this.isOptimized || (this.optimizedTransitions = new Array),
this.optimizedTransitions.push(e)
}
setOptimizedTransition(e, t) {
if (!this.isOptimized)
throw new Error("This ATNState is not optimized.");
this.optimizedTransitions[e] = t
}
removeOptimizedTransition(e) {
if (!this.isOptimized)
throw new Error("This ATNState is not optimized.");
this.optimizedTransitions.splice(e, 1)
}
}
return n([o.Override], e.prototype, "hashCode", null),
n([o.Override], e.prototype, "equals", null),
n([o.Override], e.prototype, "toString", null),
e
}
)();
t.ATNState = i,
function(e) {
e.INVALID_STATE_NUMBER = -1
}(i = t.ATNState || (t.ATNState = {}))
}
Notice that i
is defined as const
and then assigned to in the following IIFE.
This seems like a problem in the build system and resulting generated code.
See https://github.com/angular/angular-cli/issues/9495 for more info. Brings you down the rabbit hole where I gave up at https://github.com/angular/angular-cli/issues/12112. Seems like the buildOptimizer
does not like decorators
Try some flags/config changes to see what works for you.
Not an issue with antlr4ts itself so I think this issue can be closed.
Thanks! @kevinramharak
This issue stems from the use of a class and namespace with the same name. The use of a static property on the class itself would eliminate the need for the namespace as well as reduce the complexity of the code (both before and after transpilation). There are also some follow-on benefits when optimizing within the scope of a larger project as the code would be much easier to statically analyze and more safely optimize.