My new simple language, blockd, that I’ve mentioned in my last blog post is progressing quite nicely.
Since my last post a few days ago, I’ve worked on it quite a bit and I’ve got a first simple interpreter for it working.
For now, the interpreter is able to understand & execute methodcalls on objects, anonymous methods (blocks & procs in ruby) and some other minor stuff.
Heres a small example, that actually works now:
y = { |x|
Console puts: x
Console puts: (x * x)
}
y call: 10
m = { |x y|
Console puts: ((x upcase) * y)
}
m call: "cool " y: 10
square = { |y|
y * y
}
cubic = {|x| (x * x) * x}
3 times do |i|
Console puts: i
end
foo = {
3 times { |i| Console puts: (square call: i) }
}
Console puts: "--"
foo call
10 times &foo |
This basically is equivalent to the following ruby code:
y = Proc.new{|x| Console.puts(x);Console.puts(x.*(x))}
y.call(10)
m = Proc.new{|x,y| Console.puts(x.upcase().*(y))}
m.call("cool ", 10)
square = Proc.new{|y| y.*(y)}
cubic = Proc.new{|x| x.*(x).*(x)}
3.times(&Proc.new{|i| Console.puts(i)})
foo = Proc.new{3.times(&Proc.new{|i| Console.puts(square.call(i))})}
Console.puts("--")
foo.call()
10.times(&foo) |
Now, to make this clear: The ruby code above is what the current interpreter actually executes. The way I’ve implemented it for now (and for simplicity only – just to get something working first) is, that it simply parses the blockd code and translates it into equivalent ruby code, which then gets executed via eval in ruby.
Now I know, this isn’t the best way to do it, for several reasons:
Normally, an interpreter executes the code it gets fed directly, expression for expression, as this is also the case for ruby. If you have an syntax error within the code that is evaluated, the interpreter usually notices this only at the moment it reaches that part of the code and tries to evaluate it. This isn’t the case for the current interpreter I wrote here. The reason I chose to do it this way, was that I wanted to get something working as possible. I do want to change it though later on, so that each expression gets executed on the fly, once it is parsed.
So for now, i evaluate the parsetree, and each expression node gets turned into a string of equivalent ruby code, that then gets saved in a global list (within a class called Evaluator). When the parser has done it’s job and all the values (strings of ruby code) of all expression nodes have been accumulated, i simply run ruby’s eval on it and at that point do the actual evaluation (and possibly get semantic errors here – like missing variables etc.).
I still need to implement some stuff regarding classes & modules, but I’m quite happy for now, since it wasn’t so hard as I had expected before. Once I have everything working, I’ll start to actually rewrite the parser in a way, that it evaluates & executes code on the fly, expression per expression and not all of them at once at the end. This should also improve performance, which is obviously another downside of my current approach, since I have to wait for all expressions being parsed & evaluated to appropriate ruby code first, before even beginning the actual evaluation process…
For anyone, who is interested, here is a simple benchmark of the code above:
The blockd version (executed with my current interpreter):
real 0m0.264s
user 0m0.184s
sys 0m0.072s
The ruby version of the equivalent code (generated by my parser):
real 0m0.021s
user 0m0.008s
sys 0m0.008s
As you can see, there’s quite a performance overhead. Although not really noticeable by a viewer, the numbers tell the story. So I expect quite an improvement in performance, once I’ll start rewriting the interpreter in the way I’ve mentioned above. Still cool though, if you ask me