abstract flags, implement comments as words, fix some bugs
This commit is contained in:
parent
7c76bc49af
commit
1a229d5ddb
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 {
|
||||
|
53
eval.go
53
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])
|
||||
}
|
||||
}
|
||||
|
||||
|
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,
|
||||
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…
Reference in New Issue
Block a user