start using return stack to track evaluation location
This commit is contained in:
		
							
								
								
									
										37
									
								
								builtins.go
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								builtins.go
									
									
									
									
									
								
							@@ -286,7 +286,7 @@ func (b *Builtins) Rot(s *Stack) func() error {
 | 
			
		||||
func (b *Builtins) Words(d Dictionary) func() error {
 | 
			
		||||
	return func() error {
 | 
			
		||||
		for n := range d {
 | 
			
		||||
			fmt.Printf("%s ", n)
 | 
			
		||||
			fmt.Printf("%s %s\n", n, d[n].Source)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -341,6 +341,41 @@ func (b *Builtins) RFetch(s *Stack, r *Stack) func() error {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// If checks if NOS != 0 and conditionally executes any following statements if so
 | 
			
		||||
func (b *Builtins) If(s *Stack, r *Stack) func() error {
 | 
			
		||||
	return func() error {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Else executes a separate piece of code if NOS == 0 after an if check
 | 
			
		||||
func (b *Builtins) Else(s *Stack, r *Stack) func() error {
 | 
			
		||||
	return func() error {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Then ends an If/Else block
 | 
			
		||||
func (b *Builtins) Then(s *Stack, r *Stack) func() error {
 | 
			
		||||
	return func() error {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Do sets up a loop
 | 
			
		||||
func (b *Builtins) Do(s *Stack, r *Stack) func() error {
 | 
			
		||||
	return func() error {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Loop closes a loop
 | 
			
		||||
func (b *Builtins) Loop(s *Stack, r *Stack) func() error {
 | 
			
		||||
	return func() error {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CR prints a newline to standard out
 | 
			
		||||
func (b *Builtins) CR() func() error {
 | 
			
		||||
	return func() error {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								eval.go
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								eval.go
									
									
									
									
									
								
							@@ -6,57 +6,60 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Context is a set of Dictionary + Stack representing a runtime environment
 | 
			
		||||
// Context is a set of Dictionary + Stacks representing a runtime environment
 | 
			
		||||
type Context struct {
 | 
			
		||||
	Dictionary Dictionary
 | 
			
		||||
	Stack      *Stack
 | 
			
		||||
	RStack     *Stack
 | 
			
		||||
	I          int // current parser location
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Eval evaulates a given line, recursively descending into given words as needed
 | 
			
		||||
func (c *Context) Eval(line string) error {
 | 
			
		||||
	// state
 | 
			
		||||
	var word []byte
 | 
			
		||||
	var words []string
 | 
			
		||||
	var comment bool
 | 
			
		||||
	immediate := true
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(line); i = i + 1 {
 | 
			
		||||
		switch line[i] {
 | 
			
		||||
	for c.I = 0; c.I < len(line); c.I = c.I + 1 {
 | 
			
		||||
		switch line[c.I] {
 | 
			
		||||
		case '(', ')': // comments
 | 
			
		||||
			if len(word) == 0 {
 | 
			
		||||
				if line[i] == '(' {
 | 
			
		||||
				if line[c.I] == '(' {
 | 
			
		||||
					comment = true
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				comment = false
 | 
			
		||||
				continue
 | 
			
		||||
			} else {
 | 
			
		||||
				word = append(word, line[i])
 | 
			
		||||
				word = append(word, line[c.I])
 | 
			
		||||
			}
 | 
			
		||||
		case ':', ';': // COMPILE/IMMEDIATE mode swapping
 | 
			
		||||
			if len(word) == 0 {
 | 
			
		||||
				if line[i] == ':' {
 | 
			
		||||
				if line[c.I] == ':' {
 | 
			
		||||
					immediate = false
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if line[i-1] == ' ' && line[i] == ';' {
 | 
			
		||||
					def := strings.SplitN(strings.TrimSpace(string(word)), " ", 2)
 | 
			
		||||
					c.Dictionary.AddWord(def[0], nil, def[1]+" ")
 | 
			
		||||
				} else if line[c.I-1] == ' ' && line[c.I] == ';' {
 | 
			
		||||
					c.Dictionary.AddWord(words[0], nil, words[1:])
 | 
			
		||||
					word = []byte{}
 | 
			
		||||
					immediate = true
 | 
			
		||||
				} else {
 | 
			
		||||
					word = append(word, line[i])
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				word = append(word, line[c.I])
 | 
			
		||||
			}
 | 
			
		||||
		case ' ':
 | 
			
		||||
			if !immediate { // continue building our subroutine if we're not in immediate mode...
 | 
			
		||||
				word = append(word, line[i])
 | 
			
		||||
				if len(word) != 0 { // don't add empty words to our list
 | 
			
		||||
					words = append(words, strings.TrimSpace(string(word)))
 | 
			
		||||
					word = []byte{}
 | 
			
		||||
				}
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if len(word) == 0 || comment {
 | 
			
		||||
				// empty space, just continue...
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			int, err := strconv.Atoi(string(word))
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				// it was a number! put it on the stack.
 | 
			
		||||
@@ -64,6 +67,7 @@ func (c *Context) Eval(line string) error {
 | 
			
		||||
				word = []byte{}
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// it wasn't a number. Is it a word we know?
 | 
			
		||||
			w, err := c.Dictionary.GetWord(string(word))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
@@ -76,17 +80,22 @@ func (c *Context) Eval(line string) error {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				word = []byte{}
 | 
			
		||||
			} else if w.Source != "" {
 | 
			
		||||
			} else if len(w.Source) != 0 {
 | 
			
		||||
				// user-defined word; let's descend...
 | 
			
		||||
				err := c.Eval(w.Source)
 | 
			
		||||
				c.RStack.Push(c.I)
 | 
			
		||||
				err := c.Eval(strings.Join(w.Source, " ") + " ")
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				c.I, err = c.RStack.Pop()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return fmt.Errorf("error while popping from return stack: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
				word = []byte{}
 | 
			
		||||
			}
 | 
			
		||||
		default:
 | 
			
		||||
			if !comment {
 | 
			
		||||
				word = append(word, line[i])
 | 
			
		||||
				word = append(word, line[c.I])
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										57
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								main.go
									
									
									
									
									
								
							@@ -14,31 +14,33 @@ func main() {
 | 
			
		||||
 | 
			
		||||
	dict := Dictionary{}
 | 
			
		||||
	b := &Builtins{}
 | 
			
		||||
	dict.AddWord("+", b.Add(&stack), "")
 | 
			
		||||
	dict.AddWord("-", b.Sub(&stack), "")
 | 
			
		||||
	dict.AddWord("*", b.Mul(&stack), "")
 | 
			
		||||
	dict.AddWord("/", b.Div(&stack), "")
 | 
			
		||||
	dict.AddWord(".", b.Print(&stack), "")
 | 
			
		||||
	dict.AddWord("=", b.Eq(&stack), "")
 | 
			
		||||
	dict.AddWord("0=", nil, "0 = ")
 | 
			
		||||
	dict.AddWord("<>", b.NEq(&stack), "")
 | 
			
		||||
	dict.AddWord(">", b.Gt(&stack), "")
 | 
			
		||||
	dict.AddWord("<", b.Lt(&stack), "")
 | 
			
		||||
	dict.AddWord(">=", b.GtEq(&stack), "")
 | 
			
		||||
	dict.AddWord("<=", b.LtEq(&stack), "")
 | 
			
		||||
	dict.AddWord("DUP", b.Dup(&stack), "")
 | 
			
		||||
	dict.AddWord("SWAP", b.Swap(&stack), "")
 | 
			
		||||
	dict.AddWord("OVER", b.Over(&stack), "")
 | 
			
		||||
	dict.AddWord("DROP", b.Drop(&stack), "")
 | 
			
		||||
	dict.AddWord("ROT", b.Rot(&stack), "")
 | 
			
		||||
	dict.AddWord("WORDS", b.Words(dict), "")
 | 
			
		||||
	dict.AddWord(".S", b.Debug(&stack), "")
 | 
			
		||||
	dict.AddWord("EMIT", b.Emit(&stack), "")
 | 
			
		||||
	dict.AddWord("R>", b.RFrom(&stack, &rstack), "")
 | 
			
		||||
	dict.AddWord(">R", b.ToR(&stack, &rstack), "")
 | 
			
		||||
	dict.AddWord("R@", b.RFetch(&stack, &rstack), "")
 | 
			
		||||
	dict.AddWord("CR", b.CR(), "")
 | 
			
		||||
	dict.AddWord("QUIT", b.Quit(), "")
 | 
			
		||||
	dict.AddWord("+", b.Add(&stack), nil)
 | 
			
		||||
	dict.AddWord("-", b.Sub(&stack), nil)
 | 
			
		||||
	dict.AddWord("*", b.Mul(&stack), nil)
 | 
			
		||||
	dict.AddWord("/", b.Div(&stack), nil)
 | 
			
		||||
	dict.AddWord(".", b.Print(&stack), nil)
 | 
			
		||||
	dict.AddWord("=", b.Eq(&stack), nil)
 | 
			
		||||
	dict.AddWord("0=", nil, []string{"0", "="})
 | 
			
		||||
	dict.AddWord("<>", b.NEq(&stack), nil)
 | 
			
		||||
	dict.AddWord(">", b.Gt(&stack), nil)
 | 
			
		||||
	dict.AddWord("<", b.Lt(&stack), nil)
 | 
			
		||||
	dict.AddWord(">=", b.GtEq(&stack), nil)
 | 
			
		||||
	dict.AddWord("<=", b.LtEq(&stack), nil)
 | 
			
		||||
	dict.AddWord("0<", nil, []string{"0", "<"})
 | 
			
		||||
	dict.AddWord("0>", nil, []string{"0", ">"})
 | 
			
		||||
	dict.AddWord("DUP", b.Dup(&stack), nil)
 | 
			
		||||
	dict.AddWord("SWAP", b.Swap(&stack), nil)
 | 
			
		||||
	dict.AddWord("OVER", b.Over(&stack), nil)
 | 
			
		||||
	dict.AddWord("DROP", b.Drop(&stack), nil)
 | 
			
		||||
	dict.AddWord("ROT", b.Rot(&stack), nil)
 | 
			
		||||
	dict.AddWord("WORDS", b.Words(dict), nil)
 | 
			
		||||
	dict.AddWord(".S", b.Debug(&stack), nil)
 | 
			
		||||
	dict.AddWord("EMIT", b.Emit(&stack), nil)
 | 
			
		||||
	dict.AddWord("R>", b.RFrom(&stack, &rstack), nil)
 | 
			
		||||
	dict.AddWord(">R", b.ToR(&stack, &rstack), nil)
 | 
			
		||||
	dict.AddWord("R@", b.RFetch(&stack, &rstack), nil)
 | 
			
		||||
	dict.AddWord("CR", b.CR(), nil)
 | 
			
		||||
	dict.AddWord("QUIT", b.Quit(), nil)
 | 
			
		||||
 | 
			
		||||
	c := Context{
 | 
			
		||||
		Dictionary: dict,
 | 
			
		||||
@@ -64,8 +66,9 @@ func main() {
 | 
			
		||||
			fmt.Printf("bye\n")
 | 
			
		||||
			os.Exit(0)
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
			fmt.Printf("error in evaluation: %v\n", err)
 | 
			
		||||
			fmt.Printf("error in evaluation: %v", err)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Print("ok")
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Print("ok")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								words.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								words.go
									
									
									
									
									
								
							@@ -9,11 +9,11 @@ type Dictionary map[string]Word
 | 
			
		||||
type Word struct {
 | 
			
		||||
	Name   string
 | 
			
		||||
	Impl   func() error
 | 
			
		||||
	Source string
 | 
			
		||||
	Source []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddWord inserts a new word into the dictonary, optionally overwriting the existing word
 | 
			
		||||
func (d Dictionary) AddWord(name string, impl func() error, source string) {
 | 
			
		||||
func (d Dictionary) AddWord(name string, impl func() error, source []string) {
 | 
			
		||||
	d[name] = Word{
 | 
			
		||||
		Name:   name,
 | 
			
		||||
		Impl:   impl,
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import "testing"
 | 
			
		||||
func TestDictionary(t *testing.T) {
 | 
			
		||||
	d := Dictionary{}
 | 
			
		||||
 | 
			
		||||
	d.AddWord("INC", nil, "1 + ")
 | 
			
		||||
	d.AddWord("INC", nil, []string{"1", "+"})
 | 
			
		||||
	w, err := d.GetWord("INC")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fail()
 | 
			
		||||
@@ -16,7 +16,7 @@ func TestDictionary(t *testing.T) {
 | 
			
		||||
	if w.Impl != nil {
 | 
			
		||||
		t.Fail()
 | 
			
		||||
	}
 | 
			
		||||
	if w.Source != "1 +" {
 | 
			
		||||
	if len(w.Source) != 2 || w.Source[0] != "1" || w.Source[1] != "+" {
 | 
			
		||||
		t.Fail()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user