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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -44,6 +47,7 @@ func (b *Builtins) Semicolon(c *Context) func(string) error {
|
|||||||
// OpenComment puts the parser into an "ignore" mode
|
// OpenComment puts the parser into an "ignore" mode
|
||||||
func (b *Builtins) OpenComment(c *Context) func(string) error {
|
func (b *Builtins) OpenComment(c *Context) func(string) error {
|
||||||
return func(next string) error {
|
return func(next string) error {
|
||||||
|
c.Flags.SetFlag("StashImmediate", c.Flags.GetFlag("Immediate"))
|
||||||
for i := 0; i < len(next); i = i + 1 {
|
for i := 0; i < len(next); i = i + 1 {
|
||||||
switch next[i] {
|
switch next[i] {
|
||||||
case ')':
|
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
|
// CloseComment resumes parsing by consuming itself and resetting the immediate flag
|
||||||
func (b *Builtins) CloseComment(c *Context) func(string) error {
|
func (b *Builtins) CloseComment(c *Context) func(string) error {
|
||||||
return func(_ string) error {
|
return func(_ string) error {
|
||||||
c.Flags.SetFlag("Immediate", false)
|
c.Flags.SetFlag("Immediate", c.Flags.GetFlag("StashImmediate"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenQuote consumes text until its closing pair to output
|
// 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 {
|
if out == nil {
|
||||||
out = os.Stdout
|
out = os.Stdout
|
||||||
}
|
}
|
||||||
return func(next string) error {
|
return func(next string) error {
|
||||||
|
c.Flags.SetFlag("StashImmediate", c.Flags.GetFlag("Immediate"))
|
||||||
w := []byte{}
|
w := []byte{}
|
||||||
for i := 0; i < len(next); i = i + 1 {
|
for i := 0; i < len(next); i = i + 1 {
|
||||||
switch next[i] {
|
switch next[i] {
|
||||||
case close:
|
case close:
|
||||||
fmt.Fprint(out, string(w))
|
fmt.Fprint(out, string(w))
|
||||||
j, _ := r.Pop()
|
j, _ := c.RStack.Pop()
|
||||||
r.Push(j + i - 1) // push the end-point onto the stack
|
c.RStack.Push(j + i - 1) // push the end-point onto the stack
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
w = append(w, next[i])
|
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.
|
// CloseQuote consumes itself.
|
||||||
func (b *Builtins) CloseQuote(c *Context) func(string) error {
|
func (b *Builtins) CloseQuote(c *Context) func(string) error {
|
||||||
return func(next string) error {
|
return func(next string) error {
|
||||||
|
c.Flags.SetFlag("Immediate", c.Flags.GetFlag("StashImmediate"))
|
||||||
return nil
|
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.
|
// 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 {
|
func (b *Builtins) Eq(s *Stack) func(string) error {
|
||||||
return func(_ string) error {
|
return func(_ string) error {
|
||||||
@ -755,3 +775,43 @@ func (b *Builtins) Fetch(c *Context) func(string) error {
|
|||||||
return nil
|
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
|
// Context is a set of Dictionary + Stacks + Flags representing a runtime environment
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Dictionary Dictionary
|
Dictionary Dictionary
|
||||||
Stack *Stack // main stack
|
Stack *Stack // main stack
|
||||||
RStack *Stack // "return" stack, for handling loops/backward jumps
|
RStack *Stack // "return" stack, for handling loops/backward jumps
|
||||||
IfStack *Stack // tracks nested branches
|
IfStack *Stack // tracks nested branches
|
||||||
Flags Flags
|
Flags Flags
|
||||||
Words []string
|
Words []string
|
||||||
Memory Memory
|
Memory Memory
|
||||||
|
StringBuffer string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eval evaulates a given line, recursively descending into given words as needed
|
// 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?
|
// Is this a word we know?
|
||||||
w, _ := c.Dictionary.GetWord(sword)
|
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)
|
c.Words = append(c.Words, sword)
|
||||||
word = []byte{}
|
word = []byte{}
|
||||||
continue
|
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{},
|
intern: map[int]int{},
|
||||||
nextFree: 1,
|
nextFree: 1,
|
||||||
},
|
},
|
||||||
|
StringBuffer: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
b := &Builtins{}
|
b := &Builtins{}
|
||||||
@ -38,9 +39,11 @@ func main() {
|
|||||||
dict.AddWord("(", Word{Name: "(", Impl: b.OpenComment(&c), Immediate: true})
|
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.CloseComment(&c), Immediate: true})
|
||||||
dict.AddWord(`\`, Word{Name: `\`, Impl: b.EOLComment(&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.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
|
// math
|
||||||
dict.AddWord("+", Word{Name: "+", Impl: b.Add(&stack)})
|
dict.AddWord("+", Word{Name: "+", Impl: b.Add(&stack)})
|
||||||
dict.AddWord("-", Word{Name: "-", Impl: b.Sub(&stack)})
|
dict.AddWord("-", Word{Name: "-", Impl: b.Sub(&stack)})
|
||||||
|
Loading…
Reference in New Issue
Block a user