From 762b6c870ca994e9397affae1e286f18d46ec607 Mon Sep 17 00:00:00 2001 From: David Ashby Date: Wed, 24 Feb 2021 21:27:52 -0500 Subject: [PATCH] VARIABLE --- README.md | 14 +++++++++++++- builtins.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ eval.go | 4 ++++ main.go | 8 ++++++++ words.go | 11 ++++++----- 5 files changed, 84 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c2300f2..ca30dbe 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,18 @@ ok 55 ok ``` +Propser also has a basic off-stack memory model using variables and pointers: +``` +> VARIABLE FOO +ok +> 10 FOO ! +ok +> FOO @ +ok +> . +10 ok +``` + And, of course, it knows how to quit: ``` @@ -63,6 +75,6 @@ bye ## Future Plans -* Implement `VARIABLE` and `CONSTANT` and the various `? ! @` related words +* Implement `CONSTANT` * Implement loading libraries of pre-written functions both from the command-line and at run-time * Add much better readline behaviors in the interactive console (up-arrow for history, cursor movement...) diff --git a/builtins.go b/builtins.go index 1633a09..cde8e66 100644 --- a/builtins.go +++ b/builtins.go @@ -663,3 +663,56 @@ func (b *Builtins) Depth(s *Stack) func(string) error { return nil } } + +// 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 + return nil + default: + w = append(w, next[i]) + continue + } + } + return nil + } +} + +// 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 + } +} diff --git a/eval.go b/eval.go index ef25cb9..1da7d82 100644 --- a/eval.go +++ b/eval.go @@ -14,6 +14,7 @@ type Context struct { IfStack *Stack // tracks nested branches Flags Flags Words []string + Memory Memory } // Eval evaulates a given line, recursively descending into given words as needed @@ -78,6 +79,9 @@ func (c *Context) Exec(w Word, s string) error { if err != nil { return fmt.Errorf("error during nested evaluation of word %+v: %w", w, err) } + } else if w.Variable > 0 { + // it's a variable, let's get that address and put it on the stack + c.Stack.Push(w.Variable) } else { it, err := strconv.Atoi(w.Name) if err == nil { diff --git a/main.go b/main.go index f2593ba..2f375e6 100644 --- a/main.go +++ b/main.go @@ -24,6 +24,10 @@ func main() { "Immediate": true, }, Words: []string{}, + Memory: Memory{ + intern: map[int]int{}, + nextFree: 1, + }, } b := &Builtins{} @@ -78,6 +82,10 @@ func main() { dict.AddWord("2DROP", Word{Name: "2DROP", Source: []string{"DROP", "DROP"}}) dict.AddWord("2DUP", Word{Name: "2DUP", Source: []string{"OVER", "OVER"}}) dict.AddWord("2OVER", Word{Name: "2OVER", Source: []string{"3", "PICK", "3", "PICK"}}) + // memory access with variables + dict.AddWord("VARIABLE", Word{Name: "VARIABLE", Impl: b.Variable(&c)}) + dict.AddWord("!", Word{Name: "!", Impl: b.Store(&c)}) + dict.AddWord("@", Word{Name: "@", Impl: b.Fetch(&c)}) // 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)}) diff --git a/words.go b/words.go index d2a274d..8fc95a1 100644 --- a/words.go +++ b/words.go @@ -10,11 +10,12 @@ type Dictionary map[string]Word // A Word defines a subroutine type Word struct { - Name string - Impl func(string) error - Source []string - Immediate bool - BranchCheck bool + Name string // Name of our word/variable + Impl func(string) error // built-in implementation of the word + Source []string // source, if user-defined + Variable int // if this is a variable, the memory address + Immediate bool // is this word immediate? + BranchCheck bool // is this word part of IF/ELSE/THEN? } // AddWord inserts a new word into the dictonary, overwriting any existing word by that name