abstract flags, implement comments as words, fix some bugs

master
David Ashby 2 years ago
parent 7c76bc49af
commit 1a229d5ddb
  1. 43
      builtins.go
  2. 51
      eval.go
  3. 12
      flags.go
  4. 29
      main.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 {

@ -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
case ' ':
sword := strings.TrimSpace(string(word))
if len(word) == 0 {
if line[i] == '(' {
comment = true
continue
}
comment = false
// empty space, just continue...
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
}
}
// 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 len(word) == 0 || comment {
// empty space, just continue...
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
}
@ -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])
}
}

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

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

Loading…
Cancel
Save