Go: use slices for list values
This commit is contained in:
parent
fb143f4d27
commit
b210216c71
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
350
ell/ell.go
350
ell/ell.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue