Functions
drizzle now supports functions and starts to look like a proper programming language. Its syntax is similar to an unknown snake-like language used in niche fields like machine learning. Keeping in line with that theme, here is how a function is defined:
def sum(a, b):
return a + b
Simple, I know. But I still made some mistakes along the way. Consider this example:
def outer():
def inner():
noop
return inner
outer()()
Here we first call the outer
and then the returned inner
function. The parser produces a Call
expression, and the compiler then turns it into opcodes that push callee and arguments onto the stack.
struct Call {
- Identifier callee;
+ Expr callee;
Exprs arguments;
};
The initial implementation expected an identifier for the callee. That worked for simple cases, but using an expression is the correct way to do it. The syntax tree reflects that change and properly displays chained function calls.
program
def outer
def inner
noop
return
variable inner
expression_statement
call
call
variable outer
Closures
Programming languages with first-class functions support closures. They are used to bind the surrounding lexical scope and make sure that referenced variables have the same lifetime as the function using them.
def make_counter():
var i = 0
def counter():
i = i + 1
return i
return counter
var counter = make_counter()
counter() # Returns 1
counter() # Returns 2
The example would work if closures were implemented, but drizzle throws an error:
Line 4 | i = i + 1
^
SyntaxError: cannot capture local variable
Closures add quite some complexity to the code that I am not willing to introduce and to be honest: I do not need them. I tried some magic tricks to achieve a minimalistic version, but all of them fell apart when it came to recursion. The best I could do was absolute stack offsets to access global variables.