fix some string bugs and make external-file-loading work
This commit is contained in:
parent
7291ca86c8
commit
5e782bc728
68
builtins.go
68
builtins.go
@ -1,9 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
@ -44,6 +47,7 @@ func (b *Builtins) Semicolon(c *Context) func(string) error {
|
||||
// OpenComment puts the parser into an "ignore" mode
|
||||
func (b *Builtins) OpenComment(c *Context) func(string) error {
|
||||
return func(next string) error {
|
||||
c.Flags.SetFlag("StashImmediate", c.Flags.GetFlag("Immediate"))
|
||||
for i := 0; i < len(next); i = i + 1 {
|
||||
switch next[i] {
|
||||
case ')':
|
||||
@ -70,24 +74,25 @@ func (b *Builtins) EOLComment(c *Context) func(string) error {
|
||||
// CloseComment resumes parsing by consuming itself and resetting the immediate flag
|
||||
func (b *Builtins) CloseComment(c *Context) func(string) error {
|
||||
return func(_ string) error {
|
||||
c.Flags.SetFlag("Immediate", false)
|
||||
c.Flags.SetFlag("Immediate", c.Flags.GetFlag("StashImmediate"))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OpenQuote consumes text until its closing pair to output
|
||||
func (b *Builtins) OpenQuote(out io.Writer, r *Stack, close byte) func(string) error {
|
||||
func (b *Builtins) OpenQuote(out io.Writer, c *Context, close byte) func(string) error {
|
||||
if out == nil {
|
||||
out = os.Stdout
|
||||
}
|
||||
return func(next string) error {
|
||||
c.Flags.SetFlag("StashImmediate", c.Flags.GetFlag("Immediate"))
|
||||
w := []byte{}
|
||||
for i := 0; i < len(next); i = i + 1 {
|
||||
switch next[i] {
|
||||
case close:
|
||||
fmt.Fprint(out, string(w))
|
||||
j, _ := r.Pop()
|
||||
r.Push(j + i - 1) // push the end-point onto the stack
|
||||
j, _ := c.RStack.Pop()
|
||||
c.RStack.Push(j + i - 1) // push the end-point onto the stack
|
||||
return nil
|
||||
default:
|
||||
w = append(w, next[i])
|
||||
@ -101,10 +106,25 @@ func (b *Builtins) OpenQuote(out io.Writer, r *Stack, close byte) func(string) e
|
||||
// CloseQuote consumes itself.
|
||||
func (b *Builtins) CloseQuote(c *Context) func(string) error {
|
||||
return func(next string) error {
|
||||
c.Flags.SetFlag("Immediate", c.Flags.GetFlag("StashImmediate"))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// StringBuffer consumes text until its closing byte, then puts that value into the context's stringbuffer
|
||||
func (b *Builtins) StringBuffer(c *Context, close byte) func(string) error {
|
||||
return func(next string) error {
|
||||
var buff bytes.Buffer
|
||||
err := b.OpenQuote(&buff, c, '"')(next)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s, err := ioutil.ReadAll(&buff)
|
||||
c.StringBuffer = string(s)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Eq compares TOS and NOS and puts -1 on the stack if they're equal, 0 otherwise.
|
||||
func (b *Builtins) Eq(s *Stack) func(string) error {
|
||||
return func(_ string) error {
|
||||
@ -755,3 +775,43 @@ func (b *Builtins) Fetch(c *Context) func(string) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Load loads a library into the current working environment
|
||||
func (b *Builtins) Load(c *Context) func(string) error {
|
||||
return func(_ string) error {
|
||||
filename := strings.TrimSpace(c.StringBuffer)
|
||||
if filename == "" {
|
||||
return fmt.Errorf(`stringbuffer empty; try S" filename.prsp" LOAD`)
|
||||
}
|
||||
|
||||
// store stacks
|
||||
s, r, i := c.Stack, c.RStack, c.IfStack
|
||||
c.Stack = &Stack{values: []int{}}
|
||||
c.RStack = &Stack{values: []int{}}
|
||||
c.IfStack = &Stack{values: []int{}}
|
||||
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
for {
|
||||
scan := scanner.Scan()
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
err = c.Eval(line + " ") // append a space to make sure we always close out our parse loop
|
||||
if err != nil && !errors.Is(err, ErrExit) {
|
||||
return fmt.Errorf("error in library evaluation: %v", err)
|
||||
}
|
||||
if scan == false {
|
||||
break
|
||||
}
|
||||
}
|
||||
// restore stacks
|
||||
c.Stack = s
|
||||
c.RStack = r
|
||||
c.IfStack = i
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
22
eval.go
22
eval.go
@ -8,13 +8,14 @@ import (
|
||||
|
||||
// Context is a set of Dictionary + Stacks + Flags representing a runtime environment
|
||||
type Context struct {
|
||||
Dictionary Dictionary
|
||||
Stack *Stack // main stack
|
||||
RStack *Stack // "return" stack, for handling loops/backward jumps
|
||||
IfStack *Stack // tracks nested branches
|
||||
Flags Flags
|
||||
Words []string
|
||||
Memory Memory
|
||||
Dictionary Dictionary
|
||||
Stack *Stack // main stack
|
||||
RStack *Stack // "return" stack, for handling loops/backward jumps
|
||||
IfStack *Stack // tracks nested branches
|
||||
Flags Flags
|
||||
Words []string
|
||||
Memory Memory
|
||||
StringBuffer string
|
||||
}
|
||||
|
||||
// Eval evaulates a given line, recursively descending into given words as needed
|
||||
@ -32,12 +33,9 @@ func (c *Context) Eval(line string) error {
|
||||
}
|
||||
// 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") {
|
||||
// if we're not in immedate mode, and the word isn't immediate, add word to buffer and continue parsing
|
||||
if !c.Flags.GetFlag("Immediate") && !w.Immediate {
|
||||
c.Words = append(c.Words, sword)
|
||||
word = []byte{}
|
||||
continue
|
||||
|
3
examples/example.prsp
Normal file
3
examples/example.prsp
Normal file
@ -0,0 +1,3 @@
|
||||
.( Loading )
|
||||
: CUBE DUP DUP * * ;
|
||||
: SQUARE DUP * ;
|
7
main.go
7
main.go
@ -28,6 +28,7 @@ func main() {
|
||||
intern: map[int]int{},
|
||||
nextFree: 1,
|
||||
},
|
||||
StringBuffer: "",
|
||||
}
|
||||
|
||||
b := &Builtins{}
|
||||
@ -38,9 +39,11 @@ func main() {
|
||||
dict.AddWord("(", Word{Name: "(", Impl: b.OpenComment(&c), Immediate: true})
|
||||
dict.AddWord(")", Word{Name: ")", Impl: b.CloseComment(&c), Immediate: true})
|
||||
dict.AddWord(`\`, Word{Name: `\`, Impl: b.EOLComment(&c), Immediate: true})
|
||||
dict.AddWord(`."`, Word{Name: `."`, Impl: b.OpenQuote(os.Stdout, &rstack, '"')})
|
||||
dict.AddWord(`."`, Word{Name: `."`, Impl: b.OpenQuote(os.Stdout, &c, '"')})
|
||||
dict.AddWord(`"`, Word{Name: `"`, Impl: b.CloseQuote(&c)})
|
||||
dict.AddWord(`.(`, Word{Name: `.(`, Impl: b.OpenQuote(os.Stdout, &rstack, ')'), Immediate: true})
|
||||
dict.AddWord(`.(`, Word{Name: `.(`, Impl: b.OpenQuote(os.Stdout, &c, ')'), Immediate: true})
|
||||
dict.AddWord(`S"`, Word{Name: `S"`, Impl: b.StringBuffer(&c, '"')})
|
||||
dict.AddWord("LOAD", Word{Name: "LOAD", Impl: b.Load(&c)})
|
||||
// math
|
||||
dict.AddWord("+", Word{Name: "+", Impl: b.Add(&stack)})
|
||||
dict.AddWord("-", Word{Name: "-", Impl: b.Sub(&stack)})
|
||||
|
Loading…
Reference in New Issue
Block a user