Skip to content

marekvospel/pebble

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pebble

A tiny interpreted language made to learn haskell.

Features

  • 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...

Examples

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)

Compiling / running project

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 test

Running main example (app/Main.hs):

stack run

AST

The 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"]
        ]))
    ]
  ]

Review notes

  • 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 ())

About

A very simple interpreted language written in Haskell

Topics

Resources

License

Stars

Watchers

Forks

Contributors