What to do when ANTLR moves your variables out of scope
Tidbit I thought was worth passing on…
I was just adding the SimpleType rule to my grammar, and got this error:
Delphi.g:534: warning:nondeterminism between alts 1 and 2 of block upon Delphi.g:534: k==1:OPEN_PAREN
Okay, I’ve run into this before. The problem is between SubrangeType and EnumeratedType, because both can start with something in parentheses (SubrangeType could start with a parenthesized subexpression like (WM_USER + 1), and EnumeratedType is something like (alNone, alClient, alCustom)). ANTLR doesn’t like that kind of ambiguity; it likes to know that an open parenthesis means it can move right to this rule.
No problem, says I: I’ll just break out my handy-dandy semantic predicate. If lookahead shows an expression followed by ‘..’, then it’s a SubrangeType. So I made the changes, and promptly got forty-some compiler errors. Things like:
error CS0103: The name 'label' does not exist in the current context
What happened?! I looked at the DelphiParser.cs that ANTLR had generated, and saw things like this:
if (0==inputState.guessing) {
string label = "";
}
// ...
if (0==inputState.guessing) {
label = f1_AST.getText(); // <-- compiler error
}
Sure ’nuff! ANTLR had moved the variable declaration inside the if block, so by the time it got time to use it, the variable wasn’t in scope anymore. I beat my head against this for a few minutes before pulling out the ANTLR docs.
The fix was simple: I hadn’t been using quite the right ANTLR syntax. I had been declaring my variables inside the rule, along these lines:
expression:
{ string label = ""; }
// ...
(The stuff in curly braces is a custom C# code block I provide, which gets inserted into the generated code.)
This had worked fine, until I started using those rules inside a predicate. It turns out that any code blocks after the colon can be if’ed away, if ANTLR decides that it will need to be able to execute that rule in guess mode (i.e., if that rule, or one that references it, appears in the parenthesized part of a semantic predicate). And since most every rule contained several code blocks (one that declared the variable, followed by one or more that used it), and each code block got moved into its own scope, I had a problem.
The right way is to put the variable declarations before the colon. Then they won’t be if’ed out:
expression
{ string label = ""; } :
// ...
Worked like a charm.
I gave the ANTLR docs a brief skim before I started, and then dove right in to constructing a lexer and parser. I’ve learned an awful lot this way, but every now and then, it’s good to go back to the docs.
May 9th, 2006 at 11:40 am
Hi, I’m a Delphi programmer learning Ruby, came upon your site, found some interesting commonalities. I’m in Science of Mind (NOT scientology!) which is similar to Unitarian, which you are. I grew up in Wisconsin, you’re in Ohio. I relate to the debt things - I had a business fail, and declared bankruptcy.
You seem to be working hard on this Delphi parsing, and I was poking around, trying to find out what your job is, and why you’re doing it. Have you written a link?
I personally try to add barcodes to everything. http://www.less2do.com for example.
Kindred souls?
Jim
May 9th, 2006 at 8:18 pm
Not sure where you got the Ohio thing - I’m in Nebraska.
I’m writing a Delphi parser (and will probably be writing a .DFM parser) because I want tools that can do things like look through our code base for try..except blocks that don’t reraise and don’t call Application.HandleException, or to tell us which third-party controls we’re actively using and where we use them and which properties we set differently in different places that we should maybe standardize, or to find units that we’re no longer using, or to do refactorings that (unlike Delphi 2006’s refactorings) actually work across multiple source files, or… there are a lot of things I could do with it, and I’m sure I’ll find out lots more once I’ve got the tool working.