Haskell interpreter of my little functional language, Dackling.
Inspired by OCaml, but in the end, feels more like baby Haskell to me.
Important things:
-
How to run it? Simply:
make
./Dackling your_program_name
or, alternatively
./Dackling
for standard input read.
Note: interpreter does evaluation of given expressions only when it encounters EOF - for me, Ctrl+D works in standard input read mode. -
It uses layout to distinguish between instructions - every each should start at the beginning of a new line. Indentation matters when pattern matching:
match id with
| pattern1 -> ...
| pattern2 -> ...in ...
(The "in" has to start vertically between "match" and matching patterns).
Aside from that it's rather flexible. -
There are only two general types of instructions - function calls (aka expressions - those are evaluated) and definitions.
Expression should evaluate to a number, boolean or a list of numbers or booleans, or a list of lists of thereof and so on.
Otherwise we get an error.
Definitions let us define global functions. Every expression is actually a function getting called, for example3
is a constant function evaluating to Integer 3 and so on.
-
Dackling subtleties:
A whole type has to be given in front of a function. If a function takes in function, its type should be inside of "()" as inlet (Int->Int)->Bool->Int: name IntToIntArg BoolArg = if BoolArg then IntToIntArg 1 else IntToIntArg 0
":" between Type and name is mandatory.
Lambda's body has to be inside "()" unless it's a single number/true/false/identifier, sinve it has no name it has also no ":" after its type and it ends just as it started, with "\", as in\Int->Int x = (x + 1)\ 5
All lists start with "[:" and end with ":]", have elements separated with commas, elements are added to the front with "::".
0::[:1, 2, 3:]
Most of the dirty work is done by the type checker (TypeCheck.hs) - it's a source of ~90% of the errors. Errors from interpreter (Dackling.hs), aside from division/modulo by zero, should never arise. They are just there to exhaust the pattern matching.
Dackling has been based off a TestDackling.hs generated by BNFC to handle the input system smoothly from the very beginning.
My favorite (and aside from IO(), the only) monad from this project is Either, and I am using it to handle errors more or less smoothly.
Type checker and interpreter's job is actually very similar in construction ("checkType" is parallel to "eval"), but type checker is simpler in logic and more complex in error exhaustion, it's like a roller for the code.
In both of them, which I hate, there are very similar fragments that I wish I had some external function for, but I wish more to finish this whole project as soon as possible due to the deadline coming.