Go: use slices for Handler arguments

First step to replacing linked lists with something more Go-like.
This commit is contained in:
Přemysl Eric Janouch 2018-10-10 16:37:25 +02:00
parent 1ae1b9bb98
commit f4f03d1737
Signed by: p
GPG Key ID: A0420B94F92B9493
1 changed files with 150 additions and 140 deletions

View File

@ -545,7 +545,7 @@ func (p *Parser) Run() (result *V, err error) {
// --- Runtime ----------------------------------------------------------------- // --- Runtime -----------------------------------------------------------------
// Handler is a Go handler for an Ell function. // Handler is a Go handler for an Ell function.
type Handler func(*Ell, *V, **V) bool type Handler func(*Ell, []V, **V) bool
// Ell is an interpreter context. // Ell is an interpreter context.
type Ell struct { type Ell struct {
@ -666,7 +666,15 @@ func (ell *Ell) evalNative(name string, args *V, result **V) bool {
if !ell.evalArgs(args, &arguments) { if !ell.evalArgs(args, &arguments) {
return false return false
} }
return fn(ell, arguments, result) // FIXME: Must change V.Head to a slice, too! This is just a provisional
// change to not have to do both at once! Lots of copying this way.
var sliced []V
for ; arguments != nil; arguments = arguments.Next {
singledOut := *arguments
singledOut.Next = nil
sliced = append(sliced, singledOut)
}
return fn(ell, sliced, result)
} }
func (ell *Ell) evalResolved(body *V, args *V, result **V) bool { func (ell *Ell) evalResolved(body *V, args *V, result **V) bool {
@ -807,40 +815,37 @@ func NewBoolean(b bool) *V {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
func fnLocal(ell *Ell, args *V, result **V) bool { func fnLocal(ell *Ell, args []V, result **V) bool {
names := args if len(args) == 0 || args[0].Type != VTypeList {
if names == nil || names.Type != VTypeList {
return ell.Errorf("first argument must be a list") return ell.Errorf("first argument must be a list")
} }
// Duplicates or non-strings don't really matter to us, user's problem. // Duplicates or non-strings don't really matter to us, user's problem.
scope := &ell.scopes.Head scope := &ell.scopes.Head
values := names.Next values := args[1:]
for names = names.Head; names != nil; names = names.Next { for name := args[0].Head; name != nil; name = name.Next {
scopePrepend(scope, string(names.String), values.Clone()) scopePrepend(scope, string(name.String), values[0].Clone())
if values != nil { if len(values) > 0 {
values = values.Next values = values[1:]
} }
} }
return true return true
} }
func fnSet(ell *Ell, args *V, result **V) bool { func fnSet(ell *Ell, args []V, result **V) bool {
name := args if len(args) == 0 || args[0].Type != VTypeString {
if name == nil || name.Type != VTypeString {
return ell.Errorf("first argument must be string") return ell.Errorf("first argument must be string")
} }
v := name.Next if len(args) > 1 {
if v != nil { *result = args[1].Clone()
*result = v.Clone() ell.Set(args[0].String, *result)
ell.Set(string(name.String), v)
return true return true
} }
// We return an empty list for a nil value. // We return an empty list for a nil value.
if v = ell.Get(string(name.String)); v != nil { if v := ell.Get(args[0].String); v != nil {
*result = v.Clone() *result = v.Clone()
} else { } else {
*result = NewList(nil) *result = NewList(nil)
@ -848,47 +853,56 @@ func fnSet(ell *Ell, args *V, result **V) bool {
return true return true
} }
func fnList(ell *Ell, args *V, result **V) bool { func sliceToSeq(slice []V) (res *V) {
*result = NewList(args.CloneSeq()) out := &res
for _, v := range slice {
*out = v.Clone()
out = &(*out).Next
}
return
}
func fnList(ell *Ell, args []V, result **V) bool {
*result = NewList(sliceToSeq(args))
return true return true
} }
func fnValues(ell *Ell, args *V, result **V) bool { func fnValues(ell *Ell, args []V, result **V) bool {
*result = args.CloneSeq() *result = sliceToSeq(args)
return true return true
} }
func fnIf(ell *Ell, args *V, result **V) bool { func fnIf(ell *Ell, args []V, result **V) bool {
var cond, body, keyword *V var cond, body, keyword int
for cond = args; ; cond = keyword.Next { for cond = 0; ; cond = keyword + 1 {
if cond == nil { if cond >= len(args) {
return ell.Errorf("missing condition") return ell.Errorf("missing condition")
} }
if body = cond.Next; body == nil { if body = cond + 1; body >= len(args) {
return ell.Errorf("missing body") return ell.Errorf("missing body")
} }
var res *V var res *V
if !EvalAny(ell, cond, nil, &res) { if !EvalAny(ell, &args[cond], nil, &res) {
return false return false
} }
if Truthy(res) { if Truthy(res) {
return EvalAny(ell, body, nil, result) return EvalAny(ell, &args[body], nil, result)
} }
if keyword = body.Next; keyword == nil { if keyword = body + 1; keyword >= len(args) {
break break
} }
if keyword.Type != VTypeString { if args[keyword].Type != VTypeString {
return ell.Errorf("expected keyword, got list") return ell.Errorf("expected keyword, got list")
} }
switch kw := string(keyword.String); kw { switch kw := args[keyword].String; kw {
case "else": case "else":
if body = keyword.Next; body == nil { if body = keyword + 1; body >= len(args) {
return ell.Errorf("missing body") return ell.Errorf("missing body")
} }
return EvalAny(ell, body, nil, result) return EvalAny(ell, &args[body], nil, result)
case "elif": case "elif":
default: default:
return ell.Errorf("invalid keyword: %s", kw) return ell.Errorf("invalid keyword: %s", kw)
@ -897,15 +911,16 @@ func fnIf(ell *Ell, args *V, result **V) bool {
return true return true
} }
func fnMap(ell *Ell, args *V, result **V) bool { func fnMap(ell *Ell, args []V, result **V) bool {
var body, values *V if len(args) < 1 {
if body = args; body == nil {
return ell.Errorf("first argument must be a function") return ell.Errorf("first argument must be a function")
} }
if values = body.Next; values == nil || values.Type != VTypeList { if len(args) < 2 || args[0].Type != VTypeList {
return ell.Errorf("second argument must be a list") return ell.Errorf("second argument must be a list")
} }
body, values := &args[0], &args[1]
var res *V var res *V
out := &res out := &res
@ -921,37 +936,37 @@ func fnMap(ell *Ell, args *V, result **V) bool {
return true return true
} }
func fnPrint(ell *Ell, args *V, result **V) bool { func fnPrint(ell *Ell, args []V, result **V) bool {
for ; args != nil; args = args.Next { for _, arg := range args {
if args.Type != VTypeString { if arg.Type != VTypeString {
PrintV(os.Stdout, args) PrintV(os.Stdout, &arg)
} else if _, err := os.Stdout.WriteString(args.String); err != nil { } else if _, err := os.Stdout.WriteString(arg.String); err != nil {
return ell.Errorf("write failed: %s", err) return ell.Errorf("write failed: %s", err)
} }
} }
return true return true
} }
func fnCat(ell *Ell, args *V, result **V) bool { func fnCat(ell *Ell, args []V, result **V) bool {
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
for ; args != nil; args = args.Next { for _, arg := range args {
if args.Type != VTypeString { if arg.Type != VTypeString {
PrintV(buf, args) PrintV(buf, &arg)
} else { } else {
buf.WriteString(args.String) buf.WriteString(arg.String)
} }
} }
*result = NewString(buf.String()) *result = NewString(buf.String())
return true return true
} }
func fnSystem(ell *Ell, args *V, result **V) bool { func fnSystem(ell *Ell, args []V, result **V) bool {
var argv []string var argv []string
for ; args != nil; args = args.Next { for _, arg := range args {
if args.Type != VTypeString { if arg.Type != VTypeString {
return ell.Errorf("arguments must be strings") return ell.Errorf("arguments must be strings")
} }
argv = append(argv, string(args.String)) argv = append(argv, string(arg.String))
} }
if len(argv) == 0 { if len(argv) == 0 {
return ell.Errorf("command name required") return ell.Errorf("command name required")
@ -973,13 +988,12 @@ func fnSystem(ell *Ell, args *V, result **V) bool {
return true return true
} }
func fnParse(ell *Ell, args *V, result **V) bool { func fnParse(ell *Ell, args []V, result **V) bool {
body := args if len(args) < 1 || args[0].Type != VTypeString {
if body == nil || body.Type != VTypeString {
return ell.Errorf("first argument must be string") return ell.Errorf("first argument must be string")
} }
res, err := NewParser([]byte(body.String)).Run() res, err := NewParser([]byte(args[0].String)).Run()
if err != nil { if err != nil {
return ell.Errorf("%s", err) return ell.Errorf("%s", err)
} }
@ -987,14 +1001,15 @@ func fnParse(ell *Ell, args *V, result **V) bool {
return true return true
} }
func fnTry(ell *Ell, args *V, result **V) bool { func fnTry(ell *Ell, args []V, result **V) bool {
var body, handler *V var body, handler *V
if body = args; body == nil { if len(args) < 1 {
return ell.Errorf("first argument must be a function") return ell.Errorf("first argument must be a function")
} }
if handler = body.Next; handler == nil { if len(args) < 2 {
return ell.Errorf("second argument must be a function") return ell.Errorf("second argument must be a function")
} }
body, handler = &args[0], &args[1]
if EvalAny(ell, body, nil, result) { if EvalAny(ell, body, nil, result) {
return true return true
} }
@ -1006,112 +1021,111 @@ func fnTry(ell *Ell, args *V, result **V) bool {
return EvalAny(ell, handler, msg, result) return EvalAny(ell, handler, msg, result)
} }
func fnThrow(ell *Ell, args *V, result **V) bool { func fnThrow(ell *Ell, args []V, result **V) bool {
message := args if len(args) < 1 || args[0].Type != VTypeString {
if message == nil || message.Type != VTypeString {
return ell.Errorf("first argument must be string") return ell.Errorf("first argument must be string")
} }
return ell.Errorf("%s", message.String) return ell.Errorf("%s", args[0].String)
} }
func fnPlus(ell *Ell, args *V, result **V) bool { func fnPlus(ell *Ell, args []V, result **V) bool {
res := 0. res := 0.
for ; args != nil; args = args.Next { for _, arg := range args {
if args.Type != VTypeString { if arg.Type != VTypeString {
return ell.Errorf("arguments must be strings") return ell.Errorf("arguments must be strings")
} }
var arg float64 var value float64
if n, _ := fmt.Sscan(string(args.String), &arg); n < 1 { if n, _ := fmt.Sscan(arg.String, &value); n < 1 {
return ell.Errorf("invalid number: %s", args.String) return ell.Errorf("invalid number: %s", arg.String)
} }
res += arg res += value
} }
*result = NewNumber(res) *result = NewNumber(res)
return true return true
} }
func fnMinus(ell *Ell, args *V, result **V) bool { func fnMinus(ell *Ell, args []V, result **V) bool {
if args == nil || args.Type != VTypeString { if len(args) < 1 || args[0].Type != VTypeString {
return ell.Errorf("first argument must be string") return ell.Errorf("first argument must be string")
} }
var res float64 var res float64
if n, _ := fmt.Sscan(string(args.String), &res); n < 1 { if n, _ := fmt.Sscan(args[0].String, &res); n < 1 {
return ell.Errorf("invalid number: %f", args.String) return ell.Errorf("invalid number: %f", args[0].String)
} }
if args = args.Next; args == nil { if len(args) == 1 {
res = -res res = -res
} }
for ; args != nil; args = args.Next { for _, arg := range args[1:] {
if args.Type != VTypeString { if arg.Type != VTypeString {
return ell.Errorf("arguments must be strings") return ell.Errorf("arguments must be strings")
} }
var arg float64 var value float64
if n, _ := fmt.Sscan(string(args.String), &arg); n < 1 { if n, _ := fmt.Sscan(arg.String, &value); n < 1 {
return ell.Errorf("invalid number: %f", args.String) return ell.Errorf("invalid number: %f", arg.String)
} }
res -= arg res -= value
} }
*result = NewNumber(res) *result = NewNumber(res)
return true return true
} }
func fnMultiply(ell *Ell, args *V, result **V) bool { func fnMultiply(ell *Ell, args []V, result **V) bool {
res := 1. res := 1.
for ; args != nil; args = args.Next { for _, arg := range args {
if args.Type != VTypeString { if arg.Type != VTypeString {
return ell.Errorf("arguments must be strings") return ell.Errorf("arguments must be strings")
} }
var arg float64 var value float64
if n, _ := fmt.Sscan(string(args.String), &arg); n < 1 { if n, _ := fmt.Sscan(arg.String, &value); n < 1 {
return ell.Errorf("invalid number: %s", args.String) return ell.Errorf("invalid number: %s", arg.String)
} }
res *= arg res *= value
} }
*result = NewNumber(res) *result = NewNumber(res)
return true return true
} }
func fnDivide(ell *Ell, args *V, result **V) bool { func fnDivide(ell *Ell, args []V, result **V) bool {
if args == nil || args.Type != VTypeString { if len(args) < 1 || args[0].Type != VTypeString {
return ell.Errorf("first argument must be string") return ell.Errorf("first argument must be string")
} }
var res float64 var res float64
if n, _ := fmt.Sscan(string(args.String), &res); n < 1 { if n, _ := fmt.Sscan(args[0].String, &res); n < 1 {
return ell.Errorf("invalid number: %f", args.String) return ell.Errorf("invalid number: %f", args[0].String)
} }
for args = args.Next; args != nil; args = args.Next { for _, arg := range args[1:] {
if args.Type != VTypeString { if arg.Type != VTypeString {
return ell.Errorf("arguments must be strings") return ell.Errorf("arguments must be strings")
} }
var arg float64 var value float64
if n, _ := fmt.Sscan(string(args.String), &arg); n < 1 { if n, _ := fmt.Sscan(arg.String, &value); n < 1 {
return ell.Errorf("invalid number: %f", args.String) return ell.Errorf("invalid number: %f", arg.String)
} }
res /= arg res /= value
} }
*result = NewNumber(res) *result = NewNumber(res)
return true return true
} }
func fnNot(ell *Ell, args *V, result **V) bool { func fnNot(ell *Ell, args []V, result **V) bool {
if args == nil { if len(args) < 1 {
return ell.Errorf("missing argument") return ell.Errorf("missing argument")
} }
*result = NewBoolean(!Truthy(args)) *result = NewBoolean(!Truthy(&args[0]))
return true return true
} }
func fnAnd(ell *Ell, args *V, result **V) bool { func fnAnd(ell *Ell, args []V, result **V) bool {
if args == nil { if args == nil {
*result = NewBoolean(true) *result = NewBoolean(true)
return true return true
} }
for ; args != nil; args = args.Next { for _, arg := range args {
*result = nil *result = nil
if !EvalAny(ell, args, nil, result) { if !EvalAny(ell, &arg, nil, result) {
return false return false
} }
if !Truthy(*result) { if !Truthy(*result) {
@ -1122,9 +1136,9 @@ func fnAnd(ell *Ell, args *V, result **V) bool {
return true return true
} }
func fnOr(ell *Ell, args *V, result **V) bool { func fnOr(ell *Ell, args []V, result **V) bool {
for ; args != nil; args = args.Next { for _, arg := range args {
if !EvalAny(ell, args, nil, result) { if !EvalAny(ell, &arg, nil, result) {
return false return false
} }
if Truthy(*result) { if Truthy(*result) {
@ -1136,17 +1150,16 @@ func fnOr(ell *Ell, args *V, result **V) bool {
return true return true
} }
func fnEq(ell *Ell, args *V, result **V) bool { func fnEq(ell *Ell, args []V, result **V) bool {
etalon := args if len(args) < 1 || args[0].Type != VTypeString {
if etalon == nil || etalon.Type != VTypeString {
return ell.Errorf("first argument must be string") return ell.Errorf("first argument must be string")
} }
res := true etalon, res := args[0].String, true
for args = etalon.Next; args != nil; args = args.Next { for _, arg := range args[1:] {
if args.Type != VTypeString { if arg.Type != VTypeString {
return ell.Errorf("arguments must be strings") return ell.Errorf("arguments must be strings")
} }
if res = string(etalon.String) == string(args.String); !res { if res = etalon == arg.String; !res {
break break
} }
} }
@ -1154,41 +1167,39 @@ func fnEq(ell *Ell, args *V, result **V) bool {
return true return true
} }
func fnLt(ell *Ell, args *V, result **V) bool { func fnLt(ell *Ell, args []V, result **V) bool {
etalon := args if len(args) < 1 || args[0].Type != VTypeString {
if etalon == nil || etalon.Type != VTypeString {
return ell.Errorf("first argument must be string") return ell.Errorf("first argument must be string")
} }
res := true etalon, res := args[0].String, true
for args = etalon.Next; args != nil; args = args.Next { for _, arg := range args[1:] {
if args.Type != VTypeString { if arg.Type != VTypeString {
return ell.Errorf("arguments must be strings") return ell.Errorf("arguments must be strings")
} }
if res = string(etalon.String) < string(args.String); !res { if res = etalon < arg.String; !res {
break break
} }
etalon = args etalon = arg.String
} }
*result = NewBoolean(res) *result = NewBoolean(res)
return true return true
} }
func fnEquals(ell *Ell, args *V, result **V) bool { func fnEquals(ell *Ell, args []V, result **V) bool {
etalon := args if len(args) < 1 || args[0].Type != VTypeString {
if etalon == nil || etalon.Type != VTypeString {
return ell.Errorf("first argument must be string") return ell.Errorf("first argument must be string")
} }
var first, second float64 var first, second float64
if n, _ := fmt.Sscan(string(etalon.String), &first); n < 1 { if n, _ := fmt.Sscan(args[0].String, &first); n < 1 {
return ell.Errorf("invalid number: %f", etalon.String) return ell.Errorf("invalid number: %f", args[0].String)
} }
res := true res := true
for args = etalon.Next; args != nil; args = args.Next { for _, arg := range args[1:] {
if args.Type != VTypeString { if arg.Type != VTypeString {
return ell.Errorf("arguments must be strings") return ell.Errorf("arguments must be strings")
} }
if n, _ := fmt.Sscan(string(args.String), &second); n < 1 { if n, _ := fmt.Sscan(arg.String, &second); n < 1 {
return ell.Errorf("invalid number: %f", args.String) return ell.Errorf("invalid number: %f", arg.String)
} }
if res = first == second; !res { if res = first == second; !res {
break break
@ -1199,22 +1210,21 @@ func fnEquals(ell *Ell, args *V, result **V) bool {
return true return true
} }
func fnLess(ell *Ell, args *V, result **V) bool { func fnLess(ell *Ell, args []V, result **V) bool {
etalon := args if len(args) < 1 || args[0].Type != VTypeString {
if etalon == nil || etalon.Type != VTypeString {
return ell.Errorf("first argument must be string") return ell.Errorf("first argument must be string")
} }
var first, second float64 var first, second float64
if n, _ := fmt.Sscan(string(etalon.String), &first); n < 1 { if n, _ := fmt.Sscan(args[0].String, &first); n < 1 {
return ell.Errorf("invalid number: %f", etalon.String) return ell.Errorf("invalid number: %f", args[0].String)
} }
res := true res := true
for args = etalon.Next; args != nil; args = args.Next { for _, arg := range args[1:] {
if args.Type != VTypeString { if arg.Type != VTypeString {
return ell.Errorf("arguments must be strings") return ell.Errorf("arguments must be strings")
} }
if n, _ := fmt.Sscan(string(args.String), &second); n < 1 { if n, _ := fmt.Sscan(arg.String, &second); n < 1 {
return ell.Errorf("invalid number: %f", args.String) return ell.Errorf("invalid number: %f", arg.String)
} }
if res = first < second; !res { if res = first < second; !res {
break break