add DO LOOP
This commit is contained in:
parent
626e90d54c
commit
9d3f61338f
71
builtins.go
71
builtins.go
@ -362,16 +362,83 @@ func (b *Builtins) Then(s *Stack, r *Stack) func() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do sets up a loop
|
// Do sets up a loop by marking its own location as the return code.
|
||||||
|
// It also puts TOS and NOS onto the return stack to use as loop control variables.
|
||||||
func (b *Builtins) Do(s *Stack, r *Stack) func() error {
|
func (b *Builtins) Do(s *Stack, r *Stack) func() error {
|
||||||
return func() error {
|
return func() error {
|
||||||
|
tors, err := r.Pop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tos, err := s.Pop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nos, err := s.Pop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Push(nos)
|
||||||
|
r.Push(tos)
|
||||||
|
r.Push(tors)
|
||||||
|
r.Push(tors)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop closes a loop
|
// Loop closes a loop by removing its own location from the stack and letting the one that Do inserted get returned.
|
||||||
|
// If it's reached the end of the loop (checking the loop-control variables _underneath_ the return vars),
|
||||||
|
// it continues, otherwise it re-adds the original location of the Do and returns.
|
||||||
func (b *Builtins) Loop(s *Stack, r *Stack) func() error {
|
func (b *Builtins) Loop(s *Stack, r *Stack) func() error {
|
||||||
return func() error {
|
return func() error {
|
||||||
|
tors, err := r.Pop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nors, err := r.Pop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
counter, err := r.Pop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
target, err := r.Pop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
counter = counter + 1
|
||||||
|
if counter == target {
|
||||||
|
r.Push(tors)
|
||||||
|
} else {
|
||||||
|
r.Push(target)
|
||||||
|
r.Push(counter)
|
||||||
|
r.Push(nors)
|
||||||
|
r.Push(nors)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// I puts the current value of the loop counter on the top of the stack
|
||||||
|
func (b *Builtins) I(s *Stack, r *Stack) func() error {
|
||||||
|
return func() error {
|
||||||
|
tors, err := r.Pop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nors, err := r.Pop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
counter, err := r.Pop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.Push(counter)
|
||||||
|
r.Push(counter)
|
||||||
|
r.Push(nors)
|
||||||
|
r.Push(tors)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
64
eval.go
64
eval.go
@ -39,7 +39,7 @@ func (c *Context) Eval(line string) error {
|
|||||||
if line[i] == ':' {
|
if line[i] == ':' {
|
||||||
immediate = false
|
immediate = false
|
||||||
} else if line[i-1] == ' ' && line[i] == ';' {
|
} else if line[i-1] == ' ' && line[i] == ';' {
|
||||||
c.Dictionary.AddWord(words[0], nil, words[1:])
|
c.Dictionary.AddWord(words[0], nil, words[1:], false)
|
||||||
word = []byte{}
|
word = []byte{}
|
||||||
immediate = true
|
immediate = true
|
||||||
}
|
}
|
||||||
@ -47,19 +47,26 @@ func (c *Context) Eval(line string) error {
|
|||||||
word = append(word, line[i])
|
word = append(word, line[i])
|
||||||
}
|
}
|
||||||
case ' ':
|
case ' ':
|
||||||
|
sword := strings.TrimSpace(string(word))
|
||||||
if !immediate { // continue building our subroutine if we're not in immediate mode...
|
if !immediate { // continue building our subroutine if we're not in immediate mode...
|
||||||
if len(word) != 0 { // don't add empty words to our list
|
if len(word) != 0 { // don't add empty words to our list
|
||||||
words = append(words, strings.TrimSpace(string(word)))
|
// Was that a word we know?
|
||||||
word = []byte{}
|
w, _ := c.Dictionary.GetWord(sword)
|
||||||
|
// check if it's an IMMEDIATE mode toggle word
|
||||||
|
immediate = w.Immediate
|
||||||
|
if !immediate {
|
||||||
|
words = append(words, sword)
|
||||||
|
word = []byte{}
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if len(word) == 0 || comment {
|
if len(word) == 0 || comment {
|
||||||
// empty space, just continue...
|
// empty space, just continue...
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
int, err := strconv.Atoi(string(word))
|
int, err := strconv.Atoi(sword)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// it was a number! put it on the stack.
|
// it was a number! put it on the stack.
|
||||||
c.Stack.Push(int)
|
c.Stack.Push(int)
|
||||||
@ -68,30 +75,20 @@ func (c *Context) Eval(line string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// it wasn't a number. Is it a word we know?
|
// it wasn't a number. Is it a word we know?
|
||||||
w, err := c.Dictionary.GetWord(string(word))
|
w, err := c.Dictionary.GetWord(sword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not parse %s; %v", w.Name, err)
|
return fmt.Errorf("could not parse %s; %v", w.Name, err)
|
||||||
}
|
}
|
||||||
if w.Impl != nil {
|
// run word
|
||||||
// we have an implementation for that word. Run it.
|
c.RStack.Push(i)
|
||||||
err := w.Impl()
|
if err = c.Exec(w); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
word = []byte{}
|
|
||||||
} else if len(w.Source) != 0 {
|
|
||||||
// user-defined word; let's descend...
|
|
||||||
c.RStack.Push(i)
|
|
||||||
err := c.Eval(strings.Join(w.Source, " ") + " ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i, err = c.RStack.Pop()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error while popping from return stack: %v", err)
|
|
||||||
}
|
|
||||||
word = []byte{}
|
|
||||||
}
|
}
|
||||||
|
i, err = c.RStack.Pop()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while popping from return stack: %v", err)
|
||||||
|
}
|
||||||
|
word = []byte{}
|
||||||
default:
|
default:
|
||||||
if !comment {
|
if !comment {
|
||||||
word = append(word, line[i])
|
word = append(word, line[i])
|
||||||
@ -101,3 +98,20 @@ func (c *Context) Eval(line string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) Exec(w Word) error {
|
||||||
|
if w.Impl != nil {
|
||||||
|
// we have an implementation for that word. Run it.
|
||||||
|
err := w.Impl()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if len(w.Source) != 0 {
|
||||||
|
// user-defined word; let's descend...
|
||||||
|
err := c.Eval(strings.Join(w.Source, " ") + " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
58
main.go
58
main.go
@ -14,33 +14,37 @@ func main() {
|
|||||||
|
|
||||||
dict := Dictionary{}
|
dict := Dictionary{}
|
||||||
b := &Builtins{}
|
b := &Builtins{}
|
||||||
dict.AddWord("+", b.Add(&stack), nil)
|
dict.AddWord("+", b.Add(&stack), nil, false)
|
||||||
dict.AddWord("-", b.Sub(&stack), nil)
|
dict.AddWord("-", b.Sub(&stack), nil, false)
|
||||||
dict.AddWord("*", b.Mul(&stack), nil)
|
dict.AddWord("*", b.Mul(&stack), nil, false)
|
||||||
dict.AddWord("/", b.Div(&stack), nil)
|
dict.AddWord("/", b.Div(&stack), nil, false)
|
||||||
dict.AddWord(".", b.Print(&stack), nil)
|
dict.AddWord(".", b.Print(&stack), nil, false)
|
||||||
dict.AddWord("=", b.Eq(&stack), nil)
|
dict.AddWord("=", b.Eq(&stack), nil, false)
|
||||||
dict.AddWord("0=", nil, []string{"0", "="})
|
dict.AddWord("0=", nil, []string{"0", "="}, false)
|
||||||
dict.AddWord("<>", b.NEq(&stack), nil)
|
dict.AddWord("<>", b.NEq(&stack), nil, false)
|
||||||
dict.AddWord(">", b.Gt(&stack), nil)
|
dict.AddWord(">", b.Gt(&stack), nil, false)
|
||||||
dict.AddWord("<", b.Lt(&stack), nil)
|
dict.AddWord("<", b.Lt(&stack), nil, false)
|
||||||
dict.AddWord(">=", b.GtEq(&stack), nil)
|
dict.AddWord(">=", b.GtEq(&stack), nil, false)
|
||||||
dict.AddWord("<=", b.LtEq(&stack), nil)
|
dict.AddWord("<=", b.LtEq(&stack), nil, false)
|
||||||
dict.AddWord("0<", nil, []string{"0", "<"})
|
dict.AddWord("0<", nil, []string{"0", "<"}, false)
|
||||||
dict.AddWord("0>", nil, []string{"0", ">"})
|
dict.AddWord("0>", nil, []string{"0", ">"}, false)
|
||||||
dict.AddWord("DUP", b.Dup(&stack), nil)
|
dict.AddWord("DUP", b.Dup(&stack), nil, false)
|
||||||
dict.AddWord("SWAP", b.Swap(&stack), nil)
|
dict.AddWord("SWAP", b.Swap(&stack), nil, false)
|
||||||
dict.AddWord("OVER", b.Over(&stack), nil)
|
dict.AddWord("OVER", b.Over(&stack), nil, false)
|
||||||
dict.AddWord("DROP", b.Drop(&stack), nil)
|
dict.AddWord("DROP", b.Drop(&stack), nil, false)
|
||||||
dict.AddWord("ROT", b.Rot(&stack), nil)
|
dict.AddWord("ROT", b.Rot(&stack), nil, false)
|
||||||
dict.AddWord("WORDS", b.Words(dict), nil)
|
dict.AddWord("WORDS", b.Words(dict), nil, false)
|
||||||
dict.AddWord(".S", b.Debug(&stack), nil)
|
dict.AddWord(".S", b.Debug(&stack), nil, false)
|
||||||
dict.AddWord("EMIT", b.Emit(&stack), nil)
|
dict.AddWord(".R", b.Debug(&rstack), nil, false)
|
||||||
dict.AddWord("R>", b.RFrom(&stack, &rstack), nil)
|
dict.AddWord("EMIT", b.Emit(&stack), nil, false)
|
||||||
dict.AddWord(">R", b.ToR(&stack, &rstack), nil)
|
dict.AddWord("R>", b.RFrom(&stack, &rstack), nil, false)
|
||||||
dict.AddWord("R@", b.RFetch(&stack, &rstack), nil)
|
dict.AddWord(">R", b.ToR(&stack, &rstack), nil, false)
|
||||||
dict.AddWord("CR", b.CR(), nil)
|
dict.AddWord("R@", b.RFetch(&stack, &rstack), nil, false)
|
||||||
dict.AddWord("QUIT", b.Quit(), nil)
|
dict.AddWord("DO", b.Do(&stack, &rstack), nil, false)
|
||||||
|
dict.AddWord("LOOP", b.Loop(&stack, &rstack), nil, false)
|
||||||
|
dict.AddWord("I", b.I(&stack, &rstack), nil, false)
|
||||||
|
dict.AddWord("CR", b.CR(), nil, false)
|
||||||
|
dict.AddWord("QUIT", b.Quit(), nil, false)
|
||||||
|
|
||||||
c := Context{
|
c := Context{
|
||||||
Dictionary: dict,
|
Dictionary: dict,
|
||||||
|
16
words.go
16
words.go
@ -7,17 +7,19 @@ type Dictionary map[string]Word
|
|||||||
|
|
||||||
// A Word defines a subroutine
|
// A Word defines a subroutine
|
||||||
type Word struct {
|
type Word struct {
|
||||||
Name string
|
Name string
|
||||||
Impl func() error
|
Impl func() error
|
||||||
Source []string
|
Source []string
|
||||||
|
Immediate bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddWord inserts a new word into the dictonary, optionally overwriting the existing word
|
// 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, immediate bool) {
|
||||||
d[name] = Word{
|
d[name] = Word{
|
||||||
Name: name,
|
Name: name,
|
||||||
Impl: impl,
|
Impl: impl,
|
||||||
Source: source,
|
Source: source,
|
||||||
|
Immediate: immediate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import "testing"
|
|||||||
func TestDictionary(t *testing.T) {
|
func TestDictionary(t *testing.T) {
|
||||||
d := Dictionary{}
|
d := Dictionary{}
|
||||||
|
|
||||||
d.AddWord("INC", nil, []string{"1", "+"})
|
d.AddWord("INC", nil, []string{"1", "+"}, false)
|
||||||
w, err := d.GetWord("INC")
|
w, err := d.GetWord("INC")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
|
Loading…
Reference in New Issue
Block a user