Go: use slices for list values

This commit is contained in:
Přemysl Eric Janouch 2018-10-10 19:39:29 +02:00
parent fb143f4d27
commit b210216c71
Signed by: p
GPG Key ID: A0420B94F92B9493
3 changed files with 158 additions and 208 deletions

View File

@ -49,14 +49,12 @@ func main() {
os.Exit(1) os.Exit(1)
} }
var args *ell.V var args []ell.V
tail := &args
for i := 2; i < len(os.Args); i++ { for i := 2; i < len(os.Args); i++ {
*tail = ell.NewString(os.Args[i]) args = append(args, *ell.NewString(os.Args[i]))
tail = &(*tail).Next
} }
var result *ell.V var result []ell.V
if !L.EvalBlock(program, args, &result) { if !L.EvalBlock(program, args, &result) {
fmt.Printf("%s: %s\n", "runtime error", L.Error) fmt.Printf("%s: %s\n", "runtime error", L.Error)
} }

View File

@ -28,8 +28,8 @@ import (
"janouch.name/ell/ell" "janouch.name/ell/ell"
) )
func run(L *ell.Ell, program *ell.V) { func run(L *ell.Ell, program []ell.V) {
var result *ell.V var result []ell.V
if !L.EvalBlock(program, nil, &result) { 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 = ""
@ -48,8 +48,8 @@ func complete(L *ell.Ell, line string, pos int) (
head, line = line[:lastSpace+1], line[lastSpace+1:] head, line = line[:lastSpace+1], line[lastSpace+1:]
} }
for v := L.Globals; v != nil; v = v.Next { for _, v := range L.Globals {
name := v.Head.String name := v.List[0].String
if strings.HasPrefix(strings.ToLower(name), line) { if strings.HasPrefix(strings.ToLower(name), line) {
completions = append(completions, name) completions = append(completions, name)
} }

View File

@ -42,32 +42,28 @@ const (
// V is a value in the ell language. // V is a value in the ell language.
type V struct { type V struct {
Type VType // the type of this value Type VType // the type of this value
Next *V // next value in sequence List []V // the contents of a VTypeList
Head *V // the head of a VTypeList
String string // the immutable contents of a VTypeString String string // the immutable contents of a VTypeString
} }
// Clone clones a value without following the rest of its chain. // Clone clones a value including its sublists.
func (v *V) Clone() *V { func (v *V) Clone() *V {
if v == nil { if v == nil {
return nil return nil
} }
return &V{ return &V{
Type: v.Type, Type: v.Type,
Next: nil, List: CloneSeq(v.List),
Head: v.Head.CloneSeq(),
String: v.String, String: v.String,
} }
} }
// CloneSeq clones a value including the rest of its chain. // CloneSeq clones a value including the rest of its chain.
func (v *V) CloneSeq() *V { func CloneSeq(v []V) (result []V) {
var head *V for _, v := range v {
for out := &head; v != nil; v = v.Next { result = append(result, *v.Clone())
*out = v.Clone()
out = &(*out).Next
} }
return head return
} }
// NewString creates a new value containing a string. // NewString creates a new value containing a string.
@ -79,10 +75,10 @@ func NewString(string string) *V {
} }
// NewList creates a new list value containing the given sequence. // NewList creates a new list value containing the given sequence.
func NewList(head *V) *V { func NewList(list []V) *V {
return &V{ return &V{
Type: VTypeList, Type: VTypeList,
Head: head, List: list,
} }
} }
@ -334,49 +330,47 @@ func printString(w io.Writer, s *V) bool {
} }
func printBlock(w io.Writer, list *V) bool { func printBlock(w io.Writer, list *V) bool {
if list.Head == nil || string(list.Head.String) != "block" { if len(list.List) < 1 || list.List[0].String != "block" {
return false return false
} }
list = list.Head.Next sublist := list.List[1:]
for line := list; line != nil; line = line.Next { for _, subsub := range sublist {
if line.Type != VTypeList { if subsub.Type != VTypeList {
return false return false
} }
} }
_, _ = w.Write([]byte{'{'}) _, _ = w.Write([]byte{'{'})
for line := list; line != nil; line = line.Next { if len(sublist) > 0 {
_, _ = w.Write([]byte{' '}) _, _ = w.Write([]byte{' '})
PrintSeq(w, line.Head) PrintSeq(w, sublist[0].List)
for _, subsub := range sublist[1:] {
if line.Next != nil { _, _ = w.Write([]byte{';', ' '})
_, _ = w.Write([]byte{';'}) PrintSeq(w, subsub.List)
} else {
_, _ = w.Write([]byte{' '})
} }
_, _ = w.Write([]byte{' '})
} }
_, _ = w.Write([]byte{'}'}) _, _ = w.Write([]byte{'}'})
return true return true
} }
func printSet(w io.Writer, list *V) bool { func printSet(w io.Writer, list *V) bool {
if list.Head == nil || string(list.Head.String) != "set" || if len(list.List) != 2 || list.List[0].String != "set" {
list.Head.Next == nil || list.Head.Next.Next != nil {
return false return false
} }
_, _ = w.Write([]byte{'@'}) _, _ = w.Write([]byte{'@'})
PrintSeq(w, list.Head.Next) PrintSeq(w, list.List[1:])
return true return true
} }
func printList(w io.Writer, list *V) bool { func printList(w io.Writer, list *V) bool {
if list.Head == nil || string(list.Head.String) != "list" { if len(list.List) < 1 || list.List[0].String != "list" {
return false return false
} }
_, _ = w.Write([]byte{'['}) _, _ = w.Write([]byte{'['})
PrintSeq(w, list.Head.Next) PrintSeq(w, list.List[1:])
_, _ = w.Write([]byte{']'}) _, _ = w.Write([]byte{']'})
return true return true
} }
@ -391,17 +385,19 @@ func PrintV(w io.Writer, v *V) {
} }
_, _ = w.Write([]byte{'('}) _, _ = w.Write([]byte{'('})
PrintSeq(w, v.Head) PrintSeq(w, v.List)
_, _ = w.Write([]byte{')'}) _, _ = w.Write([]byte{')'})
} }
// 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, v *V) { func PrintSeq(w io.Writer, seq []V) {
for ; v != nil; v = v.Next { if len(seq) < 1 {
PrintV(w, v) return
if v.Next != nil { }
_, _ = w.Write([]byte{' '}) PrintV(w, &seq[0])
} for _, v := range seq[1:] {
_, _ = w.Write([]byte{' '})
PrintV(w, &v)
} }
} }
@ -454,92 +450,85 @@ func (p *Parser) skipNL() {
} }
} }
func parsePrefixList(seq *V, name string) *V { func parsePrefixList(seq []V, name string) *V {
prefix := NewString(name) prefix := []V{*NewString(name)}
prefix.Next = seq return NewList(append(prefix, seq...))
return NewList(prefix)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
func (p *Parser) parseV() *V { func (p *Parser) parseV() *V {
var result *V var seq []V
tail := &result
p.skipNL() p.skipNL()
switch { switch {
case p.accept(tString): case p.accept(tString):
return NewString(string(p.lexer.buf)) return NewString(string(p.lexer.buf))
case p.accept(tAt): case p.accept(tAt):
result = p.parseV() seq = []V{*p.parseV()}
return parsePrefixList(result, "set") return parsePrefixList(seq, "set")
case p.accept(tLParen): case p.accept(tLParen):
for !p.accept(tRParen) { for !p.accept(tRParen) {
*tail = p.parseV() seq = append(seq, *p.parseV())
tail = &(*tail).Next
p.skipNL() p.skipNL()
} }
return NewList(result) return NewList(seq)
case p.accept(tLBracket): case p.accept(tLBracket):
for !p.accept(tRBracket) { for !p.accept(tRBracket) {
*tail = p.parseV() seq = append(seq, *p.parseV())
tail = &(*tail).Next
p.skipNL() p.skipNL()
} }
return parsePrefixList(result, "list") return parsePrefixList(seq, "list")
case p.accept(tLBrace): case p.accept(tLBrace):
for { for {
*tail = p.parseLine() result := p.parseLine()
if *tail == nil { if result == nil {
break break
} }
tail = &(*tail).Next seq = append(seq, *result)
} }
p.expect(tRBrace) p.expect(tRBrace)
return parsePrefixList(result, "block") return parsePrefixList(seq, "block")
} }
panic(p.lexer.errorf("unexpected `%s', expected a value", p.token)) panic(p.lexer.errorf("unexpected `%s', expected a value", p.token))
} }
func (p *Parser) parseLine() *V { func (p *Parser) parseLine() *V {
var result *V var seq []V
tail := &result
for p.peek() != tRBrace && p.peek() != tAbort { for p.peek() != tRBrace && p.peek() != tAbort {
if !p.accept(tNewline) { if !p.accept(tNewline) {
*tail = p.parseV() seq = append(seq, *p.parseV())
tail = &(*tail).Next } else if len(seq) > 0 {
} else if result != nil { return NewList(seq)
return NewList(result)
} }
} }
if result != nil { if len(seq) > 0 {
return NewList(result) return NewList(seq)
} }
return nil return nil
} }
// Run runs the parser and returns a value to be interpreted or an error. // Run runs the parser and returns a value to be interpreted or an error.
func (p *Parser) Run() (result *V, err error) { func (p *Parser) Run() (seq []V, err error) {
// "The convention in the Go libraries is that even when a package // "The convention in the Go libraries is that even when a package
// uses panic internally, its external API still presents explicit // uses panic internally, its external API still presents explicit
// error return values." We're good. // error return values." We're good.
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
result, err = nil, r.(error) seq, err = nil, r.(error)
} }
}() }()
tail := &result
for { for {
*tail = p.parseLine() result := p.parseLine()
if *tail == nil { if result == nil {
break break
} }
tail = &(*tail).Next seq = append(seq, *result)
} }
p.expect(tAbort) p.expect(tAbort)
return result, nil return seq, nil
} }
// --- Runtime ----------------------------------------------------------------- // --- Runtime -----------------------------------------------------------------
@ -549,8 +538,8 @@ type Handler func(*Ell, []V, *[]V) bool
// Ell is an interpreter context. // Ell is an interpreter context.
type Ell struct { type Ell struct {
Globals *V // list of global variables Globals []*V // list of global variables
scopes *V // dynamic scopes from the newest scopes [][]*V // dynamic scopes from the newest
Native map[string]Handler // maps strings to Go functions Native map[string]Handler // maps strings to Go functions
Error string // error information Error string // error information
@ -563,53 +552,54 @@ func New() *Ell {
} }
} }
func scopeFind(scope **V, name string) **V { func scopeFind(scope []*V, name string) int {
for ; *scope != nil; scope = &(*scope).Next { for i, scope := range scope {
if string((*scope).Head.String) == name { if scope.List[0].String == name {
return scope return i
} }
} }
return nil return -1
} }
func scopePrepend(scope **V, name string, v *V) { // TODO: This is O(n), let's just make them a map[string]*V.
func scopePrepend(scope []*V, name string, v *V) []*V {
key := NewString(name) key := NewString(name)
pair := NewList(key) pair := NewList([]V{*key, *v})
key.Next = v result := make([]*V, len(scope)+1)
pair.Next = *scope copy(result[1:], scope)
*scope = pair result[0] = pair
return result
} }
// Get retrieves a value by name from the scope or from global variables. // Get retrieves a value by name from the scope or from global variables.
func (ell *Ell) Get(name string) *V { func (ell *Ell) Get(name string) *V {
var place **V for _, scope := range ell.scopes {
for scope := ell.scopes; scope != nil; scope = scope.Next { if place := scopeFind(scope, name); place >= 0 {
if place = scopeFind(&scope.Head, name); place != nil { return &scope[place].List[1]
return (*place).Head.Next
} }
} }
if place = scopeFind(&ell.Globals, name); place != nil { if place := scopeFind(ell.Globals, name); place >= 0 {
return (*place).Head.Next return &ell.Globals[place].List[1]
} }
return nil return nil
} }
// Set sets a value by name in the scope or in global variables. // Set sets a value by name in the scope or in global variables.
func (ell *Ell) Set(name string, v *V) { func (ell *Ell) Set(name string, v *V) {
var place **V for _, scope := range ell.scopes {
for scope := ell.scopes; scope != nil; scope = scope.Next { if place := scopeFind(scope, name); place >= 0 {
if place = scopeFind(&scope.Head, name); place != nil { scope[place].List[1] = *v
(*place).Head.Next = v
return return
} }
} }
// Variables only get deleted by "arg" or from the global scope. // Variables only get deleted by "arg" or from the global scope.
if place = scopeFind(&ell.Globals, name); place != nil { if place := scopeFind(ell.Globals, name); place >= 0 {
*place = (*place).Next ell.Globals[place].List[1] = *v
} else {
ell.Globals = scopePrepend(ell.Globals, name, v)
} }
scopePrepend(&ell.Globals, name, v)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -626,89 +616,54 @@ 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) bool {
var res *V for i, arg := range args {
out := &res var evaluated []V
i := 0
for ; args != nil; args = args.Next {
var evaluated *V
// Arguments should not evaporate, default to a nil value. // Arguments should not evaporate, default to a nil value.
if !ell.evalStatement(args, &evaluated) { if !ell.evalStatement(&arg, &evaluated) {
goto error // Once the code flows like this, at least make some use of it.
if ell.canModifyError() {
ell.Errorf("(argument %d) -> %s", i, ell.Error)
}
*result = nil
return false
} }
if evaluated == nil { if len(evaluated) < 1 {
evaluated = NewList(nil) evaluated = []V{*NewList(nil)}
} }
evaluated.Next = nil *result = append(*result, evaluated[0])
*out = evaluated
out = &(*out).Next
i++
} }
*result = res
return true return true
error:
// Once the code flows like this, at least make some use of it.
if ell.canModifyError() {
ell.Errorf("(argument %d) -> %s", i, ell.Error)
}
return false
} }
func sliceToSeq(slice []V) (res *V) { func (ell *Ell) evalNative(name string, args []V, result *[]V) bool {
out := &res
for _, v := range slice {
*out = v.Clone()
out = &(*out).Next
}
return
}
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 {
return ell.Errorf("unknown function") return ell.Errorf("unknown function")
} }
var arguments *V var arguments []V
if !ell.evalArgs(args, &arguments) { return ell.evalArgs(args, &arguments) && fn(ell, arguments, result)
return false
}
// 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)
}
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 {
// 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 = body.Clone() *result = []V{*body.Clone()}
return true return true
} }
var arguments *V var arguments []V
return ell.evalArgs(args, &arguments) && return ell.evalArgs(args, &arguments) &&
ell.EvalBlock(body.Head, arguments, result) ell.EvalBlock(body.List, arguments, result)
} }
func (ell *Ell) evalValue(body *V, result **V) bool { func (ell *Ell) evalValue(body []V, result *[]V) bool {
args := body.Next args := body[1:]
if body.Type == VTypeString { if body[0].Type == VTypeString {
name := string(body.String) name := body[0].String
if name == "block" { if name == "block" {
if args != nil { if len(args) > 0 {
*result = NewList(args.CloneSeq()) *result = []V{*NewList(CloneSeq(args))}
} }
return true return true
} }
@ -720,36 +675,37 @@ func (ell *Ell) evalValue(body *V, result **V) bool {
// 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 var evaluated []V
if !ell.evalStatement(body, &evaluated) { if !ell.evalStatement(&body[0], &evaluated) {
return false return false
} }
// 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 evaluated == nil { if len(evaluated) < 1 {
return true return true
} }
return ell.evalResolved(evaluated, args, result) return ell.evalResolved(&evaluated[0], args, result)
} }
func (ell *Ell) evalStatement(statement *V, result **V) bool { func (ell *Ell) evalStatement(statement *V, result *[]V) bool {
if statement.Type == VTypeString { if statement.Type == VTypeString {
*result = statement.Clone() *result = []V{*statement.Clone()}
return 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 statement.Head == nil || ell.evalValue(statement.Head, result) { if len(statement.List) < 1 ||
ell.evalValue(statement.List, result) {
return true return true
} }
*result = nil *result = nil
name := "(block)" name := "(block)"
if statement.Head.Type == VTypeString { if statement.List[0].Type == VTypeString {
name = string(statement.Head.String) name = statement.List[0].String
} }
if ell.canModifyError() { if ell.canModifyError() {
@ -758,35 +714,31 @@ func (ell *Ell) evalStatement(statement *V, result **V) bool {
return false return false
} }
func argsToScope(args *V, scope **V) { func argsToScope(args []V) []*V {
args = NewList(args) scope := scopePrepend(nil, "args", NewList(args))
scopePrepend(scope, "args", args) for i, arg := range args {
scope = scopePrepend(scope, fmt.Sprintf("%d", i+1), arg.Clone())
i := 0
for args = args.Head; args != nil; args = args.Next {
i++
scopePrepend(scope, fmt.Sprintf("%d", i), args.Clone())
} }
*scope = NewList(*scope) return scope
} }
// 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) bool {
var scope *V // TODO: This is O(n), let's just rather append and traverse in reverse.
argsToScope(args, &scope) newScopes := make([][]*V, len(ell.scopes)+1)
newScopes[0] = argsToScope(args)
scope.Next = ell.scopes copy(newScopes[1:], ell.scopes)
ell.scopes = scope ell.scopes = newScopes
ok := true ok := true
for ; body != nil; body = body.Next { for _, stmt := range body {
*result = nil *result = nil
if ok = ell.evalStatement(body, result); !ok { if ok = ell.evalStatement(&stmt, result); !ok {
break break
} }
} }
ell.scopes = scope.Next ell.scopes = ell.scopes[1:]
return ok return ok
} }
@ -798,15 +750,15 @@ func EvalAny(ell *Ell, body *V, arg *V, result *[]V) bool {
*result = []V{*body.Clone()} *result = []V{*body.Clone()}
return true return true
} }
var res *V var args []V
if !ell.EvalBlock(body.Head, arg.Clone(), &res) { if arg != nil {
args = append(args, *arg.Clone())
}
var res []V
if !ell.EvalBlock(body.List, args, &res) {
return false return false
} }
for ; res != nil; res = res.Next { *result = append(*result, res...)
singledOut := *res
singledOut.Next = nil
*result = append(*result, singledOut)
}
return true return true
} }
@ -825,7 +777,7 @@ func NewNumber(n float64) *V {
// Truthy decides whether any value is logically true. // Truthy decides whether any value is logically true.
func Truthy(v *V) bool { func Truthy(v *V) bool {
return v != nil && (v.Head != nil || len(v.String) > 0) return v != nil && (len(v.List) > 0 || len(v.String) > 0)
} }
// NewBoolean creates a new string value copying the boolean's truthiness. // NewBoolean creates a new string value copying the boolean's truthiness.
@ -844,11 +796,11 @@ func fnLocal(ell *Ell, args []V, result *[]V) bool {
} }
// 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[0]
values := args[1:] values := args[1:]
for name := args[0].Head; name != nil; name = name.Next { for _, name := range args[0].List {
scopePrepend(scope, string(name.String), values[0].Clone()) *scope = scopePrepend(*scope, name.String, values[0].Clone())
if len(values) > 0 { if len(values) > 0 {
values = values[1:] values = values[1:]
} }
@ -877,7 +829,7 @@ func fnSet(ell *Ell, args []V, result *[]V) bool {
} }
func fnList(ell *Ell, args []V, result *[]V) bool { func fnList(ell *Ell, args []V, result *[]V) bool {
*result = []V{*NewList(sliceToSeq(args))} *result = []V{*NewList(args)}
return true return true
} }
@ -936,12 +888,12 @@ 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
for v := values.Head; v != nil; v = v.Next { for _, v := range values.List {
if !EvalAny(ell, body, v, &res) { if !EvalAny(ell, body, &v, &res) {
return false return false
} }
} }
*result = []V{*NewList(sliceToSeq(res))} *result = []V{*NewList(res)}
return true return true
} }
@ -975,7 +927,7 @@ func fnSystem(ell *Ell, args []V, result *[]V) bool {
if arg.Type != VTypeString { if arg.Type != VTypeString {
return ell.Errorf("arguments must be strings") return ell.Errorf("arguments must be strings")
} }
argv = append(argv, string(arg.String)) argv = append(argv, arg.String)
} }
if len(argv) == 0 { if len(argv) == 0 {
return ell.Errorf("command name required") return ell.Errorf("command name required")
@ -1299,6 +1251,6 @@ func StdInitialize(ell *Ell) bool {
return false return false
} }
var result *V var result []V
return ell.EvalBlock(program, nil, &result) return ell.EvalBlock(program, nil, &result)
} }