move master to main #1
1 changed files with 158 additions and 2 deletions
160
forth/eval.go
160
forth/eval.go
|
@ -2,17 +2,173 @@
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type Evaluator struct {
|
type Evaluator struct {
|
||||||
|
stack []int
|
||||||
|
words map[string](func() error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Evaluator) push(n int) {
|
||||||
|
e.stack = append(e.stack, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Evaluator) pop() (int, error) {
|
||||||
|
size := len(e.stack)
|
||||||
|
if size == 0 {
|
||||||
|
return 0, errors.New("nothing to pop")
|
||||||
|
}
|
||||||
|
n := e.stack[size-1]
|
||||||
|
e.stack = e.stack[:size-1]
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Evaluator) popTwo() (int, int, error) {
|
||||||
|
if len(e.stack) < 2 {
|
||||||
|
return 0, 0, errors.New("not enough elements on stack")
|
||||||
|
}
|
||||||
|
second, _ := e.pop()
|
||||||
|
first, _ := e.pop()
|
||||||
|
return first, second, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to initialzie arithemtic operations in words map
|
||||||
|
func (e *Evaluator) arithmeticOp(op func(int, int) int) func() error {
|
||||||
|
return func() error {
|
||||||
|
a, b, err := e.popTwo()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("not enough elements on stack")
|
||||||
|
}
|
||||||
|
e.push(op(a, b))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Evaluator) initBasicWords() {
|
||||||
|
e.words["+"] = e.arithmeticOp(func(a, b int) int { return a + b })
|
||||||
|
e.words["-"] = e.arithmeticOp(func(a, b int) int { return a - b })
|
||||||
|
e.words["*"] = e.arithmeticOp(func(a, b int) int { return a * b })
|
||||||
|
e.words["/"] = func() error {
|
||||||
|
a, b, err := e.popTwo()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("not enough elements on stack")
|
||||||
|
}
|
||||||
|
if b == 0 {
|
||||||
|
return errors.New("division by zero")
|
||||||
|
}
|
||||||
|
e.push(a / b)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
e.words["dup"] = func() error {
|
||||||
|
size := len(e.stack)
|
||||||
|
if size == 0 {
|
||||||
|
return errors.New("not enough elements on stack")
|
||||||
|
}
|
||||||
|
e.stack = append(e.stack, e.stack[size-1])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
e.words["over"] = func() error {
|
||||||
|
size := len(e.stack)
|
||||||
|
if size < 2 {
|
||||||
|
return errors.New("not enough elements on stack")
|
||||||
|
}
|
||||||
|
e.stack = append(e.stack, e.stack[size-2])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
e.words["drop"] = func() error {
|
||||||
|
_, err := e.pop()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.words["swap"] = func() error {
|
||||||
|
size := len(e.stack)
|
||||||
|
if size < 2 {
|
||||||
|
return errors.New("not enough elements on stack")
|
||||||
|
}
|
||||||
|
e.stack[size-1], e.stack[size-2] = e.stack[size-2], e.stack[size-1]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Evaluator) getFunc(word string) (func() error, error) {
|
||||||
|
call, found := e.words[word]
|
||||||
|
if found {
|
||||||
|
return call, nil
|
||||||
|
}
|
||||||
|
num, err := strconv.Atoi(word)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("word is not numerical or is not defined")
|
||||||
|
}
|
||||||
|
call = func() error {
|
||||||
|
e.push(num)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return call, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Evaluator) defineWord(word string, def string) error {
|
||||||
|
_, err := strconv.Atoi(word)
|
||||||
|
if err == nil {
|
||||||
|
return errors.New("word definition must not be numeric")
|
||||||
|
}
|
||||||
|
definitionWords := strings.Fields(def)
|
||||||
|
callChain := make([](func() error), len(definitionWords))
|
||||||
|
for i, w := range definitionWords {
|
||||||
|
call, err := e.getFunc(w)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
callChain[i] = call
|
||||||
|
}
|
||||||
|
e.words[word] = func() error {
|
||||||
|
for _, call := range callChain {
|
||||||
|
err := call()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEvaluator creates evaluator.
|
// NewEvaluator creates evaluator.
|
||||||
func NewEvaluator() *Evaluator {
|
func NewEvaluator() *Evaluator {
|
||||||
return &Evaluator{}
|
eval := &Evaluator{make([]int, 0), make(map[string](func() error))}
|
||||||
|
eval.initBasicWords()
|
||||||
|
return eval
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process evaluates sequence of words or definition.
|
// Process evaluates sequence of words or definition.
|
||||||
//
|
//
|
||||||
// Returns resulting stack state and an error.
|
// Returns resulting stack state and an error.
|
||||||
func (e *Evaluator) Process(row string) ([]int, error) {
|
func (e *Evaluator) Process(row string) ([]int, error) {
|
||||||
return nil, nil
|
row = strings.ToLower(row)
|
||||||
|
if strings.HasPrefix(row, ":") && strings.HasSuffix(row, ";") {
|
||||||
|
row = strings.Trim(row, " :;")
|
||||||
|
wordAndDef := strings.SplitN(row, " ", 2)
|
||||||
|
if len(wordAndDef) < 2 {
|
||||||
|
return e.stack, errors.New("couldn't define a new word - incorrect syntax")
|
||||||
|
}
|
||||||
|
word, def := wordAndDef[0], wordAndDef[1]
|
||||||
|
err := e.defineWord(word, def)
|
||||||
|
if err != nil {
|
||||||
|
return e.stack, err
|
||||||
|
}
|
||||||
|
return e.stack, nil
|
||||||
|
}
|
||||||
|
for _, word := range strings.Fields(row) {
|
||||||
|
call, err := e.getFunc(word)
|
||||||
|
if err != nil {
|
||||||
|
return e.stack, err
|
||||||
|
}
|
||||||
|
err = call()
|
||||||
|
if err != nil {
|
||||||
|
return e.stack, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e.stack, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue