From a2e6115248919a9de88fe6320f44c075fbc1cac7 Mon Sep 17 00:00:00 2001 From: David Ashby Date: Wed, 24 Feb 2021 20:19:25 -0500 Subject: [PATCH] implement SEE and make comments suck less --- builtins.go | 194 ++++++++++++++++++++++++++++------------------- builtins_test.go | 32 ++++---- eval.go | 10 +-- main.go | 2 +- words.go | 2 +- 5 files changed, 139 insertions(+), 101 deletions(-) diff --git a/builtins.go b/builtins.go index 10ea904..2fe4004 100644 --- a/builtins.go +++ b/builtins.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "strings" ) // FALSE is a flag for, what else, false @@ -23,16 +24,16 @@ type Builtins struct{} 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 { +func (b *Builtins) Colon(c *Context) func(string) error { + return func(_ string) 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 { +func (b *Builtins) Semicolon(c *Context) func(string) error { + return func(_ string) error { c.Dictionary.AddWord(c.Words[0], Word{Name: c.Words[0], Source: c.Words[1:]}) c.Words = []string{} c.Flags.SetFlag("Immediate", true) @@ -41,26 +42,33 @@ func (b *Builtins) Semicolon(c *Context) func() error { } // 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) +func (b *Builtins) OpenComment(c *Context) func(string) error { + return func(next string) error { + 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 + } + } return nil } } // CloseComment resumes parsing -func (b *Builtins) CloseComment(c *Context) func() error { - return func() error { - c.Flags.SetFlag("Comment", false) +func (b *Builtins) CloseComment(c *Context) func(string) error { + return func(_ string) error { 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 { +func (b *Builtins) Eq(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -79,8 +87,8 @@ func (b *Builtins) Eq(s *Stack) func() error { } // 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 { +func (b *Builtins) NEq(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -99,8 +107,8 @@ func (b *Builtins) NEq(s *Stack) func() error { } // 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 { +func (b *Builtins) Lt(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -119,8 +127,8 @@ func (b *Builtins) Lt(s *Stack) func() error { } // 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 { +func (b *Builtins) Gt(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -139,8 +147,8 @@ func (b *Builtins) Gt(s *Stack) func() error { } // 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 { +func (b *Builtins) LtEq(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -159,8 +167,8 @@ func (b *Builtins) LtEq(s *Stack) func() error { } // 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 { +func (b *Builtins) GtEq(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -179,8 +187,8 @@ func (b *Builtins) GtEq(s *Stack) func() error { } // Add sums the top two numbers on the stack and pushes the result -func (b *Builtins) Add(s *Stack) func() error { - return func() error { +func (b *Builtins) Add(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -195,8 +203,8 @@ func (b *Builtins) Add(s *Stack) func() error { } // Sub performs NOS - TOS and pushes the result -func (b *Builtins) Sub(s *Stack) func() error { - return func() error { +func (b *Builtins) Sub(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -211,8 +219,8 @@ func (b *Builtins) Sub(s *Stack) func() error { } // 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 { +func (b *Builtins) Mul(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -227,8 +235,8 @@ func (b *Builtins) Mul(s *Stack) func() error { } // Div performs NOS/TOS and pushes the (integer!) result -func (b *Builtins) Div(s *Stack) func() error { - return func() error { +func (b *Builtins) Div(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -243,8 +251,8 @@ func (b *Builtins) Div(s *Stack) func() error { } // Mod performs NOS%TOS and pushes the (integer!) modulo result -func (b *Builtins) Mod(s *Stack) func() error { - return func() error { +func (b *Builtins) Mod(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -259,11 +267,11 @@ func (b *Builtins) Mod(s *Stack) func() error { } // 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 { +func (b *Builtins) Print(out io.Writer, s *Stack) func(string) error { if out == nil { out = os.Stdout } - return func() error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -274,8 +282,8 @@ func (b *Builtins) Print(out io.Writer, s *Stack) func() error { } // Dup pops the stack, then pushes two copies onto the stack -func (b *Builtins) Dup(s *Stack) func() error { - return func() error { +func (b *Builtins) Dup(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -287,8 +295,8 @@ func (b *Builtins) Dup(s *Stack) func() error { } // Swap inverts the order of TOS and NOS -func (b *Builtins) Swap(s *Stack) func() error { - return func() error { +func (b *Builtins) Swap(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -304,8 +312,8 @@ func (b *Builtins) Swap(s *Stack) func() error { } // Over duplicates NOS to TOS, resulting in NOS TOS NOS -func (b *Builtins) Over(s *Stack) func() error { - return func() error { +func (b *Builtins) Over(s *Stack) func(string) error { + return func(_ string) error { nos, err := s.Pick(1) if err != nil { return err @@ -316,16 +324,16 @@ func (b *Builtins) Over(s *Stack) func() error { } // Drop simply discards TOS -func (b *Builtins) Drop(s *Stack) func() error { - return func() error { +func (b *Builtins) Drop(s *Stack) func(string) error { + return func(_ string) 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 { +func (b *Builtins) Rot(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -346,8 +354,8 @@ func (b *Builtins) Rot(s *Stack) func() error { } // Pick duplicates TOS from within the stack to the top -func (b *Builtins) Pick(s *Stack) func() error { - return func() error { +func (b *Builtins) Pick(s *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -362,11 +370,11 @@ func (b *Builtins) Pick(s *Stack) func() error { } // Words outputs a list of all known words in the dictionary -func (b *Builtins) Words(out io.Writer, d Dictionary) func() error { +func (b *Builtins) Words(out io.Writer, d Dictionary) func(string) error { if out == nil { out = os.Stdout } - return func() error { + return func(_ string) error { for n := range d { fmt.Fprintf(out, "%s ", n) } @@ -375,11 +383,11 @@ func (b *Builtins) Words(out io.Writer, d Dictionary) func() error { } // Flags outputs a list of all flags -func (b *Builtins) Flags(out io.Writer, c Context) func() error { +func (b *Builtins) Flags(out io.Writer, c Context) func(string) error { if out == nil { out = os.Stdout } - return func() error { + return func(_ string) error { for n := range c.Flags { fmt.Fprintf(out, "%s %v\n", n, c.Flags.GetFlag(n)) } @@ -388,11 +396,11 @@ func (b *Builtins) Flags(out io.Writer, c Context) func() error { } // 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 { +func (b *Builtins) Emit(out io.Writer, s *Stack) func(string) error { if out == nil { out = os.Stdout } - return func() error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -403,8 +411,8 @@ func (b *Builtins) Emit(out io.Writer, s *Stack) func() error { } // ToR pops from the stack to the return stack -func (b *Builtins) ToR(s *Stack, r *Stack) func() error { - return func() error { +func (b *Builtins) ToR(s *Stack, r *Stack) func(string) error { + return func(_ string) error { tos, err := s.Pop() if err != nil { return err @@ -415,8 +423,8 @@ func (b *Builtins) ToR(s *Stack, r *Stack) func() error { } // RFrom pops from the return stack to the stack -func (b *Builtins) RFrom(s *Stack, r *Stack) func() error { - return func() error { +func (b *Builtins) RFrom(s *Stack, r *Stack) func(string) error { + return func(_ string) error { tors, err := r.Pop() if err != nil { return err @@ -427,8 +435,8 @@ func (b *Builtins) RFrom(s *Stack, r *Stack) func() error { } // RFetch copies from the return stack to the stack -func (b *Builtins) RFetch(s *Stack, r *Stack) func() error { - return func() error { +func (b *Builtins) RFetch(s *Stack, r *Stack) func(string) error { + return func(_ string) error { tors, err := r.Pop() if err != nil { return err @@ -443,8 +451,8 @@ func (b *Builtins) RFetch(s *Stack, r *Stack) func() error { // 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 { +func (b *Builtins) If(s *Stack, i *Stack) func(string) error { + return func(_ string) error { // 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) @@ -471,8 +479,8 @@ func (b *Builtins) If(s *Stack, i *Stack) func() error { } // Else executes a separate piece of code if the IF stack -func (b *Builtins) Else(i *Stack) func() error { - return func() error { +func (b *Builtins) Else(i *Stack) func(string) error { + return func(_ string) error { itop, err := i.Pop() if err != nil { return err @@ -490,8 +498,8 @@ func (b *Builtins) Else(i *Stack) func() error { } // Then ends an If/Else block -func (b *Builtins) Then(i *Stack) func() error { - return func() error { +func (b *Builtins) Then(i *Stack) func(string) error { + return func(_ string) error { // Pop off the existing IF/ELSE state and return _, err := i.Pop() return err @@ -500,8 +508,8 @@ func (b *Builtins) Then(i *Stack) func() error { // 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 { +func (b *Builtins) Do(s *Stack, r *Stack) func(string) error { + return func(_ string) error { tors, err := r.Pop() if err != nil { return err @@ -525,8 +533,8 @@ func (b *Builtins) Do(s *Stack, r *Stack) func() error { // 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 { +func (b *Builtins) Loop(s *Stack, r *Stack) func(string) error { + return func(_ string) error { tors, err := r.Pop() if err != nil { return err @@ -557,8 +565,8 @@ func (b *Builtins) Loop(s *Stack, r *Stack) func() error { } // 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 { +func (b *Builtins) I(s *Stack, r *Stack) func(string) error { + return func(_ string) error { counter, err := r.Pick(2) if err != nil { return err @@ -569,26 +577,58 @@ func (b *Builtins) I(s *Stack, r *Stack) func() error { } // Quit exits the repl -func (b *Builtins) Quit() func() error { - return func() error { +func (b *Builtins) Quit() func(string) error { + return func(_ string) error { return ErrExit } } // Debug prints the stack without modifying it -func (b *Builtins) Debug(out io.Writer, s *Stack) func() error { +func (b *Builtins) Debug(out io.Writer, s *Stack) func(string) error { if out == nil { out = os.Stdout } - return func() error { + return func(_ string) error { fmt.Fprint(out, s.values, " ") return nil } } +// 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 + } +} + // Depth puts the current count of stack items on the stacks -func (b *Builtins) Depth(s *Stack) func() error { - return func() error { +func (b *Builtins) Depth(s *Stack) func(string) error { + return func(_ string) error { s.Push(len(s.values)) return nil } diff --git a/builtins_test.go b/builtins_test.go index c91b565..10f2800 100644 --- a/builtins_test.go +++ b/builtins_test.go @@ -10,7 +10,7 @@ func TestAdd(t *testing.T) { values: []int{1, 2}, } b := Builtins{} - err := b.Add(&s)() + err := b.Add(&s)("") if err != nil { t.Fail() } @@ -27,7 +27,7 @@ func TestSub(t *testing.T) { values: []int{1, 2}, } b := Builtins{} - err := b.Sub(&s)() + err := b.Sub(&s)("") if err != nil { t.Fail() } @@ -44,7 +44,7 @@ func TestMul(t *testing.T) { s := Stack{ values: []int{4, 2}, } - err := b.Mul(&s)() + err := b.Mul(&s)("") if err != nil { t.Fail() } @@ -58,7 +58,7 @@ func TestMul(t *testing.T) { s = Stack{ values: []int{-4, 2}, } - err = b.Mul(&s)() + err = b.Mul(&s)("") if err != nil { t.Fail() } @@ -75,7 +75,7 @@ func TestDiv(t *testing.T) { s := Stack{ values: []int{2, 4}, } - err := b.Div(&s)() + err := b.Div(&s)("") if err != nil { t.Log(err) t.Fail() @@ -92,7 +92,7 @@ func TestDiv(t *testing.T) { s = Stack{ values: []int{2, -4}, } - err = b.Div(&s)() + err = b.Div(&s)("") if err != nil { t.Log(err) t.Fail() @@ -113,7 +113,7 @@ func TestPrint(t *testing.T) { values: []int{200}, } out := new(bytes.Buffer) - err := b.Print(out, &s)() + err := b.Print(out, &s)("") if err != nil { t.Log(err) t.Fail() @@ -133,7 +133,7 @@ func TestDup(t *testing.T) { s := Stack{ values: []int{200}, } - err := b.Dup(&s)() + err := b.Dup(&s)("") if err != nil { t.Log(err) t.Fail() @@ -153,7 +153,7 @@ func TestSwap(t *testing.T) { s := Stack{ values: []int{200, 100}, } - err := b.Swap(&s)() + err := b.Swap(&s)("") if err != nil { t.Log(err) t.Fail() @@ -173,7 +173,7 @@ func TestOver(t *testing.T) { s := Stack{ values: []int{200, 100}, } - err := b.Over(&s)() + err := b.Over(&s)("") if err != nil { t.Log(err) t.Fail() @@ -193,7 +193,7 @@ func TestDrop(t *testing.T) { s := Stack{ values: []int{200, 100}, } - err := b.Drop(&s)() + err := b.Drop(&s)("") if err != nil { t.Log(err) t.Fail() @@ -213,7 +213,7 @@ func TestRot(t *testing.T) { s := Stack{ values: []int{100, 200, 300}, } - err := b.Rot(&s)() + err := b.Rot(&s)("") if err != nil { t.Log(err) t.Fail() @@ -234,7 +234,7 @@ func TestEmit(t *testing.T) { values: []int{42}, } out := new(bytes.Buffer) - err := b.Emit(out, &s)() + err := b.Emit(out, &s)("") if err != nil { t.Log(err) t.Fail() @@ -257,7 +257,7 @@ func TestToR(t *testing.T) { r := Stack{ values: []int{}, } - err := b.ToR(&s, &r)() + err := b.ToR(&s, &r)("") if err != nil { t.Log(err) t.Fail() @@ -284,7 +284,7 @@ func TestRFrom(t *testing.T) { r := Stack{ values: []int{200, 100}, } - err := b.RFrom(&s, &r)() + err := b.RFrom(&s, &r)("") if err != nil { t.Log(err) t.Fail() @@ -311,7 +311,7 @@ func TestRFetch(t *testing.T) { r := Stack{ values: []int{200, 100}, } - err := b.RFetch(&s, &r)() + err := b.RFetch(&s, &r)("") if err != nil { t.Log(err) t.Fail() diff --git a/eval.go b/eval.go index d65f424..ef25cb9 100644 --- a/eval.go +++ b/eval.go @@ -37,9 +37,7 @@ func (c *Context) Eval(line string) error { } if !c.Flags.GetFlag("Immediate") { - if !c.Flags.GetFlag("Comment") { - c.Words = append(c.Words, sword) - } + c.Words = append(c.Words, sword) word = []byte{} continue } @@ -48,7 +46,7 @@ func (c *Context) Eval(line string) error { if len(c.IfStack.values) == 0 || (len(c.IfStack.values) > 0 && ifcheck == 1) || w.BranchCheck { // run word c.RStack.Push(i) - err := c.Exec(w) + err := c.Exec(w, line[i:]) if err != nil { return err } @@ -67,10 +65,10 @@ func (c *Context) Eval(line string) error { } // Exec wraps the branched execution of words (either built-in or user-defined) -func (c *Context) Exec(w Word) error { +func (c *Context) Exec(w Word, s string) error { if w.Impl != nil { // we have an implementation for that word. Run it. - err := w.Impl() + err := w.Impl(s) if err != nil { return fmt.Errorf("error during built-in: %w", err) } diff --git a/main.go b/main.go index 6bc404f..cdd0104 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,6 @@ func main() { IfStack: &ifstack, Flags: Flags{ "Immediate": true, - "Comment": false, }, Words: []string{}, } @@ -78,6 +77,7 @@ func main() { dict.AddWord("2OVER", Word{Name: "2OVER", Source: []string{"3", "PICK", "3", "PICK"}}) // debugging dict.AddWord("WORDS", Word{Name: "WORDS", Impl: b.Words(os.Stdout, dict)}) + dict.AddWord("SEE", Word{Name: "SEE", Impl: b.See(os.Stdout, &rstack, dict)}) dict.AddWord("FLAGS", Word{Name: "FLAGS", Impl: b.Flags(os.Stdout, c)}) dict.AddWord(".S", Word{Name: ".S", Impl: b.Debug(os.Stdout, &stack)}) dict.AddWord(".R", Word{Name: ".R", Impl: b.Debug(os.Stdout, &rstack)}) diff --git a/words.go b/words.go index 4ff825c..d2a274d 100644 --- a/words.go +++ b/words.go @@ -11,7 +11,7 @@ type Dictionary map[string]Word // A Word defines a subroutine type Word struct { Name string - Impl func() error + Impl func(string) error Source []string Immediate bool BranchCheck bool