【问题标题】:Do while and while at the same time using ANTLR使用 ANTLR 同时执行 while 和 while
【发布时间】:2013-03-29 11:03:32
【问题描述】:

之前我创建了this question,询问如何使用 ANTLR 4 创建 if/else 语句。我得到了一个很好的答案,它还展示了如何执行 while 循环。我已经用我的语言实现了这一点,现在我正在尝试使用几乎相同的原则创建一个 do-while 循环。

我的while循环语法如下:

count is 0
while count is less than 10
  count+
  if count not equals 10
    write " " + count + ": Getting there..."
  else if count equals 10
    write count + ": The end!"
  end if
end while

这就是我想要的 do-while 循环:

count is 0
do
  count+
  write "count is " + count
  if count equals 10
    write "The end!"
  end if
while count is less than 10

我已经对其进行了测试,但是它们都可以正常工作,但是不能同时工作。以下是我的语法(很抱歉将其全部发布,但我认为这是必要的)。

如果我的 WHILEEND_WHILE 令牌高于我的 DO_WHILEDO_WHILE_CONDITION 令牌,则 while 循环有效。但是,如果我在我的 do-while 循环中切换它们。如果我将 DO_WHILE_CONDITION 标记更改为 while 以外的任何其他标记,那么两者都可以。

无论如何我可以让它们都使用当前语法吗?我知道这可能是个问题,因为我对多个事物使用相同的关键字,但我希望有办法做到这一点。

//////////////////////////////////
// PARSER
//////////////////////////////////

program
 : block EOF
 ;

block
 : (statement (NEW_LINE+ | EOF))*
 ;

statement
 : assignment
 | if_statement
 | while_statement
 | until_statement
 | do_while_statement
 | write
 ;

assignment
 : ID ASSIGN expression # expressionAssignment
 | ID PLUS              # incrementAssignment
 | ID MINUS             # decrementAssignment
 ;

if_statement
 : IF condition_block (ELSE_IF condition_block)* (ELSE NEW_LINE statement_block)? END_IF
 ;

condition_block
 : expression NEW_LINE statement_block
 ;

statement_block
 : block
 ;

while_statement
 : WHILE expression NEW_LINE statement_block END_WHILE
 ;

until_statement
 : UNTIL expression NEW_LINE statement_block END_UNTIL
 ;

do_while_statement
 : DO_WHILE NEW_LINE statement_block DO_WHILE_CONDITION expression
 ;

expression
 : atom                                             # atomExpression
 | expression PLUS expression                       # plusExpression
 | expression MINUS expression                      # minusExpression
 | expression MULTIPLY expression                   # multiplicationExpression
 | expression DIVIDE expression                     # divisionExpression
 | expression PLUS                                  # incrementExpression
 | expression MINUS                                 # decrementExpression
 | expression AND expression                        # andExpression
 | expression OR expression                         # orExpression
 | expression EQUALS expression                     # equalityExpression
 | expression NOT_EQUALS expression                 # notEqualityExpression
 | expression LESS_THAN expression                  # lessThanExpression
 | expression NOT_LESS_THAN expression              # notLessThanExpression
 | expression GREATER_THAN expression               # greaterThanExpression
 | expression NOT_GREATER_THAN expression           # notGreaterThanExpression
 | expression GREATER_THAN_OR_EQUAL expression      # greaterThanOrEqualExpression
 | expression LESS_THAN_OR_EQUAL expression         # lessThanOrEqualExpression
 ;

atom
 : INT                              # integerAtom
 | FLOAT                            # floatAtom
 | BOOLEAN                          # boolAtom
 | ID                               # idAtom
 | STRING                           # stringAtom
 | OPEN_PAR expression CLOSE_PAR    # expressionAtom
 ;

write
 : WRITE expression
 ;

////////////////////////////////// 
// LEXER
//////////////////////////////////

PLUS                        : '+';
MINUS                       : '-';
MULTIPLY                    : '*';
DIVIDE                      : '/';

ASSIGN                      : 'is';
OPEN_CURLY                  : '{';
CLOSE_CURLY                 : '}';
OPEN_PAR                    : '(';
CLOSE_PAR                   : ')';
COLON                       : ':';
NEW_LINE                    : '\r'? '\n';

