The build process should be relatively straight foreward, thanks to the included CMake instructions. I used a Macbook running macOS Catalina 10.15.6 during the development of this interpreter. A github action was used to build on ubuntu 20.04 which you can see below. Windows wasn't tested but should hopefully work as well.
> cd <repo>
> mkdir build
> cd build
> cmake ..
> make
> ./schemeIf you'd like to be able to see more debug messages, build the interpreter with the following command instead: cmake -DCMAKE_BUILD_TYPE=Debug ... You can enable or disable individual log message categories in scheme.cpp.
Please also note that this was my first time writing anything substantial in C++. Weird language, especially when coming from python. Nonetheless, this was quite fun but in equal measures also frustrating. Well worth it though!
- 
free functions over class functions
- I generally like to stay as close to functional programming as possible - in this case meaning that I use classes and structs exclusively as data containers / data types. Relevant functionality is provided by free (friend) functions instead and lambdas are made use of where sensible. As my colleague Nils put it:
"good c++ code basically looks like c with more :: thrown into the mix".
 
 - I generally like to stay as close to functional programming as possible - in this case meaning that I use classes and structs exclusively as data containers / data types. Relevant functionality is provided by free (friend) functions instead and lambdas are made use of where sensible. As my colleague Nils put it:
 - 
readability over performance
- in the end, I don't care too much about how performant my solution is - if there's need for it, this can be done later. In the meantime, I'm going to gain a lot more from having code that others (and future me) can understand.
 
 - 
regex based parser
- I've got a faible for regular expressions, so I thought this was fitting. Probably not the most performant of all options, but readable and concise.
 
 - 
as DRY as possible
- 
if I can represent a syntax in a combination of other syntaxes, I'll do it. This reduces potential sources of logic errors. Examples include:
(define (plus1 x) (display "hello there!") (display "general kenobi!") (+ x 1)) ;; is treated like (define plus1 (lambda (x) (begin (display "hello there!") (display "general kenobi!") (+ x 1))))
 
 - 
 - 
pure C++
- except for loguru, an awesome, simple logging library, I'm staying clear of any non-standard libraries. In parts for simplicity, in other parts because I wanted to learn the language and not specific libraries.
 
 
- 
a range of data types
 - 
a range of builtin functions and syntax, see below
 - 
user defined functions
 - 
tail call optimizition via trampolining for longer possible recursions
 - 
lazy expression execution for single line expressions
+ 1 2 3 ;; gets expanded to ( + 1 2 3 )
 - 
lazy function definition
(define (plus1 x) (+ x 1)) ;; instead of (define plus1 (lambda (x) (+ x 1)))
 - 
run
.scmfiles on their own by passing it via the cli!scheme myscript.scm - 
type
exit!to close repl - 
enter a newline 3 times in a row to skip the current repl
 - 
type
helpto show all currently available functions and variablesλ help ======== SYNTAX ======== begin := evaluate multiple expressions and return last result def := defines a value to the given variable name define := defines a value to the given variable name help := show help text for a given element if := returns the first expression if the condition is true, the second otherwise: lambda := defines a new function quote := returns the first argument: set! := defines a value to the given variable name in all environments ======== FUNCTIONS ======== % := ( num denum ) * := multiplies all arguments with each other + := adds multiple numbers and/or strings - := subtracts the sum of multiple numbers from the first argument / := divides the first argument by the product of all other arguments> ....
 - 
type
(help <function-name>)to get function/syntax specific help messages> (help +) ======== + ======== adds multiple numbers and/or strings (+ 1 2 3) -> 6 (+ 1 2 2.5) -> 5.5 (+ "hello " "world!") -> "hello world!" (+ "hello " 1 " world!") -> "hello 1 world!" priority: string > float > integer
in case of a user defined function, the help message is replaced with the function implementation
λ (help %) ======== % ======== (lambda ( num denum ) ( ( define ( helper num denum count ) ( if ( < num denum ) num ( helper ( - num denum ) denum ( + count 1 ) ) ) ) ( if ( = denum 0 ) nil ( helper num denum 0 ) ) ))
 - 
garbage collection using a mark and sweep algorithm (note: currently broken and the actual deleting is deactivated, is currently being worked on)
 
