From 7959b06e59708beead712f5ad8a6bd1c64394d4f Mon Sep 17 00:00:00 2001 From: Evan J Date: Tue, 15 Sep 2020 14:16:42 -0700 Subject: [PATCH] Feat(*): Project init. --- go.mod | 3 + go.sum | 0 main.go | 457 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 460 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f17efdf --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.sr.ht/~evanj/forth + +go 1.14 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/main.go b/main.go new file mode 100644 index 0000000..5b0524b --- /dev/null +++ b/main.go @@ -0,0 +1,457 @@ +package main + +import ( + "errors" + "fmt" + "io" + "log" + "os" + "strconv" + "sync" + "unicode" +) + +type typ int + +const ( + TypWord typ = iota + TypNumber +) + +type node struct { + typ typ + word string + number float64 +} + +func (n node) String() string { + switch n.typ { + case TypWord: + return fmt.Sprintf("%v", n.word) + case TypNumber: + return fmt.Sprintf("%v", n.number) + default: + panic("unreachable") + } +} + +type word func(currStack []node, output chan interface{}, done chan struct{}, ech chan error) (nextStack []node) + +type d struct { + *sync.RWMutex + words map[string]word +} + +func newDict() *d { + m := map[string]word{ + ".s": builtinPrint, + "print": builtinPrint, + ".": builtinDrop, + "drop": builtinDrop, + "dup": builtinDup, + "swap": builtinSwap, + "rot": builtinRot, + "emit": builtinEmit, + "cr": builtinCR, + "=": builtinMathEq, + "eq": builtinMathEq, + ">": builtinMathGT, + "gt": builtinMathGT, + "<": builtinMathLT, + "lt": builtinMathLT, + "+": builtinMathAdd, + "add": builtinMathAdd, + "-": builtinMathSub, + "sub": builtinMathSub, + "*": builtinMathMul, + "mul": builtinMathMul, + "/": builtinMathDiv, + "div": builtinMathDiv, + "!=": builtinInvert, + "invert": builtinInvert, + "bye": builtinBye, + } + return &d{&sync.RWMutex{}, m} +} + +type s struct { + *sync.RWMutex + nodes []node +} + +func newStack() *s { + return &s{&sync.RWMutex{}, []node{}} +} + +var ( + ErrStackBad = errors.New("bad stack") + + dict = newDict() + stack = newStack() +) + +func main() { + var ( + input = make(chan byte) + mode1 = make(chan node) // for interpreter + mode2 = make(chan node) // for compiler + output = make(chan interface{}) + ech = make(chan error) + done = make(chan struct{}) + ) + + go read(os.Stdin, input, done, ech) + go token(input, mode1, mode2, ech) + go interpret(mode1, output, done, ech) + go compile(mode2, ech) + go write(os.Stdin, output) + + select { + case <-done: + return + case err := <-ech: + log.Fatal(err) + } +} + +func read(from io.Reader, c chan byte, done chan struct{}, ech chan error) { + for { + bytes := make([]byte, 1) + _, err := from.Read(bytes) + if errors.Is(err, io.EOF) { + done <- struct{}{} + return + } + if err != nil { + ech <- err + break + } + for _, b := range bytes { + c <- b + } + } +} + +func token(input chan byte, mode1, mode2 chan node, ech chan error) { + type State int + + const ( + Start State = iota + Interpret + Compile + ) + + var ( + word string + state = Start + ) + + for b := range input { + isSpace := unicode.IsSpace(rune(b)) + + switch state { + + case Start: + word = "" + + if isSpace { + break + } + + if b == ':' { + state = Compile + break + } + + state = Interpret + word += string(b) + + case Interpret: + if isSpace { + // TODO: parse current word into node, send to stack. + node, err := tonode(word) + if err != nil { + ech <- err + break + } + mode1 <- node + state = Start + break + } + + word += string(b) + + case Compile: + panic("TODO: Implement compile state in tokenizer.") + + } + } +} + +// interpret will execute words within dict with what's on stack. +func interpret(mode1 chan node, output chan interface{}, done chan struct{}, ech chan error) { + for n := range mode1 { + func(n node) { + dict.RLock() + stack.Lock() + + defer dict.RUnlock() + defer stack.Unlock() + + switch n.typ { + + case TypWord: + if word, ok := dict.words[n.word]; ok { + // Execute! + stack.nodes = word(stack.nodes, output, done, ech) + break + } + + // Add to stack. + stack.nodes = append(stack.nodes, n) + + case TypNumber: + stack.nodes = append(stack.nodes, n) + } + }(n) + } +} + +// compile will add words to dict. +func compile(stack chan node, ech chan error) { +} + +func write(to io.Writer, output chan interface{}) { + for it := range output { + fmt.Fprint(to, it) + } +} + +func tonode(input string) (node, error) { + f, err := strconv.ParseFloat(input, 64) + if err != nil { + return node{typ: TypWord, word: input}, nil + } + return node{typ: TypNumber, number: f}, nil +} + +// BUILTINS + +func builtinPrint(currStack []node, output chan interface{}, done chan struct{}, ech chan error) (nextStack []node) { + output <- fmt.Sprintf("<%d> ", len(currStack)) + for _, n := range currStack { + output <- n + output <- " " + } + output <- "\n" + return currStack +} + +func builtinDrop(currStack []node, output chan interface{}, done chan struct{}, ech chan error) (nextStack []node) { + if len(currStack) < 1 { + ech <- ErrStackBad + return currStack + } + _, nextStack = currStack[len(currStack)-1], currStack[:len(currStack)-1] + return +} + +func builtinDup(currStack []node, output chan interface{}, done chan struct{}, ech chan error) (nextStack []node) { + if len(currStack) < 1 { + ech <- ErrStackBad + return currStack + } + latest, nextStack := currStack[len(currStack)-1], currStack[:len(currStack)-1] + return append(nextStack, latest, latest) +} + +func builtinSwap(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + if len(stack) < 2 { + ech <- ErrStackBad + return stack + } + a, stack := stack[len(stack)-1], stack[:len(stack)-1] + b, stack := stack[len(stack)-1], stack[:len(stack)-1] + return append(stack, a, b) +} + +func builtinRot(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + if len(stack) < 3 { + ech <- ErrStackBad + return stack + } + a, stack := stack[len(stack)-1], stack[:len(stack)-1] + b, stack := stack[len(stack)-1], stack[:len(stack)-1] + c, stack := stack[len(stack)-1], stack[:len(stack)-1] + return append(stack, b, a, c) +} + +func builtinEmit(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + if len(stack) < 1 { + ech <- ErrStackBad + return stack + } + a, stack := stack[len(stack)-1], stack[:len(stack)-1] + if a.typ != TypNumber { + ech <- ErrStackBad + return append(stack, a) + } + output <- string(int(a.number)) // TODO: Remove int(...) here. + output <- "\n" + return stack +} + +func builtinCR(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + output <- "\n" + return stack +} + +func builtinMathEq(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + if len(stack) < 2 { + ech <- ErrStackBad + return stack + } + a, stack := stack[len(stack)-1], stack[:len(stack)-1] + b, stack := stack[len(stack)-1], stack[:len(stack)-1] + c, err := mathop(a, b, func(a, b float64) node { return totrue(a == b) }) + if err != nil { + ech <- ErrStackBad + return append(stack, b, a) + } + return append(stack, c) +} + +func builtinMathGT(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + if len(stack) < 2 { + ech <- ErrStackBad + return stack + } + a, stack := stack[len(stack)-1], stack[:len(stack)-1] + b, stack := stack[len(stack)-1], stack[:len(stack)-1] + c, err := mathop(a, b, func(a, b float64) node { return totrue(a > b) }) + if err != nil { + ech <- ErrStackBad + return append(stack, b, a) + } + return append(stack, c) +} + +func builtinMathLT(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + if len(stack) < 2 { + ech <- ErrStackBad + return stack + } + a, stack := stack[len(stack)-1], stack[:len(stack)-1] + b, stack := stack[len(stack)-1], stack[:len(stack)-1] + c, err := mathop(a, b, func(a, b float64) node { return totrue(a < b) }) + if err != nil { + ech <- ErrStackBad + return append(stack, b, a) + } + return append(stack, c) +} + +func builtinInvert(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + if len(stack) < 1 { + ech <- ErrStackBad + return stack + } + a, stack := stack[len(stack)-1], stack[:len(stack)-1] + if a.typ != TypNumber { + ech <- ErrStackBad + return append(stack, a) + } + if a.number != float64(0) { + return append(stack, node{TypNumber, "", float64(0)}) + } + return append(stack, node{TypNumber, "", float64(-1)}) +} + +func builtinBye(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + done <- struct{}{} + return stack +} + +func builtinMathAdd(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + if len(stack) < 2 { + ech <- ErrStackBad + return stack + } + a, stack := stack[len(stack)-1], stack[:len(stack)-1] + b, stack := stack[len(stack)-1], stack[:len(stack)-1] + c, err := mathop(a, b, func(a, b float64) node { + return node{TypNumber, "", a + b} + }) + if err != nil { + ech <- ErrStackBad + return append(stack, b, a) + } + return append(stack, c) +} + +func builtinMathSub(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + if len(stack) < 2 { + ech <- ErrStackBad + return stack + } + a, stack := stack[len(stack)-1], stack[:len(stack)-1] + b, stack := stack[len(stack)-1], stack[:len(stack)-1] + c, err := mathop(a, b, func(a, b float64) node { + return node{TypNumber, "", a - b} + }) + if err != nil { + ech <- ErrStackBad + return append(stack, b, a) + } + return append(stack, c) +} + +func builtinMathMul(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + if len(stack) < 2 { + ech <- ErrStackBad + return stack + } + a, stack := stack[len(stack)-1], stack[:len(stack)-1] + b, stack := stack[len(stack)-1], stack[:len(stack)-1] + c, err := mathop(a, b, func(a, b float64) node { + return node{TypNumber, "", a * b} + }) + if err != nil { + ech <- ErrStackBad + return append(stack, b, a) + } + return append(stack, c) +} + +func builtinMathDiv(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node { + if len(stack) < 2 { + ech <- ErrStackBad + return stack + } + a, stack := stack[len(stack)-1], stack[:len(stack)-1] + b, stack := stack[len(stack)-1], stack[:len(stack)-1] + c, err := mathop(a, b, func(a, b float64) node { + return node{TypNumber, "", a / b} + }) + if err != nil { + ech <- ErrStackBad + return append(stack, b, a) + } + return append(stack, c) +} + +// helpers + +func totrue(val bool) node { + if val { + return node{TypNumber, "", float64(-1)} + } + return node{TypNumber, "", float64(0)} +} + +func mathop(a, b node, f func(a, b float64) node) (node, error) { + if a.typ != TypNumber || b.typ != TypNumber { + return node{}, ErrStackBad + } + return f(b.number, a.number), nil +} -- 2.34.2