antlr4ts icon indicating copy to clipboard operation
antlr4ts copied to clipboard

Null checking and action code in generated parser

Open BurtHarris opened this issue 5 years ago • 6 comments

I'm porting the ActionExpr.g4 grammar from Java actions to TypeScript. My grammar contains this:

stat:
    expr NEWLINE {console.log($expr.v);}
    | ID '=' expr NEWLINE { this.memory[$ID.text] = $expr.v; }
    | NEWLINE;

The resulting code in the generated parser reads:

...
this.match(ActionExprParser.NEWLINE);
this.memory[(_localctx._ID != null ? _localctx._ID.text : undefined)] = _localctx._expr.v; 
}

the explicit undefined results in errors:

  • TS2538 - Type 'undefined' cannot be used as an index type.
  • TS2345 -Argument of type 'string | undefined' is not assignable to parameter of type 'string'. Type 'undefined' is not assignable to type 'string'.

BurtHarris avatar Apr 30 '20 20:04 BurtHarris

Seems to be generated by TokenPropertyRef_text(r) in TypeScript.stg

TokenPropertyRef_text(t) ::= "(<ctx(t)>._<t.label> != null ? <ctx(t)>._<t.label>.text : undefined)"

@sharwell this seems like something you might be interested in.

BurtHarris avatar Apr 30 '20 21:04 BurtHarris

Do you have ideas for how it could be resolved?

sharwell avatar Apr 30 '20 22:04 sharwell

TokenPropertyRef_text(t) ::= "<ctx(t)>._<t.label>!.text"

I that sort of punts on null checking...
An alternative might be to return the empty string if the label is null.

TokenPropertyRef_text(t) ::= "<ctx(t)>._<t.label>?.text ?? ''"

BurtHarris avatar Apr 30 '20 23:04 BurtHarris

Hmm, because Token declares text: string | undefined, it still needs a final null elimination at the end. So in the grammar, I guess I need a bang following text:

stat:
	expr NEWLINE {console.log($expr.v);}
	| ID '=' expr NEWLINE { this.memory[$ID.text!] = $expr.v; }
	| NEWLINE;

In that case, the original generated code may work OK. TypeScript 3.7 operational chaining has a nicer syntax.

I was a bit confused by another message because of how I had defined member memory = {}, but got that straightened out: I eventually settled on memory: { [property: string]: number } = {};

BurtHarris avatar Apr 30 '20 23:04 BurtHarris

What about using this in the grammar?

{ this.memory[$ID.text ?? ""] = $expr.v; }

sharwell avatar Apr 30 '20 23:04 sharwell

That sounds reasonable.

BurtHarris avatar Apr 30 '20 23:04 BurtHarris