1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-11 13:55:55 +03:00

Print a stack trace for uncaught exceptions in self-hosting

An uncaught exception in the self-hosting implementation causes the
interpreter to display the EVALed ast for each EVAL level until the
exception, something similare to a stack trace.  This does not cost
much and may help a lot debugging the self-hosting step.

On the other hand, remove try* from step0 where it serves no useful
purpose.

Because of indentation level, this diff is better viewed with the git
-b command line option.
This commit is contained in:
Nicolas Boulenguez 2021-06-04 01:26:31 +02:00 committed by Joel Martin
parent 931971e047
commit 4807c1b65f
9 changed files with 323 additions and 234 deletions

View File

@ -18,10 +18,7 @@
(if line
(do
(if (not (= "" line))
(try*
(println (rep line))
(catch* exc
(println "Uncaught exception:" exc))))
(println (rep line)))
(repl-loop (readline "mal-user> "))))))
;; main

View File

@ -1,3 +1,7 @@
;; EVAL extends this stack trace when propagating exceptions. If the
;; exception reaches the REPL loop, the full trace is printed.
(def! trace (atom ""))
;; read
(def! READ read-string)
@ -23,15 +27,20 @@
(def! EVAL (fn* [ast env]
;; (do (prn "EVAL" ast) )
(if (not (list? ast))
(eval-ast ast env)
(try*
(if (not (list? ast))
(eval-ast ast env)
;; apply list
(if (empty? ast)
ast
(let* [el (eval-ast ast env)]
(apply (first el) (rest el)))))))
;; apply list
(if (empty? ast)
ast
(let* [el (eval-ast ast env)]
(apply (first el) (rest el)))))
(catch* exc
(do
(swap! trace str "\n in mal EVAL: " ast)
(throw exc))))))
;; print
(def! PRINT pr-str)
@ -39,7 +48,7 @@
;; repl
(def! repl-env {"+" +
"-" -
"*" *
"*" *
"/" /})
(def! rep (fn* [strng]
(PRINT (EVAL (READ strng) repl-env))))
@ -52,7 +61,9 @@
(try*
(println (rep line))
(catch* exc
(println "Uncaught exception:" exc))))
(do
(println "Uncaught exception:" exc @trace)
(reset! trace "")))))
(repl-loop (readline "mal-user> "))))))
;; main

View File

@ -1,5 +1,9 @@
(load-file "../mal/env.mal")
;; EVAL extends this stack trace when propagating exceptions. If the
;; exception reaches the REPL loop, the full trace is printed.
(def! trace (atom ""))
;; read
(def! READ read-string)
@ -30,25 +34,30 @@
(def! EVAL (fn* [ast env]
;; (do (prn "EVAL" ast "/" (keys @env)) )
(if (not (list? ast))
(eval-ast ast env)
(try*
(if (not (list? ast))
(eval-ast ast env)
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el))))))))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el))))))
(catch* exc
(do
(swap! trace str "\n in mal EVAL: " ast)
(throw exc))))))
;; print
(def! PRINT pr-str)
@ -71,7 +80,9 @@
(try*
(println (rep line))
(catch* exc
(println "Uncaught exception:" exc))))
(do
(println "Uncaught exception:" exc @trace)
(reset! trace "")))))
(repl-loop (readline "mal-user> "))))))
;; main

View File

@ -1,6 +1,10 @@
(load-file "../mal/env.mal")
(load-file "../mal/core.mal")
;; EVAL extends this stack trace when propagating exceptions. If the
;; exception reaches the REPL loop, the full trace is printed.
(def! trace (atom ""))
;; read
(def! READ read-string)
@ -31,38 +35,43 @@
(def! EVAL (fn* [ast env]
;; (do (prn "EVAL" ast "/" (keys @env)) )
(if (not (list? ast))
(eval-ast ast env)
(try*
(if (not (list? ast))
(eval-ast ast env)
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'do a0)
(let* [el (eval-ast (rest ast) env)]
(nth el (- (count el) 1)))
(= 'do a0)
(let* [el (eval-ast (rest ast) env)]
(nth el (- (count el) 1)))
(= 'if a0)
(if (EVAL (nth ast 1) env)
(EVAL (nth ast 2) env)
(if (> (count ast) 3)
(EVAL (nth ast 3) env)))
(= 'if a0)
(if (EVAL (nth ast 1) env)
(EVAL (nth ast 2) env)
(if (> (count ast) 3)
(EVAL (nth ast 3) env)))
(= 'fn* a0)
(fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
(= 'fn* a0)
(fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el))))))))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el))))))
(catch* exc
(do
(swap! trace str "\n in mal EVAL: " ast)
(throw exc))))))
;; print
(def! PRINT pr-str)
@ -86,7 +95,9 @@
(try*
(println (rep line))
(catch* exc
(println "Uncaught exception:" exc))))
(do
(println "Uncaught exception:" exc @trace)
(reset! trace "")))))
(repl-loop (readline "mal-user> "))))))
;; main

