objpascal, r: Support catchless try*.
[jackhill/mal.git] / r / stepA_mal.r
CommitLineData
f947d503
JM
1if(!exists("..readline..")) source("readline.r")
2if(!exists("..types..")) source("types.r")
3if(!exists("..reader..")) source("reader.r")
4if(!exists("..printer..")) source("printer.r")
5if(!exists("..env..")) source("env.r")
6if(!exists("..core..")) source("core.r")
7
8# read
9READ <- function(str) {
10 return(read_str(str))
11}
12
13# eval
14is_pair <- function(x) {
15 .sequential_q(x) && length(x) > 0
16}
17
18quasiquote <- function(ast) {
19 if (!is_pair(ast)) {
20 new.list(new.symbol("quote"),
21 ast)
22 } else if (.symbol_q(ast[[1]]) && ast[[1]] == "unquote") {
23 ast[[2]]
24 } else if (is_pair(ast[[1]]) &&
25 .symbol_q(ast[[1]][[1]]) &&
26 ast[[1]][[1]] == "splice-unquote") {
27 new.list(new.symbol("concat"),
28 ast[[1]][[2]],
29 quasiquote(slice(ast, 2)))
30 } else {
31 new.list(new.symbol("cons"),
32 quasiquote(ast[[1]]),
33 quasiquote(slice(ast, 2)))
34 }
35}
36
37is_macro_call <- function(ast, env) {
38 if(.list_q(ast) &&
39 .symbol_q(ast[[1]]) &&
40 (!.nil_q(Env.find(env, ast[[1]])))) {
41 exp <- Env.get(env, ast[[1]])
42 return(.malfunc_q(exp) && exp$ismacro)
43 }
44 FALSE
45}
46
47macroexpand <- function(ast, env) {
48 while(is_macro_call(ast, env)) {
49 mac <- Env.get(env, ast[[1]])
50 ast <- fapply(mac, slice(ast, 2))
51 }
52 ast
53}
54
55eval_ast <- function(ast, env) {
56 if (.symbol_q(ast)) {
57 Env.get(env, ast)
58 } else if (.list_q(ast)) {
59 new.listl(lapply(ast, function(a) EVAL(a, env)))
60 } else if (.vector_q(ast)) {
61 new.vectorl(lapply(ast, function(a) EVAL(a, env)))
62 } else if (.hash_map_q(ast)) {
63 lst <- list()
64 for(k in ls(ast)) {
65 lst[[length(lst)+1]] = k
66 lst[[length(lst)+1]] = EVAL(ast[[k]], env)
67 }
68 new.hash_mapl(lst)
69 } else {
70 ast
71 }
72}
73
74EVAL <- function(ast, env) {
75 repeat {
76
77 #cat("EVAL: ", .pr_str(ast,TRUE), "\n", sep="")
dc191336
JM
78 if (!.list_q(ast)) { return(eval_ast(ast, env)) }
79 if (length(ast) == 0) { return(ast) }
f947d503
JM
80
81 # apply list
82 ast <- macroexpand(ast, env)
d5b81cc0 83 if (!.list_q(ast)) return(eval_ast(ast, env))
f947d503
JM
84
85 switch(paste("l",length(ast),sep=""),
86 l0={ return(ast) },
87 l1={ a0 <- ast[[1]]; a1 <- NULL; a2 <- NULL },
88 l2={ a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- NULL },
89 { a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- ast[[3]] })
90 if (length(a0) > 1) a0sym <- "__<*fn*>__"
91 else a0sym <- as.character(a0)
92 if (a0sym == "def!") {
93 res <- EVAL(a2, env)
94 return(Env.set(env, a1, res))
95 } else if (a0sym == "let*") {
96 let_env <- new.Env(env)
97 for(i in seq(1,length(a1),2)) {
98 Env.set(let_env, a1[[i]], EVAL(a1[[i+1]], let_env))
99 }
100 ast <- a2
101 env <- let_env
102 } else if (a0sym == "quote") {
103 return(a1)
104 } else if (a0sym == "quasiquote") {
105 ast <- quasiquote(a1)
106 } else if (a0sym == "defmacro!") {
107 func <- EVAL(a2, env)
108 func$ismacro = TRUE
109 return(Env.set(env, a1, func))
110 } else if (a0sym == "macroexpand") {
111 return(macroexpand(a1, env))
112 } else if (a0sym == "try*") {
113 edata <- new.env()
114 tryCatch({
115 return(EVAL(a1, env))
116 }, error=function(err) {
117 edata$exc <- get_error(err)
118 })
119 if ((!is.null(a2)) && a2[[1]] == "catch*") {
120 return(EVAL(a2[[3]], new.Env(env,
121 new.list(a2[[2]]),
122 new.list(edata$exc))))
123 } else {
dc191336 124 throw(edata$exc)
f947d503
JM
125 }
126 } else if (a0sym == "do") {
127 eval_ast(slice(ast,2,length(ast)-1), env)
128 ast <- ast[[length(ast)]]
129 } else if (a0sym == "if") {
130 cond <- EVAL(a1, env)
131 if (.nil_q(cond) || identical(cond, FALSE)) {
132 if (length(ast) < 4) return(nil)
133 ast <- ast[[4]]
134 } else {
135 ast <- a2
136 }
137 } else if (a0sym == "fn*") {
138 return(malfunc(EVAL, a2, env, a1))
139 } else {
140 el <- eval_ast(ast, env)
141 f <- el[[1]]
142 if (class(f) == "MalFunc") {
143 ast <- f$ast
144 env <- f$gen_env(slice(el,2))
145 } else {
146 return(do.call(f,slice(el,2)))
147 }
148 }
149
150 }
151}
152
153# print
154PRINT <- function(exp) {
155 return(.pr_str(exp, TRUE))
156}
157
158# repl loop
159repl_env <- new.Env()
160rep <- function(str) return(PRINT(EVAL(READ(str), repl_env)))
161
162# core.r: defined using R
163for(k in names(core_ns)) { Env.set(repl_env, k, core_ns[[k]]) }
164Env.set(repl_env, "eval", function(ast) EVAL(ast, repl_env))
165Env.set(repl_env, "*ARGV*", new.list())
166
167# core.mal: defined using the language itself
168. <- rep("(def! *host-language* \"R\")")
169. <- rep("(def! not (fn* (a) (if a false true)))")
170. <- rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
171. <- rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
29ba1fb6
DM
172. <- rep("(def! *gensym-counter* (atom 0))")
173. <- rep("(def! gensym (fn* [] (symbol (str \"G__\" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))")
174. <- rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))")
f947d503
JM
175
176
177args <- commandArgs(trailingOnly = TRUE)
178if (length(args) > 0) {
8a16f953 179 Env.set(repl_env, "*ARGV*", new.listl(slice(as.list(args),2)))
9b3362e8
JM
180 tryCatch({
181 . <- rep(concat("(load-file \"", args[[1]], "\")"))
182 }, error=function(err) {
183 cat("Error: ", get_error(err),"\n", sep="")
184 })
f947d503
JM
185 quit(save="no", status=0)
186}
187
188. <- rep("(println (str \"Mal [\" *host-language* \"]\"))")
189repeat {
190 line <- readline("user> ")
191 if (is.null(line)) { cat("\n"); break }
192 tryCatch({
193 cat(rep(line),"\n", sep="")
194 }, error=function(err) {
dd7a4f55 195 cat("Error: ", .pr_str(get_error(err),TRUE),"\n", sep="")
f947d503
JM
196 })
197 # R debug/fatal with tracebacks:
198 #cat(rep(line),"\n", sep="")
199}