From ac880c26a6e89710d77336c1563f180d720c25f4 Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 14 May 2018 21:48:44 +0200 Subject: [PATCH] all: add optional args --- README.md | 1 - ast/ast.go | 64 +++++++++++-------- eval/eval.go | 163 ++++++++++++++++++++++++++++------------------- parser/parser.go | 12 ++-- 4 files changed, 139 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index c2b7f05..4124fa9 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ - Destructuring assignment - `maptable` - `coerce` -- `o` - `len` - `mac` - `\`` diff --git a/ast/ast.go b/ast/ast.go index 7bed41e..0661b96 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -3,6 +3,8 @@ package ast import ( "fmt" "strings" + + "github.com/cevaris/ordered_map" ) type AST struct { @@ -21,8 +23,8 @@ const ( Fn Char Table - Prim - Quoted + Prim + Quoted ) func (tag Tag) String() string { @@ -56,20 +58,20 @@ func (ast *AST) Type() *AST { "cons", } - if ast.Tag == Quoted { - return ast.Val.(*AST).Type() - } + if ast.Tag == Quoted { + return ast.Val.(*AST).Type() + } - name := names[ast.Tag] - if ast.Tag == Num { - val := ast.Val.(float64) - if val == float64(int64(val)) { - name = "int" - } - } + name := names[ast.Tag] + if ast.Tag == Num { + val := ast.Val.(float64) + if val == float64(int64(val)) { + name = "int" + } + } - res := AST{Symbol, name} - return &res + res := AST{Symbol, name} + return &res } func (ast *AST) Pretty() string { @@ -87,11 +89,12 @@ func (ast *AST) Pretty() string { for _, elem := range val.Params { agg = append(agg, elem) } - opt := "" - if val.HasOpt() { - opt = " . " + *val.Opt - } - body := val.Body.Pretty() + opt := "" + if val.HasRest() { + opt = " . " + *val.Rest + } + // TODO: opt + body := val.Body.Pretty() return "(fn (" + strings.Join(agg, " ") + opt + ") " + body + ")" } else if ast.Tag == Char { return "#" + string(ast.Val.(rune)) @@ -104,8 +107,8 @@ func (ast *AST) Pretty() string { return "#hash(" + strings.Join(agg, " ") + ")" } else if ast.Tag == Prim { - return "#" - } + return "#" + } return fmt.Sprintf("%v", ast.Val) } @@ -117,16 +120,16 @@ type Env struct { type PrimFn func([]*AST) (*AST, error) type Primitive struct { - Name string - Fn PrimFn + Name string + Fn PrimFn } -func newEnv(parent *Env) Env { +func NewEnv(parent *Env) Env { return Env{parent, make(map[string]*AST)} } func ParentEnv() Env { - return newEnv(nil) + return NewEnv(nil) } func (e *Env) Lookup(elem string) (*AST, error) { @@ -145,11 +148,16 @@ func (e *Env) Lookup(elem string) (*AST, error) { type Func struct { Params []string - Opt *string + Rest *string + Opt *ordered_map.OrderedMap Body *AST Env Env } -func (f *Func) HasOpt() bool { - return f.Opt != nil +func (f *Func) HasRest() bool { + return f.Rest != nil +} + +func (f *Func) HasOpt() bool { + return f.Opt.Len() != 0 } diff --git a/eval/eval.go b/eval/eval.go index 61a9cde..9e2e817 100644 --- a/eval/eval.go +++ b/eval/eval.go @@ -6,6 +6,8 @@ import ( "fmt" "strings" + "github.com/cevaris/ordered_map" + "github.com/hellerve/argos/ast" ) @@ -14,24 +16,24 @@ var falseVal = ast.AST{ast.Bool, false} var nilVal = ast.AST{ast.List, []*ast.AST{}} func RootEnv() ast.Env { - e := ast.ParentEnv() + e := ast.ParentEnv() - prims := map[string]ast.PrimFn{ - "cons": evalCons, - "car": evalCar, - "cdr": evalCdr, - "null?": evalNull, - "pr": evalPr, - "table": evalTable, - "type": evalType, - } + prims := map[string]ast.PrimFn{ + "cons": evalCons, + "car": evalCar, + "cdr": evalCdr, + "null?": evalNull, + "pr": evalPr, + "table": evalTable, + "type": evalType, + } - for k, v := range prims { - prim := ast.AST{ast.Prim, ast.Primitive{k, v}} - e.Values[k] = &prim - } + for k, v := range prims { + prim := ast.AST{ast.Prim, ast.Primitive{k, v}} + e.Values[k] = &prim + } - return e + return e } func checkArity(input []*ast.AST, arity int, name string) error { @@ -202,9 +204,9 @@ func evalCons(input []*ast.AST) (*ast.AST, error) { func evalCar(input []*ast.AST) (*ast.AST, error) { err := checkArity(input, 1, "car") - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } lst := input[0] @@ -264,27 +266,45 @@ func evalFn(input []*ast.AST, e ast.Env) (*ast.AST, error) { } var argsStr []string - var opt *string = nil + var rest *string = nil + opt := ordered_map.NewOrderedMap() - argslst := args.Val.([]*ast.AST) + argslst := args.Val.([]*ast.AST) for i, a := range argslst { - if a.Tag != ast.Symbol { + if a.Tag != ast.Symbol && a.Tag != ast.List { return nil, fmt.Errorf("Argument list cannot contain %s", a.Pretty()) } - val := a.Val.(string) - // TODO: error handling - if val == "." { - str := argslst[i+1].Val.(string) - opt = &str - break - } + + if a.Tag == ast.List { + argv := a.Val.([]*ast.AST) + argvln := len(argv) + if (argvln != 3 && argvln != 2) || argv[0].Tag != ast.Symbol || argv[0].Val.(string) != "o" || argv[0].Tag != ast.Symbol { + return nil, fmt.Errorf("Argument list cannot contain %s", a.Pretty()) + } + if argvln == 2 { + opt.Set(argv[1].Val.(string), nil) + } else if argvln == 3 { + opt.Set(argv[1].Val.(string), argv[2]) + } + continue + } + + val := a.Val.(string) + // TODO: error handling + if val == "." { + str := argslst[i+1].Val.(string) + rest = &str + break + } argsStr = append(argsStr, val) } body := input[1] - res := ast.AST{ast.Fn, ast.Func{argsStr, opt, body, e}} + fe := ast.NewEnv(&e) + + res := ast.AST{ast.Fn, ast.Func{argsStr, rest, opt, body, fe}} return &res, nil } @@ -292,7 +312,7 @@ func funcApply(f ast.Func, args []*ast.AST, e ast.Env) (*ast.AST, error) { plen := len(f.Params) alen := len(args) - if plen != alen && !f.HasOpt() { + if plen != alen && !f.HasRest() && !f.HasOpt() { return nil, fmt.Errorf("Function expected %d arguments, was called with %d.", plen, alen) } @@ -300,11 +320,22 @@ func funcApply(f ast.Func, args []*ast.AST, e ast.Env) (*ast.AST, error) { f.Env.Values[a] = args[i] } - if f.HasOpt() { - lst := ast.AST{ast.List, args[plen:]} - quoted := ast.AST{ast.Quoted, &lst} - f.Env.Values[*f.Opt] = "ed - } + if f.HasRest() { + lst := ast.AST{ast.List, args[plen:]} + quoted := ast.AST{ast.Quoted, &lst} + f.Env.Values[*f.Rest] = "ed + } else { + iter := f.Opt.IterFunc() + i := plen + for kv, ok := iter(); ok; kv, ok = iter() { + if i < alen { + f.Env.Values[kv.Key.(string)] = args[i] + } else { + f.Env.Values[kv.Key.(string)] = kv.Value.(*ast.AST) + } + i++ + } + } return Eval(f.Body, f.Env) } @@ -358,19 +389,19 @@ func evalApply(l []*ast.AST, e ast.Env) (*ast.AST, error) { return nil, err } - // TODO: error handling - args, err := Eval(l[1], e) + // TODO: error handling + args, err := Eval(l[1], e) if err != nil { return nil, err } - if args.Tag != ast.Quoted { - return nil, fmt.Errorf("Argument 2 to apply must be a list, got %s", args.Pretty()) - } + if args.Tag != ast.Quoted { + return nil, fmt.Errorf("Argument 2 to apply must be a list, got %s", args.Pretty()) + } - argslst := args.Val.(*ast.AST).Val.([]*ast.AST) - return evalList(append([]*ast.AST{l[0]}, argslst...), e) + argslst := args.Val.(*ast.AST).Val.([]*ast.AST) + return evalList(append([]*ast.AST{l[0]}, argslst...), e) } func evalWhile(l []*ast.AST, e ast.Env) (*ast.AST, error) { @@ -425,18 +456,18 @@ func evalType(l []*ast.AST) (*ast.AST, error) { func evalSymbolList(l []*ast.AST, e ast.Env) (*ast.AST, error) { sym := l[0].Val.(string) - l = l[1:] - // TODO: make more of these primitives + l = l[1:] + // TODO: make more of these primitives switch sym { case "=": return evalDef(l, e) case "is": return evalEq(l, e) - case "apply": - return evalApply(l, e) + case "apply": + return evalApply(l, e) case "quote": - lst := l[0] - res := ast.AST{ast.Quoted, lst} + lst := l[0] + res := ast.AST{ast.Quoted, lst} return &res, nil case "fn": return evalFn(l, e) @@ -533,38 +564,38 @@ func evalIdx(l []*ast.AST, e ast.Env) (*ast.AST, error) { } func primCall(f ast.Primitive, l []*ast.AST, e ast.Env) (*ast.AST, error) { - var args []*ast.AST + var args []*ast.AST - for _, a := range l { - res, err := Eval(a, e) - if err != nil { - return nil, err - } - args = append(args, res) - } + for _, a := range l { + res, err := Eval(a, e) + if err != nil { + return nil, err + } + args = append(args, res) + } - return f.Fn(args) + return f.Fn(args) } func evalList(l []*ast.AST, e ast.Env) (*ast.AST, error) { head := l[0] - var err error + var err error - if head.Tag == ast.List { - head, err = Eval(head, e) + if head.Tag == ast.List { + head, err = Eval(head, e) - if err != nil { - return nil, err - } - } + if err != nil { + return nil, err + } + } switch head.Tag { case ast.Symbol: return evalSymbolList(l, e) case ast.Fn: return funcApply(head.Val.(ast.Func), l[1:], e) - case ast.Prim: - return primCall(head.Val.(ast.Primitive), l[1:], e) + case ast.Prim: + return primCall(head.Val.(ast.Primitive), l[1:], e) case ast.String, ast.Quoted, ast.Table: return evalIdx(append([]*ast.AST{head}, l[1:]...), e) } diff --git a/parser/parser.go b/parser/parser.go index 460747c..83dfde6 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -94,16 +94,16 @@ func parseValue(input []string) (*ast.AST, error, []string) { if input[0][0] == '~' { fn := ast.AST{ast.Symbol, input[0][1:]} - complement := ast.AST{ast.Symbol, "complement"} + complement := ast.AST{ast.Symbol, "complement"} res = ast.AST{ast.List, []*ast.AST{&complement, &fn}} return &res, nil, input[1:] } if strings.Contains(input[0], ":") { - comp := ast.AST{ast.Symbol, "compose"} + comp := ast.AST{ast.Symbol, "compose"} fns := []*ast.AST{&comp} for _, fn := range strings.Split(input[0], ":") { - astfn := ast.AST{ast.Symbol, fn} + astfn := ast.AST{ast.Symbol, fn} fns = append(fns, &astfn) } res = ast.AST{ast.List, fns} @@ -116,9 +116,9 @@ func parseValue(input []string) (*ast.AST, error, []string) { func makeFn(bodyStatements []*ast.AST) ast.AST { body := ast.AST{ast.List, bodyStatements} - underscore := ast.AST{ast.Symbol, "_"} + underscore := ast.AST{ast.Symbol, "_"} args := ast.AST{ast.List, []*ast.AST{&underscore}} - fn := ast.AST{ast.Symbol, "fn"} + fn := ast.AST{ast.Symbol, "fn"} return ast.AST{ast.List, []*ast.AST{&fn, &args, &body}} } @@ -140,7 +140,7 @@ func parseToken(input []string) (*ast.AST, error, []string) { return nil, err, input } - quote := ast.AST{ast.Symbol, "quote"} + quote := ast.AST{ast.Symbol, "quote"} res := ast.AST{ast.List, []*ast.AST{"e, tmp}} return &res, nil, input }