396 lines
9.8 KiB
Plaintext
396 lines
9.8 KiB
Plaintext
|
|
(fn Emitter (ast) (do
|
|
(let output ())
|
|
|
|
(fn generate () (do
|
|
(call emit "#!/usr/bin/env node\n")
|
|
(call emit_exprs ast)
|
|
(return (call strings_join output))
|
|
))
|
|
|
|
(fn emit_exprs (exprs) (do
|
|
(for expr exprs (do
|
|
(call emit_expr expr)
|
|
(call emit "\n")
|
|
))
|
|
))
|
|
|
|
(fn emit_expr (expr) (do
|
|
(let (ty line) expr)
|
|
(if (== ty "list") (do
|
|
(call emit_list expr)
|
|
) (if (== ty "int") (
|
|
(let (_ _ value) expr)
|
|
(call emit (call format "%" value))
|
|
) (if (== ty "string") (
|
|
(let (_ _ value) expr)
|
|
(call emit (call format "\"%\"" (call string_escape value)))
|
|
) (if (== ty "ident") (
|
|
(let (_ _ value) expr)
|
|
(call emit (call format "_%" value))
|
|
) (do
|
|
(call panic "unknown expr type '%' on line %" ty line)
|
|
)))))
|
|
))
|
|
|
|
(fn emit_list (expr) (do
|
|
(let (ty line s) expr)
|
|
(if (== (call len s) 0) (do
|
|
(call emit "[]")
|
|
(return)
|
|
))
|
|
(let ((_ _ id)) s)
|
|
(if (== id "fn") (do
|
|
(let (_ (_ _ name) (_ _ params) body) s)
|
|
(call emit (call format "function _%(" name))
|
|
|
|
(let first true)
|
|
(for (_ _ name) params (do
|
|
(if (not first) (do
|
|
(call emit ", ")
|
|
))
|
|
(= first false)
|
|
|
|
(call emit (call format "_%" name))
|
|
))
|
|
(call emit (call format ") {\n" name))
|
|
(call emit_expr body)
|
|
(call emit "}")
|
|
) (if (== id "do") (do
|
|
(call emit_exprs (call slice s 1))
|
|
) (if (== id "return") (do
|
|
(let (_ value) s)
|
|
(call emit "return ")
|
|
(if (!= value null) (do
|
|
(call emit_expr value)
|
|
) (do
|
|
(call emit "null")
|
|
))
|
|
(call emit ";")
|
|
) (if (== id "call") (do
|
|
(let (_ callee) s)
|
|
(let args (call slice s 2))
|
|
(call emit_expr callee)
|
|
(call emit "(")
|
|
|
|
(let first true)
|
|
(for arg args (do
|
|
(if (not first) (do
|
|
(call emit ", ")
|
|
))
|
|
(= first false)
|
|
|
|
(call emit_expr arg)
|
|
))
|
|
|
|
(call emit ")")
|
|
) (do
|
|
(call emit "[")
|
|
(let first true)
|
|
(for e s (do
|
|
(if (not first) (do
|
|
(call emit ", ")
|
|
))
|
|
(= first false)
|
|
|
|
(call emit_expr e)
|
|
))
|
|
(call emit "]")
|
|
)))))
|
|
))
|
|
|
|
(fn emit (str) (do
|
|
(call push output str)
|
|
))
|
|
|
|
(return (generate))
|
|
))
|
|
|
|
(fn string_escape (str) (do
|
|
(let str_len (call len str))
|
|
(let i 0)
|
|
(let result "")
|
|
(loop (do
|
|
(if (>= i str_len) (break))
|
|
(let ch (call at str i))
|
|
(if (== ch "\"") (do
|
|
(+= result "\\\"")
|
|
) (if (== ch "\t") (do
|
|
(+= result "\\t")
|
|
) (if (== ch "\r") (do
|
|
(+= result "\\r")
|
|
) (if (== ch "\n") (do
|
|
(+= result "\\n")
|
|
) (if (== ch "\0") (do
|
|
(+= result "\\0")
|
|
) (do
|
|
(+= result ch)
|
|
))))))
|
|
(+= i 1)
|
|
))
|
|
(return result)
|
|
))
|
|
|
|
(fn Parser (tokens) (do
|
|
(let i 0)
|
|
(let tok (call at tokens i))
|
|
|
|
(fn parse () (do
|
|
(let exprs ())
|
|
(loop (do
|
|
(if (call done) (break))
|
|
(call push exprs (call parse_expr))
|
|
))
|
|
(return exprs)
|
|
))
|
|
|
|
(fn parse_expr () (do
|
|
(let (ty line value) tok)
|
|
(if (call eat "(") (do
|
|
(let values ())
|
|
(loop (do
|
|
(if (call test ")") (break))
|
|
(call push values (call parse_expr))
|
|
))
|
|
(if (not (call eat ")")) (do
|
|
(call panic "expected ')' on line %" (call at tok 1))
|
|
))
|
|
(return ("list" line values))
|
|
) (if (call eat "string") (do
|
|
(return ("string" line value))
|
|
) (if (call eat "int") (do
|
|
(return ("int" line (call string_to_int value)))
|
|
) (if (call eat "ident") (do
|
|
(return ("ident" line value))
|
|
) (do
|
|
(call panic "expected expression, got '%' on line %" ty line)
|
|
)))))
|
|
))
|
|
|
|
(fn eat (pat) (do
|
|
(if (not (call test pat)) (return false))
|
|
(call step)
|
|
(return true)
|
|
))
|
|
|
|
(fn step () (do
|
|
(+= i 1)
|
|
(if (not (call done)) (do
|
|
(let new_tok (call at tokens i))
|
|
(= tok new_tok)
|
|
))
|
|
))
|
|
|
|
(fn test (pat) (do
|
|
(if (call done) (return false))
|
|
(let (ty) tok)
|
|
(return (== pat ty))
|
|
))
|
|
|
|
(fn done () (do
|
|
(return (>= i (call len tokens)))
|
|
))
|
|
|
|
(return (parse))
|
|
))
|
|
|
|
(fn tokenize (text) (do
|
|
(let text_len (call len text))
|
|
|
|
(let tokens ())
|
|
(let i 0)
|
|
(let line 1)
|
|
|
|
(loop (do
|
|
(if (>= i text_len) (break))
|
|
|
|
(let ch (call at text i))
|
|
|
|
(if (call contains " \t\r\n" ch) (do
|
|
(if (== ch "\n") (do
|
|
(+= line 1)
|
|
))
|
|
(+= i 1)
|
|
) (if (call slice_eq text i "//") (do
|
|
(loop (do
|
|
(if (or (>= i text_len) (== (call at text i) "\n")) (do
|
|
(break)
|
|
))
|
|
(+= i 1)
|
|
))
|
|
) (if (call contains "()" ch) (do
|
|
(call push tokens (ch line))
|
|
(+= i 1)
|
|
) (if (== ch "\"") (do
|
|
(let value "")
|
|
(+= i 1)
|
|
(= ch (call at text i))
|
|
(loop (do
|
|
(if (or (>= i text_len) (== ch "\"")) (do
|
|
(break)
|
|
))
|
|
(if (== ch "\\") (do
|
|
(+= i 1)
|
|
(if (>= i text_len) (do
|
|
(break)
|
|
))
|
|
(= ch (call at text i))
|
|
(if (== ch "t") (do
|
|
(+= value "\t")
|
|
) (if (== ch "r") (do
|
|
(+= value "\r")
|
|
) (if (== ch "n") (do
|
|
(+= value "\n")
|
|
) (if (== ch "0") (do
|
|
(+= value "\n")
|
|
) (do
|
|
(+= value ch)
|
|
)))))
|
|
) (do
|
|
(+= value ch)
|
|
))
|
|
(+= i 1)
|
|
(= ch (call at text i))
|
|
))
|
|
(if (or (>= i text_len) (!= ch "\"") (do
|
|
(call panic "expected '\"' on line %" line)
|
|
)))
|
|
(+= i 1)
|
|
(call push tokens ("string" line value))
|
|
) (if (call contains "0123456789" ch) (do
|
|
(let value "")
|
|
(loop (do
|
|
(= ch (call at text i))
|
|
(if (or (>= i text_len) (not (call contains "0123456789" ch))) (do
|
|
(break)
|
|
))
|
|
(+= value ch)
|
|
(+= i 1)
|
|
))
|
|
(call push tokens ("int" line value))
|
|
) (if (call contains identChars ch) (do
|
|
(let value "")
|
|
(loop (do
|
|
(= ch (call at text i))
|
|
(if (or (>= i text_len) (not (call contains identChars ch))) (do
|
|
(break)
|
|
))
|
|
(+= value ch)
|
|
(+= i 1)
|
|
))
|
|
(call push tokens ("ident" line value))
|
|
) (do
|
|
(call println "illegal char '%'" ch)
|
|
(+= i 1)
|
|
)))))))
|
|
|
|
))
|
|
(return tokens)
|
|
))
|
|
|
|
(let identChars (+ "abcdefghijklmnopqrstuvwxyz"
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+-*/%&|=?!<>'_"))
|
|
|
|
(fn contains (text ch) (do
|
|
(let text_len (call len text))
|
|
(let i 0)
|
|
(loop (do
|
|
(if (>= i text_len) (break))
|
|
(if (== (call at text i) ch) (do
|
|
(return true)
|
|
))
|
|
(+= i 1)
|
|
))
|
|
(return false)
|
|
))
|
|
|
|
(fn slice_eq (str slice_idx substr) (do
|
|
(let str_len (call len str))
|
|
(let substr_len (call len substr))
|
|
(let i 0)
|
|
(loop (do
|
|
(if (>= i substr_len)
|
|
(return true))
|
|
(if (>= (+ slice_idx i) str_len)
|
|
(return false))
|
|
(if (!= (call at str (+ slice_idx i)) (call at substr i))
|
|
(return false))
|
|
(+= i 1)
|
|
))
|
|
(return true)
|
|
))
|
|
|
|
(fn print_expr (expr depth) (do
|
|
(let (ty line value) expr)
|
|
(if (== ty "list") (do
|
|
(call println "%(% %" (call indent depth) ty line)
|
|
(for e value (do
|
|
(call print_expr e (+ depth 1))
|
|
))
|
|
(call println "%)" (call indent depth))
|
|
) (do
|
|
(call println "%%" (call indent depth) expr)
|
|
))
|
|
))
|
|
|
|
(fn indent (depth) (do
|
|
(let space "")
|
|
(let i 0)
|
|
(loop (do
|
|
(if (>= i depth) (break))
|
|
(+= space " ")
|
|
(+= i 1)
|
|
))
|
|
(return space)
|
|
))
|
|
|
|
(fn slice (list idx) (do
|
|
(let list_len (call len list))
|
|
(let elems ())
|
|
(let i idx)
|
|
(loop (do
|
|
(if (>= i list_len) (break))
|
|
(call push elems (call at list i))
|
|
(+= i 1)
|
|
))
|
|
(return elems)
|
|
))
|
|
|
|
(let text (call read_text_file "program.phi"))
|
|
|
|
(call println "reading file...")
|
|
(call println "=== text ===")
|
|
(call println text)
|
|
|
|
(call println "tokenizing...")
|
|
(let tokens (call tokenize text))
|
|
|
|
(call println "=== tokens ===")
|
|
(for (tok line value) tokens (do
|
|
(call println "%\t%\t%" line tok (if (!= value null) value ""))
|
|
))
|
|
|
|
(call println "parsing...")
|
|
(let parser (call Parser tokens))
|
|
(let (parse) parser)
|
|
(let ast (call parse))
|
|
|
|
(call println "=== ast ===")
|
|
(for expr ast (do
|
|
(call print_expr expr 0)
|
|
))
|
|
|
|
(call println "emitting...")
|
|
(let emitter (call Emitter ast))
|
|
(let (emit) emitter)
|
|
(let js_code (call emit))
|
|
|
|
(call println "=== js ===")
|
|
(call println js_code)
|
|
|
|
(call println "writing file...")
|
|
(call write_text_file "out.js" js_code)
|
|
|
|
|
|
|