A toy programming language for me to learn and explore compilers/the compilation process/compiler internals/etc.
- Clone the repo
cargo install
cargo build -r
(optional: this will build to./target/release
)
You can run the interpreter via cargo run -- run <file>
or start the REPL with just cargo run -- repl
.
The language can be best described as lisp without the parentheses, everything is declared and read left to right. Comments are declared using #
unlike lisp — semicolons actually have meaning (they are delimiters, as they should be).
Variables can be assigned and reassigned using =
:
= foo 24
= foo "bar"
There are primitive data types such as integers, floats, strings and booleans as per usual. However, there is no explicit null
. Instead, null
is represented via an empty expression ()
(also known as "unit" in some actual languages).
Note Operators will only compare arguments of the same type, except for
==
with()
.
Definition | Description |
---|---|
== a b ... |
Equality: checks if a is equal to any of the other arguments. |
> a b ... |
Comparison: checks if a is greater than any of the other arguments. |
>= a b ... |
Comparison: checks if a is greater than or equal to any of the other arguments. |
< a b ... |
Comparison: checks if a is less than any of the other arguments. |
<= a b ... |
Comparison: checks if a is less than or equal to any of the other arguments. |
+ a b ... |
Addition: adds all the arguments sequentially. |
- a ... |
Subtraction: subtracts all the arguments sequentially. Negates if there is only one. |
* a b ... |
Multiplication: multiplies all the arguments sequentially. |
/ a b ... |
Division: divides all the arguments sequentially. |
&& a b ... |
Logic And: checks if all arguments are truthy. |
|| a b ... |
Logic Or: checks if at least one argument is truthy. |
! a |
Inverse: gets the inverse value of a . Only works for boolean values. |
There is no explicit !=
(not equal) operator because this can be achieved by combining the inverse and equals operators:
# works the same
! == 2 4 # boolean : true
Functions can be declared using braces. A function's return type is inferred from last expression in the function block. To call a function, simply specify provide the arguments after the function name. If the function doesn't take any arguments, it can be called with ()
.
= random { 42 }
random () # integer : 42
Function parameters can be defined in brackets following the opening brace:
= add { [a b] + a b }
add 2 3 # integer : 5
Note that calling a function that has a singular argument with ()
still works:
= is_null { [a] == a () }
is_null () # boolean : true
Because of this rule, this allows functions to be passed around as arguments:
= sum_numbers { [fn] fn 2 4 6 }
= add { [a b c] + a b c }
= subtract { [a b c] - a b c }
sum_numbers add # integer : 12
sum_numbers subtract # integer : -8
Alternatively, you can pass in a function literal directly (i.e. closures) but this may not be a permanent feature:
sum_numbers { [a b c] + a b c } # integer : 12
Control flow in the form of if
and else
statements is possible (chained else-if
statements coming soon). Here's fibonacci (it actually works):
= fib { [n]
if < n 2 {
1
} else {
+ (fib - n 2) (fib - n 1)
}
}
fib 12 # integer : 233
- Control statements (
for
) - Module management (
import
,export
) - Method calls
- Data types (
object
,init
) - Function type signatures
- Error management (
error
,catch
) - Null safety
- Separation of interpretation and compilation
This repository is managed under the Mozilla Public License v2.
© 2023 devnote-dev