View File

@ -1,6 +1,10 @@
(load-file "../mal/env.mal")
(load-file "../mal/core.mal")
;; EVAL extends this stack trace when propagating exceptions. If the
;; exception reaches the REPL loop, the full trace is printed.
(def! trace (atom ""))
;; read
(def! READ read-string)
@ -31,38 +35,43 @@
(def! EVAL (fn* [ast env]
;; (do (prn "EVAL" ast "/" (keys @env)) )
(if (not (list? ast))
(eval-ast ast env)
(try*
(if (not (list? ast))
(eval-ast ast env)
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'do a0)
(let* [el (eval-ast (rest ast) env)]
(nth el (- (count el) 1)))
(= 'do a0)
(let* [el (eval-ast (rest ast) env)]
(nth el (- (count el) 1)))
(= 'if a0)
(if (EVAL (nth ast 1) env)
(EVAL (nth ast 2) env)
(if (> (count ast) 3)
(EVAL (nth ast 3) env)))
(= 'if a0)
(if (EVAL (nth ast 1) env)
(EVAL (nth ast 2) env)
(if (> (count ast) 3)
(EVAL (nth ast 3) env)))
(= 'fn* a0)
(fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
(= 'fn* a0)
(fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el))))))))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el))))))
(catch* exc
(do
(swap! trace str "\n in mal EVAL: " ast)
(throw exc))))))
;; print
(def! PRINT pr-str)
@ -89,7 +98,9 @@
(try*
(println (rep line))
(catch* exc
(println "Uncaught exception:" exc))))
(do
(println "Uncaught exception:" exc @trace)
(reset! trace "")))))
(repl-loop (readline "mal-user> "))))))
;; main

View File

@ -1,6 +1,10 @@
(load-file "../mal/env.mal")
(load-file "../mal/core.mal")
;; EVAL extends this stack trace when propagating exceptions. If the
;; exception reaches the REPL loop, the full trace is printed.
(def! trace (atom ""))
;; read
(def! READ read-string)
@ -49,47 +53,52 @@
(def! EVAL (fn* [ast env]
;; (do (prn "EVAL" ast "/" (keys @env)) )
(if (not (list? ast))
(eval-ast ast env)
(try*
(if (not (list? ast))
(eval-ast ast env)
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'quote a0)
(nth ast 1)
(= 'quote a0)
(nth ast 1)
(= 'quasiquoteexpand a0)
(QUASIQUOTE (nth ast 1))
(= 'quasiquoteexpand a0)
(QUASIQUOTE (nth ast 1))
(= 'quasiquote a0)
(EVAL (QUASIQUOTE (nth ast 1)) env)
(= 'quasiquote a0)
(EVAL (QUASIQUOTE (nth ast 1)) env)
(= 'do a0)
(let* [el (eval-ast (rest ast) env)]
(nth el (- (count el) 1)))
(= 'do a0)
(let* [el (eval-ast (rest ast) env)]
(nth el (- (count el) 1)))
(= 'if a0)
(if (EVAL (nth ast 1) env)
(EVAL (nth ast 2) env)
(if (> (count ast) 3)
(EVAL (nth ast 3) env)))
(= 'if a0)
(if (EVAL (nth ast 1) env)
(EVAL (nth ast 2) env)
(if (> (count ast) 3)
(EVAL (nth ast 3) env)))
(= 'fn* a0)
(fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
(= 'fn* a0)
(fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el))))))))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el))))))
(catch* exc
(do
(swap! trace str "\n in mal EVAL: " ast)
(throw exc))))))
;; print
(def! PRINT pr-str)
@ -116,7 +125,9 @@
(try*
(println (rep line))
(catch* exc
(println "Uncaught exception:" exc))))
(do
(println "Uncaught exception:" exc @trace)
(reset! trace "")))))
(repl-loop (readline "mal-user> "))))))
;; main

View File

