diff options
| author | Joel Martin <github@martintribe.org> | 2014-10-06 21:03:03 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2014-10-06 21:03:03 -0500 |
| commit | 17e1c5f9f4006399398e8bb7e219a79962ebf3f0 (patch) | |
| tree | cb7de0111d91c56ae61ee500a2798264773cf54b | |
| parent | af8fdff41e260b1b21be0e127afb536980f43804 (diff) | |
| download | mal-17e1c5f9f4006399398e8bb7e219a79962ebf3f0.tar.gz mal-17e1c5f9f4006399398e8bb7e219a79962ebf3f0.zip | |
go: add step5_tco. Refactor env.
Move EnvType interface definition to types.go. Remove Env pointers.
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | docs/step_notes.txt | 21 | ||||
| -rw-r--r-- | go/Makefile | 5 | ||||
| -rw-r--r-- | go/src/printer/printer.go | 4 | ||||
| -rw-r--r-- | go/src/step1_read_print/step1_read_print.go | 19 | ||||
| -rw-r--r-- | go/src/step2_eval/step2_eval.go | 7 | ||||
| -rw-r--r-- | go/src/step3_env/step3_env.go | 19 | ||||
| -rw-r--r-- | go/src/step4_if_fn_do/step4_if_fn_do.go | 25 | ||||
| -rw-r--r-- | go/src/step5_tco/step5_tco.go | 190 | ||||
| -rw-r--r-- | go/src/types/types.go | 46 | ||||
| -rw-r--r-- | js/types.js | 2 |
11 files changed, 287 insertions, 52 deletions
@@ -29,6 +29,7 @@ EXCLUDE_TESTS += test^c^step5 # segfault EXCLUDE_TESTS += test^cs^step5 # fatal stack overflow fault EXCLUDE_TESTS += test^make^step5 # no TCO capability/step EXCLUDE_TESTS += test^mal^step5 # no TCO capability/step +EXCLUDE_TESTS += test^go^step5 # test completes, even at 100,000 EXCLUDE_TESTS += test^php^step5 # test completes, even at 100,000 EXCLUDE_TESTS += test^ruby^step5 # test completes, even at 100,000 diff --git a/docs/step_notes.txt b/docs/step_notes.txt index 02ad305..5d29888 100644 --- a/docs/step_notes.txt +++ b/docs/step_notes.txt @@ -233,9 +233,9 @@ Step Notes: - step5_tco - types module: - mal function type: - - stores: func, exp, env, params - - func is EVAL in native mal case, otherwise reference to - platform function + - stores: eval, exp, env, params + - eval is EVAL in native mal case (needed for map function + later), otherwise reference to platform function - if metadata support, then store exp, env, params as metadata - printer @@ -245,8 +245,19 @@ Step Notes: - cases where we directly return result of EVAL, instead set ast and env to what would be put in the EVAL, then loop. - do, if, "apply" - - for "apply" case, set env to new Env based on properties - on the function + - "apply" + - if mal function type + - set env to new Env based on properties on the function + - if native function, same as before + - Details: + - types.EXT + - create Mal function type to store eval, exp, env, params + - cp step4_if_fn_do.EXT to step5_tco.EXT + - wrap EVAL in infinite while loop + - in let*, do, and if: + - set ast and env and loop (no return) + - in fn* create Mal function type + - step6_file - core module: diff --git a/go/Makefile b/go/Makefile index a029af7..7b7f2f1 100644 --- a/go/Makefile +++ b/go/Makefile @@ -4,12 +4,13 @@ export GOPATH := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) SOURCES_BASE = src/types/types.go src/reader/reader.go src/printer/printer.go \ src/env/env.go src/core/core.go -SOURCES_LISP = src/step4_if_fn_do/step4_if_fn_do.go +SOURCES_LISP = src/step5_tco/step5_tco.go SOURCES = $(SOURCES_BASE) $(SOURCES_LISP) ##################### -SRCS = step0_repl.go step1_read_print.go step2_eval.go step3_env.go step4_if_fn_do.go +SRCS = step0_repl.go step1_read_print.go step2_eval.go step3_env.go \ + step4_if_fn_do.go step5_tco.go BINS = $(SRCS:%.go=%) ##################### diff --git a/go/src/printer/printer.go b/go/src/printer/printer.go index 8cb4a40..5050c5a 100644 --- a/go/src/printer/printer.go +++ b/go/src/printer/printer.go @@ -45,6 +45,10 @@ func Pr_str(obj types.MalType, print_readably bool) string { return tobj.Val case nil: return "nil" + case types.MalFunc: + return "(fn* " + + Pr_str(tobj.Params, true) + " " + + Pr_str(tobj.Exp, true) + ")" default: return fmt.Sprintf("%v", obj) } diff --git a/go/src/step1_read_print/step1_read_print.go b/go/src/step1_read_print/step1_read_print.go index 1f020af..edd8c42 100644 --- a/go/src/step1_read_print/step1_read_print.go +++ b/go/src/step1_read_print/step1_read_print.go @@ -9,34 +9,35 @@ import ( ) import ( - "types" + . "types" "reader" "printer" ) // read -func READ(str string) (types.MalType, error) { +func READ(str string) (MalType, error) { return reader.Read_str(str) } // eval -func EVAL(ast types.MalType, env string) (types.MalType, error) { +func EVAL(ast MalType, env string) (MalType, error) { return ast, nil } // print -func PRINT(exp types.MalType) (types.MalType, error) { +func PRINT(exp MalType) (string, error) { return printer.Pr_str(exp, true), nil } // repl -func rep(str string) (types.MalType, error) { - var exp types.MalType +func rep(str string) (MalType, error) { + var exp MalType + var res string var e error if exp, e = READ(str); e != nil { return nil, e } if exp, e = EVAL(exp, ""); e != nil { return nil, e } - if exp, e = PRINT(exp); e != nil { return nil, e } - return exp, nil + if res, e = PRINT(exp); e != nil { return nil, e } + return res, nil } func main() { @@ -49,7 +50,7 @@ func main() { if (err != nil) { return } - var out types.MalType + var out MalType var e error if out, e = rep(text); e != nil { if e.Error() == "<empty line>" { continue } diff --git a/go/src/step2_eval/step2_eval.go b/go/src/step2_eval/step2_eval.go index 31d6f64..3ab35ed 100644 --- a/go/src/step2_eval/step2_eval.go +++ b/go/src/step2_eval/step2_eval.go @@ -79,7 +79,7 @@ func EVAL(ast MalType, env map[string]MalType) (MalType, error) { } // print -func PRINT(exp MalType) (MalType, error) { +func PRINT(exp MalType) (string, error) { return printer.Pr_str(exp, true), nil } @@ -101,11 +101,12 @@ var repl_env = map[string]MalType{ // repl func rep(str string) (MalType, error) { var exp MalType + var res string var e error if exp, e = READ(str); e != nil { return nil, e } if exp, e = EVAL(exp, repl_env); e != nil { return nil, e } - if exp, e = PRINT(exp); e != nil { return nil, e } - return exp, nil + if res, e = PRINT(exp); e != nil { return nil, e } + return res, nil } func main() { diff --git a/go/src/step3_env/step3_env.go b/go/src/step3_env/step3_env.go index 3d35dbe..68a8f0c 100644 --- a/go/src/step3_env/step3_env.go +++ b/go/src/step3_env/step3_env.go @@ -22,7 +22,7 @@ func READ(str string) (MalType, error) { } // eval -func eval_ast(ast MalType, env Env) (MalType, error) { +func eval_ast(ast MalType, env EnvType) (MalType, error) { //fmt.Printf("eval_ast: %#v\n", ast) if Symbol_Q(ast) { return env.Get(ast.(Symbol).Val) @@ -61,7 +61,7 @@ func eval_ast(ast MalType, env Env) (MalType, error) { } } -func EVAL(ast MalType, env Env) (MalType, error) { +func EVAL(ast MalType, env EnvType) (MalType, error) { //fmt.Printf("EVAL: %#v\n", ast) switch ast.(type) { case List: // continue @@ -87,7 +87,7 @@ func EVAL(ast MalType, env Env) (MalType, error) { if e != nil { return nil, e } return env.Set(a1.(Symbol).Val, res), nil case "let*": - let_env, e := NewEnv(&env, nil, nil) + let_env, e := NewEnv(env, nil, nil) if e != nil { return nil, e } arr1, e := GetSlice(a1) if e != nil { return nil, e } @@ -95,11 +95,11 @@ func EVAL(ast MalType, env Env) (MalType, error) { if !Symbol_Q(arr1[i]) { return nil, errors.New("non-symbol bind value") } - exp, e := EVAL(arr1[i+1], *let_env) + exp, e := EVAL(arr1[i+1], let_env) if e != nil { return nil, e } let_env.Set(arr1[i].(Symbol).Val, exp) } - return EVAL(a2, *let_env) + return EVAL(a2, let_env) default: el, e := eval_ast(ast, env) if e != nil { return nil, e } @@ -110,7 +110,7 @@ func EVAL(ast MalType, env Env) (MalType, error) { } // print -func PRINT(exp MalType) (MalType, error) { +func PRINT(exp MalType) (string, error) { return printer.Pr_str(exp, true), nil } @@ -120,11 +120,12 @@ var repl_env, _ = NewEnv(nil, nil, nil) // repl func rep(str string) (MalType, error) { var exp MalType + var res string var e error if exp, e = READ(str); e != nil { return nil, e } - if exp, e = EVAL(exp, *repl_env); e != nil { return nil, e } - if exp, e = PRINT(exp); e != nil { return nil, e } - return exp, nil + if exp, e = EVAL(exp, repl_env); e != nil { return nil, e } + if res, e = PRINT(exp); e != nil { return nil, e } + return res, nil } func main() { diff --git a/go/src/step4_if_fn_do/step4_if_fn_do.go b/go/src/step4_if_fn_do/step4_if_fn_do.go index f0cc41f..ef12006 100644 --- a/go/src/step4_if_fn_do/step4_if_fn_do.go +++ b/go/src/step4_if_fn_do/step4_if_fn_do.go @@ -23,7 +23,7 @@ func READ(str string) (MalType, error) { } // eval -func eval_ast(ast MalType, env Env) (MalType, error) { +func eval_ast(ast MalType, env EnvType) (MalType, error) { //fmt.Printf("eval_ast: %#v\n", ast) if Symbol_Q(ast) { return env.Get(ast.(Symbol).Val) @@ -62,8 +62,8 @@ func eval_ast(ast MalType, env Env) (MalType, error) { } } -func EVAL(ast MalType, env Env) (MalType, error) { - //fmt.Printf("EVAL: %#v\n", ast) +func EVAL(ast MalType, env EnvType) (MalType, error) { + //fmt.Printf("EVAL: %v\n", printer.Pr_str(ast, true)) switch ast.(type) { case List: // continue default: return eval_ast(ast, env) @@ -88,7 +88,7 @@ func EVAL(ast MalType, env Env) (MalType, error) { if e != nil { return nil, e } return env.Set(a1.(Symbol).Val, res), nil case "let*": - let_env, e := NewEnv(&env, nil, nil) + let_env, e := NewEnv(env, nil, nil) if e != nil { return nil, e } arr1, e := GetSlice(a1) if e != nil { return nil, e } @@ -96,11 +96,11 @@ func EVAL(ast MalType, env Env) (MalType, error) { if !Symbol_Q(arr1[i]) { return nil, errors.New("non-symbol bind value") } - exp, e := EVAL(arr1[i+1], *let_env) + exp, e := EVAL(arr1[i+1], let_env) if e != nil { return nil, e } let_env.Set(arr1[i].(Symbol).Val, exp) } - return EVAL(a2, *let_env) + return EVAL(a2, let_env) case "do": el, e := eval_ast(List{ast.(List).Val[1:]}, env) if e != nil { return nil, e } @@ -123,9 +123,9 @@ func EVAL(ast MalType, env Env) (MalType, error) { return func(arguments []MalType) (MalType, error) { a1s, e := GetSlice(a1) if e != nil { return nil, e } - new_env, e := NewEnv(&env, a1s, arguments) + new_env, e := NewEnv(env, a1s, arguments) if e != nil { return nil, e } - return EVAL(a2, *new_env) + return EVAL(a2, new_env) }, nil default: el, e := eval_ast(ast, env) @@ -137,7 +137,7 @@ func EVAL(ast MalType, env Env) (MalType, error) { } // print -func PRINT(exp MalType) (MalType, error) { +func PRINT(exp MalType) (string, error) { return printer.Pr_str(exp, true), nil } @@ -147,11 +147,12 @@ var repl_env, _ = NewEnv(nil, nil, nil) // repl func rep(str string) (MalType, error) { var exp MalType + var res string var e error if exp, e = READ(str); e != nil { return nil, e } - if exp, e = EVAL(exp, *repl_env); e != nil { return nil, e } - if exp, e = PRINT(exp); e != nil { return nil, e } - return exp, nil + if exp, e = EVAL(exp, repl_env); e != nil { return nil, e } + if res, e = PRINT(exp); e != nil { return nil, e } + return res, nil } func main() { diff --git a/go/src/step5_tco/step5_tco.go b/go/src/step5_tco/step5_tco.go new file mode 100644 index 0000000..e426f16 --- /dev/null +++ b/go/src/step5_tco/step5_tco.go @@ -0,0 +1,190 @@ +package main + +import ( + "bufio" + //"io" + "fmt" + "os" + "strings" + "errors" +) + +import ( + . "types" + "reader" + "printer" + . "env" + "core" +) + +// read +func READ(str string) (MalType, error) { + return reader.Read_str(str) +} + +// eval +func eval_ast(ast MalType, env EnvType) (MalType, error) { + //fmt.Printf("eval_ast: %#v\n", ast) + if Symbol_Q(ast) { + return env.Get(ast.(Symbol).Val) + } else if List_Q(ast) { + lst := []MalType{} + for _, a := range ast.(List).Val { + exp, e := EVAL(a, env) + if e != nil { return nil, e } + lst = append(lst, exp) + } + return List{lst}, nil + } else if Vector_Q(ast) { + lst := []MalType{} + for _, a := range ast.(Vector).Val { + exp, e := EVAL(a, env) + if e != nil { return nil, e } + lst = append(lst, exp) + } + return Vector{lst}, nil + } else if Hash_Map_Q(ast) { + m := ast.(map[string]MalType) + new_hm := map[string]MalType{} + for k, v := range m { + ke, e1 := EVAL(k, env) + if e1 != nil { return nil, e1 } + if _, ok := ke.(string); !ok { + return nil, errors.New("non string hash-map key") + } + kv, e2 := EVAL(v, env) + if e2 != nil { return nil, e2 } + new_hm[ke.(string)] = kv + } + return new_hm, nil + } else { + return ast, nil + } +} + +func EVAL(ast MalType, env EnvType) (MalType, error) { + for { + + //fmt.Printf("EVAL: %v\n", printer.Pr_str(ast, true)) + switch ast.(type) { + case List: // continue + default: return eval_ast(ast, env) + } + + // apply list + a0 := ast.(List).Val[0] + var a1 MalType = nil; var a2 MalType = nil + switch len(ast.(List).Val) { + case 1: + a1 = nil; a2 = nil + case 2: + a1 = ast.(List).Val[1]; a2 = nil + default: + a1 = ast.(List).Val[1]; a2 = ast.(List).Val[2] + } + a0sym := "__<*fn*>__" + if Symbol_Q(a0) { a0sym = a0.(Symbol).Val } + switch a0sym { + case "def!": + res, e := EVAL(a2, env) + if e != nil { return nil, e } + return env.Set(a1.(Symbol).Val, res), nil + case "let*": + let_env, e := NewEnv(env, nil, nil) + if e != nil { return nil, e } + arr1, e := GetSlice(a1) + if e != nil { return nil, e } + for i := 0; i < len(arr1); i+=2 { + if !Symbol_Q(arr1[i]) { + return nil, errors.New("non-symbol bind value") + } + exp, e := EVAL(arr1[i+1], let_env) + if e != nil { return nil, e } + let_env.Set(arr1[i].(Symbol).Val, exp) + } + ast = a2 + env = let_env + case "do": + lst := ast.(List).Val + _, e := eval_ast(List{lst[1:len(lst)-1]}, env) + if e != nil { return nil, e } + if len(lst) == 1 { return nil, nil } + ast = lst[len(lst)-1] + case "if": + cond, e := EVAL(a1, env) + if e != nil { return nil, e } + if cond == nil || cond == false { + if len(ast.(List).Val) >= 4 { + ast = ast.(List).Val[3] + } else { + return nil, nil + } + } else { + ast = a2 + } + case "fn*": + fn := MalFunc{EVAL, a2, env, a1} + return fn, nil + default: + el, e := eval_ast(ast, env) + if e != nil { return nil, e } + f := el.(List).Val[0] + if MalFunc_Q(f) { + fn := f.(MalFunc) + ast = fn.Exp + env, e = NewEnv(fn.Env, fn.Params.(List).Val, el.(List).Val[1:]) + if e != nil { return nil, e } + } else { + fn, ok := f.(func([]MalType)(MalType, error)) + if !ok { return nil, errors.New("attempt to call non-function") } + return fn(el.(List).Val[1:]) + } + } + + } // TCO loop +} + +// print +func PRINT(exp MalType) (string, error) { + return printer.Pr_str(exp, true), nil +} + + +var repl_env, _ = NewEnv(nil, nil, nil) + +// repl +func rep(str string) (MalType, error) { + var exp MalType + var res string + var e error + if exp, e = READ(str); e != nil { return nil, e } + if exp, e = EVAL(exp, repl_env); e != nil { return nil, e } + if res, e = PRINT(exp); e != nil { return nil, e } + return res, nil +} + +func main() { + for k, v := range core.NS { + repl_env.Set(k, v) + } + rep("(def! not (fn* (a) (if a false true)))") + + rdr := bufio.NewReader(os.Stdin); + // repl loop + for { + fmt.Print("user> "); + text, err := rdr.ReadString('\n'); + text = strings.TrimRight(text, "\n"); + if (err != nil) { + return + } + var out MalType + var e error + if out, e = rep(text); e != nil { + if e.Error() == "<empty line>" { continue } + fmt.Printf("Error: %v\n", e) + continue + } + fmt.Printf("%v\n", out) + } +} diff --git a/go/src/types/types.go b/go/src/types/types.go index 95bdc1f..f0ab427 100644 --- a/go/src/types/types.go +++ b/go/src/types/types.go @@ -6,34 +6,54 @@ import ( //"fmt" ) -//type Error interface { -// error -//} +//import ( +// "env" +//) type MalType interface { } +type EnvType interface { + //Find(key string) *EnvType + Find(key string) EnvType + Set(key string, value MalType) MalType + Get(key string) (MalType, error) +} + +// Symbols type Symbol struct { Val string } -type List struct { - Val []MalType +func Symbol_Q(obj MalType) bool { + switch obj.(type) { + case Symbol: return true + default: return false + } } -type Vector struct { - Val []MalType + +// Functions +type MalFunc struct { + Eval func(MalType, EnvType) (MalType, error) + Exp MalType + Env EnvType + Params MalType } -// Symbols -func Symbol_Q(obj MalType) bool { +func MalFunc_Q(obj MalType) bool { switch obj.(type) { - case Symbol: return true - default: return false + case MalFunc: return true + default: return false } } + // Lists +type List struct { + Val []MalType +} + func List_Q(obj MalType) bool { switch obj.(type) { case List: return true @@ -42,6 +62,10 @@ func List_Q(obj MalType) bool { } // Vectors +type Vector struct { + Val []MalType +} + func Vector_Q(obj MalType) bool { switch obj.(type) { case Vector: return true diff --git a/js/types.js b/js/types.js index d288231..de90d54 100644 --- a/js/types.js +++ b/js/types.js @@ -4,7 +4,7 @@ if (typeof module === 'undefined') { var exports = types; } -// General fucnctions +// General functions function _obj_type(obj) { if (_symbol_Q(obj)) { return 'symbol'; } |
