[antlr-interest] The right way to invoke templates with null arguments?
Tim Clark
timgclark at gmail.com
Thu Jun 21 03:57:48 PDT 2007
Hi
My application is outputting 'assembler code' for a stack machine.
Corresponding to this tree parser rule:
-------------A------------------
ifStatement
:
^(IF_STAT e=expr s1=statement s2=statement?)
-> ifStat(
expr={$e.st},
stat1={$s1.st},
stat2={$s2.st},
L1={myHelper.nextLabel()},
L2={myHelper.nextLabel()}
)
-------------------------------
I have this template:
-------------------------------
ifStat(expr,stat1,stat2,L1,L2) ::= <<
<expr>
<if(stat2)>
ifFalse <L1>
<stat1>
goto <L2>
<L1>:
<stat2>
<L2>:
<else>
ifFalse <L1>
<stat1>
<L1>:
<endif>
>>
-------------------------------
so that the code generated for
if(expr) stat
should be something like this:
-------------------------------
<expr> // leaves result on stack
ifFalse L_1 // consumes top of stack
<stat1>
L_1:
-------------------------------
and the code generated for
if(expr) stat1 else stat2
should be something like this:
-------------------------------
<expr>
ifFalse L_1
<stat1>
goto L_2
L_1:
<stat2>
L_2:
-------------------------------
However, in the first case (no else clause) the template rewrite part of the
code generated for the rule throws a NullPointerException, because $s2 is
null.
If I change the rule to:
--------------B-----------------
ifStat
:
^(IF_STAT e=expr s1=statement s2=statement?)
-> {$e2 != null}?
ifStat(
expr={$e.st},
stat1={$s1.st},
stat2={$s2.st},
L1={myHelper.nextLabel()},
L2={myHelper.nextLabel()}
)
-> ifStat(
expr={$e.st},
stat1={$s1.st},
L1={myHelper.nextLabel()}
)
;
-------------------------------
then Antlr complains with this message: "missing attribute access on rule
scope: s2". This is unfortunate, because it seems to me that this is the
right way to express this. [Manually changing the parser code generated for
form (A) to test $s2 for nullness does actually makes it work.]
It doesn't help to change the semantic predicate to {$s2.st}? (which
persuades Anlr to accept it), because it's $s2 that is null, not $s2.st, so
the exception still happens.
It would seem that the only solution is to change the rule to this:
----------------C---------------
ifStat
options{ backtrack=true;}
:
^(IF_STAT e=expr s1=statement s2=statement)
-> ifStat(
expr={$e.st},
stat1={$s1.st},
stat2={$s2.st},
L1={myHelper.nextLabel()},
L2={myHelper.nextLabel()}
)
|
^(IF_STAT e=expr s1=statement)
-> ifStat(
expr={$e.st},
stat1={$s1.st},
L1={myHelper.nextLabel()}
)
;
-------------------------------
The backtracking option is necessary, otherwise Anlr sees it as ambiguous.
This version does work. I don't think it's the right way, and for more
complicated constructions like for statements, or case statements, there are
many more possible alternatives to cover.
My question is this: is this how it's intended to be, or is there a bug,
preventing what I think is the right way from working?
The obvious solution that springs to mind, to change STAttrMap (in the Antlr
code generator) like this:
public static class STAttrMap extends HashMap {
public STAttrMap put(String attrName, Object value) {
if(value != null) super.put(attrName, value);
return this;
}
public STAttrMap put(String attrName, int value) {
super.put(attrName, new Integer(value));
return this;
}
}
unfortunately does not work, because one is invoking templates with things
like $e.st, where it's $e that may be null, not $e.st.
Regards
Tim
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.antlr.org/pipermail/antlr-interest/attachments/20070621/535fc439/attachment.html
More information about the antlr-interest
mailing list