@ -1,6 +1,10 @@
(load-file "../mal/env.mal")
(load-file "../mal/core.mal")
;; EVAL extends this stack trace when propagating exceptions. If the
;; exception reaches the REPL loop, the full trace is printed.
(def! trace (atom ""))
;; read
(def! READ read-string)
@ -57,55 +61,60 @@
(def! EVAL (fn* [ast env]
;; (do (prn "EVAL" ast "/" (keys @env)) )
(let* [ast (MACROEXPAND ast env)]
(if (not (list? ast))
(eval-ast ast env)
(try*
(let* [ast (MACROEXPAND ast env)]
(if (not (list? ast))
(eval-ast ast env)
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'quote a0)
(nth ast 1)
(= 'quote a0)
(nth ast 1)
(= 'quasiquoteexpand a0)
(QUASIQUOTE (nth ast 1))
(= 'quasiquoteexpand a0)
(QUASIQUOTE (nth ast 1))
(= 'quasiquote a0)
(EVAL (QUASIQUOTE (nth ast 1)) env)
(= 'quasiquote a0)
(EVAL (QUASIQUOTE (nth ast 1)) env)
(= 'defmacro! a0)
(env-set env (nth ast 1) (hash-map :__MAL_MACRO__
(EVAL (nth ast 2) env)))
(= 'defmacro! a0)
(env-set env (nth ast 1) (hash-map :__MAL_MACRO__
(EVAL (nth ast 2) env)))
(= 'macroexpand a0)
(MACROEXPAND (nth ast 1) env)
(= 'macroexpand a0)
(MACROEXPAND (nth ast 1) env)
(= 'do a0)
(let* [el (eval-ast (rest ast) env)]
(nth el (- (count el) 1)))
(= 'do a0)
(let* [el (eval-ast (rest ast) env)]
(nth el (- (count el) 1)))
(= 'if a0)
(if (EVAL (nth ast 1) env)
(EVAL (nth ast 2) env)
(if (> (count ast) 3)
(EVAL (nth ast 3) env)))
(= 'if a0)
(if (EVAL (nth ast 1) env)
(EVAL (nth ast 2) env)
(if (> (count ast) 3)
(EVAL (nth ast 3) env)))
(= 'fn* a0)
(fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
(= 'fn* a0)
(fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el)))))))))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el)))))))
(catch* exc
(do
(swap! trace str "\n in mal EVAL: " ast)
(throw exc))))))
;; print
(def! PRINT pr-str)
@ -133,7 +142,9 @@
(try*
(println (rep line))
(catch* exc
(println "Uncaught exception:" exc))))
(do
(println "Uncaught exception:" exc @trace)
(reset! trace "")))))
(repl-loop (readline "mal-user> "))))))
;; main

View File

@ -1,6 +1,10 @@
(load-file "../mal/env.mal")
(load-file "../mal/core.mal")
;; EVAL extends this stack trace when propagating exceptions. If the
;; exception reaches the REPL loop, the full trace is printed.
(def! trace (atom ""))
;; read
(def! READ read-string)
@ -57,64 +61,71 @@
(def! EVAL (fn* [ast env]
;; (do (prn "EVAL" ast "/" (keys @env)) )
(let* [ast (MACROEXPAND ast env)]
(if (not (list? ast))
(eval-ast ast env)
(try*
(let* [ast (MACROEXPAND ast env)]
(if (not (list? ast))
(eval-ast ast env)
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'quote a0)
(nth ast 1)
(= 'quote a0)
(nth ast 1)
(= 'quasiquoteexpand a0)
(QUASIQUOTE (nth ast 1))
(= 'quasiquoteexpand a0)
(QUASIQUOTE (nth ast 1))
(= 'quasiquote a0)
(EVAL (QUASIQUOTE (nth ast 1)) env)
(= 'quasiquote a0)
(EVAL (QUASIQUOTE (nth ast 1)) env)
(= 'defmacro! a0)
(env-set env (nth ast 1) (hash-map :__MAL_MACRO__
(EVAL (nth ast 2) env)))
(= 'defmacro! a0)
(env-set env (nth ast 1) (hash-map :__MAL_MACRO__
(EVAL (nth ast 2) env)))
(= 'macroexpand a0)
(MACROEXPAND (nth ast 1) env)
(= 'macroexpand a0)
(MACROEXPAND (nth ast 1) env)
(= 'try* a0)
(if (< (count ast) 3)
(EVAL (nth ast 1) env)
(try*
(= 'try* a0)
(if (< (count ast) 3)
(EVAL (nth ast 1) env)
(catch* exc
(let* [a2 (nth ast 2)]
(EVAL (nth a2 2) (new-env env [(nth a2 1)] [exc]))))))
(try*
(EVAL (nth ast 1) env)
(catch* exc
(do
(reset! trace "")
(let* [a2 (nth ast 2)]
(EVAL (nth a2 2) (new-env env [(nth a2 1)] [exc])))))))
(= 'do a0)
(let* [el (eval-ast (rest ast) env)]
(nth el (- (count el) 1)))
(= 'do a0)
(let* [el (eval-ast (rest ast) env)]
(nth el (- (count el) 1)))
(= 'if a0)
(if (EVAL (nth ast 1) env)
(EVAL (nth ast 2) env)
(if (> (count ast) 3)
(EVAL (nth ast 3) env)))
(= 'if a0)
(if (EVAL (nth ast 1) env)
(EVAL (nth ast 2) env)
(if (> (count ast) 3)
(EVAL (nth ast 3) env)))
(= 'fn* a0)
(fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
(= 'fn* a0)
(fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el)))))))))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el)))))))
(catch* exc
(do
(swap! trace str "\n in mal EVAL: " ast)
(throw exc))))))
;; print
(def! PRINT pr-str)
@ -142,7 +153,9 @@
(try*
(println (rep line))
(catch* exc
(println "Uncaught exception:" exc))))
(do
(println "Uncaught exception:" exc @trace)
(reset! trace "")))))
(repl-loop (readline "mal-user> "))))))
;; main

