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 {
@@ -21,8 +23,8 @@ const (
Fn Fn
Char Char
Table Table
Prim Prim
Quoted Quoted
) )
func (tag Tag) String() string { func (tag Tag) String() string {
@@ -56,20 +58,20 @@ func (ast *AST) Type() *AST {
"cons", "cons",
} }
if ast.Tag == Quoted { if ast.Tag == Quoted {
return ast.Val.(*AST).Type() return ast.Val.(*AST).Type()
} }
name := names[ast.Tag] name := names[ast.Tag]
if ast.Tag == Num { if ast.Tag == Num {
val := ast.Val.(float64) val := ast.Val.(float64)
if val == float64(int64(val)) { if val == float64(int64(val)) {
name = "int" name = "int"
} }
} }
res := AST{Symbol, name} res := AST{Symbol, name}
return &res return &res
} }
func (ast *AST) Pretty() string { func (ast *AST) Pretty() string {
@@ -87,11 +89,12 @@ func (ast *AST) Pretty() string {
for _, elem := range val.Params { for _, elem := range val.Params {
agg = append(agg, elem) agg = append(agg, elem)
} }
opt := "" opt := ""
if val.HasOpt() { if val.HasRest() {
opt = " . " + *val.Opt opt = " . " + *val.Rest
} }
body := val.Body.Pretty() // TODO: opt
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 {
return "#" + string(ast.Val.(rune)) return "#" + string(ast.Val.(rune))
@@ -104,8 +107,8 @@ func (ast *AST) Pretty() string {
return "#hash(" + strings.Join(agg, " ") + ")" return "#hash(" + strings.Join(agg, " ") + ")"
} else if ast.Tag == Prim { } else if ast.Tag == Prim {
return "#<procedure: " + ast.Val.(Primitive).Name + ">" return "#<procedure: " + ast.Val.(Primitive).Name + ">"
} }
return fmt.Sprintf("%v", ast.Val) return fmt.Sprintf("%v", ast.Val)
} }
@@ -117,16 +120,16 @@ type Env struct {
type PrimFn func([]*AST) (*AST, error) type PrimFn func([]*AST) (*AST, error)
type Primitive struct { type Primitive struct {
Name string Name string
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"
) )
@@ -14,24 +16,24 @@ var falseVal = ast.AST{ast.Bool, false}
var nilVal = ast.AST{ast.List, []*ast.AST{}} var nilVal = ast.AST{ast.List, []*ast.AST{}}
func RootEnv() ast.Env { func RootEnv() ast.Env {
e := ast.ParentEnv() e := ast.ParentEnv()
prims := map[string]ast.PrimFn{ prims := map[string]ast.PrimFn{
"cons": evalCons, "cons": evalCons,
"car": evalCar, "car": evalCar,
"cdr": evalCdr, "cdr": evalCdr,
"null?": evalNull, "null?": evalNull,
"pr": evalPr, "pr": evalPr,
"table": evalTable, "table": evalTable,
"type": evalType, "type": evalType,
} }
for k, v := range prims { for k, v := range prims {
prim := ast.AST{ast.Prim, ast.Primitive{k, v}} prim := ast.AST{ast.Prim, ast.Primitive{k, v}}
e.Values[k] = &prim e.Values[k] = &prim
} }
return e return e
} }
func checkArity(input []*ast.AST, arity int, name string) error { 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) { func evalCar(input []*ast.AST) (*ast.AST, error) {
err := checkArity(input, 1, "car") err := checkArity(input, 1, "car")
if err != nil { if err != nil {
return nil, err return nil, err
} }
lst := input[0] lst := input[0]
@@ -264,27 +266,45 @@ 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())
} }
val := a.Val.(string)
// TODO: error handling if a.Tag == ast.List {
if val == "." { argv := a.Val.([]*ast.AST)
str := argslst[i+1].Val.(string) argvln := len(argv)
opt = &str if (argvln != 3 && argvln != 2) || argv[0].Tag != ast.Symbol || argv[0].Val.(string) != "o" || argv[0].Tag != ast.Symbol {
break 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) argsStr = append(argsStr, val)
} }
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,11 +320,22 @@ 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)
} }
@@ -358,19 +389,19 @@ func evalApply(l []*ast.AST, e ast.Env) (*ast.AST, error) {
return nil, err return nil, err
} }
// TODO: error handling // TODO: error handling
args, err := Eval(l[1], e) args, err := Eval(l[1], e)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if args.Tag != ast.Quoted { if args.Tag != ast.Quoted {
return nil, fmt.Errorf("Argument 2 to apply must be a list, got %s", args.Pretty()) return nil, fmt.Errorf("Argument 2 to apply must be a list, got %s", args.Pretty())
} }
argslst := args.Val.(*ast.AST).Val.([]*ast.AST) argslst := args.Val.(*ast.AST).Val.([]*ast.AST)
return evalList(append([]*ast.AST{l[0]}, argslst...), e) return evalList(append([]*ast.AST{l[0]}, argslst...), e)
} }
func evalWhile(l []*ast.AST, e ast.Env) (*ast.AST, error) { 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) { func evalSymbolList(l []*ast.AST, e ast.Env) (*ast.AST, error) {
sym := l[0].Val.(string) sym := l[0].Val.(string)
l = l[1:] l = l[1:]
// TODO: make more of these primitives // TODO: make more of these primitives
switch sym { switch sym {
case "=": case "=":
return evalDef(l, e) return evalDef(l, e)
case "is": case "is":
return evalEq(l, e) return evalEq(l, e)
case "apply": case "apply":
return evalApply(l, e) return evalApply(l, e)
case "quote": case "quote":
lst := l[0] lst := l[0]
res := ast.AST{ast.Quoted, lst} res := ast.AST{ast.Quoted, lst}
return &res, nil return &res, nil
case "fn": case "fn":
return evalFn(l, e) 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) { 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 { for _, a := range l {
res, err := Eval(a, e) res, err := Eval(a, e)
if err != nil { if err != nil {
return nil, err return nil, err
} }
args = append(args, res) args = append(args, res)
} }
return f.Fn(args) return f.Fn(args)
} }
func evalList(l []*ast.AST, e ast.Env) (*ast.AST, error) { func evalList(l []*ast.AST, e ast.Env) (*ast.AST, error) {
head := l[0] head := l[0]
var err error var err error
if head.Tag == ast.List { if head.Tag == ast.List {
head, err = Eval(head, e) head, err = Eval(head, e)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
switch head.Tag { switch head.Tag {
case ast.Symbol: case ast.Symbol:
return evalSymbolList(l, e) return evalSymbolList(l, e)
case ast.Fn: case ast.Fn:
return funcApply(head.Val.(ast.Func), l[1:], e) return funcApply(head.Val.(ast.Func), l[1:], e)
case ast.Prim: case ast.Prim:
return primCall(head.Val.(ast.Primitive), l[1:], e) return primCall(head.Val.(ast.Primitive), l[1:], e)
case ast.String, ast.Quoted, ast.Table: case ast.String, ast.Quoted, ast.Table:
return evalIdx(append([]*ast.AST{head}, l[1:]...), e) return evalIdx(append([]*ast.AST{head}, l[1:]...), e)
} }

View File

@@ -94,16 +94,16 @@ func parseValue(input []string) (*ast.AST, error, []string) {
if input[0][0] == '~' { if input[0][0] == '~' {
fn := ast.AST{ast.Symbol, input[0][1:]} 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}} res = ast.AST{ast.List, []*ast.AST{&complement, &fn}}
return &res, nil, input[1:] return &res, nil, input[1:]
} }
if strings.Contains(input[0], ":") { if strings.Contains(input[0], ":") {
comp := ast.AST{ast.Symbol, "compose"} comp := ast.AST{ast.Symbol, "compose"}
fns := []*ast.AST{&comp} fns := []*ast.AST{&comp}
for _, fn := range strings.Split(input[0], ":") { for _, fn := range strings.Split(input[0], ":") {
astfn := ast.AST{ast.Symbol, fn} astfn := ast.AST{ast.Symbol, fn}
fns = append(fns, &astfn) fns = append(fns, &astfn)
} }
res = ast.AST{ast.List, fns} 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 { func makeFn(bodyStatements []*ast.AST) ast.AST {
body := ast.AST{ast.List, bodyStatements} body := ast.AST{ast.List, bodyStatements}
underscore := ast.AST{ast.Symbol, "_"} underscore := ast.AST{ast.Symbol, "_"}
args := ast.AST{ast.List, []*ast.AST{&underscore}} 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}} 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 return nil, err, input
} }
quote := ast.AST{ast.Symbol, "quote"} quote := ast.AST{ast.Symbol, "quote"}
res := ast.AST{ast.List, []*ast.AST{&quote, tmp}} res := ast.AST{ast.List, []*ast.AST{&quote, tmp}}
return &res, nil, input return &res, nil, input
} }