93 lines
2.4 KiB
Markdown
93 lines
2.4 KiB
Markdown
# milly
|
|
|
|
milly is an hobby, and should not be taken seriously.
|
|
|
|
Interpreter for a Minimal ML-like language whose ideal goal is to be used for bootstrapping compilers and interpreters.
|
|
|
|
## How to build
|
|
|
|
milly's interpreter is written in portable ISO C11, sources are in `src/` and header files are in `include/`.
|
|
|
|
In order to build it, you must have **GNU Make** installed (in the future i'll try to remove this dependency)
|
|
and you just have to run:
|
|
|
|
> $ make
|
|
|
|
in the project root to obtain the executable `milly`.
|
|
|
|
## The language
|
|
|
|
milly is a minimal dialect of Standard ML and Haskell,
|
|
here is a list of the features (or better, limitations) I want to introduce:
|
|
|
|
- Keep the implementation <5000 LOC
|
|
- Eager evaluation (call by value)
|
|
- Algebraic Datatypes (pretty useful for representing syntax trees)
|
|
- First class functions
|
|
- Pattern patching (pretty useful for analyzing syntax trees)
|
|
- Integers, booleans, strings
|
|
- Mutable `ref` cell
|
|
- FFI to C
|
|
- Polymorphic types à la Hindley Milner (probably unfeasible)
|
|
- Automatic currying of functions, with optimised partial evaluation (unfeasible)
|
|
- Delimited continuations (this is an overkill)
|
|
- Easy syntax allowing easy kakoune support
|
|
- A formal specification (this should be quite manageable to do, and also a good way to learn denotational semantics)
|
|
|
|
milly's full grammar is specified as a pair of lex and yacc files in `ref_parser/`.
|
|
|
|
kakoune plugin for milly is in `kakoune_rc/milly.kak`.
|
|
|
|
Here is a small example of the syntax:
|
|
|
|
```
|
|
# Helpers
|
|
datatype 'a list {
|
|
Cons of ('a, 'a list),
|
|
Empty
|
|
}
|
|
|
|
datatype 'a maybe {
|
|
Just of 'a,
|
|
Nothing
|
|
}
|
|
|
|
# Lambda calculus term, nameless representation
|
|
datatype term {
|
|
Var of int,
|
|
Int of int,
|
|
Abs of term,
|
|
App of (term, term)
|
|
}
|
|
|
|
# Result of the evaluation
|
|
datatype value {
|
|
Int of int,
|
|
Closure of (env, term)
|
|
}
|
|
|
|
alias env = value list
|
|
|
|
typecheck add_binding : value -> env -> env
|
|
def add_binding v env = Cons v env
|
|
|
|
typecheck lookup : int -> env -> value maybe
|
|
def lookup _ <Empty> = Nothing
|
|
| lookup 0 <Cons x xs> = Just x
|
|
| lookup n <Cons x xs> = lookup (subtract n 1) xs
|
|
|
|
typecheck eval : env -> term -> value
|
|
def eval e <Var idx> =
|
|
match lookup idx e {
|
|
case <Just v> -> v
|
|
case <Nothing> -> abort "Variable out of scope"
|
|
}
|
|
|
|
| eval e <Int x> = Int x
|
|
| eval e <Abs t> = Closure e t
|
|
| eval e <App t1 t2> =
|
|
let def new_env = add_binding (eval e t2) e
|
|
in eval new_env t1
|
|
```
|
|
|