https://github.com/marekvospel/pebble
A very simple interpreted language written in Haskell
https://github.com/marekvospel/pebble
haskell interpreter programming-language
Last synced: about 19 hours ago
JSON representation
A very simple interpreted language written in Haskell
- Host: GitHub
- URL: https://github.com/marekvospel/pebble
- Owner: marekvospel
- License: mit
- Created: 2025-10-23T10:44:04.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2026-02-04T00:52:13.000Z (5 months ago)
- Last Synced: 2026-05-05T15:51:57.082Z (about 2 months ago)
- Topics: haskell, interpreter, programming-language
- Language: Haskell
- Homepage:
- Size: 59.6 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Pebble
A tiny interpreted language made to learn haskell.
## Features
- [X] Everything is an expression. *`let x = if (something) 42 else 100;` works!*
- [X] Calling functions. *You can do while loops using recursion!*
- [X] Very simple AST. *Arithmetic operations are just function calls!*
- [X] STD that can interact with IO. *Soon, even reading from stdin!*
- [X] Clean implementation. *Using State+Except Monads!*
- [X] 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](#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:
```sh
stack test
```
Running main example (`app/Main.hs`):
```sh
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)
```haskell
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](#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`)
```haskell
-- 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 ())
```