IF                          : 'if';
ELSE_IF                     : 'else if';
ELSE                        : 'else';
END_IF                      : 'end if';

WHILE                       : 'while';
END_WHILE                   : 'end while';

UNTIL                       : 'until';
END_UNTIL                   : 'end until';

DO_WHILE                    : 'do';
DO_WHILE_CONDITION          : 'while';

EQUALS                      : 'equals';
NOT_EQUALS                  : 'not equals';
LESS_THAN                   : 'is less than';
NOT_LESS_THAN               : 'is not less than';
GREATER_THAN                : 'is greater than';
NOT_GREATER_THAN            : 'is not greater than';
GREATER_THAN_OR_EQUAL       : 'is greater than or equals';
LESS_THAN_OR_EQUAL          : 'is less than or equals';
WRITE                       : 'write';

AND                         : 'and';
OR                          : 'or';
NOT                         : 'not';

BOOLEAN
 : 'TRUE' | 'true' | 'YES' | 'yes'
 | 'FALSE' | 'false' | 'NO' | 'no'
 ;

INT
 : (PLUS | MINUS)? NUMBER+
 ;

FLOAT
 : (PLUS | MINUS)? NUMBER+ ('.' | ',') (NUMBER+)?
 | (PLUS | MINUS)? (NUMBER+)? ('.' | ',') NUMBER+
 ;

NUMBER
 : '0'..'9'
 ;