View File

@ -1,6 +1,10 @@
(load-file "../mal/env.mal")
(load-file "../mal/core.mal")
;; EVAL extends this stack trace when propagating exceptions. If the
;; exception reaches the REPL loop, the full trace is printed.
(def! trace (atom ""))
;; read
(def! READ read-string)
@ -57,64 +61,71 @@
(def! EVAL (fn* [ast env]
;; (do (prn "EVAL" ast "/" (keys @env)) )
(let* [ast (MACROEXPAND ast env)]
(if (not (list? ast))
(eval-ast ast env)
(try*
(let* [ast (MACROEXPAND ast env)]
(if (not (list? ast))
(eval-ast ast env)
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
;; apply list
(let* [a0 (first ast)]
(cond
(empty? ast)
ast
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'def! a0)
(env-set env (nth ast 1) (EVAL (nth ast 2) env))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'let* a0)
(LET (new-env env) (nth ast 1) (nth ast 2))
(= 'quote a0)
(nth ast 1)
(= 'quote a0)
(nth ast 1)
(= 'quasiquoteexpand a0)
(QUASIQUOTE (nth ast 1))
(= 'quasiquoteexpand a0)
(QUASIQUOTE (nth ast 1))
(= 'quasiquote a0)
(EVAL (QUASIQUOTE (nth ast 1)) env)
(= 'quasiquote a0)
(EVAL (QUASIQUOTE (nth ast 1)) env)
(= 'defmacro! a0)
(env-set env (nth ast 1) (hash-map :__MAL_MACRO__
(EVAL (nth ast 2) env)))
(= 'defmacro! a0)
(env-set env (nth ast 1) (hash-map :__MAL_MACRO__
(EVAL (nth ast 2) env)))
(= 'macroexpand a0)
(MACROEXPAND (nth ast 1) env)
(= 'macroexpand a0)
(MACROEXPAND (nth ast 1) env)
(= 'try* a0)
(if (< (count ast) 3)
(EVAL (nth ast 1) env)
(try*
(= 'try* a0)
(if (< (count ast) 3)
(EVAL (nth ast 1) env)
(catch* exc
(let* [a2 (nth ast 2)]
(EVAL (nth a2 2) (new-env env [(nth a2 1)] [exc]))))))
(try*
(EVAL (nth ast 1) env)
(catch* exc
(do
(reset! trace "")
(let* [a2 (nth ast 2)]
(EVAL (nth a2 2) (new-env env [(nth a2 1)] [exc])))))))
(= 'do a0)
(let* [el (eval-ast (rest ast) env)]
(nth el (- (count el) 1)))
(= 'do a0)
(let* [el (eval-ast (rest ast) env)]
(nth el (- (count el) 1)))
(= 'if a0)
(if (EVAL (nth ast 1) env)
(EVAL (nth ast 2) env)
(if (> (count ast) 3)
(EVAL (nth ast 3) env)))
(= 'if a0)
(if (EVAL (nth ast 1) env)
(EVAL (nth ast 2) env)
(if (> (count ast) 3)
(EVAL (nth ast 3) env)))
(= 'fn* a0)
(fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
(= 'fn* a0)
(fn* [& args] (EVAL (nth ast 2) (new-env env (nth ast 1) args)))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el)))))))))
"else"
(let* [el (eval-ast ast env)]
(apply (first el) (rest el)))))))
(catch* exc
(do
(swap! trace str "\n in mal EVAL: " ast)
(throw exc))))))
;; print
(def! PRINT pr-str)
@ -143,7 +154,9 @@
(try*
(println (rep line))
(catch* exc
(println "Uncaught exception:" exc))))
(do
(println "Uncaught exception:" exc @trace)
(reset! trace "")))))
(repl-loop (readline "mal-user> "))))))
;; main