abstract flags, implement comments as words, fix some bugs

This commit is contained in:
David 2021-02-15 19:24:04 -05:00
parent 7c76bc49af
commit 1a229d5ddb
4 changed files with 85 additions and 52 deletions

View File

@ -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
View File

@ -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
View 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
View File

@ -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")
} }
} }
} }