all: add optional args
This commit is contained in:
163
eval/eval.go
163
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)
|
||||
}
|
||||
|
Reference in New Issue
Block a user