Nathan Braswell's Current Programming Language / Compiler Research

Repository: https://github.com/limvot/kraken

Table of Contents: If you're impatient, jump to the code examples!

Concept:

About:

Currently, I am bootstrapping this new core Lisp out of my prior compiler for my programming language, Kraken. I have implemented the first version of the FUN-GLL algorithm and have working vaus and context-free reader macros.

The general flow is that the input files will be executed with the core Lisp interpreter, and if there is a "main" symbol defined the compiler emits C code for that function & all other functions & data that it references. In this way the language supports very powerful meta-programming at compile time, including adding syntax to the language, arbitrary computation, and importing other files, and then compiles into a static executable.

Below are a few examples of using the vau / live grammar modification / context-free reader macros to implement basic methods as well as embed the BF language into the core Lisp. The core Lisp implementation has been compiled to WebAssembly and should be able to run in your browser. Feel free to make edits and play around below.
Note that the current implementation is inefficient, and sometimes has problems running in phone web browsers.

Runnable Example Code:


; Of course (println "Hello World") ; Just print 3 (println "Math works:" (+ 1 2))

Output:

Vau/Kernel as simple core:

By constructing our core language on a very simple Vau/Kernel base, we can keep the base truely tiny, and build up normal Lisp functions and programming language features in the language itself. This should help implement other programming languages concisely, and will hopefully make optimization easier and more broadly applicable.
Below is the current prelude that adds quoting, quasiquoting, syntax for vectors and quoting/quasiquoting, do, if, let, and even lambda itself!

