diff options
| author | Joel Martin <github@martintribe.org> | 2014-10-09 22:10:15 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-01-06 21:57:02 -0600 |
| commit | 1771ab50b87c745181e4e30f94b63e3f23d33dac (patch) | |
| tree | c1c9b5d80ba9261c5f16bab1b52c1bb0f559407c | |
| parent | f2544a9467ea032aff505b3ced3b4b3510a828fe (diff) | |
| download | mal-1771ab50b87c745181e4e30f94b63e3f23d33dac.tar.gz mal-1771ab50b87c745181e4e30f94b63e3f23d33dac.zip | |
go: update README. Backport Func usage.
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | README.md | 12 | ||||
| -rw-r--r-- | docs/TODO | 6 | ||||
| -rw-r--r-- | docs/step_notes.txt | 40 | ||||
| -rw-r--r-- | go/src/core/core.go | 33 | ||||
| -rw-r--r-- | go/src/step5_tco/step5_tco.go | 6 | ||||
| -rw-r--r-- | go/src/step6_file/step6_file.go | 15 | ||||
| -rw-r--r-- | go/src/step7_quote/step7_quote.go | 15 | ||||
| -rw-r--r-- | go/src/step8_macros/step8_macros.go | 17 | ||||
| -rw-r--r-- | go/src/stepA_more/stepA_more.go | 19 | ||||
| -rw-r--r-- | go/src/types/types.go | 29 | ||||
| -rw-r--r-- | tests/step1_read_print.mal | 1 | ||||
| -rw-r--r-- | tests/step7_quote.mal | 5 |
13 files changed, 165 insertions, 34 deletions
@@ -23,6 +23,7 @@ cs/*.dll cs/*.mdb clojure/target clojure/.lein-repl-history +go/step* java/target/ java/dependency-reduced-pom.xml rust/step0_repl @@ -3,12 +3,13 @@ ## Description Mal is an interpreter for a subset of the Clojure programming -language. Mal is implemented from scratch in 13 different languages: +language. Mal is implemented from scratch in 14 different languages: * Bash shell * C * C# * Clojure +* Go * Java * Javascript ([Online Demo](http://kanaka.github.io/mal)) * GNU Make @@ -85,6 +86,15 @@ cd clojure lein with-profile +stepX trampoline run ``` +### Go + +``` +cd go +make +./stepX_YYY +``` + + ### Java 1.7 ``` @@ -33,7 +33,6 @@ C#: Clojure: Go: - - use reflect to simplify several points - consider variable arguments in places where it makes sense https://gobyexample.com/variadic-functions @@ -110,6 +109,7 @@ Future Implementations: * Shell (Bash 4) - Tier 2 + - R - Scala - Haskell * Clojure @@ -142,4 +142,8 @@ Future Implementations: - Others: - Forth (Stack-based) - BF (Crazy) + - TeX/LaTeX + - Basic interpreter in TeX: http://ctanhg.scharrer-online.de/pkg/basix.html + - Cheat Sheet: http://www.stdout.org/~winston/latex/latexsheet.pd + - latex '\nonstopmode\input' blah.tex diff --git a/docs/step_notes.txt b/docs/step_notes.txt index 94665d8..44cf8f8 100644 --- a/docs/step_notes.txt +++ b/docs/step_notes.txt @@ -323,10 +323,23 @@ Step Notes: - EVAL: - add 'defmacro!' and 'macroexpand' - set ismacro property on function + - Details: + - cp step7_quote.EXT to step8_macros.EXT + - if compiled update Makefile + - add isMacro property to Mal Function type + - may need to go back and adjust step5-7 + - implement is_macro_call and macroexpand + - call macroexpand on ast before apply in EVAL + - add defmacro! and macroexpand to EVAL switch + - make test^go^step8 should pass some basic macros + - add first, rest and nth to core.ns + - make test^go^step8 should now pass - step9_interop - convert returned data to mal data - recursive, similar to pr_str + - Details: + - can be done after stepA_more - stepA_more - core module: @@ -340,6 +353,33 @@ Step Notes: otherwise extracts full value - set and print *host-language* - define cond and or macros using REP/RE + - Details: + - cp step8_macros.EXT to stepA_more.EXT + - if compiled update Makefile + - core.ns implement nil?, true?, false?, symbol?, sequential?, + vector, vector? + - add mal error type which wraps normal mal type + - in core.ns add throw which wraps type in mal error type + and throws/raises/sets exception + - add try*/catch* support to EVAL + - if mal error type, bind to catch* bind symbol + - otherwise, bind string of error to catch* bind symbol + - implement apply, map in core.ns + - make test^go^stepA + - implement readline.EXT + - provide option (e.g. commented out) to link with GNU + readline (GPL) or libedit (BSD) + - add hash-map functions: hash-map, map?, assoc, dissoc, get, + contains?, keys, vals + - add metadata support to List, Vector, HashMap, and Functions + - add reader macro + - may need to box HashMap and native functions + - add atom type, reader macro and functions: with_meta, meta + - get `make test^go^stepA` to fully pass + - get `./stepA_more ../mal/step1_read_print` to pass + - continue for each mal step until ../mal/stepA_more + - Now self-hosting! + - Extra defintions needed for self-hosting - core module: diff --git a/go/src/core/core.go b/go/src/core/core.go index f5d5842..57cf932 100644 --- a/go/src/core/core.go +++ b/go/src/core/core.go @@ -195,6 +195,34 @@ func do_map(a []MalType) (MalType, error) { return List{results,nil}, nil } +func conj(a []MalType) (MalType, error) { + if len(a) <2 { return nil, errors.New("conj requires at least 2 arguments") } + switch seq := a[0].(type) { + case List: + new_slc := []MalType{} + for i := len(a)-1 ; i > 0 ; i-=1 { + new_slc = append(new_slc, a[i]) + } + return List{append(new_slc, seq.Val...),nil}, nil + case Vector: + new_slc := seq.Val + for _, x := range a[1:] { + new_slc = append(new_slc, x) + } + return Vector{new_slc,nil}, nil + } + + if !HashMap_Q(a[0]) { return nil, errors.New("dissoc called on non-hash map") } + new_hm := copy_hash_map(a[0].(HashMap)) + for i := 1; i < len(a); i+=1 { + key := a[i] + if !String_Q(key) { return nil, errors.New("dissoc called with non-string key") } + delete(new_hm.Val,key.(string)) + } + return new_hm, nil +} + + // Metadata functions func with_meta(a []MalType) (MalType, error) { @@ -204,6 +232,7 @@ func with_meta(a []MalType) (MalType, error) { case List: return List{tobj.Val,m}, nil case Vector: return Vector{tobj.Val,m}, nil case HashMap: return HashMap{tobj.Val,m}, nil + case Func: return Func{tobj.Fn,m}, nil case MalFunc: fn := tobj; fn.Meta = m; return fn, nil default: return nil, errors.New("with-meta not supported on type") } @@ -215,6 +244,7 @@ func meta(a []MalType) (MalType, error) { case List: return tobj.Meta, nil case Vector: return tobj.Meta, nil case HashMap: return tobj.Meta, nil + case Func: return tobj.Meta, nil case MalFunc: return tobj.Meta, nil default: return nil, errors.New("meta not supported on type") } @@ -319,11 +349,14 @@ var NS = map[string]MalType{ "count": count, "apply": apply, "map": do_map, + "conj": conj, "with-meta": with_meta, "meta": meta, "atom": func(a []MalType) (MalType, error) { return &Atom{a[0],nil}, nil }, + "atom?": func(a []MalType) (MalType, error) { + return Atom_Q(a[0]), nil }, "deref": deref, "reset!": reset_BANG, "swap!": swap_BANG, diff --git a/go/src/step5_tco/step5_tco.go b/go/src/step5_tco/step5_tco.go index 344a533..9e176bb 100644 --- a/go/src/step5_tco/step5_tco.go +++ b/go/src/step5_tco/step5_tco.go @@ -133,9 +133,9 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { env, e = NewEnv(fn.Env, fn.Params, List{el.(List).Val[1:],nil}) if e != nil { return nil, e } } else { - fn, ok := f.(func([]MalType)(MalType, error)) + fn, ok := f.(Func) if !ok { return nil, errors.New("attempt to call non-function") } - return fn(el.(List).Val[1:]) + return fn.Fn(el.(List).Val[1:]) } } @@ -164,7 +164,7 @@ func rep(str string) (MalType, error) { func main() { // core.go: defined using go for k, v := range core.NS { - repl_env.Set(k, v) + repl_env.Set(k, Func{v.(func([]MalType)(MalType,error)),nil}) } // core.mal: defined using the language itself diff --git a/go/src/step6_file/step6_file.go b/go/src/step6_file/step6_file.go index 51fe3cb..748d158 100644 --- a/go/src/step6_file/step6_file.go +++ b/go/src/step6_file/step6_file.go @@ -134,9 +134,9 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { env, e = NewEnv(fn.Env, fn.Params, List{el.(List).Val[1:],nil}) if e != nil { return nil, e } } else { - fn, ok := f.(func([]MalType)(MalType, error)) + fn, ok := f.(Func) if !ok { return nil, errors.New("attempt to call non-function") } - return fn(el.(List).Val[1:]) + return fn.Fn(el.(List).Val[1:]) } } @@ -165,10 +165,10 @@ func rep(str string) (MalType, error) { func main() { // core.go: defined using go for k, v := range core.NS { - repl_env.Set(k, v) + repl_env.Set(k, Func{v.(func([]MalType)(MalType,error)),nil}) } - repl_env.Set("eval", func(a []MalType) (MalType, error) { - return EVAL(a[0], repl_env) }) + repl_env.Set("eval", Func{func(a []MalType) (MalType, error) { + return EVAL(a[0], repl_env) },nil}) repl_env.Set("*ARGV*", List{}) // core.mal: defined using the language itself @@ -182,7 +182,10 @@ func main() { args = append(args, a) } repl_env.Set("*ARGV*", List{args,nil}) - rep("(load-file \"" + os.Args[1] + "\")") + if _,e := rep("(load-file \"" + os.Args[1] + "\")"); e != nil { + fmt.Printf("Error: %v\n", e) + os.Exit(1) + } os.Exit(0) } diff --git a/go/src/step7_quote/step7_quote.go b/go/src/step7_quote/step7_quote.go index abf7b45..999129e 100644 --- a/go/src/step7_quote/step7_quote.go +++ b/go/src/step7_quote/step7_quote.go @@ -167,9 +167,9 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { env, e = NewEnv(fn.Env, fn.Params, List{el.(List).Val[1:],nil}) if e != nil { return nil, e } } else { - fn, ok := f.(func([]MalType)(MalType, error)) + fn, ok := f.(Func) if !ok { return nil, errors.New("attempt to call non-function") } - return fn(el.(List).Val[1:]) + return fn.Fn(el.(List).Val[1:]) } } @@ -198,10 +198,10 @@ func rep(str string) (MalType, error) { func main() { // core.go: defined using go for k, v := range core.NS { - repl_env.Set(k, v) + repl_env.Set(k, Func{v.(func([]MalType)(MalType,error)),nil}) } - repl_env.Set("eval", func(a []MalType) (MalType, error) { - return EVAL(a[0], repl_env) }) + repl_env.Set("eval", Func{func(a []MalType) (MalType, error) { + return EVAL(a[0], repl_env) },nil}) repl_env.Set("*ARGV*", List{}) // core.mal: defined using the language itself @@ -215,7 +215,10 @@ func main() { args = append(args, a) } repl_env.Set("*ARGV*", List{args,nil}) - rep("(load-file \"" + os.Args[1] + "\")") + if _,e := rep("(load-file \"" + os.Args[1] + "\")"); e != nil { + fmt.Printf("Error: %v\n", e) + os.Exit(1) + } os.Exit(0) } diff --git a/go/src/step8_macros/step8_macros.go b/go/src/step8_macros/step8_macros.go index 0feb739..3264159 100644 --- a/go/src/step8_macros/step8_macros.go +++ b/go/src/step8_macros/step8_macros.go @@ -206,9 +206,9 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { env, e = NewEnv(fn.Env, fn.Params, List{el.(List).Val[1:],nil}) if e != nil { return nil, e } } else { - fn, ok := f.(func([]MalType)(MalType, error)) + fn, ok := f.(Func) if !ok { return nil, errors.New("attempt to call non-function") } - return fn(el.(List).Val[1:]) + return fn.Fn(el.(List).Val[1:]) } } @@ -237,15 +237,17 @@ func rep(str string) (MalType, error) { func main() { // core.go: defined using go for k, v := range core.NS { - repl_env.Set(k, v) + repl_env.Set(k, Func{v.(func([]MalType)(MalType,error)),nil}) } - repl_env.Set("eval", func(a []MalType) (MalType, error) { - return EVAL(a[0], repl_env) }) + repl_env.Set("eval", Func{func(a []MalType) (MalType, error) { + return EVAL(a[0], repl_env) },nil}) repl_env.Set("*ARGV*", List{}) // core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))") rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") + 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)))))))") + rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") // called with mal script to load and eval if len(os.Args) > 1 { @@ -254,7 +256,10 @@ func main() { args = append(args, a) } repl_env.Set("*ARGV*", List{args,nil}) - rep("(load-file \"" + os.Args[1] + "\")") + if _,e := rep("(load-file \"" + os.Args[1] + "\")"); e != nil { + fmt.Printf("Error: %v\n", e) + os.Exit(1) + } os.Exit(0) } diff --git a/go/src/stepA_more/stepA_more.go b/go/src/stepA_more/stepA_more.go index d573ad0..322ee36 100644 --- a/go/src/stepA_more/stepA_more.go +++ b/go/src/stepA_more/stepA_more.go @@ -228,9 +228,9 @@ func EVAL(ast MalType, env EnvType) (MalType, error) { env, e = NewEnv(fn.Env, fn.Params, List{el.(List).Val[1:],nil}) if e != nil { return nil, e } } else { - fn, ok := f.(func([]MalType)(MalType, error)) + fn, ok := f.(Func) if !ok { return nil, errors.New("attempt to call non-function") } - return fn(el.(List).Val[1:]) + return fn.Fn(el.(List).Val[1:]) } } @@ -259,15 +259,18 @@ func rep(str string) (MalType, error) { func main() { // core.go: defined using go for k, v := range core.NS { - repl_env.Set(k, v) + repl_env.Set(k, Func{v.(func([]MalType)(MalType,error)),nil}) } - repl_env.Set("eval", func(a []MalType) (MalType, error) { - return EVAL(a[0], repl_env) }) + repl_env.Set("eval", Func{func(a []MalType) (MalType, error) { + return EVAL(a[0], repl_env) },nil}) repl_env.Set("*ARGV*", List{}) // core.mal: defined using the language itself + rep("(def! *host-language* \"go\")") rep("(def! not (fn* (a) (if a false true)))") rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") + 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)))))))") + rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") // called with mal script to load and eval if len(os.Args) > 1 { @@ -276,11 +279,15 @@ func main() { args = append(args, a) } repl_env.Set("*ARGV*", List{args,nil}) - rep("(load-file \"" + os.Args[1] + "\")") + if _,e := rep("(load-file \"" + os.Args[1] + "\")"); e != nil { + fmt.Printf("Error: %v\n", e) + os.Exit(1) + } os.Exit(0) } // repl loop + rep("(println (str \"Mal [\" *host-language* \"]\"))") for { text, err := readline.Readline("user> ") text = strings.TrimRight(text, "\n"); diff --git a/go/src/types/types.go b/go/src/types/types.go index 616c8c7..613bfb5 100644 --- a/go/src/types/types.go +++ b/go/src/types/types.go @@ -65,6 +65,16 @@ func String_Q(obj MalType) bool { // Functions +type Func struct { + Fn func([]MalType) (MalType, error) + Meta MalType +} + +func Func_Q(obj MalType) bool { + if obj == nil { return false } + return reflect.TypeOf(obj).Name() == "Func" +} + type MalFunc struct { Eval func(MalType, EnvType) (MalType, error) Exp MalType @@ -97,6 +107,8 @@ func Apply(f_mt MalType, a []MalType) (MalType, error) { env, e := f.GenEnv(f.Env, f.Params, List{a,nil}) if e != nil { return nil, e } return f.Eval(f.Exp, env) + case Func: + return f.Fn(a) case func([]MalType)(MalType, error): return f(a) default: @@ -206,18 +218,25 @@ func Equal_Q(a MalType, b MalType) bool { } //av := reflect.ValueOf(a); bv := reflect.ValueOf(b) //fmt.Printf("here2: %#v\n", reflect.TypeOf(a).Name()) - switch reflect.TypeOf(a).Name() { - case "Symbol": + //switch reflect.TypeOf(a).Name() { + switch a.(type) { + case Symbol: return a.(Symbol).Val == b.(Symbol).Val - case "List": fallthrough - case "Vector": + case List: + as,_ := GetSlice(a); bs,_ := GetSlice(b) + if len(as) != len(bs) { return false } + for i := 0; i < len(as); i+=1 { + if !Equal_Q(as[i], bs[i]) { return false } + } + return true + case Vector: as,_ := GetSlice(a); bs,_ := GetSlice(b) if len(as) != len(bs) { return false } for i := 0; i < len(as); i+=1 { if !Equal_Q(as[i], bs[i]) { return false } } return true - case "HashMap": + case HashMap: return false default: return a == b diff --git a/tests/step1_read_print.mal b/tests/step1_read_print.mal index 7772de9..2b7c269 100644 --- a/tests/step1_read_print.mal +++ b/tests/step1_read_print.mal @@ -78,6 +78,7 @@ abc-def ;; ;; Testing reader errors +;;; TODO: fix these so they fail correctly (1 2 ; expected ')', got EOF [1 2 diff --git a/tests/step7_quote.mal b/tests/step7_quote.mal index 9166065..38dac01 100644 --- a/tests/step7_quote.mal +++ b/tests/step7_quote.mal @@ -73,3 +73,8 @@ ;=>false (= "abc" 'abc) ;=>false + +;;;;; Test quine +;;; TODO: needs expect line length fix +;;;((fn* [q] (quasiquote ((unquote q) (quote (unquote q))))) (quote (fn* [q] (quasiquote ((unquote q) (quote (unquote q))))))) +;;;=>((fn* [q] (quasiquote ((unquote q) (quote (unquote q))))) (quote (fn* [q] (quasiquote ((unquote q) (quote (unquote q))))))) |
