initial commit; we have evaluation!
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					prosper
 | 
				
			||||||
 | 
					.DS_Store
 | 
				
			||||||
							
								
								
									
										186
									
								
								builtins.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								builtins.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,186 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Builtins is a handy holder for our various default words
 | 
				
			||||||
 | 
					type Builtins struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Add sums the top two numbers on the stack and pushes the result
 | 
				
			||||||
 | 
					func (b *Builtins) Add(s *Stack) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							r1, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							r2, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s.Push(r1 + r2)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Sub performs NOS - TOS and pushes the result
 | 
				
			||||||
 | 
					func (b *Builtins) Sub(s *Stack) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							r1, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							r2, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s.Push(r2 - r1)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mul multiplies the two numbers on the top of the stack and pushes the result
 | 
				
			||||||
 | 
					func (b *Builtins) Mul(s *Stack) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							r1, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							r2, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s.Push(r2 * r1)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Div performs NOS/TOS and pushes the (integer!) result
 | 
				
			||||||
 | 
					func (b *Builtins) Div(s *Stack) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							r1, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							r2, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s.Push(r2 / r1)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Print pops the stack and outputs it to stdout
 | 
				
			||||||
 | 
					func (b *Builtins) Print(s *Stack) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							r1, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							fmt.Print(r1, " ")
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Dup pops the stack, then pushes two copies onto the stack
 | 
				
			||||||
 | 
					func (b *Builtins) Dup(s *Stack) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							r1, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s.Push(r1)
 | 
				
			||||||
 | 
							s.Push(r1)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Swap inverts the order of TOS and NOS
 | 
				
			||||||
 | 
					func (b *Builtins) Swap(s *Stack) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							r1, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							r2, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s.Push(r1)
 | 
				
			||||||
 | 
							s.Push(r2)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Over duplicates NOS to TOS, resulting in NOS TOS NOS
 | 
				
			||||||
 | 
					func (b *Builtins) Over(s *Stack) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							r1, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							r2, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s.Push(r2)
 | 
				
			||||||
 | 
							s.Push(r1)
 | 
				
			||||||
 | 
							s.Push(r2)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Drop simply discards TOS
 | 
				
			||||||
 | 
					func (b *Builtins) Drop(s *Stack) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							_, err := s.Pop()
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Rot cycles the first three items on the stack: TOS 1 2 3 -> TOS 3 1 2
 | 
				
			||||||
 | 
					func (b *Builtins) Rot(s *Stack) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							r1, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							r2, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							r3, err := s.Pop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s.Push(r2)
 | 
				
			||||||
 | 
							s.Push(r1)
 | 
				
			||||||
 | 
							s.Push(r3)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Words outputs a list of all known words in the dictionary
 | 
				
			||||||
 | 
					func (b *Builtins) Words(d Dictionary) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							for n := range d {
 | 
				
			||||||
 | 
								fmt.Printf("%s ", n)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CR prints a newline to standard out
 | 
				
			||||||
 | 
					func (b *Builtins) CR() func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							fmt.Print("\n")
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Debug prints the stack without modifying it
 | 
				
			||||||
 | 
					func (b *Builtins) Debug(s *Stack) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							fmt.Print(s.values, " ")
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										94
									
								
								eval.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								eval.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Context is a set of Dictionary + Stack representing a runtime environment
 | 
				
			||||||
 | 
					type Context struct {
 | 
				
			||||||
 | 
						Dictionary Dictionary
 | 
				
			||||||
 | 
						Stack      *Stack
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Eval evaulates a given line, recursively descending into given words as needed
 | 
				
			||||||
 | 
					func (c *Context) Eval(line string) error {
 | 
				
			||||||
 | 
						// state
 | 
				
			||||||
 | 
						var word []byte
 | 
				
			||||||
 | 
						var comment bool
 | 
				
			||||||
 | 
						immediate := true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := 0; i < len(line); i = i + 1 {
 | 
				
			||||||
 | 
							switch line[i] {
 | 
				
			||||||
 | 
							case '(', ')': // comments
 | 
				
			||||||
 | 
								if len(word) == 0 {
 | 
				
			||||||
 | 
									if line[i] == '(' {
 | 
				
			||||||
 | 
										comment = true
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									comment = false
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									word = append(word, line[i])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case ':', ';': // COMPILE/IMMEDIATE mode swapping
 | 
				
			||||||
 | 
								if len(word) == 0 {
 | 
				
			||||||
 | 
									if line[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]+" ")
 | 
				
			||||||
 | 
										word = []byte{}
 | 
				
			||||||
 | 
										immediate = true
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										word = append(word, line[i])
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case ' ':
 | 
				
			||||||
 | 
								if !immediate { // continue building our subroutine if we're not in immediate mode...
 | 
				
			||||||
 | 
									word = append(word, line[i])
 | 
				
			||||||
 | 
									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.
 | 
				
			||||||
 | 
									c.Stack.Push(int)
 | 
				
			||||||
 | 
									word = []byte{}
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// it wasn't a number. Is it a word we know?
 | 
				
			||||||
 | 
								w, err := c.Dictionary.GetWord(string(word))
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return fmt.Errorf("could not parse %s; %v", w.Name, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if w.Impl != nil {
 | 
				
			||||||
 | 
									// we have an implementation for that word. Run it.
 | 
				
			||||||
 | 
									err := w.Impl()
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									word = []byte{}
 | 
				
			||||||
 | 
								} else if w.Source != "" {
 | 
				
			||||||
 | 
									// user-defined word; let's descend...
 | 
				
			||||||
 | 
									err := c.Eval(w.Source)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									word = []byte{}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								if !comment {
 | 
				
			||||||
 | 
									word = append(word, line[i])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										53
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						stack := Stack{values: []int{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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("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("CR", b.CR(), "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c := Context{
 | 
				
			||||||
 | 
							Dictionary: dict,
 | 
				
			||||||
 | 
							Stack:      &stack,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reader := bufio.NewReader(os.Stdin)
 | 
				
			||||||
 | 
						fmt.Print("prosper")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// read loop
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							fmt.Print("\n> ")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							line, err := reader.ReadString('\n')
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								fmt.Println(err.Error())
 | 
				
			||||||
 | 
								os.Exit(1)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							line = strings.TrimSpace(line)
 | 
				
			||||||
 | 
							err = c.Eval(line + " ") // append a space to make sure we always close out our parse loop
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								fmt.Printf("error in evaluation: %v\n", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							fmt.Print("ok")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								mem.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								mem.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Memory provides an addressable map of integer cells
 | 
				
			||||||
 | 
					type Memory struct {
 | 
				
			||||||
 | 
						intern   map[int]int
 | 
				
			||||||
 | 
						nextFree int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Read takes a starting address and a count of cells to read after it;
 | 
				
			||||||
 | 
					// (0, 2) reads the first two cells out of memory, for example.
 | 
				
			||||||
 | 
					func (m *Memory) Read(addr int, count int) []int {
 | 
				
			||||||
 | 
						r := []int{}
 | 
				
			||||||
 | 
						for i := 0; i < count; i++ {
 | 
				
			||||||
 | 
							r = append(r, m.intern[addr+i])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return r
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Write inserts the given values into memory, overwriting any existing contents,
 | 
				
			||||||
 | 
					// starting at the provided memory address and incrementing upward.
 | 
				
			||||||
 | 
					func (m *Memory) Write(addr int, values []int) error {
 | 
				
			||||||
 | 
						if addr < 0 {
 | 
				
			||||||
 | 
							return fmt.Errorf("addr out of range")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i := range values {
 | 
				
			||||||
 | 
							m.intern[addr+i] = values[i]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// we've written past our marker, note that
 | 
				
			||||||
 | 
						if m.nextFree < addr+len(values) {
 | 
				
			||||||
 | 
							m.nextFree = addr + len(values)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NextFreeAddress provides the furthest "out" address, beyond which is uninitialized memory.
 | 
				
			||||||
 | 
					// Old memory is never "reclaimed", even if the program manually 0s it out.
 | 
				
			||||||
 | 
					// If you want to build your own GC, knock yourself out.
 | 
				
			||||||
 | 
					func (m *Memory) NextFreeAddress() int {
 | 
				
			||||||
 | 
						return m.nextFree
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										69
									
								
								mem_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								mem_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestMemWrite(t *testing.T) {
 | 
				
			||||||
 | 
						m := Memory{
 | 
				
			||||||
 | 
							intern: make(map[int]int),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// write in some memory, not starting at the head
 | 
				
			||||||
 | 
						err := m.Write(1, []int{1, 2, 3})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Log(err.Error())
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// only three values in memory
 | 
				
			||||||
 | 
						if len(m.intern) != 3 {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if m.NextFreeAddress() != 4 {
 | 
				
			||||||
 | 
							t.Log("expected nextFree to be 4, got", m.nextFree)
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// can't write to negative addresses
 | 
				
			||||||
 | 
						if m.Write(-1, []int{1}) == nil {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestMemRead(t *testing.T) {
 | 
				
			||||||
 | 
						m := Memory{
 | 
				
			||||||
 | 
							intern: map[int]int{0: 1, 1: 2, 2: 3, 100: 101},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Read two known locations
 | 
				
			||||||
 | 
						r := m.Read(0, 2)
 | 
				
			||||||
 | 
						if len(r) != 2 {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if r[0] != 1 || r[1] != 2 {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Read past known memory
 | 
				
			||||||
 | 
						r2 := m.Read(100, 2)
 | 
				
			||||||
 | 
						if len(r2) != 2 {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if r2[0] != 101 || r2[1] != 0 {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// read empty memory
 | 
				
			||||||
 | 
						m2 := Memory{
 | 
				
			||||||
 | 
							intern: map[int]int{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						r3 := m2.Read(0, 100)
 | 
				
			||||||
 | 
						if len(r3) != 100 {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i := range r3 {
 | 
				
			||||||
 | 
							if r3[i] != 0 {
 | 
				
			||||||
 | 
								t.Fail()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								stack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								stack.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Stack is a stack of integers with no defined max depth
 | 
				
			||||||
 | 
					type Stack struct {
 | 
				
			||||||
 | 
						values []int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Pop returns the top of the stack
 | 
				
			||||||
 | 
					func (s *Stack) Pop() (int, error) {
 | 
				
			||||||
 | 
						if len(s.values) == 0 {
 | 
				
			||||||
 | 
							return 0, fmt.Errorf("stack empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						i := s.values[0]
 | 
				
			||||||
 | 
						s.values = s.values[1:]
 | 
				
			||||||
 | 
						return i, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Push adds a value to the top of the stack
 | 
				
			||||||
 | 
					func (s *Stack) Push(i int) {
 | 
				
			||||||
 | 
						s.values = append([]int{i}, s.values...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										44
									
								
								stack_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								stack_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestStackPop(t *testing.T) {
 | 
				
			||||||
 | 
						s := Stack{
 | 
				
			||||||
 | 
							values: []int{1, 2, 3},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						i, err := s.Pop()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Log(err.Error())
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if i != 1 {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(s.values) != 2 {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						s = Stack{
 | 
				
			||||||
 | 
							values: []int{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						i, err = s.Pop()
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							t.Log("expected error")
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if i != 0 {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestStackPush(t *testing.T) {
 | 
				
			||||||
 | 
						s := Stack{
 | 
				
			||||||
 | 
							values: []int{1, 2, 3},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						s.Push(4)
 | 
				
			||||||
 | 
						if len(s.values) != 4 {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if s.values[0] != 4 || s.values[1] != 1 {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										33
									
								
								words.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								words.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Dictionary is a simple map of names to words
 | 
				
			||||||
 | 
					type Dictionary map[string]Word
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A Word defines a subroutine
 | 
				
			||||||
 | 
					type Word struct {
 | 
				
			||||||
 | 
						Name   string
 | 
				
			||||||
 | 
						Impl   func() error
 | 
				
			||||||
 | 
						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) {
 | 
				
			||||||
 | 
						d[name] = Word{
 | 
				
			||||||
 | 
							Name:   name,
 | 
				
			||||||
 | 
							Impl:   impl,
 | 
				
			||||||
 | 
							Source: source,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetWord returns a word from the dictionary or an error if it's undefined
 | 
				
			||||||
 | 
					func (d Dictionary) GetWord(name string) (Word, error) {
 | 
				
			||||||
 | 
						w, ok := d[name]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return Word{
 | 
				
			||||||
 | 
								Name: name,
 | 
				
			||||||
 | 
							}, fmt.Errorf("no word found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return w, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										22
									
								
								words_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								words_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDictionary(t *testing.T) {
 | 
				
			||||||
 | 
						d := Dictionary{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						d.AddWord("INC", nil, "1 + ")
 | 
				
			||||||
 | 
						w, err := d.GetWord("INC")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if w.Name != "INC" {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if w.Impl != nil {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if w.Source != "1 +" {
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user