Implementing Goto

new topic     » topic index » view thread      » older message » newer message

Here's an attempt to implement a GOTO statement in Euphoria. It doesn't work 
(yet), but it's a start. Hopefully people can help me out here. I haven't 
seen the latest release of the Euphoria source, so perhaps there are some 
answers in there, too.

The grammar is:

   LABEL <label>
   GOTO <label>

To keep things simple in this example, I'll only allow one label and one 
forward reference. Here's an example:

   puts( 1, "this should execute" )
   goto jump_here
   puts( 1, "this shouldn't execute" )
   label jump_here

OK, on to the code. The first thing that needs to be done is to define 
'label' and 'goto' as Euphoria keywords. This is pretty easy.

1. Create the LABEL and GOTO token in the reserved words list. 
   Add the following to RESWORDS.H after the definition of NAMESPACE:

         #define LABEL    524
         #define GOTO     525

2. Add the keywords "label" and "goto" to the keyword list. 
   Go to KEYLIST.H and in the keylist struct, add after "platform":

         {"label",       S_KEYWORD, LABEL, 0, 0, 0},
         {"goto",        S_KEYWORD, GOTO, 0, 0, 0},

Now that the keywords are defined, we need to implement the grammar. 
Statements are implemented in PARSER.C in two places - parser() and 
Statement_list(). parser() is used to implement command level statements. For 
no compelling reason, we'll only allow 'goto' to be defined in functions and 
procedures. 

3. Add the following to the case statement in parser() in PARSER.C:

    case LABEL:
        CompilerErr("label only allowed in function or procedure");
        break;

    case GOTO:
        CompilerErr("goto only allowed in function or procedure");
        break;

4. Add the following to Statement_list() in PARSER.C:

    case LABEL:
        StartSourceLine(TRUE);
        Label_statement();
        break;

    case GOTO:
        StartSourceLine(TRUE);
        Goto_statement();
        break;
        
For the first pass, I'll implement dummy versions that parse, but don't do 
anything:

5. Here's a dummy version of the code to parse the label 
    grammar. It needs to be added in PARSER.C before the
    definition of Statement_list(). The 'label' has already been
    parsed by Statement_list(), so we just need to read the 
    label that follows. This is done by token(). If token() can't
    find the token in the table, it creates it as a variable:

    static void Label_statement()
    /* parse a label statement */
    {
        TOKEN label;

        /* read the label */
        token( label );
        if (label.id != VARIABLE) {
            CompileErr("a label name is expected here");
        }    
      }

6. The dummy version of 'goto' has the same grammar - read
    the label that follows:

    static void Goto_statement()
    /* parse a goto statement */
    {
        TOKEN label;

        /* read the label */
        token( label );
        if (label.id != VARIABLE) {
            CompileErr("a label name is expected here");
        }    
      }

Compile the code, and you have a version Euphoria that supports the 'goto' 
grammar. It doesn't actually do anything, but it doesn't crash, either. So 
it's a good start.

Time to get to the more speculative part - the part that *does* crash.

Looking through do_exec (in EXECUTE.C), I tried to find an opcode that 
performs an unconditional jump. L_EXIT looks like it'll do (actually, L_EXIT, 
L_ENDWHILE, L_ELSE and L_SPLIT_ELSE look like they do the same thing), so I 
figure something like this:

   emit( EXIT )
   emit_addr(  <address> )

is what I'm after. It *looks* like CodeIndex holds the address where the code 
is being compiled to. 

I need two variables - one to hold the address of the label, and the other to 
hold the address of the goto to backpatch. In more advanced implementations, 
these would be coded as tables:

  int *goto_address = NULL;
  int *goto_backpatch = NULL;

When 'label' executes, I can grab CodeIndex and store it, and backpatch any 
prior 'goto' statements:

  static void Label_statement()
  /* parse a label statement */
  {
      TOKEN label;

      /* read the label */
      token( label );
      if (label.id != VARIABLE) {
          CompileErr("a label name is expected here");
      }
      
      /* address already defined? */
      if (goto_address != NULL) {
          CompileErr("label defined multiple times");
      }

      /* set address */
      goto_address = CodeIndex;

      /* backpatch prior statement? */
      if (goto_backpatch != NULL) {
         backpatch( goto_backpatch, goto_address );
      }
      
    }

When 'goto' executes, if the label has been defined, it compiles:

   emit_op( EXIT )
   emit_addr( goto_address )

Otherwise, it compiles:

   emit_op( EXIT )
   emit_addr( NULL )
    
and stores the address that NULL is stored into goto_address to backpatch 
later:

  static void Goto_statement()
  /* parse a goto statement */
  {
      TOKEN label;

      /* read the label */
      token( label );
      if (label.id != VARIABLE ) {
          CompileErr("a label name is expected here");
      }

      /* emit an EXIT */
      emit_op( EXIT );

      /* does the label exist? */
      if (goto_address) {
         emit_addr( goto_address );

      } else {
          /* store address to backpatch*/
          goto_backpatch = CodeIndex;

          /* put in dummy address */
          emit_addr( NULL );
      }

    }

Unfortunately, this crashes and burns, so I'm getting *something* wrong.

Anyone want to help me puzzle this one out?

Thanks!

-- David Cuny

new topic     » topic index » view thread      » older message » newer message

Search



Quick Links

User menu

Not signed in.

Misc Menu