2021-02-14 02:09:56 +00:00
|
|
|
package main
|
|
|
|
|
2021-02-20 17:09:04 +00:00
|
|
|
import (
|
2021-02-27 01:49:10 +00:00
|
|
|
"bufio"
|
|
|
|
"bytes"
|
2021-02-20 20:52:27 +00:00
|
|
|
"errors"
|
2021-02-20 17:09:04 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2021-02-27 01:49:10 +00:00
|
|
|
"io/ioutil"
|
2021-02-20 17:09:04 +00:00
|
|
|
"os"
|
2021-02-25 01:19:25 +00:00
|
|
|
"strings"
|
2021-02-20 17:09:04 +00:00
|
|
|
)
|
2021-02-14 02:09:56 +00:00
|
|
|
|
2021-02-20 23:59:41 +00:00
|
|
|
// FALSE is a flag for, what else, false
|
2021-02-20 23:03:02 +00:00
|
|
|
var FALSE = 0
|
2021-02-20 23:59:41 +00:00
|
|
|
|
|
|
|
// TRUE is a flag for, what else, true.
|
|
|
|
// It's -1 because historically, FORTHs would define TRUE as "all bits set to 1 in a cell",
|
|
|
|
// and interpreting that literally flips the sign flag in a signed integer.
|
|
|
|
// More generally, TRUE is defined as "not FALSE" for most purposes, and any non-zero integer will work.
|
2021-02-20 23:03:02 +00:00
|
|
|
var TRUE = -1
|
|
|
|
|
2021-02-14 02:09:56 +00:00
|
|
|
// Builtins is a handy holder for our various default words
|
|
|
|
type Builtins struct{}
|
|
|
|
|
2021-02-14 02:37:56 +00:00
|
|
|
// ErrExit is a special sentinel value to cease computation and quit
|
|
|
|
var ErrExit = fmt.Errorf("exit requested")
|
|
|
|
|
2021-02-15 20:35:40 +00:00
|
|
|
// Colon sets the COMPILE/IMMEDIATE flag to COMPILE
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Colon(c *Context) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-16 00:24:04 +00:00
|
|
|
c.Flags.SetFlag("Immediate", false)
|
2021-02-15 20:35:40 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Semicolon sets the COMPILE/IMMEDIATE flag back to IMMEDIATE and adds the defintion to the dictionary
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Semicolon(c *Context) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-20 20:52:27 +00:00
|
|
|
c.Dictionary.AddWord(c.Words[0], Word{Name: c.Words[0], Source: c.Words[1:]})
|
2021-02-16 00:24:04 +00:00
|
|
|
c.Words = []string{}
|
|
|
|
c.Flags.SetFlag("Immediate", true)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// OpenComment puts the parser into an "ignore" mode
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) OpenComment(c *Context) func(string) error {
|
|
|
|
return func(next string) error {
|
2021-02-27 01:49:10 +00:00
|
|
|
c.Flags.SetFlag("StashImmediate", c.Flags.GetFlag("Immediate"))
|
2021-02-25 01:19:25 +00:00
|
|
|
for i := 0; i < len(next); i = i + 1 {
|
|
|
|
switch next[i] {
|
|
|
|
case ')':
|
|
|
|
j, _ := c.RStack.Pop()
|
|
|
|
c.RStack.Push(j + i - 1) // push the end-point onto the stack
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2021-02-16 00:24:04 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-26 03:09:59 +00:00
|
|
|
// EOLComment discards the rest of the parsed line
|
|
|
|
func (b *Builtins) EOLComment(c *Context) func(string) error {
|
|
|
|
return func(next string) error {
|
|
|
|
j, _ := c.RStack.Pop()
|
|
|
|
c.RStack.Push(j + len(next)) // push the end-point onto the stack
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-25 01:44:56 +00:00
|
|
|
// CloseComment resumes parsing by consuming itself and resetting the immediate flag
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) CloseComment(c *Context) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-27 01:49:10 +00:00
|
|
|
c.Flags.SetFlag("Immediate", c.Flags.GetFlag("StashImmediate"))
|
2021-02-15 20:35:40 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-25 01:44:56 +00:00
|
|
|
// OpenQuote consumes text until its closing pair to output
|
2021-02-27 01:49:10 +00:00
|
|
|
func (b *Builtins) OpenQuote(out io.Writer, c *Context, close byte) func(string) error {
|
2021-02-25 01:44:56 +00:00
|
|
|
if out == nil {
|
|
|
|
out = os.Stdout
|
|
|
|
}
|
|
|
|
return func(next string) error {
|
2021-02-27 01:49:10 +00:00
|
|
|
c.Flags.SetFlag("StashImmediate", c.Flags.GetFlag("Immediate"))
|
2021-02-25 01:44:56 +00:00
|
|
|
w := []byte{}
|
|
|
|
for i := 0; i < len(next); i = i + 1 {
|
|
|
|
switch next[i] {
|
|
|
|
case close:
|
|
|
|
fmt.Fprint(out, string(w))
|
2021-02-27 01:49:10 +00:00
|
|
|
j, _ := c.RStack.Pop()
|
|
|
|
c.RStack.Push(j + i - 1) // push the end-point onto the stack
|
2021-02-25 01:44:56 +00:00
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
w = append(w, next[i])
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// CloseQuote consumes itself.
|
|
|
|
func (b *Builtins) CloseQuote(c *Context) func(string) error {
|
|
|
|
return func(next string) error {
|
2021-02-27 01:49:10 +00:00
|
|
|
c.Flags.SetFlag("Immediate", c.Flags.GetFlag("StashImmediate"))
|
2021-02-25 01:44:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-27 01:49:10 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-14 17:20:34 +00:00
|
|
|
// Eq compares TOS and NOS and puts -1 on the stack if they're equal, 0 otherwise.
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Eq(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if tos == nos {
|
2021-02-20 23:03:02 +00:00
|
|
|
s.Push(TRUE)
|
2021-02-14 17:20:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
2021-02-20 23:03:02 +00:00
|
|
|
s.Push(FALSE)
|
2021-02-14 17:20:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NEq compares TOS and NOS and puts -1 on the stack if they're not equal, 0 otherwise.
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) NEq(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if tos != nos {
|
2021-02-20 23:03:02 +00:00
|
|
|
s.Push(TRUE)
|
2021-02-14 17:20:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
2021-02-20 23:03:02 +00:00
|
|
|
s.Push(FALSE)
|
2021-02-14 17:20:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lt compares TOS and NOS and puts -1 on the stack if TOS is less than NOS, 0 otherwise.
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Lt(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-20 23:03:02 +00:00
|
|
|
if nos < tos {
|
|
|
|
s.Push(TRUE)
|
2021-02-14 17:20:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
2021-02-20 23:03:02 +00:00
|
|
|
s.Push(FALSE)
|
2021-02-14 17:20:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gt compares TOS and NOS and puts -1 on the stack if TOS is greater than NOS, 0 otherwise.
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Gt(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-20 23:03:02 +00:00
|
|
|
if nos > tos {
|
|
|
|
s.Push(TRUE)
|
2021-02-14 17:20:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
2021-02-20 23:03:02 +00:00
|
|
|
s.Push(FALSE)
|
2021-02-14 17:20:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// LtEq compares TOS and NOS and puts -1 on the stack if TOS is less than or equal to NOS, 0 otherwise.
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) LtEq(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-20 23:03:02 +00:00
|
|
|
if nos <= tos {
|
|
|
|
s.Push(TRUE)
|
2021-02-14 17:20:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
2021-02-20 23:03:02 +00:00
|
|
|
s.Push(FALSE)
|
2021-02-14 17:20:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GtEq compares TOS and NOS and puts -1 on the stack if TOS is greater than or equal to NOS, 0 otherwise.
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) GtEq(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-20 23:03:02 +00:00
|
|
|
if nos >= tos {
|
|
|
|
s.Push(TRUE)
|
2021-02-14 17:20:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
2021-02-20 23:03:02 +00:00
|
|
|
s.Push(FALSE)
|
2021-02-14 17:20:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-14 02:09:56 +00:00
|
|
|
// Add sums the top two numbers on the stack and pushes the result
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Add(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
nos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
s.Push(tos + nos)
|
2021-02-14 02:09:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sub performs NOS - TOS and pushes the result
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Sub(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
nos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
s.Push(nos - tos)
|
2021-02-14 02:09:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mul multiplies the two numbers on the top of the stack and pushes the result
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Mul(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
nos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
s.Push(nos * tos)
|
2021-02-14 02:09:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Div performs NOS/TOS and pushes the (integer!) result
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Div(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
nos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
s.Push(nos / tos)
|
2021-02-14 02:09:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-20 23:03:02 +00:00
|
|
|
// Mod performs NOS%TOS and pushes the (integer!) modulo result
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Mod(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-20 23:03:02 +00:00
|
|
|
tos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.Push(nos % tos)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-20 17:09:04 +00:00
|
|
|
// Print pops the stack and outputs it to the writer with a trailing space (defaults to stdout)
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Print(out io.Writer, s *Stack) func(string) error {
|
2021-02-20 17:09:04 +00:00
|
|
|
if out == nil {
|
|
|
|
out = os.Stdout
|
|
|
|
}
|
2021-02-25 01:19:25 +00:00
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-20 17:09:04 +00:00
|
|
|
fmt.Fprint(out, tos, " ")
|
2021-02-14 02:09:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dup pops the stack, then pushes two copies onto the stack
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Dup(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
s.Push(tos)
|
|
|
|
s.Push(tos)
|
2021-02-14 02:09:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Swap inverts the order of TOS and NOS
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Swap(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
nos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
s.Push(tos)
|
|
|
|
s.Push(nos)
|
2021-02-14 02:09:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Over duplicates NOS to TOS, resulting in NOS TOS NOS
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Over(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-15 01:32:38 +00:00
|
|
|
nos, err := s.Pick(1)
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
s.Push(nos)
|
2021-02-14 02:09:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drop simply discards TOS
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Drop(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 02:09:56 +00:00
|
|
|
_, err := s.Pop()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rot cycles the first three items on the stack: TOS 1 2 3 -> TOS 3 1 2
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Rot(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
nos, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
p3, err := s.Pop()
|
2021-02-14 02:09:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
s.Push(nos)
|
|
|
|
s.Push(tos)
|
|
|
|
s.Push(p3)
|
2021-02-14 02:09:56 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-20 23:47:10 +00:00
|
|
|
// Pick duplicates TOS from within the stack to the top
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Pick(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-20 23:47:10 +00:00
|
|
|
tos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
swp, err := s.Pick(tos)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.Push(swp)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-14 02:09:56 +00:00
|
|
|
// Words outputs a list of all known words in the dictionary
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Words(out io.Writer, d Dictionary) func(string) error {
|
2021-02-20 22:12:34 +00:00
|
|
|
if out == nil {
|
|
|
|
out = os.Stdout
|
|
|
|
}
|
2021-02-25 01:19:25 +00:00
|
|
|
return func(_ string) error {
|
2021-02-14 02:09:56 +00:00
|
|
|
for n := range d {
|
2021-02-20 22:12:34 +00:00
|
|
|
fmt.Fprintf(out, "%s ", n)
|
2021-02-14 02:09:56 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-16 00:24:04 +00:00
|
|
|
// Flags outputs a list of all flags
|
2021-02-27 03:09:43 +00:00
|
|
|
func (b *Builtins) Flags(out io.Writer, c *Context) func(string) error {
|
2021-02-20 22:12:34 +00:00
|
|
|
if out == nil {
|
|
|
|
out = os.Stdout
|
|
|
|
}
|
2021-02-25 01:19:25 +00:00
|
|
|
return func(_ string) error {
|
2021-02-16 00:24:04 +00:00
|
|
|
for n := range c.Flags {
|
2021-02-20 22:12:34 +00:00
|
|
|
fmt.Fprintf(out, "%s %v\n", n, c.Flags.GetFlag(n))
|
2021-02-16 00:24:04 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-14 02:37:56 +00:00
|
|
|
// Emit outputs the UTF-8 rune for the int on the top of the stack
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Emit(out io.Writer, s *Stack) func(string) error {
|
2021-02-20 17:09:04 +00:00
|
|
|
if out == nil {
|
|
|
|
out = os.Stdout
|
|
|
|
}
|
2021-02-25 01:19:25 +00:00
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
2021-02-14 02:37:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-20 17:09:04 +00:00
|
|
|
fmt.Fprint(out, string(rune(tos))+" ")
|
2021-02-14 02:37:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-14 16:58:43 +00:00
|
|
|
// ToR pops from the stack to the return stack
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) ToR(s *Stack, r *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tos, err := s.Pop()
|
2021-02-14 16:58:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
r.Push(tos)
|
2021-02-14 16:58:43 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RFrom pops from the return stack to the stack
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) RFrom(s *Stack, r *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tors, err := r.Pop()
|
2021-02-14 16:58:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
s.Push(tors)
|
2021-02-14 16:58:43 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RFetch copies from the return stack to the stack
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) RFetch(s *Stack, r *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 17:20:34 +00:00
|
|
|
tors, err := r.Pop()
|
2021-02-14 16:58:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
r.Push(tors)
|
|
|
|
s.Push(tors)
|
2021-02-14 16:58:43 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-20 20:52:27 +00:00
|
|
|
// If checks if TOS != 0 and executes any following statements if it is. If TOS == 0, skip execution until ELSE or THEN.
|
|
|
|
// Nested IFs and ELSE/THENs inside "non-executing" blocks still manipulate the if stack, in order to properly balance
|
|
|
|
// IFs and THENs. i.e. `0 IF 0 IF 42 EMIT THEN 42 EMIT THEN` should not print anything, but a naive "go until you see any THEN"
|
|
|
|
// would output "* ".
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) If(s *Stack, i *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-20 20:52:27 +00:00
|
|
|
// check to see the current IF state. If we're inside a non-executing branch,
|
|
|
|
// add our own non-execution state, AND tell ELSE to skip its work to boot.
|
|
|
|
itop, err := i.Pick(0)
|
|
|
|
if err != nil && !errors.Is(err, ErrUnderflow) {
|
|
|
|
return err
|
|
|
|
} else if err == nil && itop == 0 {
|
|
|
|
i.Push(-1)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
tos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-20 23:03:02 +00:00
|
|
|
// TOS was FALSE, don't execute this branch
|
|
|
|
if tos == FALSE {
|
2021-02-20 20:52:27 +00:00
|
|
|
i.Push(0)
|
2021-02-20 23:03:02 +00:00
|
|
|
return nil
|
2021-02-20 20:52:27 +00:00
|
|
|
}
|
2021-02-20 23:03:02 +00:00
|
|
|
// TOS wasn't 0, execute until we see ELSE/THEN
|
|
|
|
i.Push(1)
|
2021-02-14 19:27:02 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-20 20:52:27 +00:00
|
|
|
// Else executes a separate piece of code if the IF stack
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Else(i *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-20 20:52:27 +00:00
|
|
|
itop, err := i.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// don't execute until we see our THEN
|
|
|
|
if itop == -1 || itop == 1 {
|
|
|
|
i.Push(0)
|
|
|
|
}
|
|
|
|
// we weren't running code, but now we can until THEN
|
|
|
|
if itop == 0 {
|
|
|
|
i.Push(1)
|
|
|
|
}
|
2021-02-14 19:27:02 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then ends an If/Else block
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Then(i *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-20 20:52:27 +00:00
|
|
|
// Pop off the existing IF/ELSE state and return
|
|
|
|
_, err := i.Pop()
|
|
|
|
return err
|
2021-02-14 19:27:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-15 01:26:30 +00:00
|
|
|
// Do sets up a loop by marking its own location as the return code.
|
|
|
|
// It also puts TOS and NOS onto the return stack to use as loop control variables.
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Do(s *Stack, r *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-15 01:26:30 +00:00
|
|
|
tors, err := r.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
tos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nos, err := s.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
r.Push(nos)
|
|
|
|
r.Push(tos)
|
|
|
|
r.Push(tors)
|
|
|
|
r.Push(tors)
|
2021-02-14 19:27:02 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-15 01:26:30 +00:00
|
|
|
// Loop closes a loop by removing its own location from the stack and letting the one that Do inserted get returned.
|
|
|
|
// If it's reached the end of the loop (checking the loop-control variables _underneath_ the return vars),
|
|
|
|
// it continues, otherwise it re-adds the original location of the Do and returns.
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Loop(s *Stack, r *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-15 01:26:30 +00:00
|
|
|
tors, err := r.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nors, err := r.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
counter, err := r.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
target, err := r.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
counter = counter + 1
|
|
|
|
if counter == target {
|
|
|
|
r.Push(tors)
|
|
|
|
} else {
|
|
|
|
r.Push(target)
|
|
|
|
r.Push(counter)
|
|
|
|
r.Push(nors)
|
|
|
|
r.Push(nors)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// I puts the current value of the loop counter on the top of the stack
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) I(s *Stack, r *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-15 01:32:38 +00:00
|
|
|
counter, err := r.Pick(2)
|
2021-02-15 01:26:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.Push(counter)
|
2021-02-14 19:27:02 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-14 02:37:56 +00:00
|
|
|
// Quit exits the repl
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Quit() func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-14 02:37:56 +00:00
|
|
|
return ErrExit
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-14 02:09:56 +00:00
|
|
|
// Debug prints the stack without modifying it
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Debug(out io.Writer, s *Stack) func(string) error {
|
2021-02-20 22:12:34 +00:00
|
|
|
if out == nil {
|
|
|
|
out = os.Stdout
|
|
|
|
}
|
2021-02-25 01:19:25 +00:00
|
|
|
return func(_ string) error {
|
2021-02-20 22:12:34 +00:00
|
|
|
fmt.Fprint(out, s.values, " ")
|
2021-02-14 02:09:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2021-02-20 23:47:10 +00:00
|
|
|
|
2021-02-25 01:19:25 +00:00
|
|
|
// See prints the defintion of a word
|
|
|
|
func (b *Builtins) See(out io.Writer, r *Stack, d Dictionary) func(string) error {
|
|
|
|
if out == nil {
|
|
|
|
out = os.Stdout
|
|
|
|
}
|
|
|
|
return func(next string) error {
|
|
|
|
w := []byte{}
|
|
|
|
// start at 1, not 0, because a ' ' is the character at 0.
|
|
|
|
for i := 1; i < len(next); i = i + 1 {
|
|
|
|
switch next[i] {
|
|
|
|
case ' ':
|
|
|
|
word, err := d.GetWord(string(w))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if word.Impl != nil {
|
|
|
|
fmt.Fprintf(out, "%s [INTERNAL]\n", string(w))
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(out, ": %s %s ;\n", string(w), strings.Join(word.Source, " "))
|
|
|
|
}
|
|
|
|
j, err := r.Pop()
|
|
|
|
r.Push(j + i)
|
|
|
|
return err
|
|
|
|
default:
|
|
|
|
w = append(w, next[i])
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-20 23:47:10 +00:00
|
|
|
// Depth puts the current count of stack items on the stacks
|
2021-02-25 01:19:25 +00:00
|
|
|
func (b *Builtins) Depth(s *Stack) func(string) error {
|
|
|
|
return func(_ string) error {
|
2021-02-20 23:47:10 +00:00
|
|
|
s.Push(len(s.values))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2021-02-25 02:27:52 +00:00
|
|
|
|
|
|
|
// Variable adds a new word to the dictionary that returns a pointer to memory
|
|
|
|
func (b *Builtins) Variable(c *Context) func(string) error {
|
|
|
|
return func(next string) error {
|
|
|
|
w := []byte{}
|
|
|
|
for i := 1; i < len(next); i = i + 1 {
|
|
|
|
switch next[i] {
|
|
|
|
case ' ':
|
|
|
|
next := c.Memory.NextFreeAddress()
|
|
|
|
if next == 0 {
|
|
|
|
// don't use the 0 cell, since we can't distinguish that from the uninitialized field
|
|
|
|
next = 1
|
|
|
|
}
|
|
|
|
c.Dictionary.AddWord(string(w), Word{Name: string(w), Variable: next})
|
|
|
|
j, _ := c.RStack.Pop()
|
|
|
|
c.RStack.Push(j + i - 1) // push the end-point onto the stack
|
2021-02-26 02:13:03 +00:00
|
|
|
return c.Memory.Write(next, []int{0})
|
2021-02-25 02:27:52 +00:00
|
|
|
default:
|
|
|
|
w = append(w, next[i])
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-26 02:04:35 +00:00
|
|
|
// Constant adds a new word to the dictionary that puts a value on the stack
|
|
|
|
func (b *Builtins) Constant(c *Context) func(string) error {
|
|
|
|
return func(next string) error {
|
|
|
|
w := []byte{}
|
|
|
|
for i := 1; i < len(next); i = i + 1 {
|
|
|
|
switch next[i] {
|
|
|
|
case ' ':
|
|
|
|
v, err := c.Stack.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.Dictionary.AddWord(string(w), Word{
|
|
|
|
Name: string(w),
|
|
|
|
Impl: func(_ string) error {
|
|
|
|
c.Stack.Push(v)
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
})
|
|
|
|
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])
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-25 02:27:52 +00:00
|
|
|
// Store places a value in memory
|
|
|
|
func (b *Builtins) Store(c *Context) func(string) error {
|
|
|
|
return func(_ string) error {
|
|
|
|
addr, err := c.Stack.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
val, err := c.Stack.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return c.Memory.Write(addr, []int{val})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch returns a value from memory and puts it on the stack
|
|
|
|
func (b *Builtins) Fetch(c *Context) func(string) error {
|
|
|
|
return func(_ string) error {
|
|
|
|
addr, err := c.Stack.Pop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
res := c.Memory.Read(addr, 1)
|
|
|
|
c.Stack.Push(res[0])
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2021-02-27 01:49:10 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|