abstract flags, implement comments as words, fix some bugs
This commit is contained in:
		
							
								
								
									
										43
									
								
								builtins.go
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								builtins.go
									
									
									
									
									
								
							@@ -9,9 +9,9 @@ type Builtins struct{}
 | 
				
			|||||||
var ErrExit = fmt.Errorf("exit requested")
 | 
					var ErrExit = fmt.Errorf("exit requested")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Colon sets the COMPILE/IMMEDIATE flag to COMPILE
 | 
					// Colon sets the COMPILE/IMMEDIATE flag to COMPILE
 | 
				
			||||||
func (b *Builtins) Colon(f *bool) func() error {
 | 
					func (b *Builtins) Colon(c *Context) func() error {
 | 
				
			||||||
	return func() error {
 | 
						return func() error {
 | 
				
			||||||
		*f = false
 | 
							c.Flags.SetFlag("Immediate", false)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -20,7 +20,26 @@ func (b *Builtins) Colon(f *bool) func() error {
 | 
				
			|||||||
func (b *Builtins) Semicolon(c *Context) func() error {
 | 
					func (b *Builtins) Semicolon(c *Context) func() error {
 | 
				
			||||||
	return func() error {
 | 
						return func() error {
 | 
				
			||||||
		c.Dictionary.AddWord(c.Words[0], nil, c.Words[1:], false)
 | 
							c.Dictionary.AddWord(c.Words[0], nil, c.Words[1:], false)
 | 
				
			||||||
		c.Immediate = true
 | 
							c.Words = []string{}
 | 
				
			||||||
 | 
							c.Flags.SetFlag("Immediate", true)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// OpenComment puts the parser into an "ignore" mode
 | 
				
			||||||
 | 
					func (b *Builtins) OpenComment(c *Context) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							c.Flags.SetFlag("Comment", true)
 | 
				
			||||||
 | 
							c.Flags.SetFlag("Immediate", false)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CloseComment resumes parsing
 | 
				
			||||||
 | 
					func (b *Builtins) CloseComment(c *Context) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							c.Flags.SetFlag("Comment", false)
 | 
				
			||||||
 | 
							c.Flags.SetFlag("Immediate", false)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -303,6 +322,16 @@ func (b *Builtins) Words(d Dictionary) func() error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Flags outputs a list of all flags
 | 
				
			||||||
 | 
					func (b *Builtins) Flags(c Context) func() error {
 | 
				
			||||||
 | 
						return func() error {
 | 
				
			||||||
 | 
							for n := range c.Flags {
 | 
				
			||||||
 | 
								fmt.Printf("%s %v\n", n, c.Flags.GetFlag(n))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Emit outputs the UTF-8 rune for the int on the top of the stack
 | 
					// Emit outputs the UTF-8 rune for the int on the top of the stack
 | 
				
			||||||
func (b *Builtins) Emit(s *Stack) func() error {
 | 
					func (b *Builtins) Emit(s *Stack) func() error {
 | 
				
			||||||
	return func() error {
 | 
						return func() error {
 | 
				
			||||||
@@ -443,14 +472,6 @@ func (b *Builtins) I(s *Stack, r *Stack) func() error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CR prints a newline to standard out
 | 
					 | 
				
			||||||
func (b *Builtins) CR() func() error {
 | 
					 | 
				
			||||||
	return func() error {
 | 
					 | 
				
			||||||
		fmt.Print("\n")
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Quit exits the repl
 | 
					// Quit exits the repl
 | 
				
			||||||
func (b *Builtins) Quit() func() error {
 | 
					func (b *Builtins) Quit() func() error {
 | 
				
			||||||
	return func() error {
 | 
						return func() error {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										53
									
								
								eval.go
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								eval.go
									
									
									
									
									
								
							@@ -6,12 +6,12 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Context is a set of Dictionary + Stacks representing a runtime environment
 | 
					// Context is a set of Dictionary + Stacks + Flags representing a runtime environment
 | 
				
			||||||
type Context struct {
 | 
					type Context struct {
 | 
				
			||||||
	Dictionary Dictionary
 | 
						Dictionary Dictionary
 | 
				
			||||||
	Stack      *Stack
 | 
						Stack      *Stack
 | 
				
			||||||
	RStack     *Stack
 | 
						RStack     *Stack
 | 
				
			||||||
	Immediate  bool
 | 
						Flags      Flags
 | 
				
			||||||
	Words      []string
 | 
						Words      []string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -19,40 +19,31 @@ type Context struct {
 | 
				
			|||||||
func (c *Context) Eval(line string) error {
 | 
					func (c *Context) Eval(line string) error {
 | 
				
			||||||
	// state
 | 
						// state
 | 
				
			||||||
	var word []byte
 | 
						var word []byte
 | 
				
			||||||
	var comment bool
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := 0; i < len(line); i = i + 1 {
 | 
						for i := 0; i < len(line); i = i + 1 {
 | 
				
			||||||
		switch line[i] {
 | 
							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 ' ':
 | 
							case ' ':
 | 
				
			||||||
			sword := strings.TrimSpace(string(word))
 | 
								sword := strings.TrimSpace(string(word))
 | 
				
			||||||
			if !c.Immediate { // continue building our subroutine if we're not in immediate mode...
 | 
								if len(word) == 0 {
 | 
				
			||||||
				if len(word) != 0 { // don't add empty words to our list
 | 
					 | 
				
			||||||
					// Was that a word we know?
 | 
					 | 
				
			||||||
					w, _ := c.Dictionary.GetWord(sword)
 | 
					 | 
				
			||||||
					// check if it's an IMMEDIATE mode toggle word
 | 
					 | 
				
			||||||
					c.Immediate = w.Immediate
 | 
					 | 
				
			||||||
					if !c.Immediate {
 | 
					 | 
				
			||||||
						c.Words = append(c.Words, sword)
 | 
					 | 
				
			||||||
						word = []byte{}
 | 
					 | 
				
			||||||
						continue
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if len(word) == 0 || comment {
 | 
					 | 
				
			||||||
				// empty space, just continue...
 | 
									// empty space, just continue...
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								// Is this a word we know?
 | 
				
			||||||
 | 
								w, _ := c.Dictionary.GetWord(sword)
 | 
				
			||||||
 | 
								// check if it's an IMMEDIATE mode toggle word
 | 
				
			||||||
 | 
								if !c.Flags.GetFlag("Immediate") {
 | 
				
			||||||
 | 
									c.Flags.SetFlag("Immediate", w.Immediate)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if !c.Flags.GetFlag("Immediate") && !c.Flags.GetFlag("Comment") {
 | 
				
			||||||
 | 
									c.Words = append(c.Words, sword)
 | 
				
			||||||
 | 
									word = []byte{}
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if !c.Flags.GetFlag("Immediate") && c.Flags.GetFlag("Comment") {
 | 
				
			||||||
 | 
									word = []byte{}
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			int, err := strconv.Atoi(sword)
 | 
								int, err := strconv.Atoi(sword)
 | 
				
			||||||
			if err == nil {
 | 
								if err == nil {
 | 
				
			||||||
@@ -63,7 +54,7 @@ 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(sword)
 | 
								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)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -78,9 +69,7 @@ func (c *Context) Eval(line string) error {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			word = []byte{}
 | 
								word = []byte{}
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			if !comment {
 | 
								word = append(word, line[i])
 | 
				
			||||||
				word = append(word, line[i])
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								flags.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								flags.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Flags is a simple map of strings to boolean flag fields
 | 
				
			||||||
 | 
					type Flags map[string]bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f Flags) GetFlag(s string) bool {
 | 
				
			||||||
 | 
						return f[s]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f Flags) SetFlag(s string, b bool) {
 | 
				
			||||||
 | 
						f[s] = b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										29
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								main.go
									
									
									
									
									
								
							@@ -18,14 +18,20 @@ func main() {
 | 
				
			|||||||
		Dictionary: dict,
 | 
							Dictionary: dict,
 | 
				
			||||||
		Stack:      &stack,
 | 
							Stack:      &stack,
 | 
				
			||||||
		RStack:     &rstack,
 | 
							RStack:     &rstack,
 | 
				
			||||||
		Immediate:  true,
 | 
							Flags: Flags{
 | 
				
			||||||
		Words:      []string{},
 | 
								"Immediate": true,
 | 
				
			||||||
 | 
								"Comment":   false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Words: []string{},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b := &Builtins{}
 | 
						b := &Builtins{}
 | 
				
			||||||
	// word definitions
 | 
						// word definitions
 | 
				
			||||||
	dict.AddWord(":", b.Colon(&c.Immediate), nil, false)
 | 
						dict.AddWord(":", b.Colon(&c), nil, false)
 | 
				
			||||||
	dict.AddWord(";", b.Semicolon(&c), nil, true)
 | 
						dict.AddWord(";", b.Semicolon(&c), nil, true)
 | 
				
			||||||
 | 
						// comments
 | 
				
			||||||
 | 
						dict.AddWord("(", b.OpenComment(&c), nil, true)
 | 
				
			||||||
 | 
						dict.AddWord(")", b.CloseComment(&c), nil, true)
 | 
				
			||||||
	// math
 | 
						// math
 | 
				
			||||||
	dict.AddWord("+", b.Add(&stack), nil, false)
 | 
						dict.AddWord("+", b.Add(&stack), nil, false)
 | 
				
			||||||
	dict.AddWord("-", b.Sub(&stack), nil, false)
 | 
						dict.AddWord("-", b.Sub(&stack), nil, false)
 | 
				
			||||||
@@ -34,7 +40,7 @@ func main() {
 | 
				
			|||||||
	// output
 | 
						// output
 | 
				
			||||||
	dict.AddWord(".", b.Print(&stack), nil, false)
 | 
						dict.AddWord(".", b.Print(&stack), nil, false)
 | 
				
			||||||
	dict.AddWord("EMIT", b.Emit(&stack), nil, false)
 | 
						dict.AddWord("EMIT", b.Emit(&stack), nil, false)
 | 
				
			||||||
	dict.AddWord("CR", b.CR(), nil, false)
 | 
						dict.AddWord("CR", nil, []string{"10", "EMIT"}, false) // emit a newline
 | 
				
			||||||
	// logic
 | 
						// logic
 | 
				
			||||||
	dict.AddWord("=", b.Eq(&stack), nil, false)
 | 
						dict.AddWord("=", b.Eq(&stack), nil, false)
 | 
				
			||||||
	dict.AddWord("0=", nil, []string{"0", "="}, false)
 | 
						dict.AddWord("0=", nil, []string{"0", "="}, false)
 | 
				
			||||||
@@ -53,6 +59,7 @@ func main() {
 | 
				
			|||||||
	dict.AddWord("ROT", b.Rot(&stack), nil, false)
 | 
						dict.AddWord("ROT", b.Rot(&stack), nil, false)
 | 
				
			||||||
	// debugging
 | 
						// debugging
 | 
				
			||||||
	dict.AddWord("WORDS", b.Words(dict), nil, false)
 | 
						dict.AddWord("WORDS", b.Words(dict), nil, false)
 | 
				
			||||||
 | 
						dict.AddWord("FLAGS", b.Flags(c), nil, false)
 | 
				
			||||||
	dict.AddWord(".S", b.Debug(&stack), nil, false)
 | 
						dict.AddWord(".S", b.Debug(&stack), nil, false)
 | 
				
			||||||
	dict.AddWord(".R", b.Debug(&rstack), nil, false)
 | 
						dict.AddWord(".R", b.Debug(&rstack), nil, false)
 | 
				
			||||||
	dict.AddWord("R>", b.RFrom(&stack, &rstack), nil, false)
 | 
						dict.AddWord("R>", b.RFrom(&stack, &rstack), nil, false)
 | 
				
			||||||
@@ -66,11 +73,15 @@ func main() {
 | 
				
			|||||||
	dict.AddWord("BYE", b.Quit(), nil, false)
 | 
						dict.AddWord("BYE", b.Quit(), nil, false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	reader := bufio.NewReader(os.Stdin)
 | 
						reader := bufio.NewReader(os.Stdin)
 | 
				
			||||||
	fmt.Print("prosper")
 | 
						fmt.Print("prosper\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// read loop
 | 
						// read loop
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		fmt.Print("\n> ")
 | 
							if c.Flags["Immediate"] {
 | 
				
			||||||
 | 
								fmt.Print("> ")
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								fmt.Print("    ")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		line, err := reader.ReadString('\n')
 | 
							line, err := reader.ReadString('\n')
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@@ -83,9 +94,9 @@ func main() {
 | 
				
			|||||||
			fmt.Printf("bye\n")
 | 
								fmt.Printf("bye\n")
 | 
				
			||||||
			os.Exit(0)
 | 
								os.Exit(0)
 | 
				
			||||||
		} else if err != nil {
 | 
							} else if err != nil {
 | 
				
			||||||
			fmt.Printf("error in evaluation: %v", err)
 | 
								fmt.Printf("error in evaluation: %v\n", err)
 | 
				
			||||||
		} else {
 | 
							} else if c.Flags["Immediate"] {
 | 
				
			||||||
			fmt.Print("ok")
 | 
								fmt.Print("ok\n")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user