You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
4.7 KiB
Go
239 lines
4.7 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
type builtin struct {
|
|
// name of the function
|
|
name string
|
|
|
|
// minimum number of arguments
|
|
arity int
|
|
|
|
// whether we will accept arbitrarily large numbers of arguments or not
|
|
variadic bool
|
|
|
|
// function to be called
|
|
fn func([]interface{}) (interface{}, error)
|
|
}
|
|
|
|
// begins by evaluating all of its inputs. An error on input evaluation will
|
|
// stop evaluation of a builtin. After evaluating its inputs, an arity check
|
|
// is performed to see if the proper number of arguments have been supplied.
|
|
// Perhaps this is the wrong order, I'm unsure. Finally, the procudure is
|
|
// passed the post-evaluation arguments to be executed.
|
|
func (b builtin) call(env *environment, rawArgs []interface{}) (interface{}, error) {
|
|
// eval all arguments first
|
|
args := make([]interface{}, 0, len(rawArgs))
|
|
for _, raw := range rawArgs {
|
|
v, err := eval(raw, env)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
args = append(args, v)
|
|
}
|
|
|
|
if err := b.checkArity(len(rawArgs)); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return b.fn(args)
|
|
}
|
|
|
|
func (b builtin) checkArity(n int) error {
|
|
if n == b.arity {
|
|
return nil
|
|
}
|
|
|
|
if b.variadic && n > b.arity {
|
|
return nil
|
|
}
|
|
|
|
return arityError{
|
|
expected: b.arity,
|
|
received: n,
|
|
name: b.name,
|
|
variadic: b.variadic,
|
|
}
|
|
}
|
|
|
|
var add = builtin{
|
|
name: "+",
|
|
variadic: true,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
return accumulator{
|
|
name: "+",
|
|
floatFn: func(left, right float64) (float64, error) {
|
|
return left + right, nil
|
|
},
|
|
intFn: func(left, right int64) (int64, error) {
|
|
return left + right, nil
|
|
},
|
|
}.total(vals)
|
|
},
|
|
}
|
|
|
|
var sub = builtin{
|
|
name: "-",
|
|
variadic: true,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
return accumulator{
|
|
name: "-",
|
|
floatFn: func(left, right float64) (float64, error) {
|
|
return left - right, nil
|
|
},
|
|
intFn: func(left, right int64) (int64, error) {
|
|
return left - right, nil
|
|
},
|
|
}.total(vals)
|
|
},
|
|
}
|
|
|
|
var mul = builtin{
|
|
name: "*",
|
|
variadic: true,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
return accumulator{
|
|
name: "*",
|
|
floatFn: func(left, right float64) (float64, error) {
|
|
return left * right, nil
|
|
},
|
|
intFn: func(left, right int64) (int64, error) {
|
|
return left * right, nil
|
|
},
|
|
acc: 1,
|
|
accf: 1.0,
|
|
}.total(vals)
|
|
},
|
|
}
|
|
|
|
var div = builtin{
|
|
name: "/",
|
|
variadic: true,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
return accumulator{
|
|
name: "/",
|
|
floatFn: func(left, right float64) (float64, error) {
|
|
if right == 0.0 {
|
|
return 0.0, errors.New("float division by zero")
|
|
}
|
|
return left / right, nil
|
|
},
|
|
intFn: func(left, right int64) (int64, error) {
|
|
if right == 0 {
|
|
return 0, errors.New("int division by zero")
|
|
}
|
|
return left / right, nil
|
|
},
|
|
}.total(vals)
|
|
},
|
|
}
|
|
|
|
var not = builtin{
|
|
name: "not",
|
|
arity: 1,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
return !booleanize(vals[0]), nil
|
|
},
|
|
}
|
|
|
|
var length = builtin{
|
|
name: "length",
|
|
arity: 1,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
s, ok := vals[0].(*sexp)
|
|
if !ok {
|
|
return nil, fmt.Errorf("first argument must be sexp, received %v", reflect.TypeOf(vals[0]))
|
|
}
|
|
return len(s.items), nil
|
|
},
|
|
}
|
|
|
|
var lst = builtin{
|
|
name: "list",
|
|
variadic: true,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
return &sexp{items: vals, quotelvl: 1}, nil
|
|
},
|
|
}
|
|
|
|
var islist = builtin{
|
|
name: "list?",
|
|
arity: 1,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
_, ok := vals[0].(*sexp)
|
|
return ok, nil
|
|
},
|
|
}
|
|
|
|
var isnull = builtin{
|
|
name: "null?",
|
|
arity: 1,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
s, ok := vals[0].(*sexp)
|
|
if !ok {
|
|
return false, nil
|
|
}
|
|
return len(s.items) == 0, nil
|
|
},
|
|
}
|
|
|
|
var issymbol = builtin{
|
|
name: "symbol?",
|
|
arity: 1,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
_, ok := vals[0].(symbol)
|
|
return ok, nil
|
|
},
|
|
}
|
|
|
|
var cons = builtin{
|
|
name: "cons",
|
|
arity: 2,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
s := &sexp{items: vals[0:1]}
|
|
switch t := vals[1].(type) {
|
|
case *sexp:
|
|
s.items = append(s.items, t.items...)
|
|
default:
|
|
s.items = append(s.items, t)
|
|
}
|
|
return s, nil
|
|
},
|
|
}
|
|
|
|
var car = builtin{
|
|
name: "car",
|
|
arity: 1,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
s, ok := vals[0].(*sexp)
|
|
if !ok {
|
|
return nil, errors.New("expected list")
|
|
}
|
|
return s.items[0], nil
|
|
},
|
|
}
|
|
|
|
var cdr = builtin{
|
|
name: "cdr",
|
|
arity: 1,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
s, ok := vals[0].(*sexp)
|
|
if !ok {
|
|
return nil, errors.New("expected list")
|
|
}
|
|
return s.items[1:], nil
|
|
},
|
|
}
|
|
|
|
var names = builtin{
|
|
name: "names",
|
|
arity: 0,
|
|
fn: func(vals []interface{}) (interface{}, error) {
|
|
return universe.keys(), nil
|
|
},
|
|
}
|