Search code examples
if-statementantlrbytecodejasminjvm-bytecode

How to generate bytecode for if-else statement


how do I generate the code corresponding to a bytecode instruction IF THEN - ELSE with the optioanal ELSE branch?

For example, the program If-else.pas is considered correct, while the program If.pas isn't considered correct, because it doesn't contains the ELSE branch.

If-else.pas

var a, b : integer;
begin
    a := 3;
    b := 5;
    if a > b then 
        print(a)
    else 
        print(b)
end

If.pas

var a, b : integer;
begin
    a := 3;
    b := 5;
    if a > b then 
        print(a)
end

So Jasmin give me this error:

Output.j:62: JAS Error: Label: L11 has not been added to the code.

Output.j: Found 1 errors

My grammar .g has this rule:

stmt -> ID := expr
     | print( expr )
     | if( expr ) then ( stmt ) [ else stmt ]?
     | while( expr ) do stmt
     | begin stmt [ ; stmt ]* end

For the if-else statement I wrote this:

'if' 
    {
        int lfalse = code.newLabel(); //Generates a new number for the LABEL
        int lnext = lfalse;
    }
    ( expr )
    {
        if($expr.type != Type.BOOLEAN) //Checking the condition is boolean
            throw new IllegalArgumentException("Type error in '( expr )': expr is not a boolean."); 
        code.emit(Opcode.IFEQ, lfalse); //I create the instruction IFEQ L(lfalse)
    }
    'then' s1 = stmt 
    {   
        lnext = code.newLabel(); //Generates a new number for the LABEL
        code.emit(Opcode.GOTO, lnext); //I create the instruction GOTO L(lnext)
        code.emit(Opcode.LABEL, lfalse); //I create the instruction L(lfalse):
    }
    ( 'else' s2 = stmt 
    {       
        code.emit(Opcode.LABEL, lnext); //I create the instruction L(lnext):
    })?

But in this way the second branch is not optional, but must always be present. How do I make it optional? I thought it was necessary the question mark (( 'else' s2 = stmt )?), but no. I am using ANTLR.

Thanks.

I do not know if the bytecode files (. J) generated by Jasmin will be useful, but I write it.

If-else.j

    ldc 3
    istore 1
    ldc 5
    istore 0
    iload 1
    iload 0
    if_icmpgt L7
    ldc 0
    goto L8
  L7:
    ldc 1
  L8:
    ifeq L4
    iload 1
    invokestatic Output/printInt(I)V
    goto L11
  L4:
    iload 0
    invokestatic Output/printInt(I)V
  L11:
    return 

If.j

  ldc 3
  istore 1
  ldc 5
  istore 0
  iload 1
  iload 0
  if_icmpgt L7
  ldc 0
  goto L8
L7:
  ldc 1
L8:
  ifeq L4
  iload 1
  invokestatic Output/printInt(I)V
  goto L11
L4:
  return 

Solution

  • The problem here is that you're always generating a jump to LNEXT, but you don't generate the label itself when there's no else clause, leading to invalid code. You need to generate the label unconditionally.

    I'm not familiar with Antlr, but based on the way your code is written, I suspect this is the correct way to do it.

    'if' 
        {
            int lfalse = code.newLabel(); //Generates a new number for the LABEL
            int lnext = lfalse;
        }
        ( expr )
        {
            if($expr.type != Type.BOOLEAN) //Checking the condition is boolean
                throw new IllegalArgumentException("Type error in '( expr )': expr is not a boolean."); 
            code.emit(Opcode.IFEQ, lfalse); //I create the instruction IFEQ L(lfalse)
        }
        'then' s1 = stmt 
        {   
            lnext = code.newLabel(); //Generates a new number for the LABEL
            code.emit(Opcode.GOTO, lnext); //I create the instruction GOTO L(lnext)
            code.emit(Opcode.LABEL, lfalse); //I create the instruction L(lfalse):
        }
        ( 'else' s2 = stmt )?
        {       
            code.emit(Opcode.LABEL, lnext); //I create the instruction L(lnext):
        }