diff --git a/builtins.go b/builtins.go index 30b4cb3..e45e52c 100644 --- a/builtins.go +++ b/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 { diff --git a/eval.go b/eval.go index ede9315..bdc4437 100644 --- a/eval.go +++ b/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,40 +19,31 @@ 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? - 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 { + 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 + 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) 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? - 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,9 +69,7 @@ func (c *Context) Eval(line string) error { } word = []byte{} default: - if !comment { - word = append(word, line[i]) - } + word = append(word, line[i]) } } diff --git a/flags.go b/flags.go new file mode 100644 index 0000000..44501d5 --- /dev/null +++ b/flags.go @@ -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 +} diff --git a/main.go b/main.go index ed7808a..779aae5 100644 --- a/main.go +++ b/main.go @@ -18,14 +18,20 @@ func main() { Dictionary: dict, Stack: &stack, RStack: &rstack, - Immediate: true, - Words: []string{}, + 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") } } }