Go: use slices for Handler results

This commit is contained in:
Přemysl Eric Janouch 2018-10-10 17:13:05 +02:00
parent f4f03d1737
commit fb143f4d27
Signed by: p
GPG Key ID: A0420B94F92B9493
1 changed files with 81 additions and 72 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 {
@ -656,6 +656,15 @@ error:
return false return false
} }
func sliceToSeq(slice []V) (res *V) {
out := &res
for _, v := range slice {
*out = v.Clone()
out = &(*out).Next
}
return
}
func (ell *Ell) evalNative(name string, args *V, result **V) bool { func (ell *Ell) evalNative(name string, args *V, result **V) bool {
fn := ell.Native[name] fn := ell.Native[name]
if fn == nil { if fn == nil {
@ -674,7 +683,12 @@ func (ell *Ell) evalNative(name string, args *V, result **V) bool {
singledOut.Next = nil singledOut.Next = nil
sliced = append(sliced, singledOut) sliced = append(sliced, singledOut)
} }
return fn(ell, sliced, result) var res []V
if !fn(ell, sliced, &res) {
return false
}
*result = sliceToSeq(res)
return true
} }
func (ell *Ell) evalResolved(body *V, args *V, result **V) bool { func (ell *Ell) evalResolved(body *V, args *V, result **V) bool {
@ -778,13 +792,22 @@ func (ell *Ell) EvalBlock(body *V, args *V, result **V) bool {
// --- Standard library -------------------------------------------------------- // --- Standard library --------------------------------------------------------
// EvalAny evaluates any value. // EvalAny evaluates any value and appends to the result.
func EvalAny(ell *Ell, body *V, arg *V, result **V) bool { func EvalAny(ell *Ell, body *V, arg *V, result *[]V) bool {
if body.Type == VTypeString { if body.Type == VTypeString {
*result = body.Clone() *result = []V{*body.Clone()}
return true return true
} }
return ell.EvalBlock(body.Head, arg.Clone(), result) var res *V
if !ell.EvalBlock(body.Head, arg.Clone(), &res) {
return false
}
for ; res != nil; res = res.Next {
singledOut := *res
singledOut.Next = nil
*result = append(*result, singledOut)
}
return true
} }
// NewNumber creates a new string value containing a number. // NewNumber creates a new string value containing a number.
@ -815,7 +838,7 @@ func NewBoolean(b bool) *V {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
func fnLocal(ell *Ell, args []V, result **V) bool { func fnLocal(ell *Ell, args []V, result *[]V) bool {
if len(args) == 0 || args[0].Type != VTypeList { if len(args) == 0 || args[0].Type != VTypeList {
return ell.Errorf("first argument must be a list") return ell.Errorf("first argument must be a list")
} }
@ -833,46 +856,37 @@ func fnLocal(ell *Ell, args []V, result **V) bool {
return true return true
} }
func fnSet(ell *Ell, args []V, result **V) bool { func fnSet(ell *Ell, args []V, result *[]V) bool {
if len(args) == 0 || args[0].Type != VTypeString { if len(args) == 0 || args[0].Type != VTypeString {
return ell.Errorf("first argument must be string") return ell.Errorf("first argument must be string")
} }
if len(args) > 1 { if len(args) > 1 {
*result = args[1].Clone() *result = []V{*args[1].Clone()}
ell.Set(args[0].String, *result) ell.Set(args[0].String, &(*result)[0])
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(args[0].String); v != nil { if v := ell.Get(args[0].String); v != nil {
*result = v.Clone() *result = []V{*v.Clone()}
} else { } else {
*result = NewList(nil) *result = []V{*NewList(nil)}
} }
return true return true
} }
func sliceToSeq(slice []V) (res *V) { func fnList(ell *Ell, args []V, result *[]V) bool {
out := &res *result = []V{*NewList(sliceToSeq(args))}
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 = sliceToSeq(args) *result = 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 int var cond, body, keyword int
for cond = 0; ; cond = keyword + 1 { for cond = 0; ; cond = keyword + 1 {
if cond >= len(args) { if cond >= len(args) {
@ -882,11 +896,11 @@ func fnIf(ell *Ell, args []V, result **V) bool {
return ell.Errorf("missing body") return ell.Errorf("missing body")
} }
var res *V var res []V
if !EvalAny(ell, &args[cond], nil, &res) { if !EvalAny(ell, &args[cond], nil, &res) {
return false return false
} }
if Truthy(res) { if len(res) > 0 && Truthy(&res[0]) {
return EvalAny(ell, &args[body], nil, result) return EvalAny(ell, &args[body], nil, result)
} }
@ -911,7 +925,7 @@ 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 {
if len(args) < 1 { if len(args) < 1 {
return ell.Errorf("first argument must be a function") return ell.Errorf("first argument must be a function")
} }
@ -921,22 +935,17 @@ func fnMap(ell *Ell, args []V, result **V) bool {
body, values := &args[0], &args[1] body, values := &args[0], &args[1]
var res *V var res []V
out := &res
for v := values.Head; v != nil; v = v.Next { for v := values.Head; v != nil; v = v.Next {
if !EvalAny(ell, body, v, out) { if !EvalAny(ell, body, v, &res) {
return false return false
} }
for *out != nil {
out = &(*out).Next
} }
} *result = []V{*NewList(sliceToSeq(res))}
*result = NewList(res)
return true return true
} }
func fnPrint(ell *Ell, args []V, result **V) bool { func fnPrint(ell *Ell, args []V, result *[]V) bool {
for _, arg := range args { for _, arg := range args {
if arg.Type != VTypeString { if arg.Type != VTypeString {
PrintV(os.Stdout, &arg) PrintV(os.Stdout, &arg)
@ -947,7 +956,7 @@ func fnPrint(ell *Ell, args []V, result **V) bool {
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 _, arg := range args { for _, arg := range args {
if arg.Type != VTypeString { if arg.Type != VTypeString {
@ -956,11 +965,11 @@ func fnCat(ell *Ell, args []V, result **V) bool {
buf.WriteString(arg.String) buf.WriteString(arg.String)
} }
} }
*result = NewString(buf.String()) *result = []V{*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 _, arg := range args { for _, arg := range args {
if arg.Type != VTypeString { if arg.Type != VTypeString {
@ -979,16 +988,16 @@ func fnSystem(ell *Ell, args []V, result **V) bool {
// Approximation of system(3) return value to match C ell at least a bit. // Approximation of system(3) return value to match C ell at least a bit.
if err := cmd.Run(); err == nil { if err := cmd.Run(); err == nil {
*result = NewNumber(0) *result = []V{*NewNumber(0)}
} else if _, ok := err.(*exec.Error); ok { } else if _, ok := err.(*exec.Error); ok {
return ell.Errorf("%s", err) return ell.Errorf("%s", err)
} else { } else {
*result = NewNumber(1) *result = []V{*NewNumber(1)}
} }
return true return true
} }
func fnParse(ell *Ell, args []V, result **V) bool { func fnParse(ell *Ell, args []V, result *[]V) bool {
if len(args) < 1 || args[0].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")
} }
@ -997,11 +1006,11 @@ func fnParse(ell *Ell, args []V, result **V) bool {
if err != nil { if err != nil {
return ell.Errorf("%s", err) return ell.Errorf("%s", err)
} }
*result = NewList(res) *result = []V{*NewList(res)}
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 len(args) < 1 { if len(args) < 1 {
return ell.Errorf("first argument must be a function") return ell.Errorf("first argument must be a function")
@ -1021,14 +1030,14 @@ 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 {
if len(args) < 1 || args[0].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")
} }
return ell.Errorf("%s", args[0].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 _, arg := range args { for _, arg := range args {
if arg.Type != VTypeString { if arg.Type != VTypeString {
@ -1040,11 +1049,11 @@ func fnPlus(ell *Ell, args []V, result **V) bool {
} }
res += value res += value
} }
*result = NewNumber(res) *result = []V{*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 len(args) < 1 || args[0].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")
} }
@ -1067,11 +1076,11 @@ func fnMinus(ell *Ell, args []V, result **V) bool {
} }
res -= value res -= value
} }
*result = NewNumber(res) *result = []V{*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 _, arg := range args { for _, arg := range args {
if arg.Type != VTypeString { if arg.Type != VTypeString {
@ -1083,11 +1092,11 @@ func fnMultiply(ell *Ell, args []V, result **V) bool {
} }
res *= value res *= value
} }
*result = NewNumber(res) *result = []V{*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 len(args) < 1 || args[0].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")
} }
@ -1106,21 +1115,21 @@ func fnDivide(ell *Ell, args []V, result **V) bool {
} }
res /= value res /= value
} }
*result = NewNumber(res) *result = []V{*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 len(args) < 1 { if len(args) < 1 {
return ell.Errorf("missing argument") return ell.Errorf("missing argument")
} }
*result = NewBoolean(!Truthy(&args[0])) *result = []V{*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 = []V{*NewBoolean(true)}
return true return true
} }
for _, arg := range args { for _, arg := range args {
@ -1128,29 +1137,29 @@ func fnAnd(ell *Ell, args []V, result **V) bool {
if !EvalAny(ell, &arg, nil, result) { if !EvalAny(ell, &arg, nil, result) {
return false return false
} }
if !Truthy(*result) { if len(*result) < 1 || !Truthy(&(*result)[0]) {
*result = NewBoolean(false) *result = []V{*NewBoolean(false)}
return true return true
} }
} }
return true return true
} }
func fnOr(ell *Ell, args []V, result **V) bool { func fnOr(ell *Ell, args []V, result *[]V) bool {
for _, arg := range args { for _, arg := range args {
if !EvalAny(ell, &arg, nil, result) { if !EvalAny(ell, &arg, nil, result) {
return false return false
} }
if Truthy(*result) { if len(*result) > 0 && Truthy(&(*result)[0]) {
return true return true
} }
*result = nil *result = nil
} }
*result = NewBoolean(false) *result = []V{*NewBoolean(false)}
return true return true
} }
func fnEq(ell *Ell, args []V, result **V) bool { func fnEq(ell *Ell, args []V, result *[]V) bool {
if len(args) < 1 || args[0].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")
} }
@ -1163,11 +1172,11 @@ func fnEq(ell *Ell, args []V, result **V) bool {
break break
} }
} }
*result = NewBoolean(res) *result = []V{*NewBoolean(res)}
return true return true
} }
func fnLt(ell *Ell, args []V, result **V) bool { func fnLt(ell *Ell, args []V, result *[]V) bool {
if len(args) < 1 || args[0].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")
} }
@ -1181,11 +1190,11 @@ func fnLt(ell *Ell, args []V, result **V) bool {
} }
etalon = arg.String etalon = arg.String
} }
*result = NewBoolean(res) *result = []V{*NewBoolean(res)}
return true return true
} }
func fnEquals(ell *Ell, args []V, result **V) bool { func fnEquals(ell *Ell, args []V, result *[]V) bool {
if len(args) < 1 || args[0].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")
} }
@ -1206,11 +1215,11 @@ func fnEquals(ell *Ell, args []V, result **V) bool {
} }
first = second first = second
} }
*result = NewBoolean(res) *result = []V{*NewBoolean(res)}
return true return true
} }
func fnLess(ell *Ell, args []V, result **V) bool { func fnLess(ell *Ell, args []V, result *[]V) bool {
if len(args) < 1 || args[0].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")
} }
@ -1231,7 +1240,7 @@ func fnLess(ell *Ell, args []V, result **V) bool {
} }
first = second first = second
} }
*result = NewBoolean(res) *result = []V{*NewBoolean(res)}
return true return true
} }