Milestone 4: Control Flow
In this fourth milestone, you'll add conditional statements, while loops, for-each loops, and local functions to your spreadsheet. These features will build on your previous work in the lexer, parser, and model classes. You'll need to expand your environment abstraction and expression hierarchy from milestone 1. You'll be adding new structures to the spreadsheet grammar, which means you'll be extending the lexer and parser from milestone 2. If your model and view are cleanly separated, you shouldn't need to make many 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 #[2, 1] > #[2, 0]
print("then")
1
else
print("else")
0
end
if #[2, 1] > #[2, 0]
print("then")
1
else
print("else")
0
endMake a conditional node 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, a conditional examines 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 the condition is not true and no else block is present, 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:
count = 0
for value in [4, 0]..[5, 3]
if value > 0.0
count = count + 1
end
end
count = 0
for value in [4, 0]..[5, 3]
if value > 0.0
count = count + 1
end
endMake a for-each node in your model classes that stores the name of the loop variable, the starting cell address, the ending cell address, and the block. Augment your lexer, parser, and visitors to handle for-each loops.
When evaluated, it iterates through the two-dimensional window of cells and assigns each cell's value to a local variable with the given name. The block is evaluated for each cell in the given window. The value returned by the block on the final iteration is the value returned by the larger loop.
Function Definitions
Add support for function definitions so that users can write code like this:
function double(x)
xx = x + x
return xx
end
function double(x) xx = x + x return xx end
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 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 if statements, while loops, for-each loops, and local 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.