@@ 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
+}