2
votes

I'm trying to figure out the grammar for the following syntax.

foreach
    where x = 1 when some_variable = true
    where x = 2 when some_variable = false
    where y = 0
        print z              // Main block

    when none                // optional
        print 'not found'    // Exception block
endfor

My grammar looks like:

foreach_stmt    : 'for' 'each' where_opt* blockstmt* whennone_opt? 'endfor'
                ;
where_opt       : 'where' cond_clause
                ;
cond_clause     : test when_opt*
                ;
when_opt        : 'when' test
                ;
whennone_opt    : 'when' 'none' blockstmt*
                ;
test            : or_test
                ;
// further rules omitted

But when the main block is blank, for example

foreach
    where x = 1
        // main block is blank, do nothing
    when none
        print 'none'
endfor

In this case my grammar considers "when none" is a cond_clause to "where x = 1" which is not what I'm expecting.

Also consider the following case:

foreach
    where x = 1 when none = 2
        print 'none'
    // exceptional block is blank
endfor

Where the "none" can be a variable, and "none = 2" should match the "test" rule so it's part of "where...when...".

However when "none" is not in an expression statement, I want "when none" match the "foreach" rather than the previous "where". How can I modify my grammar to do this?

Sorry this title sucks but I don't know how to describe the problem in a few words. Any help would be greatly appreciated.

1
Why do you allow none to be a keyword and an identifier at the same time? You're making it hard for yourself to parse such sources. I'm also wondering what blockstmt could be: could you post your entire grammar?Bart Kiers
@Bart I didn't designed that language(Actually it's the script language of genexus, a tool of mystery), so I have to live with all the tricky parts of it. While none is a valid variable name theoretically, I think it's also acceptable to ignore the case since one will hardly do such crazy thing I guess... The entire grammar is a bit large and most rules are not related to this problem. I'll try to write a minimal grammar.Shaung
@Shaung, ah, I see. Did you extract these rules yourself, or did you create them based on a formal (BNF) specification from the makers of genexus? If it's the latter, could you post a link to the specification? Thanks!Bart Kiers
@Bart There is a wiki wiki.gxtechnical.com/commwiki/servlet/…, where most BNF can be found. Basically I extracted the rule based on the information. But they are not complete and there are also some undocumented syntaxes, which I encountered when parsing several real world projects.Shaung
I don't see a BNF specification when I click that link. What you posted is, what looks to me, more a list of function/methods. More like an API. By using the search, I came across this question, which is unanswered, so my guess is this language is proprietary making it hard to write a proper grammar for it without having a good knowledge of said language. Since I know nothing about it, I don't think I can be of much help.Bart Kiers

1 Answers

2
votes

The parser generated from the following ANTLR grammar:

grammar Genexus;

parse
  :  foreach_stmt* EOF
  ;

foreach_stmt    
  :  'foreach' where_opt* blockstmt* whennone_opt? 'endfor'
  ;

where_opt
  :  'where' cond_clause
  ;

cond_clause
  :  test when_opt*
  ;

when_opt
  :  'when' test
  ;

whennone_opt
  :  'when' 'none' blockstmt*
  ;

test
  :  identifier '=' atom
  ;

identifier
  :  'none'
  |  Identifier
  ;

blockstmt
  :  'print' atom
  ;

atom
  :  Boolean
  |  Number
  |  StringLiteral
  |  Identifier
  ;

Number
  :  '0'..'9'+
  ;

Boolean
  :  'true'
  |  'false'
  ;

Identifier
  :  ('a'..'z' | 'A'..'Z' | '_')+
  ;

StringLiteral
  :  '\'' ~'\''* '\''
  ;

Ignore
  :  (' ' | '\t' | '\r' | '\n') {skip();}
  |  '//' ~('\r' | '\n')*       {skip();}
  |  '/*' .* '*/'               {skip();}
  ;

Produces the following 3 parse-trees from your examples:


1

Source:

foreach
    where x = 1 when some_variable = true
    where x = 2 when some_variable = false
    where y = 0
        print z              // Main block

    when none                // optional
        print 'not found'    // Exception block
endfor

Parse tree:

enter image description here

larger image


2

Source:

foreach
    where x = 1
        // main block is blank, do nothing
    when none
        print 'none'
endfor

Parse tree:

enter image description here


3

Source:

foreach
    where x = 1 when none = 2
        print 'none'
    // exceptional block is blank
endfor

Parse tree:

enter image description here