all: add optional args
This commit is contained in:
@@ -9,7 +9,6 @@
|
|||||||
- Destructuring assignment
|
- Destructuring assignment
|
||||||
- `maptable`
|
- `maptable`
|
||||||
- `coerce`
|
- `coerce`
|
||||||
- `o`
|
|
||||||
- `len`
|
- `len`
|
||||||
- `mac`
|
- `mac`
|
||||||
- `\``
|
- `\``
|
||||||
|
64
ast/ast.go
64
ast/ast.go
@@ -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
|
||||||
}
|
}
|
||||||
|
163
eval/eval.go
163
eval/eval.go
@@ -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] = "ed
|
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)
|
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)
|
||||||
}
|
}
|
||||||
|
@@ -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{"e, tmp}}
|
res := ast.AST{ast.List, []*ast.AST{"e, tmp}}
|
||||||
return &res, nil, input
|
return &res, nil, input
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user