all: add optional args

This commit is contained in:
2018-05-14 21:48:44 +02:00
parent fae99aedd8
commit ac880c26a6
4 changed files with 139 additions and 101 deletions

View File

@@ -9,7 +9,6 @@
- Destructuring assignment - Destructuring assignment
- `maptable` - `maptable`
- `coerce` - `coerce`
- `o`
- `len` - `len`
- `mac` - `mac`
- `\`` - `\``

View File

@@ -3,6 +3,8 @@ package ast
import ( import (
"fmt" "fmt"
"strings" "strings"
"github.com/cevaris/ordered_map"
) )
type AST struct { type AST struct {
@@ -88,9 +90,10 @@ func (ast *AST) Pretty() string {
agg = append(agg, elem) agg = append(agg, elem)
} }
opt := "" opt := ""
if val.HasOpt() { if val.HasRest() {
opt = " . " + *val.Opt opt = " . " + *val.Rest
} }
// TODO: opt
body := val.Body.Pretty() body := val.Body.Pretty()
return "(fn (" + strings.Join(agg, " ") + opt + ") " + body + ")" return "(fn (" + strings.Join(agg, " ") + opt + ") " + body + ")"
} else if ast.Tag == Char { } else if ast.Tag == Char {
@@ -121,12 +124,12 @@ type Primitive struct {
Fn PrimFn Fn PrimFn
} }
func newEnv(parent *Env) Env { func NewEnv(parent *Env) Env {
return Env{parent, make(map[string]*AST)} return Env{parent, make(map[string]*AST)}
} }
func ParentEnv() Env { func ParentEnv() Env {
return newEnv(nil) return NewEnv(nil)
} }
func (e *Env) Lookup(elem string) (*AST, error) { func (e *Env) Lookup(elem string) (*AST, error) {
@@ -145,11 +148,16 @@ func (e *Env) Lookup(elem string) (*AST, error) {
type Func struct { type Func struct {
Params []string Params []string
Opt *string Rest *string
Opt *ordered_map.OrderedMap
Body *AST Body *AST
Env Env Env Env
} }
func (f *Func) HasOpt() bool { func (f *Func) HasRest() bool {
return f.Opt != nil return f.Rest != nil
}
func (f *Func) HasOpt() bool {
return f.Opt.Len() != 0
} }

View File

@@ -6,6 +6,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/cevaris/ordered_map"
"github.com/hellerve/argos/ast" "github.com/hellerve/argos/ast"
) )
@@ -264,18 +266,34 @@ func evalFn(input []*ast.AST, e ast.Env) (*ast.AST, error) {
} }
var argsStr []string 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 { 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()) return nil, fmt.Errorf("Argument list cannot contain %s", a.Pretty())
} }
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) val := a.Val.(string)
// TODO: error handling // TODO: error handling
if val == "." { if val == "." {
str := argslst[i+1].Val.(string) str := argslst[i+1].Val.(string)
opt = &str rest = &str
break break
} }
@@ -284,7 +302,9 @@ func evalFn(input []*ast.AST, e ast.Env) (*ast.AST, error) {
body := input[1] 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 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) plen := len(f.Params)
alen := len(args) 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) return nil, fmt.Errorf("Function expected %d arguments, was called with %d.", plen, alen)
} }
@@ -300,10 +320,21 @@ func funcApply(f ast.Func, args []*ast.AST, e ast.Env) (*ast.AST, error) {
f.Env.Values[a] = args[i] f.Env.Values[a] = args[i]
} }
if f.HasOpt() { if f.HasRest() {
lst := ast.AST{ast.List, args[plen:]} lst := ast.AST{ast.List, args[plen:]}
quoted := ast.AST{ast.Quoted, &lst} quoted := ast.AST{ast.Quoted, &lst}
f.Env.Values[*f.Opt] = &quoted f.Env.Values[*f.Rest] = &quoted
} 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) return Eval(f.Body, f.Env)