Milestone 4: Control Flow
In this fourth milestone, you'll add conditional statements, loops, and functions to you box project. These features will build on your previous work in the lexer, parser, and model classes. You'll need to expand your runtime environment abstraction and expression hierarchy from milestone 1. You'll be adding new structures to the grammar, which means you'll be expanding the lexer and parser from milestone 2. If your model and view are cleanly separated, you shouldn't need to make any changes to your user interface from milestone 3.
Let's examine each of these new features in detail.
Conditional Statements
Add support for conditional statements so that users can write code like this:
if x > y
1
else
0
end
if x > y 1 else 0 end
Make a conditional node type in your model classes that stores the condition, a then block, and an optional else block. Augment your lexer, parser, and visitors to handle conditional nodes. When evaluated, the node evaluates the condition and chooses which block to evaluate. The value returned by the conditional is the value returned by the block that gets executed. If no block is executed, return null as a model primitive.
While Loops
Add support for while loops so that users can write code like this:
i = 1
sum = 0
while i <= 100
sum = sum + i
i = i + 1
end
print sum
i = 1 sum = 0 while i <= 100 sum = sum + i i = i + 1 end print sum
Make a while node type in your model classes that stores the condition expression and the block. Augment your lexer, parser, and visitors to handle while loops. When evaluated, it checks the condition. If it's true, it executes the block. It repeats this until the condition is false. Watch out for infinite loops.
For-each Loops
Add support for for-each loops so that users can write code like this:
sum = 0
for i in [0, 100]
sum = sum + i
end
print sum
sum = 0 for i in [0, 100] sum = sum + i end print sum
Make a for-each node type in your model classes that stores the name of the loop variable, the starting value, the ending value, and the block. Allow the values to be arbitrary expressions, not necessarily literals. Augment your lexer, parser, and visitors to handle for-each loops. When evaluated, the for-each iterates through its range, sets the named variable to the current value, and executes the block. It returns the last iteration's value.
Function Definitions
Add support for function definitions so that users can write code like this:
function double(x)
return x + x
function double(x) return x + x
Make a function definition node type in your model classes that stores the function's name, a list of formal parameter names, and the function body. Augment your lexer, parser, and visitors to handle function definitions. Add a dictionary of function definitions to your runtime object. When this node is evaluated, store this node in the runtime's function definitions keyed by its name. Do not execute the block, as the function has not yet been called.
Also add a return node type. Since a return statement upsets the normal control flow of a sequential execution, have it throw an exception carrying the returned value. The exception isn't actually bad. It's just a convenient way to jump to the caller instead of running the next statement in the body.
Function Calls
Add support for function calls so that users can write code like this:
print double(4 + 1)
print double(4 + 1)
Make a function call node type in your model classes that stores the called function's name and a list of actual parameter expressions. Augment your lexer, parser, and visitors to handle function calls.
When a function call is evaluated, execute the named function's body. But don't use the same runtime. Since functions run in a different scope, make a new runtime with bindings for the parameters. If the body throws a return statement exception, catch it and return the value. Any other exception should propagate. If there was no exception and return value from the body, return null as a model primitive.
Submission
To submit your milestone, follow the instructions. In particular, do these three things:
- Submit your project to the official GitHub repository that your instructor made for you.
- Record a 3–5 minute screencast of you walking through your code and commenting on it. Do not exceed 5 minutes. Your instructor has many videos to watch. Post it to Canvas Studio according to the instructions.
- Complete the ready date submission form on Canvas.
In your video, demonstrate entering conditionals, while loops, for-each loops, and functions using the Curses interface that you built for milestone 3.
Don't comment on or show every single line of code in your video. Select a few representative snippets. Do comment on programming language ideas that interest you or challenged you.