2021-02-14 02:09:56 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import "fmt"
|
|
|
|
|
|
|
|
// 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-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(-1)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
s.Push(0)
|
|
|
|
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(-1)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
s.Push(0)
|
|
|
|
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 tos < nos {
|
|
|
|
s.Push(-1)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
s.Push(0)
|
|
|
|
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 tos > nos {
|
|
|
|
s.Push(-1)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
s.Push(0)
|
|
|
|
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 tos <= nos {
|
|
|
|
s.Push(-1)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
s.Push(0)
|
|
|
|
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 tos >= nos {
|
|
|
|
s.Push(-1)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
s.Push(0)
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print pops the stack and outputs it to stdout
|
|
|
|
func (b *Builtins) Print(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
|
|
|
fmt.Print(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 {
|
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
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Words outputs a list of all known words in the dictionary
|
|
|
|
func (b *Builtins) Words(d Dictionary) func() error {
|
|
|
|
return func() error {
|
|
|
|
for n := range d {
|
2021-02-15 01:32:38 +00:00
|
|
|
fmt.Printf("%s ", n)
|
2021-02-14 02:09:56 +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
|
|
|
|
func (b *Builtins) Emit(s *Stack) func() error {
|
|
|
|
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
|
|
|
|
}
|
2021-02-14 17:20:34 +00:00
|
|
|
fmt.Print(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-14 19:27:02 +00:00
|
|
|
// If checks if NOS != 0 and conditionally executes any following statements if so
|
|
|
|
func (b *Builtins) If(s *Stack, r *Stack) func() error {
|
|
|
|
return func() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Else executes a separate piece of code if NOS == 0 after an if check
|
|
|
|
func (b *Builtins) Else(s *Stack, r *Stack) func() error {
|
|
|
|
return func() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then ends an If/Else block
|
|
|
|
func (b *Builtins) Then(s *Stack, r *Stack) func() error {
|
|
|
|
return func() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-14 19:27:02 +00:00
|
|
|
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)
|
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-14 19:27:02 +00:00
|
|
|
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 {
|
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:09:56 +00:00
|
|
|
// CR prints a newline to standard out
|
|
|
|
func (b *Builtins) CR() func() error {
|
|
|
|
return func() error {
|
|
|
|
fmt.Print("\n")
|
|
|
|
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(s *Stack) func() error {
|
|
|
|
return func() error {
|
|
|
|
fmt.Print(s.values, " ")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|