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")
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
		*f = false
 | 
			
		||||
		c.Flags.SetFlag("Immediate", false)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -20,7 +20,26 @@ func (b *Builtins) Colon(f *bool) func() error {
 | 
			
		||||
func (b *Builtins) Semicolon(c *Context) func() error {
 | 
			
		||||
	return func() error {
 | 
			
		||||
		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
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
func (b *Builtins) Emit(s *Stack) 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
 | 
			
		||||
func (b *Builtins) Quit() func() error {
 | 
			
		||||
	return func() error {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										41
									
								
								eval.go
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								eval.go
									
									
									
									
									
								
							@@ -6,12 +6,12 @@ import (
 | 
			
		||||
	"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 {
 | 
			
		||||
	Dictionary Dictionary
 | 
			
		||||
	Stack      *Stack
 | 
			
		||||
	RStack     *Stack
 | 
			
		||||
	Immediate  bool
 | 
			
		||||
	Flags      Flags
 | 
			
		||||
	Words      []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -19,38 +19,29 @@ type Context struct {
 | 
			
		||||
func (c *Context) Eval(line string) error {
 | 
			
		||||
	// state
 | 
			
		||||
	var word []byte
 | 
			
		||||
	var comment bool
 | 
			
		||||
 | 
			
		||||
	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 ' ':
 | 
			
		||||
			sword := strings.TrimSpace(string(word))
 | 
			
		||||
			if !c.Immediate { // continue building our subroutine if we're not in immediate mode...
 | 
			
		||||
				if len(word) != 0 { // don't add empty words to our list
 | 
			
		||||
					// Was that a word we know?
 | 
			
		||||
			if len(word) == 0 {
 | 
			
		||||
				// empty space, just continue...
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// Is this 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 {
 | 
			
		||||
			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 len(word) == 0 || comment {
 | 
			
		||||
				// empty space, just continue...
 | 
			
		||||
			if !c.Flags.GetFlag("Immediate") && c.Flags.GetFlag("Comment") {
 | 
			
		||||
				word = []byte{}
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -63,7 +54,7 @@ func (c *Context) Eval(line string) error {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 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 {
 | 
			
		||||
				return fmt.Errorf("could not parse %s; %v", w.Name, err)
 | 
			
		||||
			}
 | 
			
		||||
@@ -78,11 +69,9 @@ func (c *Context) Eval(line string) error {
 | 
			
		||||
			}
 | 
			
		||||
			word = []byte{}
 | 
			
		||||
		default:
 | 
			
		||||
			if !comment {
 | 
			
		||||
			word = append(word, line[i])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								main.go
									
									
									
									
									
								
							@@ -18,14 +18,20 @@ func main() {
 | 
			
		||||
		Dictionary: dict,
 | 
			
		||||
		Stack:      &stack,
 | 
			
		||||
		RStack:     &rstack,
 | 
			
		||||
		Immediate:  true,
 | 
			
		||||
		Flags: Flags{
 | 
			
		||||
			"Immediate": true,
 | 
			
		||||
			"Comment":   false,
 | 
			
		||||
		},
 | 
			
		||||
		Words: []string{},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b := &Builtins{}
 | 
			
		||||
	// word definitions
 | 
			
		||||
	dict.AddWord(":", b.Colon(&c.Immediate), nil, false)
 | 
			
		||||
	dict.AddWord(":", b.Colon(&c), nil, false)
 | 
			
		||||
	dict.AddWord(";", b.Semicolon(&c), nil, true)
 | 
			
		||||
	// comments
 | 
			
		||||
	dict.AddWord("(", b.OpenComment(&c), nil, true)
 | 
			
		||||
	dict.AddWord(")", b.CloseComment(&c), nil, true)
 | 
			
		||||
	// math
 | 
			
		||||
	dict.AddWord("+", b.Add(&stack), nil, false)
 | 
			
		||||
	dict.AddWord("-", b.Sub(&stack), nil, false)
 | 
			
		||||
@@ -34,7 +40,7 @@ func main() {
 | 
			
		||||
	// output
 | 
			
		||||
	dict.AddWord(".", b.Print(&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
 | 
			
		||||
	dict.AddWord("=", b.Eq(&stack), nil, false)
 | 
			
		||||
	dict.AddWord("0=", nil, []string{"0", "="}, false)
 | 
			
		||||
@@ -53,6 +59,7 @@ func main() {
 | 
			
		||||
	dict.AddWord("ROT", b.Rot(&stack), nil, false)
 | 
			
		||||
	// debugging
 | 
			
		||||
	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(".R", b.Debug(&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)
 | 
			
		||||
 | 
			
		||||
	reader := bufio.NewReader(os.Stdin)
 | 
			
		||||
	fmt.Print("prosper")
 | 
			
		||||
	fmt.Print("prosper\n")
 | 
			
		||||
 | 
			
		||||
	// read loop
 | 
			
		||||
	for {
 | 
			
		||||
		fmt.Print("\n> ")
 | 
			
		||||
		if c.Flags["Immediate"] {
 | 
			
		||||
			fmt.Print("> ")
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Print("    ")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		line, err := reader.ReadString('\n')
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@@ -83,9 +94,9 @@ func main() {
 | 
			
		||||
			fmt.Printf("bye\n")
 | 
			
		||||
			os.Exit(0)
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
			fmt.Printf("error in evaluation: %v", err)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Print("ok")
 | 
			
		||||
			fmt.Printf("error in evaluation: %v\n", err)
 | 
			
		||||
		} else if c.Flags["Immediate"] {
 | 
			
		||||
			fmt.Print("ok\n")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user