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
}