- numbers (integers & floats)
 - strings
 - lists / cons
 - booleans
 - other singletons (nil '(), EOF, ...)
 - user defined functions
 
| syntax | symbol | type | example | result | 
|---|---|---|---|---|
| begin | begin | 
syntax | (begin(+ 1 1)(+ 2 2)) | 
4 | 
| define | define | 
syntax | (define a 10) | 
defines variable a as 10 | 
| set | set! | 
syntax | (set! a 10) | 
defines a as 10 in current and all paren environments | 
| if | if | 
syntax | (if 1 (+ 1 1) (+ 2 2)) | 
4 | 
| quote | quote | 
syntax | (quote 1 2 3)(quote (1 2 3)) | 
1(1 2 3) | 
| lambda | lambda | 
syntax | (lambda (x y) (+ x 1)) | 
creates new user defined function | 
| help | help | 
syntax | (help)(help +) | 
shows all bindings shows help text for passed function  | 
| exit | exit! | 
keyword | exit! | 
exits the scheme repl elegantly | 
| function | symbol | type | example | result | 
|---|---|---|---|---|
| addition | + | 
builtin | (+ 1 2 5) | 
8 | 
| subtraction | - | 
builtin | (- 10 5) | 
5 | 
| multiplication | * | 
builtin | (* 2 2) | 
4 | 
| division | / | 
builtin | (/ 5 2)(/ 6 2) | 
2.53 | 
| modulo | % | 
udf | (% 5 2) | 
1 | 
| power of n | pow | 
udf | (pow 10 3) | 
1000 | 
| greater than | > | 
udf | (> 5 2) | 
#t | 
| lesser than | < | 
builtin | (< 5 2) | 
#f | 
| greater than or equal | >= | 
udf | (>= 5 5) | 
#t | 
| lesser than or equal | <= | 
udf | (<= 5 5) | 
#t | 
| equal numbers | = | 
builtin | (= 1 11) | 
#f | 
| equal strings | equal-string? | 
builtin | (equal-string? "a" "a") | 
#t | 
| equal objects | eq? | 
builtin | (eq? 1 1) | 
#f | 
| equal? | equal? | 
udf | (equal? 1.0 0) | 
#t | 
| list | list | 
builtin | (list 1 2 3) | 
(1 2 3) | 
| cons | cons | 
builtin | (cons 1 '(2 3)) | 
(1 2 3) | 
| car | car | 
builtin | (car '(1 2 3)) | 
1 | 
| cdr | cdr | 
builtin | (cdr '(1 2 3)) | 
(2 3) | 
| function arglist | function-arglist | 
builtin | (function-arglist plus1) | 
(x) | 
| function body | function-body | 
builtin | (function-body plus1) | 
(+ x 1) | 
| to bool | to-bool | 
udf | (to-bool 1)(to-bool 0) | 
#t#f | 
| not | not | 
udf | (not #t) | 
#f | 
| and | and | 
udf | (and #t #f) | 
#f | 
| or | or | 
udf | (or #t #f) | 
#t | 
| xor | xor | 
udf | (xor #t #t) | 
#f | 
| nand | nand | 
udf | (nand 0 0) | 
#t | 
| is string | string? | 
builtin | (string? "asd") | 
#t | 
| is number | number? | 
builtin | (number? 0) | 
#t | 
| is cons | cons? | 
builtin | (cons? '(1 2 3)) | 
#t | 
| is function | function? | 
builtin | (function? +) | 
#t | 
| is user defined function | user-function? | 
builtin | (user-function? +) | 
#f | 
| is real bool value | bool? | 
builtin | (bool? 1)(bool? #f) | 
#f#t | 
| display | display | 
builtin | (display 1) | 
displays 1 | 
| minimum | min | 
udf | (min 4 1) | 
1 | 
| maximum | max | 
udf | (max 4 1) | 
4 | 
| for loop | for-loop | 
udf | (for-loop 0 10 diplay) | 
displays 0 ... 9 | 
| factorial | factorial | 
udf | (factorial 4) | 
10 | 
| fibonacci | fib | 
udf | (fib 10) | 
55 | 
- code optimisation
 - garbage collection
 - full test coverage
 
- curses based interface
- text manipulation is atm not great (can't move the curser left or right except via deleting the current input
 - can't press up to get past input
 - limited possibilities for visualization
 - no tab-completion
 - implement whole UI in a curses library could make this a lot more usable
 
 - new vector data type
 
