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
 | 
					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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										8
									
								
								eval.go
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								eval.go
									
									
									
									
									
								
							@@ -15,6 +15,7 @@ type Context struct {
 | 
				
			|||||||
	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)})
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user