(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) )) (call println "reading file...") (let text (call read_text_file "program.phi")) //(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)