Implemented forth
This commit is contained in:
parent
79bb3b15d7
commit
63dc763245
1 changed files with 158 additions and 2 deletions
160
forth/eval.go
160
forth/eval.go
|
@ -2,17 +2,173 @@
|
|||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
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.
|
||||
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.
|
||||
//
|
||||
// Returns resulting stack state and an 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