Pretty similar to the same chapter in crafting an interpreter in go, in this case it seem a little bit more engineered, also lox, altough simple, has more feature than monkey.
Define some class that in the next chapter will be used to build the AST. Really nice explanation of the visitor pattern, even better than GoF.
I really like the algorithm to parse expression into the AST, I find it easier to understand than Pratt Parser.
The java code is not that idiomatic, but since the nature of the project it is ok. This tell more about the standards and what we are taught to be good in a language rather than this code is bad.
It work as expected, and also behave as in many other language.
It is untuitive that the right hand side is evaluated before the left hand side, so the a + 2
use the a
from outer env and bind to a
in the inner env.
The print statement use the a
from the inner/local env.
I’ve made some mistake in the past and picking up the project after a couple of months I was not able to find the mistake, hence I copied a version that work up to chapter 9 (if you ever see this, thanks Silvernitro).
You can create control flow with dynamic dispatch, it is a little bit clumsy and not so much readable.
So if you ever want to create a programming language without branching statements, please, keep it to yourself.
To complete the answer we can define methods that act as if
and else
based on the type.
To make it feasible we can make one method that accept our concept of truth
and another that accept falsiness
(of course both should be able to accept a code block).
Dynamic dispatch is far more slower than branching hence we need our, hypothetical, language need to be optimized for dynamic dispatch. I can’t name a language that use this concept for iteration but I can made some guesses:
ruby
, it support traditional loops, but it can also be implemented with objects.smalltalk, pharo
, in such languages I’ve heard that everything (even more than java) is an object.java
, in particular withstream
(i.e.IntStream
) you can write loop in an object oriented way (to me streams doesn’t seem to functionals).
Add break
instruction.
Because in smalltalk everything is an object, hence if you create a function you have a constructor with a predetermined number of argument. If you call such a function with the wrong number it won’t compile.
Add anonymous function.
fun scope(a) {
var a = "local";
}
Of course it is a valid lox script. When a function is defined the paramters are binded to the function environment. Subsequently the rest of the instruction override the local env. For me a language should do what lox is doing, allow the code and use the innermost definition in the function body. Some exception can be made for functional language, but not by much.
From now on I will not implement the exercise like implement ?:, implement break, they stack up adding more complexity at each chapter, instead I will provide a desccription of how I will proceed.
A function only need to be defined, instead an initializer need to be executed before we know the value to associate to the variable.
All the ones I can think about will allow such definition, to me make quite sense.
I want to initialize a
, to initialize a
I need to access a
, but it is defined outside the scope, hence the expression on the right refer to the outer (global) a
.
After the resolve
method I will check if every variable defined is accessed, even through the map I already have or through another one.
Maybe an array that count the references in the blocks to each variable (except definition and initialization), if it is
Pretty simple, provided an ArrayList
and a BigInteger
generator the request is pretty trivial, I just need to add an index to the variables fields.
Altough it suggest to reuse the class
keyword I will prefer a new keyword for static methods.
The idea that come to mind is to do something similar to this
.
When a static method is found it should add a function to the scope of the class (it require a new scope who sits below this
).
I don’t think I need to add more code to access class.static_method(...)
.
Yuck, this look sick and awful at the same time! To implement this I will check if after an identifier there is a body, in such case I will parse it (this doesn’t seem trivial). After I have parsed the body I will create a function with such name but no parameter. Maybe I should add a new rule to call this kind of methods without the use oof parentheses.
Using public
and private
make your language more robust, less flexible, it follow more the encapsulation.
This usually lead to hard to extend and hard to maintain code (on the long run I mean, especially I you pick the wrong abstractions) but more performant.
I only say this because among the language that I’ve used the ones with public, private are more perfomant (compared to other OO languages).
On the other hand avoid such complicancy let you write code faster it also easy to refactor.
None of that, I find multiple inheritance bad, in general OOP but I may have bias on this regard. Something similar that I liked is multimethods in clojure.
This will work only with single inheritance.
To make it work, when I bind/resolve super I must also bind inner
in the superclass to point to the subclass environment.
?:
, I’ve implemented it but it is lost in time (in git) now.orelse
keyword that act likezig
.- More than adding a new feature, I would like to write a langauge in which everything is a expression, this may create some problems with
if
,loop
, etc…
So far so good, have a builtin dynamic array, testing suite and nicer syntax is almost a cheat. The approach used to implement dynamic array in C is to gross for me to type out.
Still pretty doable, maybe I can implement the OpCode for add, sub, mul, div in a better way.
The use of global variable to simplify the code already showed its limitations, I can’t test my code properly because test are parallel, I may disable this feature, but this doesn’t solve the problem. I may should rewrite this first 2 chapter to make the `VM` and the `Scanner` local and not global.
`(` used as prefix for grouping and infix for calling (functions or methods)
Add a field `parseMixfix` into the `ParseRule` struct and then parse and produce the bytecode accordingly.