STRING
 : '"' ( '\\"' | ~["] )* '"'
 ;

ID
 : ('a'..'z' | 'A'..'Z' | '0'..'9')+
 ;

WHITESPACE
 : [ \t]+ -> skip
 ;

COMMENT
 : ( ';;' .*? ';;' | ';' ~[\r\n]* ) -> skip
 ;  

【问题讨论】:

    标签: while-loop antlr do-while antlr4


    【解决方案1】:

    在创建标记时,词法分析器考虑到解析器在某个时间点可能需要什么。查看描述规则的问答(适用于 v3 和 v4):Antlr v3 error with parser/lexer rules

    这意味着在您的情况下,规则DO_WHILE_CONDITION

    WHILE                       : 'while';
    ...
    DO_WHILE_CONDITION          : 'while';
    

    永远不会匹配。

    除此之外,用空格“粘合”关键字通常不是一个好主意。考虑输入何时为"end  if"(2 个空格)。最好创建 2 个标记:ENDIF,并在解析器规则中使用它们。

    试试这样的:

    program
     : block
     ; 
    
    block
     : NEW_LINE* (statement (NEW_LINE+ | EOF))*
     ;
    
    statement
     : assignment
     | if_statement
     | while_statement
     | until_statement
     | do_while_statement
     | write
     ;
    
    assignment
     : ID IS expression # expressionAssignment
     | ID PLUS          # incrementAssignment
     | ID MINUS         # decrementAssignment
     ;
    
    if_statement
     : IF condition_block (ELSE IF condition_block)* (ELSE NEW_LINE statement_block)? END IF
     ;
    
    condition_block
     : expression NEW_LINE statement_block
     ;
    
    statement_block
     : block
     ;
    
    while_statement
     : WHILE expression NEW_LINE statement_block END WHILE
     ;
    
    until_statement
     : UNTIL expression NEW_LINE statement_block END UNTIL
     ;
    
    do_while_statement
     : DO NEW_LINE statement_block WHILE expression
     ;
    
    // Added unary expressions instead of combining them in the lexer.
    expression
     : atom                                            # atomExpression
     | MINUS expression                                # unaryMinusExpression
     | PLUS expression                                 # unaryPlusExpression
     | expression PLUS expression                      # plusExpression
     | expression MINUS expression                     # minusExpression
     | expression MULTIPLY expression                  # multiplicationExpression
     | expression DIVIDE expression                    # divisionExpression
     | expression PLUS                                 # incrementExpression
     | expression MINUS                                # decrementExpression
     | expression AND expression                       # andExpression
     | expression OR expression                        # orExpression
     | expression EQUALS expression                    # equalityExpression
     | expression NOT EQUALS expression                # notEqualityExpression
     | expression IS LESS THAN expression              # lessThanExpression
     | expression IS NOT LESS THAN expression          # notLessThanExpression
     | expression IS GREATER THAN expression           # greaterThanExpression
     | expression IS NOT GREATER THAN expression       # notGreaterThanExpression
     | expression IS GREATER THAN OR EQUALS expression # greaterThanOrEqualExpression
     | expression IS LESS THAN OR EQUALS expression    # lessThanOrEqualExpression
     ;
    
    atom
     : INT                              # integerAtom
     | FLOAT                            # floatAtom
     | bool                             # boolAtom
     | ID                               # idAtom
     | STRING                           # stringAtom
     | OPEN_PAR expression CLOSE_PAR    # expressionAtom
     ;
    
    write
     : WRITE expression
     ;
    
    // By making this a parser rule, you needn't inspect the lexer rule 
    // to see if it's true or false.
    bool
     : TRUE
     | FALSE
     ;
    
    ////////////////////////////////// 
    // LEXER
    //////////////////////////////////
    
    PLUS                        : '+';
    MINUS                       : '-';
    MULTIPLY                    : '*';
    DIVIDE                      : '/';
    
    OPEN_CURLY                  : '{';
    CLOSE_CURLY                 : '}';
    OPEN_PAR                    : '(';
    CLOSE_PAR                   : ')';
    COLON                       : ':';
    NEW_LINE                    : '\r'? '\n';
    
    IF                          : 'if';
    ELSE                        : 'else';
    END                         : 'end';
    WHILE                       : 'while';
    UNTIL                       : 'until';
    DO                          : 'do';
    EQUALS                      : 'equals';
    NOT                         : 'not';
    IS                          : 'is';
    LESS                        : 'less';
    THAN                        : 'than';
    GREATER                     : 'greater';
    WRITE                       : 'write';
    AND                         : 'and';
    OR                          : 'or';
    
    TRUE  : 'TRUE'  | 'true'  | 'YES' | 'yes';
    FALSE : 'FALSE' | 'false' | 'NO'  | 'no';
    
    INT
     : DIGIT+
     ;
    
    // (DIGIT+)? is the same as: DIGIT*
    FLOAT
     : DIGIT+ [.,] DIGIT*
     | DIGIT* [.,] DIGIT+
     ;
    
    // If a rule can never become a token on its own (an INT will always 
    // be created instead of a DIGIT), mark it as a 'fragment'.
    fragment DIGIT
     : [0-9]
     ;
    
    // Added support for escaped backslashes.
    STRING
     : '"' ( '\\"' | '\\\\' | ~["\\] )* '"'
     ;
    
    // Can it start with a digit? Maybe this is better: [a-zA-Z] [a-zA-Z0-9]*
    ID
     : [a-zA-Z0-9]+
     ;
    
    WHITESPACE
     : [ \t]+ -> skip
     ;
    
    COMMENT
     : ( ';;' .*? ';;' | ';' ~[\r\n]* ) -> skip
     ;  
    

    哪个解析器都可以在没有问题的情况下进行 while 构造。另请注意,我对您的语法进行了细微调整(请参阅内联 cmets)。一元表达式很重要,否则1-2 将被标记为2 个INT 标记,在解析器中无法匹配为expression

    【讨论】:

    • 谢谢!这解决了问题,这确实是对语法的一些重大修正。我没有想过需要将使用空格的标记拆分为多个标记以及一元表达式。我真的很高兴你抓住了这一点。我不完全确定究竟是什么解决了 while/do-while 问题。两个表达式使用相同的标记吗?
    • @simonbs,do-while 解析器规则使用了标记 DO_WHILE_CONDITION,但由于规则 WHILE 匹配相同的字符 和,因此词法分析器永远不会创建此标记i> 放在DO_WHILE_CONDITION 之前。
    • 好的,我明白了。谢谢!在旁注中,我发现规则必须是 program : block; block : NEW_LINE* (statement (NEW_LINE+ | EOF))* 有两次 EOF 如果输入没有以新行结束,它会期望它两次。
    • @simonbs,我相应地调整了我的答案(在这种情况下请随意编辑我的答案!)。
    猜你喜欢
    • 1970-01-01
    • 2014-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-25
    • 1970-01-01
    相关资源
    最近更新 更多