Interpreter Project: Control Flow

This step has been far easier than the last. It is getting to be quite simple to add features now. Add the AST Node description, add code to the parser to look for the new syntax, create the AST Node object from the syntax and finally write the visit method in the interpreter to execute the AST Node.

The current code can be seen here.

Syntactic Sugar

I like the way the for loop was added. I doubt I would have thought to implement it this way. If you have read many programming books you probably have heard that any for loop can be rewritten as another kind of loop. For instance:

for(int i = 0; i < 10; i++)
{
   print i;
}

Could also be written as a while loop like so:

int i = 0;
while(i < 10)
{
   print i;
   i += 1;
}

The for loop is called syntactic sugar. It is just a more concise way of writing some other construct.

The interesting thing about how it is implemented in the interpreter is that the for loop is deconstructed and put back together as a while loop. It makes the interpreter cleaner since it doesn't need different looping methods, but it does make parsing the for loop look ugly.

# A traditional 'for' looping statement like the one in C.
def for_statement
  consume(:lparen, "Expect '(' after 'for'.")

  initializer = nil
  if match?(:semicolon)
    initializer = nil
  elsif match?(:var)
    initializer = var_declaration
  else
    initializer = expression_statement
  end

  condition = nil
  if !check?(:semicolon)
    condition = expression
  end
  consume(:semicolon, "Expect ';' after loop condition.")

  increment = nil
  if !check?(:rparen)
    increment = expression
  end
  consume(:rparen, "Expect ')' after for clause.")

  body = statement

  if !increment.nil?
    body = Ringo::Block.new([body, Ringo::Expression.new(increment)])
  end

  if condition.nil?
    condition = Ringo::Literal.new(true)
  end
  body = Ringo::While.new(condition, body)

  if !initializer.nil?
    body = Ringo::Block.new([initializer, body])
  end

  return body
end

That looks like a mess, but all it is doing is breaking apart a for statement and building an equivalent while loop out of it. Things get tricky because any of the three clauses within the parentheses can be empty, so the parser has to handle those cases.

Moving Forward

Up next is functions. I can't wait. Functions are the defining feature for all languages right? Is there a single programming language that doesn't have functions in some form or another? I doubt it. I have a feeling that implementing function calls is going to be tricky. I will have to tell the difference between an identifier and a function name, store the function name in the symbol table along with the associated code and somehow execute those statements when they are called.