Go: make use of multiple return values
This commit is contained in:
parent
2717cd569b
commit
2fe3c4753f
|
@ -53,9 +53,7 @@ func main() {
|
||||||
for i := 2; i < len(os.Args); i++ {
|
for i := 2; i < len(os.Args); i++ {
|
||||||
args = append(args, *ell.NewString(os.Args[i]))
|
args = append(args, *ell.NewString(os.Args[i]))
|
||||||
}
|
}
|
||||||
|
if _, ok := L.EvalBlock(program, args); !ok {
|
||||||
var result []ell.V
|
|
||||||
if !L.EvalBlock(program, args, &result) {
|
|
||||||
fmt.Printf("%s: %s\n", "runtime error", L.Error)
|
fmt.Printf("%s: %s\n", "runtime error", L.Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func run(L *ell.Ell, program []ell.V) {
|
func run(L *ell.Ell, program []ell.V) {
|
||||||
var result []ell.V
|
if result, ok := L.EvalBlock(program, nil); !ok {
|
||||||
if !L.EvalBlock(program, nil, &result) {
|
|
||||||
fmt.Printf("\x1b[31m%s: %s\x1b[0m\n", "runtime error", L.Error)
|
fmt.Printf("\x1b[31m%s: %s\x1b[0m\n", "runtime error", L.Error)
|
||||||
L.Error = ""
|
L.Error = ""
|
||||||
} else {
|
} else {
|
||||||
|
|
297
ell/ell.go
297
ell/ell.go
|
@ -346,7 +346,7 @@ func printBlock(w io.Writer, list *V) bool {
|
||||||
_, _ = w.Write([]byte{' '})
|
_, _ = w.Write([]byte{' '})
|
||||||
PrintSeq(w, sublist[0].List)
|
PrintSeq(w, sublist[0].List)
|
||||||
for _, subsub := range sublist[1:] {
|
for _, subsub := range sublist[1:] {
|
||||||
_, _ = w.Write([]byte{';', ' '})
|
_, _ = w.Write([]byte("; "))
|
||||||
PrintSeq(w, subsub.List)
|
PrintSeq(w, subsub.List)
|
||||||
}
|
}
|
||||||
_, _ = w.Write([]byte{' '})
|
_, _ = w.Write([]byte{' '})
|
||||||
|
@ -391,13 +391,12 @@ func PrintV(w io.Writer, v *V) {
|
||||||
|
|
||||||
// PrintSeq serializes a sequence of values to the given writer.
|
// PrintSeq serializes a sequence of values to the given writer.
|
||||||
func PrintSeq(w io.Writer, seq []V) {
|
func PrintSeq(w io.Writer, seq []V) {
|
||||||
if len(seq) < 1 {
|
if len(seq) > 0 {
|
||||||
return
|
PrintV(w, &seq[0])
|
||||||
}
|
for _, v := range seq[1:] {
|
||||||
PrintV(w, &seq[0])
|
_, _ = w.Write([]byte{' '})
|
||||||
for _, v := range seq[1:] {
|
PrintV(w, &v)
|
||||||
_, _ = w.Write([]byte{' '})
|
}
|
||||||
PrintV(w, &v)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,16 +450,15 @@ func (p *Parser) skipNL() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePrefixList(seq []V, name string) *V {
|
func parsePrefixList(seq []V, name string) *V {
|
||||||
prefix := []V{*NewString(name)}
|
return NewList(append([]V{*NewString(name)}, seq...))
|
||||||
return NewList(append(prefix, seq...))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
func (p *Parser) parseV() *V {
|
func (p *Parser) parseV() *V {
|
||||||
var seq []V
|
|
||||||
|
|
||||||
p.skipNL()
|
p.skipNL()
|
||||||
|
|
||||||
|
var seq []V
|
||||||
switch {
|
switch {
|
||||||
case p.accept(tString):
|
case p.accept(tString):
|
||||||
return NewString(string(p.lexer.buf))
|
return NewString(string(p.lexer.buf))
|
||||||
|
@ -495,7 +493,6 @@ func (p *Parser) parseV() *V {
|
||||||
|
|
||||||
func (p *Parser) parseLine() *V {
|
func (p *Parser) parseLine() *V {
|
||||||
var seq []V
|
var seq []V
|
||||||
|
|
||||||
for p.peek() != tRBrace && p.peek() != tAbort {
|
for p.peek() != tRBrace && p.peek() != tAbort {
|
||||||
if !p.accept(tNewline) {
|
if !p.accept(tNewline) {
|
||||||
seq = append(seq, *p.parseV())
|
seq = append(seq, *p.parseV())
|
||||||
|
@ -534,7 +531,7 @@ func (p *Parser) Run() (seq []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 *Ell, args []V) (result []V, ok bool)
|
||||||
|
|
||||||
// Ell is an interpreter context.
|
// Ell is an interpreter context.
|
||||||
type Ell struct {
|
type Ell struct {
|
||||||
|
@ -590,10 +587,11 @@ func (ell *Ell) Set(name string, v *V) {
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
// Errorf sets an error message in the interpreter context and returns false.
|
// Errorf sets an error message in the interpreter context and returns an empty
|
||||||
func (ell *Ell) Errorf(format string, args ...interface{}) bool {
|
// sequence and false.
|
||||||
|
func (ell *Ell) Errorf(format string, args ...interface{}) ([]V, bool) {
|
||||||
ell.Error = fmt.Sprintf(format, args...)
|
ell.Error = fmt.Sprintf(format, args...)
|
||||||
return false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ell *Ell) canModifyError() bool {
|
func (ell *Ell) canModifyError() bool {
|
||||||
|
@ -602,92 +600,89 @@ func (ell *Ell) canModifyError() bool {
|
||||||
return ell.Error == "" || ell.Error[0] != '_'
|
return ell.Error == "" || ell.Error[0] != '_'
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ell *Ell) evalArgs(args []V, result *[]V) bool {
|
func (ell *Ell) evalArgs(args []V) (result []V, ok bool) {
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
var evaluated []V
|
evaluated, ok := ell.evalStatement(&arg)
|
||||||
// Arguments should not evaporate, default to a nil value.
|
if !ok {
|
||||||
if !ell.evalStatement(&arg, &evaluated) {
|
|
||||||
// Once the code flows like this, at least make some use of it.
|
// Once the code flows like this, at least make some use of it.
|
||||||
if ell.canModifyError() {
|
if ell.canModifyError() {
|
||||||
ell.Errorf("(argument %d) -> %s", i, ell.Error)
|
ell.Errorf("(argument %d) -> %s", i, ell.Error)
|
||||||
}
|
}
|
||||||
*result = nil
|
return nil, false
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
// Arguments should not evaporate, default to a nil value.
|
||||||
if len(evaluated) < 1 {
|
if len(evaluated) < 1 {
|
||||||
evaluated = []V{*NewList(nil)}
|
evaluated = []V{*NewList(nil)}
|
||||||
}
|
}
|
||||||
*result = append(*result, evaluated[0])
|
result = append(result, evaluated[0])
|
||||||
}
|
}
|
||||||
return true
|
return result, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ell *Ell) evalNative(name string, args []V, result *[]V) bool {
|
func (ell *Ell) evalNative(name string, args []V) (result []V, ok bool) {
|
||||||
fn := ell.Native[name]
|
fn := ell.Native[name]
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
return ell.Errorf("unknown function")
|
return ell.Errorf("unknown function")
|
||||||
}
|
}
|
||||||
|
if arguments, ok := ell.evalArgs(args); ok {
|
||||||
var arguments []V
|
return fn(ell, arguments)
|
||||||
return ell.evalArgs(args, &arguments) && fn(ell, arguments, result)
|
}
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ell *Ell) evalResolved(body *V, args []V, result *[]V) bool {
|
func (ell *Ell) evalResolved(body *V, args []V) (result []V, ok bool) {
|
||||||
// Resolving names recursively could be pretty fatal, let's not do that.
|
// Resolving names recursively could be pretty fatal, let's not do that.
|
||||||
if body.Type == VTypeString {
|
if body.Type == VTypeString {
|
||||||
*result = []V{*body.Clone()}
|
return []V{*body}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
var arguments []V
|
if arguments, ok := ell.evalArgs(args); ok {
|
||||||
return ell.evalArgs(args, &arguments) &&
|
return ell.EvalBlock(body.List, arguments)
|
||||||
ell.EvalBlock(body.List, arguments, result)
|
}
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ell *Ell) evalValue(body []V, result *[]V) bool {
|
func (ell *Ell) evalValue(body []V) (result []V, ok bool) {
|
||||||
args := body[1:]
|
args := body[1:]
|
||||||
if body[0].Type == VTypeString {
|
if body[0].Type == VTypeString {
|
||||||
name := body[0].String
|
name := body[0].String
|
||||||
if name == "block" {
|
if name == "block" {
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
*result = []V{*NewList(CloneSeq(args))}
|
result = []V{*NewList(CloneSeq(args))}
|
||||||
}
|
}
|
||||||
return true
|
return result, true
|
||||||
}
|
}
|
||||||
if body := ell.Get(name); body != nil {
|
if body := ell.Get(name); body != nil {
|
||||||
return ell.evalResolved(body, args, result)
|
return ell.evalResolved(body, args)
|
||||||
}
|
}
|
||||||
return ell.evalNative(name, args, result)
|
return ell.evalNative(name, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// When someone tries to call a block directly, we must evaluate it;
|
// When someone tries to call a block directly, we must evaluate it;
|
||||||
// e.g. something like `{ choose [@f1 @f2 @f3] } arg1 arg2 arg3`.
|
// e.g. something like `{ choose [@f1 @f2 @f3] } arg1 arg2 arg3`.
|
||||||
var evaluated []V
|
if evaluated, ok := ell.evalStatement(&body[0]); !ok {
|
||||||
if !ell.evalStatement(&body[0], &evaluated) {
|
return nil, false
|
||||||
return false
|
} else if len(evaluated) > 0 {
|
||||||
|
return ell.evalResolved(&evaluated[0], args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// It might a bit confusing that this doesn't evaluate arguments
|
// It might a bit confusing that this doesn't evaluate arguments
|
||||||
// but neither does "block" and there's nothing to do here.
|
// but neither does "block" and there's nothing to do here.
|
||||||
if len(evaluated) < 1 {
|
return nil, true
|
||||||
return true
|
|
||||||
}
|
|
||||||
return ell.evalResolved(&evaluated[0], args, result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ell *Ell) evalStatement(statement *V, result *[]V) bool {
|
func (ell *Ell) evalStatement(statement *V) (result []V, ok bool) {
|
||||||
if statement.Type == VTypeString {
|
if statement.Type == VTypeString {
|
||||||
*result = []V{*statement.Clone()}
|
return []V{*statement}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executing a nil value results in no value. It's not very different from
|
// Executing a nil value results in no value. It's not very different from
|
||||||
// calling a block that returns no value--it's for our callers to resolve.
|
// calling a block that returns no value--it's for our callers to resolve.
|
||||||
if len(statement.List) < 1 ||
|
if len(statement.List) < 1 {
|
||||||
ell.evalValue(statement.List, result) {
|
return nil, true
|
||||||
return true
|
}
|
||||||
|
if result, ok = ell.evalValue(statement.List); ok {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
*result = nil
|
|
||||||
|
|
||||||
name := "(block)"
|
name := "(block)"
|
||||||
if statement.List[0].Type == VTypeString {
|
if statement.List[0].Type == VTypeString {
|
||||||
|
@ -697,7 +692,7 @@ func (ell *Ell) evalStatement(statement *V, result *[]V) bool {
|
||||||
if ell.canModifyError() {
|
if ell.canModifyError() {
|
||||||
ell.Errorf("%s -> %s", name, ell.Error)
|
ell.Errorf("%s -> %s", name, ell.Error)
|
||||||
}
|
}
|
||||||
return false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func argsToScope(args []V) map[string]V {
|
func argsToScope(args []V) map[string]V {
|
||||||
|
@ -710,38 +705,34 @@ func argsToScope(args []V) map[string]V {
|
||||||
|
|
||||||
// EvalBlock executes a block and returns whatever the last statement returned,
|
// EvalBlock executes a block and returns whatever the last statement returned,
|
||||||
// eats args.
|
// eats args.
|
||||||
func (ell *Ell) EvalBlock(body []V, args []V, result *[]V) bool {
|
func (ell *Ell) EvalBlock(body []V, args []V) (result []V, ok bool) {
|
||||||
ell.scopes = append(ell.scopes, argsToScope(args))
|
ell.scopes = append(ell.scopes, argsToScope(args))
|
||||||
|
|
||||||
ok := true
|
ok = true
|
||||||
for _, stmt := range body {
|
for _, stmt := range body {
|
||||||
*result = nil
|
if result, ok = ell.evalStatement(&stmt); !ok {
|
||||||
if ok = ell.evalStatement(&stmt, result); !ok {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ell.scopes = ell.scopes[:len(ell.scopes)-1]
|
ell.scopes = ell.scopes[:len(ell.scopes)-1]
|
||||||
return ok
|
return result, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Standard library --------------------------------------------------------
|
// --- Standard library --------------------------------------------------------
|
||||||
|
|
||||||
// EvalAny evaluates any value and appends to the result.
|
// 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, ok bool) {
|
||||||
if body.Type == VTypeString {
|
if body.Type == VTypeString {
|
||||||
*result = []V{*body.Clone()}
|
return []V{*body}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
var args []V
|
var args []V
|
||||||
if arg != nil {
|
if arg != nil {
|
||||||
args = append(args, *arg.Clone())
|
args = append(args, *arg.Clone())
|
||||||
}
|
}
|
||||||
var res []V
|
if res, ok := ell.EvalBlock(body.List, args); ok {
|
||||||
if !ell.EvalBlock(body.List, args, &res) {
|
return res, true
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
*result = append(*result, res...)
|
return nil, false
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNumber creates a new string value containing a number.
|
// NewNumber creates a new string value containing a number.
|
||||||
|
@ -772,7 +763,7 @@ func NewBoolean(b bool) *V {
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
func fnLocal(ell *Ell, args []V, result *[]V) bool {
|
func fnLocal(ell *Ell, args []V) (result []V, ok 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")
|
||||||
}
|
}
|
||||||
|
@ -787,40 +778,38 @@ func fnLocal(ell *Ell, args []V, result *[]V) bool {
|
||||||
values = values[1:]
|
values = values[1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return nil, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnSet(ell *Ell, args []V, result *[]V) bool {
|
func fnSet(ell *Ell, args []V) (result []V, ok 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 = []V{*args[1].Clone()}
|
result = []V{*args[1].Clone()}
|
||||||
ell.Set(args[0].String, &(*result)[0])
|
ell.Set(args[0].String, &result[0])
|
||||||
return true
|
return result, 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{*v.Clone()}
|
result = []V{*v.Clone()}
|
||||||
} else {
|
} else {
|
||||||
*result = []V{*NewList(nil)}
|
result = []V{*NewList(nil)}
|
||||||
}
|
}
|
||||||
return true
|
return result, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnList(ell *Ell, args []V, result *[]V) bool {
|
func fnList(ell *Ell, args []V) (result []V, ok bool) {
|
||||||
*result = []V{*NewList(args)}
|
return []V{*NewList(args)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnValues(ell *Ell, args []V, result *[]V) bool {
|
func fnValues(ell *Ell, args []V) (result []V, ok bool) {
|
||||||
*result = args
|
return args, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnIf(ell *Ell, args []V, result *[]V) bool {
|
func fnIf(ell *Ell, args []V) (result []V, ok 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) {
|
||||||
|
@ -831,11 +820,11 @@ func fnIf(ell *Ell, args []V, result *[]V) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
var res []V
|
var res []V
|
||||||
if !EvalAny(ell, &args[cond], nil, &res) {
|
if res, ok = EvalAny(ell, &args[cond], nil); !ok {
|
||||||
return false
|
return nil, false
|
||||||
}
|
}
|
||||||
if len(res) > 0 && Truthy(&res[0]) {
|
if len(res) > 0 && Truthy(&res[0]) {
|
||||||
return EvalAny(ell, &args[body], nil, result)
|
return EvalAny(ell, &args[body], nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if keyword = body + 1; keyword >= len(args) {
|
if keyword = body + 1; keyword >= len(args) {
|
||||||
|
@ -850,16 +839,16 @@ func fnIf(ell *Ell, args []V, result *[]V) bool {
|
||||||
if body = keyword + 1; body >= len(args) {
|
if body = keyword + 1; body >= len(args) {
|
||||||
return ell.Errorf("missing body")
|
return ell.Errorf("missing body")
|
||||||
}
|
}
|
||||||
return EvalAny(ell, &args[body], nil, result)
|
return EvalAny(ell, &args[body], nil)
|
||||||
case "elif":
|
case "elif":
|
||||||
default:
|
default:
|
||||||
return ell.Errorf("invalid keyword: %s", kw)
|
return ell.Errorf("invalid keyword: %s", kw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return nil, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnMap(ell *Ell, args []V, result *[]V) bool {
|
func fnMap(ell *Ell, args []V) (result []V, ok 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")
|
||||||
}
|
}
|
||||||
|
@ -868,18 +857,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
|
|
||||||
for _, v := range values.List {
|
for _, v := range values.List {
|
||||||
if !EvalAny(ell, body, &v, &res) {
|
res, ok := EvalAny(ell, body, &v)
|
||||||
return false
|
if !ok {
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
result = append(result, res...)
|
||||||
}
|
}
|
||||||
*result = []V{*NewList(res)}
|
return []V{*NewList(result)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnPrint(ell *Ell, args []V, result *[]V) bool {
|
func fnPrint(ell *Ell, args []V) (result []V, ok 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)
|
||||||
|
@ -887,10 +875,10 @@ func fnPrint(ell *Ell, args []V, result *[]V) bool {
|
||||||
return ell.Errorf("write failed: %s", err)
|
return ell.Errorf("write failed: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return nil, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnCat(ell *Ell, args []V, result *[]V) bool {
|
func fnCat(ell *Ell, args []V) (result []V, ok 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 {
|
||||||
|
@ -899,11 +887,10 @@ func fnCat(ell *Ell, args []V, result *[]V) bool {
|
||||||
buf.WriteString(arg.String)
|
buf.WriteString(arg.String)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*result = []V{*NewString(buf.String())}
|
return []V{*NewString(buf.String())}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnSystem(ell *Ell, args []V, result *[]V) bool {
|
func fnSystem(ell *Ell, args []V) (result []V, ok bool) {
|
||||||
var argv []string
|
var argv []string
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if arg.Type != VTypeString {
|
if arg.Type != VTypeString {
|
||||||
|
@ -922,16 +909,15 @@ 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 = []V{*NewNumber(0)}
|
return []V{*NewNumber(0)}, true
|
||||||
} 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 = []V{*NewNumber(1)}
|
return []V{*NewNumber(1)}, true
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnParse(ell *Ell, args []V, result *[]V) bool {
|
func fnParse(ell *Ell, args []V) (result []V, ok 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")
|
||||||
}
|
}
|
||||||
|
@ -940,11 +926,10 @@ 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 = []V{*NewList(res)}
|
return []V{*NewList(res)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnTry(ell *Ell, args []V, result *[]V) bool {
|
func fnTry(ell *Ell, args []V) (result []V, ok 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")
|
||||||
|
@ -953,25 +938,23 @@ func fnTry(ell *Ell, args []V, result *[]V) bool {
|
||||||
return ell.Errorf("second argument must be a function")
|
return ell.Errorf("second argument must be a function")
|
||||||
}
|
}
|
||||||
body, handler = &args[0], &args[1]
|
body, handler = &args[0], &args[1]
|
||||||
if EvalAny(ell, body, nil, result) {
|
if result, ok = EvalAny(ell, body, nil); ok {
|
||||||
return true
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := NewString(ell.Error)
|
msg := NewString(ell.Error)
|
||||||
ell.Error = ""
|
ell.Error = ""
|
||||||
*result = nil
|
return EvalAny(ell, handler, msg)
|
||||||
|
|
||||||
return EvalAny(ell, handler, msg, result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnThrow(ell *Ell, args []V, result *[]V) bool {
|
func fnThrow(ell *Ell, args []V) (result []V, ok 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, ok bool) {
|
||||||
res := 0.
|
res := 0.
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if arg.Type != VTypeString {
|
if arg.Type != VTypeString {
|
||||||
|
@ -983,11 +966,10 @@ func fnPlus(ell *Ell, args []V, result *[]V) bool {
|
||||||
}
|
}
|
||||||
res += value
|
res += value
|
||||||
}
|
}
|
||||||
*result = []V{*NewNumber(res)}
|
return []V{*NewNumber(res)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnMinus(ell *Ell, args []V, result *[]V) bool {
|
func fnMinus(ell *Ell, args []V) (result []V, ok 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")
|
||||||
}
|
}
|
||||||
|
@ -1010,11 +992,10 @@ func fnMinus(ell *Ell, args []V, result *[]V) bool {
|
||||||
}
|
}
|
||||||
res -= value
|
res -= value
|
||||||
}
|
}
|
||||||
*result = []V{*NewNumber(res)}
|
return []V{*NewNumber(res)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnMultiply(ell *Ell, args []V, result *[]V) bool {
|
func fnMultiply(ell *Ell, args []V) (result []V, ok bool) {
|
||||||
res := 1.
|
res := 1.
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if arg.Type != VTypeString {
|
if arg.Type != VTypeString {
|
||||||
|
@ -1026,11 +1007,10 @@ func fnMultiply(ell *Ell, args []V, result *[]V) bool {
|
||||||
}
|
}
|
||||||
res *= value
|
res *= value
|
||||||
}
|
}
|
||||||
*result = []V{*NewNumber(res)}
|
return []V{*NewNumber(res)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnDivide(ell *Ell, args []V, result *[]V) bool {
|
func fnDivide(ell *Ell, args []V) (result []V, ok 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")
|
||||||
}
|
}
|
||||||
|
@ -1049,51 +1029,46 @@ func fnDivide(ell *Ell, args []V, result *[]V) bool {
|
||||||
}
|
}
|
||||||
res /= value
|
res /= value
|
||||||
}
|
}
|
||||||
*result = []V{*NewNumber(res)}
|
return []V{*NewNumber(res)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnNot(ell *Ell, args []V, result *[]V) bool {
|
func fnNot(ell *Ell, args []V) (result []V, ok bool) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return ell.Errorf("missing argument")
|
return ell.Errorf("missing argument")
|
||||||
}
|
}
|
||||||
*result = []V{*NewBoolean(!Truthy(&args[0]))}
|
return []V{*NewBoolean(!Truthy(&args[0]))}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnAnd(ell *Ell, args []V, result *[]V) bool {
|
func fnAnd(ell *Ell, args []V) (result []V, ok bool) {
|
||||||
if args == nil {
|
if args == nil {
|
||||||
*result = []V{*NewBoolean(true)}
|
return []V{*NewBoolean(true)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
*result = nil
|
result, ok = EvalAny(ell, &arg, nil)
|
||||||
if !EvalAny(ell, &arg, nil, result) {
|
if !ok {
|
||||||
return false
|
return nil, false
|
||||||
}
|
}
|
||||||
if len(*result) < 1 || !Truthy(&(*result)[0]) {
|
if len(result) < 1 || !Truthy(&result[0]) {
|
||||||
*result = []V{*NewBoolean(false)}
|
return []V{*NewBoolean(false)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return result, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnOr(ell *Ell, args []V, result *[]V) bool {
|
func fnOr(ell *Ell, args []V) (result []V, ok bool) {
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if !EvalAny(ell, &arg, nil, result) {
|
result, ok = EvalAny(ell, &arg, nil)
|
||||||
return false
|
if !ok {
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
if len(*result) > 0 && Truthy(&(*result)[0]) {
|
if len(result) > 0 && Truthy(&result[0]) {
|
||||||
return true
|
return result, true
|
||||||
}
|
}
|
||||||
*result = nil
|
|
||||||
}
|
}
|
||||||
*result = []V{*NewBoolean(false)}
|
return []V{*NewBoolean(false)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnEq(ell *Ell, args []V, result *[]V) bool {
|
func fnEq(ell *Ell, args []V) (result []V, ok 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,11 +1081,10 @@ func fnEq(ell *Ell, args []V, result *[]V) bool {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*result = []V{*NewBoolean(res)}
|
return []V{*NewBoolean(res)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnLt(ell *Ell, args []V, result *[]V) bool {
|
func fnLt(ell *Ell, args []V) (result []V, ok 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")
|
||||||
}
|
}
|
||||||
|
@ -1124,11 +1098,10 @@ func fnLt(ell *Ell, args []V, result *[]V) bool {
|
||||||
}
|
}
|
||||||
etalon = arg.String
|
etalon = arg.String
|
||||||
}
|
}
|
||||||
*result = []V{*NewBoolean(res)}
|
return []V{*NewBoolean(res)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnEquals(ell *Ell, args []V, result *[]V) bool {
|
func fnEquals(ell *Ell, args []V) (result []V, ok 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")
|
||||||
}
|
}
|
||||||
|
@ -1149,11 +1122,10 @@ func fnEquals(ell *Ell, args []V, result *[]V) bool {
|
||||||
}
|
}
|
||||||
first = second
|
first = second
|
||||||
}
|
}
|
||||||
*result = []V{*NewBoolean(res)}
|
return []V{*NewBoolean(res)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnLess(ell *Ell, args []V, result *[]V) bool {
|
func fnLess(ell *Ell, args []V) (result []V, ok 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")
|
||||||
}
|
}
|
||||||
|
@ -1174,8 +1146,7 @@ func fnLess(ell *Ell, args []V, result *[]V) bool {
|
||||||
}
|
}
|
||||||
first = second
|
first = second
|
||||||
}
|
}
|
||||||
*result = []V{*NewBoolean(res)}
|
return []V{*NewBoolean(res)}, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -1233,6 +1204,6 @@ func StdInitialize(ell *Ell) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var result []V
|
_, ok := ell.EvalBlock(program, nil)
|
||||||
return ell.EvalBlock(program, nil, &result)
|
return ok
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue