add DO LOOP
This commit is contained in:
		
							
								
								
									
										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()
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user