(set! current-env (vau de () de)) (set! quote (vau _ (x) x)) (add_grammar_rule (quote form) (quote ( "'" optional_WS form )) (vau de (_ _ f) (vector quote (eval f de)))) (add_grammar_rule 'form '( "\\[" optional_WS space_forms optional_WS "\\]" ) (vau de (_ _ fs _ _) (concat (vector vector) (eval fs de)))) (set! vapply (vau de (f p ede) (eval (concat [(eval f de)] (eval p de)) (eval ede de)))) (set! let1 (vau de (s v b) (eval [[vau '_ [s] b] (eval v de)] de))) (set! let (vau de (vs b) (cond (= (len vs) 0) (eval b de) true (vapply let1 [(idx vs 0) (idx vs 1) [let (slice vs 2 -1) b]] de)))) ;(set! lambda (vau se (p b) (let1 f (eval [vau '_ p b] se) (vau de (& op) (vapply f (map (vau dde (ip) (eval (eval ip dde) de)) op) se))))) (set! lambda (vau se (p b) (wrap (eval [vau '_ p b] se)))) (set! fun (vau se (n p b) (eval [set! n [lambda p b]] se))) (fun lapply (f p) (eval (concat [(unwrap f)] p) (current-env))) (fun do (& params) (cond (= 0 (len params)) nil true (idx params (- (len params) 1)))) (set! if (vau de (con than & else) (cond (eval con de) (eval than de) (> (len else) 0) (eval (idx else 0) de) true nil))) (fun print_through (x) (let (_ (println x)) x)) (fun is_pair? (x) (and (vector? x) (> (len x) 0))) (set! quasiquote (vau de (x) (cond (is_pair? x) (cond (and (symbol? (idx x 0)) (= (get-text (idx x 0)) "unquote")) (eval (idx x 1) de) true (cond (and (is_pair? (idx x 0)) (symbol? (idx (idx x 0) 0)) (= (get-text (idx (idx x 0) 0)) "splice-unquote")) (concat (eval (idx (idx x 0) 1) de) (vapply quasiquote [(slice x 1 -1)] de)) true (concat [(vapply quasiquote [(idx x 0)] de)] (vapply quasiquote [(slice x 1 -1)] de)))) true x))) (add_grammar_rule 'form '("`" optional_WS form) (lambda (_ _ f) ['quasiquote f])) (add_grammar_rule 'form '("~" optional_WS form) (lambda (_ _ f) ['unquote f])) (add_grammar_rule 'form '("," optional_WS form) (lambda (_ _ f) ['splice-unquote f])) (println "now with both vector and quasiquote syntax, check out " `(1 2 3 ~(+ 7 8) ,[ 5 6 7]))

Output:

Method Example:

Let's use our meta system (attaching objects to other objects) to implement basic objects/methods. We will attach a vector of alternating symbols / functions (to make this example simple, since maps aren't built in) to our data as the meta, then look up methods on it when we perform a call. The add_grammar_rule function modifies the grammar/parser currently being used to parse the file and operates as a super-powerful reader macro. We use it in this code to add a rule that transforms
a.b(c, d)
into
(method-call a 'b c d)
where method-call is the function that looks up the symbol 'b on the meta object attached to a and calls it with the rest of the parameters.

; Load prelude so we get fun, lambda, if, quoting, etc (load-file "./k_prime_stdlib/prelude.kp") ; First quick lookup function, since maps are not built in (fun get-value-helper (dict key i) (if (>= i (len dict)) nil (if (= key (idx dict i)) (idx dict (+ i 1)) (get-value-helper dict key (+ i 2))))) (fun get-value (dict key) (get-value-helper dict key 0)) ; Our actual method call function (fun method-call (object method & arguments) (let (method_fn (get-value (meta object) method)) (if (= method_fn nil) (println "no method " method) (lapply method_fn (concat [object] arguments))))) ; Some nice syntactic sugar for method calls (add_grammar_rule 'form ['form "\\." 'atom 'optional_WS "\\(" 'optional_WS 'space_forms 'optional_WS "\\)"] (lambda (o _ m _ _ _ p _ _) `(method-call ~o '~m ,p))) ; Ok, let's create our object by hand for this example (set! actual_obj (with-meta [0] [ 'inc (lambda (o) (set-idx! o 0 (+ (idx o 0) 1))) 'dec (lambda (o) (set-idx! o 0 (- (idx o 0) 1))) 'set (lambda (o n) (set-idx! o 0 n)) 'get (lambda (o) (idx o 0)) ])) (do ; Use our new sugar actual_obj.set(1337) actual_obj.inc() (println "get: " actual_obj.get()) actual_obj.dec() (println "get: " actual_obj.get()) ; Use methods directly (method-call actual_obj 'set 654) (method-call actual_obj 'inc) (println "get: " (method-call actual_obj 'get)) (method-call actual_obj 'dec) (method-call actual_obj 'dec) (println "get: " (method-call actual_obj 'get)) nil)

Output:

More Complicated Example: BF as an embedded language


(load-file "./k_prime_stdlib/prelude.kp") ; We don't have atoms built in, mutable vectors ; are our base building block. In order to make the ; following BF implementation nice, let's add atoms! ; They will be implmented as length 1 vectors with nice syntax for deref (fun make-atom (x) [x]) (fun set-atom! (x y) (set-idx! x 0 y)) (fun get-atom (x) (idx x 0)) (add_grammar_rule 'form ["@" 'form] (lambda (_ x) `(get-atom ~x))) ; Now begin by defining our BF syntax & semantics ; Define our tokens as BF atoms (add_grammar_rule 'bfs_atom ["<"] (lambda (_) '(set-atom! cursor (- @cursor 1)))) (add_grammar_rule 'bfs_atom [">"] (lambda (_) '(set-atom! cursor (+ @cursor 1)))) (add_grammar_rule 'bfs_atom ["\\+"] (lambda (_) '(set-idx! tape @cursor (+ (idx tape @cursor) 1)))) (add_grammar_rule 'bfs_atom ["-"] (lambda (_) '(set-idx! tape @cursor (- (idx tape @cursor) 1)))) (add_grammar_rule 'bfs_atom [","] (lambda (_) '(let (value (idx input @inptr)) (do (set-atom! inptr (+ 1 @inptr)) (set-idx! tape @cursor value))))) (add_grammar_rule 'bfs_atom ["."] (lambda (_) '(set-atom! output (concat [(idx tape @cursor)] @output)))) ; Define strings of BF atoms (add_grammar_rule 'bfs ['bfs_atom *] (lambda (x) x)) ; Add loop as an atom ; (note that closure cannot yet close over itself by value, so we pass it in) (add_grammar_rule 'bfs_atom ["\\[" 'bfs "]"] (lambda (_ x _) `(let (f (lambda (f) (if (= 0 (idx tape @cursor)) nil (do ,x (f f))))) (f f)))) ; For now, stick BFS rule inside an unambigious BFS block ; Also add setup code (add_grammar_rule 'form ["bf" 'optional_WS "{" 'optional_WS 'bfs 'optional_WS "}"] (lambda (_ _ _ _ x _ _) `(lambda (input) (let ( tape (vector 0 0 0 0 0) cursor (make-atom 0) inptr (make-atom 0) output (make-atom (vector)) ) (do (println "beginning bfs") ,x (idx output 0)))))) ; Let's try it out! This BF program prints the input 3 times (println (bf { ,>+++[<.>-] } [1337])) ; we can also have it compile into our main program (fun main () (do (println "BF: " (bf { ,>+++[<.>-] } [1337])) 0))

Output:

Next Steps