add DO LOOP

This commit is contained in:
David 2021-02-14 20:26:30 -05:00
parent 626e90d54c
commit 9d3f61338f
5 changed files with 149 additions and 62 deletions

View File

@ -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
} }
} }

48
eval.go
View File

@ -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?
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{} 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,22 +75,13 @@ 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.
err := w.Impl()
if err != nil {
return err
}
word = []byte{}
} else if len(w.Source) != 0 {
// user-defined word; let's descend...
c.RStack.Push(i) c.RStack.Push(i)
err := c.Eval(strings.Join(w.Source, " ") + " ") if err = c.Exec(w); err != nil {
if err != nil {
return err return err
} }
i, err = c.RStack.Pop() i, err = c.RStack.Pop()
@ -91,7 +89,6 @@ func (c *Context) Eval(line string) error {
return fmt.Errorf("error while popping from return stack: %v", err) return fmt.Errorf("error while popping from return stack: %v", err)
} }
word = []byte{} 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
View File

@ -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,

View File

@ -10,14 +10,16 @@ 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,
} }
} }

View File

@ -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()