fix some string bugs and make external-file-loading work
This commit is contained in:
		
							
								
								
									
										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
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								eval.go
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								eval.go
									
									
									
									
									
								
							@@ -15,6 +15,7 @@ type Context struct {
 | 
			
		||||
	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)})
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user