package main import ( "fmt" "io" "os" ) // Builtins is a handy holder for our various default words type Builtins struct{} // ErrExit is a special sentinel value to cease computation and quit var ErrExit = fmt.Errorf("exit requested") // Colon sets the COMPILE/IMMEDIATE flag to COMPILE func (b *Builtins) Colon(c *Context) func() error { return func() error { c.Flags.SetFlag("Immediate", false) 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 { c.Dictionary.AddWord(c.Words[0], nil, c.Words[1:], false) 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) return nil } } // 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 } } // Add sums the top two numbers on the stack and pushes the result func (b *Builtins) Add(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(tos + nos) return nil } } // Sub performs NOS - TOS and pushes the result func (b *Builtins) Sub(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 } } // 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 { tos, err := s.Pop() if err != nil { return err } nos, err := s.Pop() if err != nil { return err } s.Push(nos * tos) return nil } } // Div performs NOS/TOS and pushes the (integer!) result func (b *Builtins) Div(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 } } // 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 } return func() error { tos, err := s.Pop() if err != nil { return err } fmt.Fprint(out, tos, " ") return nil } } // Dup pops the stack, then pushes two copies onto the stack func (b *Builtins) Dup(s *Stack) func() error { return func() error { tos, err := s.Pop() if err != nil { return err } s.Push(tos) s.Push(tos) return nil } } // Swap inverts the order of TOS and NOS func (b *Builtins) Swap(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(tos) s.Push(nos) 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) if err != nil { return err } s.Push(nos) 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 { tos, err := s.Pop() if err != nil { return err } nos, err := s.Pop() if err != nil { return err } p3, err := s.Pop() if err != nil { return err } s.Push(nos) s.Push(tos) s.Push(p3) 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 { fmt.Printf("%s ", n) } return nil } } // Flags outputs a list of all flags func (b *Builtins) Flags(c Context) func() error { return func() error { for n := range c.Flags { fmt.Printf("%s %v\n", n, c.Flags.GetFlag(n)) } return nil } } // 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 } return func() error { tos, err := s.Pop() if err != nil { return err } fmt.Fprint(out, string(rune(tos))+" ") return nil } } // ToR pops from the stack to the return stack func (b *Builtins) ToR(s *Stack, r *Stack) func() error { return func() error { tos, err := s.Pop() if err != nil { return err } r.Push(tos) return nil } } // RFrom pops from the return stack to the stack func (b *Builtins) RFrom(s *Stack, r *Stack) func() error { return func() error { tors, err := r.Pop() if err != nil { return err } s.Push(tors) return nil } } // RFetch copies from the return stack to the stack func (b *Builtins) RFetch(s *Stack, r *Stack) func() error { return func() error { tors, err := r.Pop() if err != nil { return err } r.Push(tors) s.Push(tors) return nil } } // 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 } } // 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 { 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 } } // 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 { 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) if err != nil { return err } s.Push(counter) return nil } } // Quit exits the repl func (b *Builtins) Quit() func() error { return func() error { return ErrExit } } // Debug prints the stack without modifying it func (b *Builtins) Debug(s *Stack) func() error { return func() error { fmt.Print(s.values, " ") return nil } }