prosper/builtins.go

607 lines
12 KiB
Go
Raw Normal View History

2021-02-14 02:09:56 +00:00
package main
import (
2021-02-20 20:52:27 +00:00
"errors"
"fmt"
"io"
"os"
)
2021-02-14 02:09:56 +00:00
var FALSE = 0
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
func (b *Builtins) Colon(c *Context) func() error {
2021-02-15 20:35:40 +00:00
return func() error {
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
func (b *Builtins) Semicolon(c *Context) func() error {
return func() error {
2021-02-20 20:52:27 +00:00
c.Dictionary.AddWord(c.Words[0], Word{Name: c.Words[0], Source: c.Words[1:]})
c.Words = []string{}
c.Flags.SetFlag("Immediate", true)
return nil
}
}
// OpenComment puts the parser into an "ignore" mode
func (b *Builtins) OpenComment(c *Context) func() error {
return func() error {
c.Flags.SetFlag("Comment", true)
c.Flags.SetFlag("Immediate", false)
return nil
}
}
// CloseComment resumes parsing
func (b *Builtins) CloseComment(c *Context) func() error {
return func() error {
c.Flags.SetFlag("Comment", false)
c.Flags.SetFlag("Immediate", false)
2021-02-15 20:35:40 +00:00
return nil
}
}
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.
func (b *Builtins) Eq(s *Stack) func() error {
return func() error {
tos, err := s.Pop()
if err != nil {
return err
}
nos, err := s.Pop()
if err != nil {
return err
}
if tos == nos {
s.Push(TRUE)
2021-02-14 17:20:34 +00:00
return nil
}
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.
func (b *Builtins) NEq(s *Stack) func() error {
return func() error {
tos, err := s.Pop()
if err != nil {
return err
}
nos, err := s.Pop()
if err != nil {
return err
}
if tos != nos {
s.Push(TRUE)
2021-02-14 17:20:34 +00:00
return nil
}
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.
func (b *Builtins) Lt(s *Stack) func() error {
return func() error {
tos, err := s.Pop()
if err != nil {
return err
}
nos, err := s.Pop()
if err != nil {
return err
}
if nos < tos {
s.Push(TRUE)
2021-02-14 17:20:34 +00:00
return nil
}
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.
func (b *Builtins) Gt(s *Stack) func() error {
return func() error {
tos, err := s.Pop()
if err != nil {
return err
}
nos, err := s.Pop()
if err != nil {
return err
}
if nos > tos {
s.Push(TRUE)
2021-02-14 17:20:34 +00:00
return nil
}
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.
func (b *Builtins) LtEq(s *Stack) func() error {
return func() error {
tos, err := s.Pop()
if err != nil {
return err
}
nos, err := s.Pop()
if err != nil {
return err
}
if nos <= tos {
s.Push(TRUE)
2021-02-14 17:20:34 +00:00
return nil
}
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.
func (b *Builtins) GtEq(s *Stack) func() error {
return func() error {
tos, err := s.Pop()
if err != nil {
return err
}
nos, err := s.Pop()
if err != nil {
return err
}
if nos >= tos {
s.Push(TRUE)
2021-02-14 17:20:34 +00:00
return nil
}
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
func (b *Builtins) Add(s *Stack) func() error {
return func() 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
func (b *Builtins) Sub(s *Stack) func() error {
return func() 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
func (b *Builtins) Mul(s *Stack) func() error {
return func() 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
func (b *Builtins) Div(s *Stack) func() error {
return func() 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
}
}
// Mod performs NOS%TOS and pushes the (integer!) modulo result
func (b *Builtins) Mod(s *Stack) func() error {
return func() error {
tos, err := s.Pop()
if err != nil {
return err
}
nos, err := s.Pop()
if err != nil {
return err
}
s.Push(nos % tos)
return nil
}
}
// DivMod performs NOS/TOS & NOS%TOS and pushes the (integer!) results: TOS as quotient, NOS as remainder
func (b *Builtins) DivMod(s *Stack) func() error {
return func() error {
tos, err := s.Pop()
if err != nil {
return err
}
nos, err := s.Pop()
if err != nil {
return err
}
s.Push(nos % tos)
s.Push(nos / tos)
return nil
}
}
// Print pops the stack and outputs it to the writer with a trailing space (defaults to stdout)
func (b *Builtins) Print(out io.Writer, s *Stack) func() error {
if out == nil {
out = os.Stdout
}
2021-02-14 02:09:56 +00:00
return func() 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
}
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
func (b *Builtins) Dup(s *Stack) func() error {
return func() 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
func (b *Builtins) Swap(s *Stack) func() error {
return func() 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
func (b *Builtins) Over(s *Stack) func() error {
return func() error {
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
func (b *Builtins) Drop(s *Stack) func() error {
return func() error {
_, err := s.Pop()
return err
}
}
// Rot cycles the first three items on the stack: TOS 1 2 3 -> TOS 3 1 2
func (b *Builtins) Rot(s *Stack) func() error {
return func() 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
func (b *Builtins) Pick(s *Stack) func() error {
return func() error {
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
func (b *Builtins) Words(out io.Writer, d Dictionary) func() error {
if out == nil {
out = os.Stdout
}
2021-02-14 02:09:56 +00:00
return func() error {
for n := range d {
fmt.Fprintf(out, "%s ", n)
2021-02-14 02:09:56 +00:00
}
return nil
}
}
// Flags outputs a list of all flags
func (b *Builtins) Flags(out io.Writer, c Context) func() error {
if out == nil {
out = os.Stdout
}
return func() error {
for n := range c.Flags {
fmt.Fprintf(out, "%s %v\n", n, c.Flags.GetFlag(n))
}
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
func (b *Builtins) Emit(out io.Writer, s *Stack) func() error {
if out == nil {
out = os.Stdout
}
2021-02-14 02:37:56 +00:00
return func() 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
}
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
func (b *Builtins) ToR(s *Stack, r *Stack) func() error {
return func() 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
func (b *Builtins) RFrom(s *Stack, r *Stack) func() error {
return func() 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
func (b *Builtins) RFetch(s *Stack, r *Stack) func() error {
return func() 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 "* ".
func (b *Builtins) If(s *Stack, i *Stack) func() error {
return func() 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
}
// TOS was FALSE, don't execute this branch
if tos == FALSE {
2021-02-20 20:52:27 +00:00
i.Push(0)
return nil
2021-02-20 20:52:27 +00:00
}
// TOS wasn't 0, execute until we see ELSE/THEN
i.Push(1)
return nil
}
}
2021-02-20 20:52:27 +00:00
// Else executes a separate piece of code if the IF stack
func (b *Builtins) Else(i *Stack) func() error {
return func() 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)
}
return nil
}
}
// Then ends an If/Else block
2021-02-20 20:52:27 +00:00
func (b *Builtins) Then(i *Stack) func() error {
return func() 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-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.
func (b *Builtins) Do(s *Stack, r *Stack) func() error {
return func() 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)
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.
func (b *Builtins) Loop(s *Stack, r *Stack) func() error {
return func() 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
func (b *Builtins) I(s *Stack, r *Stack) func() error {
return func() error {
counter, err := r.Pick(2)
2021-02-15 01:26:30 +00:00
if err != nil {
return err
}
s.Push(counter)
return nil
}
}
2021-02-14 02:37:56 +00:00
// Quit exits the repl
func (b *Builtins) Quit() func() error {
return func() error {
return ErrExit
}
}
2021-02-14 02:09:56 +00:00
// Debug prints the stack without modifying it
func (b *Builtins) Debug(out io.Writer, s *Stack) func() error {
if out == nil {
out = os.Stdout
}
2021-02-14 02:09:56 +00:00
return func() error {
fmt.Fprint(out, s.values, " ")
2021-02-14 02:09:56 +00:00
return nil
}
}
2021-02-20 23:47:10 +00:00
// Depth puts the current count of stack items on the stacks
func (b *Builtins) Depth(s *Stack) func() error {
return func() error {
s.Push(len(s.values))
return nil
}
}