antlr4ts icon indicating copy to clipboard operation
antlr4ts copied to clipboard

error -- assignment to const

Open tawalters61 opened this issue 5 years ago • 7 comments

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)

tawalters61 avatar May 30 '19 23:05 tawalters61

I don't see any constant variables in PredictionContextCache.ts. Can you provide some additional information about how this is occurring?

sharwell avatar Jun 04 '19 11:06 sharwell

I am having the same error. It happens if buildOptimizer is turned on. see https://angular.io/cli/build --buildOptimizer=true|false option. image image

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 avatar Jun 26 '19 11:06 kingfolk

@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?

kevinramharak avatar Jun 26 '19 21:06 kevinramharak

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

kingfolk avatar Jun 27 '19 00:06 kingfolk

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.

kevinramharak avatar Jun 27 '19 17:06 kevinramharak

Thanks! @kevinramharak

kingfolk avatar Jun 28 '19 06:06 kingfolk

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.

clydin avatar Jul 03 '19 16:07 clydin