Search code examples
parsingantlrqbasic

Grammar parser suggestion (ANTLR)


Well I'm trying to write a simple QBasic grammar on Antlr4. And 'Else-If' loop won't works correctly, it automatically converts to assigncommandafter THEN. And could you review my grammar, is there any improvements?

How to write an string in regular expression.(also with cyrillic alphabets)

Should i write these key words ('PRINT' 'IF')? or use lexer(like ..PRINTKEY; PRINTKEY : 'PRINT')

grammar Hello3;

// AssignCommand; MainCommand; FlowCommand
prog : (assigncommand | maincommand | flowcommand)+;

// AssignInt; AssignString
// MyAge = PreviousAge + 1
// MyName$ = FirstName$ + MiddleName$ + LastName$
assigncommand : assignint | assignstring;
assignint : IDINT '=' (IDINT | INT) (OPERATORMATH (IDINT | INT))* '\n'+;
assignstring : IDSTRING '=' (IDSTRING | STRING) ('+' (IDSTRING | STRING))* '\n'+;

//PrintCommand, InputCommand
//PRINT MyName$, MyAge, "Hello", 123
//INPUT "What is your name?", yourname$
//(or)INPUT yourname$ 
maincommand : printcommand | inputcommand;
printcommand : 'PRINT' (',' (IDINT | IDSTRING | STRING | INT))+ '\n'+;
inputcommand : 'INPUT' (IDINT | IDSTRING | STRING)? ',' (IDINT | IDSTRING) '\n'+;


//If-ElseFlow; WhileFlow
//If-Else-Add; Else-Add
//
//IF a > 3 THEN
//PRINT a
//a = a -1
//ELSE IF a = 1 THEN
//b = a
//END IF
//
//WHILE a > 3
//a = a - 1
//PRINT a
//WEND
flowcommand : ifelseflow | whileflow;
ifelseflow : 'IF' conditionflow 'THEN' '\n' ifelseadd* elseadd* 'END' 'IF' '\n'+;
whileflow : 'WHILE' conditionflow '\n' (assigncommand | maincommand | flowcommand)*  'WEND' '\n'+;

conditionflow : ((INT | IDINT) OPERATORBOOL (INT | IDINT)) | ((STRING | IDSTRING) '=' (STRING | IDSTRING));
ifelseadd : 'ELSEIF' conditionflow 'THEN' '\n' ((assigncommand | maincommand | flowcommand) '\n')+;
elseadd : 'ELSE' '\n' ((assigncommand | maincommand | flowcommand) '\n')+;

//Lexers
INT : [0-9]+;
STRING : '"' [a-zA-Z\u0400-\u04FF\0-9' ''?'':']+ '"'; 
IDINT : [a-zA-Z]([a-zA-Z0-9]*);       //MyAge
IDSTRING : [a-zA-Z]([a-zA-Z0-9]*)'$';  //MyName$ 
OPERATORMATH : '+'|'-'|'*'|'/';
OPERATORBOOL : '='|'>'|'<'|'>='|'<=';
WS : [ \t\r]+ -> skip;

Solution

  • Like you, I found implementation of if..else constructs in a BASIC-like language a real challenge to implement. I found some good resources online. Please take a look at my grammar snip:

    ifstmt
    :   IF condition_block (ELSE IF condition_block)* (ELSE stmt_block)?
    ;
    
    condition_block
    :   expr stmt_block
    ;
    
    stmt_block
    :   OBRACE statement+ CBRACE
    |   statement
    ;
    

    And my implementation (in C# visitor pattern):

        public override MuValue VisitIfstmt(LISBASICParser.IfstmtContext context)
        {
            LISBASICParser.Condition_blockContext[] conditions = context.condition_block();
            bool evaluatedBlock = false;
            foreach (LISBASICParser.Condition_blockContext condition in conditions)
            {
                MuValue evaluated = Visit(condition.expr());
                if (evaluated.AsBoolean())
                {
                    evaluatedBlock = true;
                    Visit(condition.stmt_block());
                    break;
                }
            }
            if (!evaluatedBlock && context.stmt_block() != null)
            {
                Visit(context.stmt_block());
            }
            return MuValue.Void;
        }
    

    I borrowed the MuValue idea from Bart Kiers's excellent implementation of his Mu language. Lots of great ideas in that project of his.