A tiny interpreted language made to learn haskell.
- Everything is an expression.
let x = if (something) 42 else 100;works! - Calling functions. You can do while loops using recursion!
- Very simple AST. Arithmetic operations are just function calls!
- STD that can interact with IO. Soon, even reading from stdin!
- Clean implementation. Using State+Except Monads!
- Scoped variables. Your variables aren't polluted from previous calls!
- Stack traces. Required internals for this are in place...
- Parser, REPL. Needs more than a bit of work...
Since I haven't implemented the parser yet, pebble is written using it's AST. Luckily thanks to Haskell's data type definitions, this feels much closer to actual programming language than other ASTs.
There is an example program inside app/Main.hs (Factorial calculation).
Main.hs includes the logic to start evaluating any AST. You can execute
any other example. (see #AST section)
This project is using the stack toolchain to manage ghc versions, install dependencies, build modules and/or run tests. To install stack, visit https://docs.haskellstack.org/en/stable/
Running tests:
stack testRunning main example (app/Main.hs):
stack runThe AST types are described inside src/AST.hs. As mentioned, since everything
is an expression, almost everything is part of the data Expression type.
Here is an example of fibonacci's number calculation using pebble. (Uses all AST nodes that pebble has)
program :: AST.Root
program = [
FunctionDecl "main" [] [
VariableAssign "result" (FunctionCall "fib" [ExprLiteral (LitInt 10)]),
FunctionCall "print" [ExprLiteral (LitString "Fib 10: "), ExprIdent "result"]
],
FunctionDecl "fib" ["target"] [
FunctionCall "fib_impl" [ExprLiteral (LitInt 0), ExprLiteral (LitInt 1), ExprLiteral (LitInt 1), ExprIdent "target"]
],
FunctionDecl "fib_impl" ["x", "y", "i", "target"] [
If (FunctionCall "eq" [ExprIdent "i", ExprIdent "target"]) (ExprIdent "y")
(Just (BlockStatement [
VariableAssign "temp" (FunctionCall "add" [ExprIdent "x", ExprIdent "y"]),
FunctionCall "fib_impl" [ExprIdent "y", ExprIdent "temp", FunctionCall "add" [ExprIdent "i", ExprLiteral (LitInt 1)], ExprIdent "target"]
]))
]
]- I haven't implemented many of the bonus point exercies in favor of learning more about Haskell (namely Monads) and writing clean code, I hope this gets taken into account when reviewing.
- I didn't have much time because of other exams, however I'd like to finish at least all features in the #features section sometime in the future (and release this project to github when the semester is done)
- I haven't implemented while loops, since I didn't like the idea for this
project (and they can be done using functions & ifs) Here is how a while loop
could be implemented (Additions to
AST.hs&Eval.hs)
-- AST
data Expr = ... | While Expression [Expression]
--
eval_expr :: Expression -> EvalM Value
eval_expr (While predicate block) = do
cond <- eval_expr predicate
case cond of
VBool True -> do
modify $ \st -> st { callStack = (StackFrame {}:(callStack st)) }
val <- eval_while predicate block
cleanup_frame
pure val
_ -> do
pure (VVoid ())
eval_while :: Expression -> [Expression] -> EvalM Value
eval_while predicate block = do
res <- eval_block block
cond <- eval_expr predicate
case cond of
VBool True -> eval_while predicate block
_ -> do
pure (case res of
(val:_) -> val
_ -> VVoid ())