~evanj/forth

6e11f951c0549fd9e102d5a45121b11dbc5c035f — Evan J 1 year, 4 months ago 7959b06
Feat(user defined words): -
1 files changed, 74 insertions(+), 20 deletions(-)

M main.go
M main.go => main.go +74 -20
@@ 4,7 4,6 @@ import (
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"sync"


@@ 93,24 92,25 @@ var (
func main() {
	var (
		input  = make(chan byte)
		mode1  = make(chan node) // for interpreter
		mode2  = make(chan node) // for compiler
		mode1  = make(chan node) // for interpreter/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 token(input, mode1, 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)
	for {
		select {
		case <-done:
			return
		case err := <-ech:
			output <- err
			output <- "\n"
		}
	}
}



@@ 132,7 132,7 @@ func read(from io.Reader, c chan byte, done chan struct{}, ech chan error) {
	}
}

func token(input chan byte, mode1, mode2 chan node, ech chan error) {
func token(input chan byte, mode1 chan node, ech chan error) {
	type State int

	const (


@@ 142,8 142,10 @@ func token(input chan byte, mode1, mode2 chan node, ech chan error) {
	)

	var (
		word  string
		state = Start
		defName string
		defBody []node
		word    string
		state   = Start
	)

	for b := range input {


@@ 159,6 161,8 @@ func token(input chan byte, mode1, mode2 chan node, ech chan error) {
			}

			if b == ':' {
				defName = ""
				defBody = []node{}
				state = Compile
				break
			}


@@ 168,10 172,10 @@ func token(input chan byte, mode1, mode2 chan node, ech chan error) {

		case Interpret:
			if isSpace {
				// TODO: parse current word into node, send to stack.
				node, err := tonode(word)
				if err != nil {
					ech <- err
					state = Start
					break
				}
				mode1 <- node


@@ 182,7 186,40 @@ func token(input chan byte, mode1, mode2 chan node, ech chan error) {
			word += string(b)

		case Compile:
			panic("TODO: Implement compile state in tokenizer.")
			if isSpace && defName == "" && word != "" {
				defName = word
				word = ""
				break
			}

			if (isSpace || b == ';') && word != "" {
				node, err := tonode(word)
				if err != nil {
					ech <- err
					state = Start
					break
				}
				defBody = append(defBody, node)
				word = ""
				if b == ';' {
					// In case user doesn't put space inbetween last word and ';'.
					addword(defName, defBody)
					state = Start
				}
				break
			}

			if b == ';' {
				addword(defName, defBody)
				state = Start
				break
			}

			if isSpace {
				break
			}

			word += string(b)

		}
	}


@@ 217,10 254,6 @@ func interpret(mode1 chan node, output chan interface{}, done chan struct{}, ech
	}
}

// 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)


@@ 296,7 329,7 @@ func builtinEmit(stack []node, output chan interface{}, done chan struct{}, ech 
		ech <- ErrStackBad
		return append(stack, a)
	}
	output <- string(int(a.number)) // TODO: Remove int(...) here.
	output <- string(int(a.number)) // NOTE: Are there any additional checks we could do here?
	output <- "\n"
	return stack
}


@@ 455,3 488,24 @@ func mathop(a, b node, f func(a, b float64) node) (node, error) {
	}
	return f(b.number, a.number), nil
}

func addword(name string, nodes []node) {
	dict.Lock()
	defer dict.Unlock()
	dict.words[name] = func(stack []node, output chan interface{}, done chan struct{}, ech chan error) []node {
		for _, n := range nodes {
			func(n node) {
				dict.RLock()
				defer dict.RUnlock()
				if word, ok := dict.words[n.word]; ok {
					// Execute!
					stack = word(stack, output, done, ech)
					return
				}
				// Add to stack.
				stack = append(stack, n)
			}(n)
		}
		return stack
	}
}