package forth import ( "errors" "strconv" "strings" ) var ( ErrEOL = errors.New("end of line") ErrStack = errors.New("stack underflow") ErrZero = errors.New("division by zero") ErrWord = errors.New("name cannot be a number") ErrUnknown = errors.New("unknown word") ) type stack []int func (s *stack) push(n ...int) error { *s = append(*s, n...) return nil } func (s *stack) pop() (int, error) { depth := len(*s) if depth < 1 { return 0, ErrStack } tos := (*s)[depth-1] *s = (*s)[:depth-1] return tos, nil } func (s *stack) pop2() (int, int, error) { tos, err := s.pop() // top on stack if err != nil { return 0, 0, err } nos, err := s.pop() // next on stack if err != nil { return 0, 0, err } return tos, nos, nil } func (s *stack) values() []int { return []int(*s) } type word func(*stack) error type dictionary map[string][]word func (d dictionary) find(name string) ([]word, bool) { words, ok := d[strings.ToLower(name)] return words, ok } func (d dictionary) add(name string, words ...word) { d[strings.ToLower(name)] = words } func add(s *stack) error { tos, nos, err := s.pop2() if err != nil { return err } return s.push(nos + tos) } func sub(s *stack) error { tos, nos, err := s.pop2() if err != nil { return err } return s.push(nos - tos) } func mul(s *stack) error { tos, nos, err := s.pop2() if err != nil { return err } return s.push(nos * tos) } func div(s *stack) error { tos, nos, err := s.pop2() if err != nil { return err } if tos == 0 { return ErrZero } return s.push(nos / tos) } func dup(s *stack) error { tos, err := s.pop() if err != nil { return err } return s.push(tos, tos) } func drop(s *stack) error { _, err := s.pop() return err } func swap(s *stack) error { tos, nos, err := s.pop2() if err != nil { return err } return s.push(tos, nos) } func over(s *stack) error { tos, nos, err := s.pop2() if err != nil { return err } return s.push(nos, tos, nos) } func literal(n int) word { return func(s *stack) error { return s.push(n) } } func number(s string) (int, bool) { n, err := strconv.Atoi(s) return n, err == nil } func colon(dict dictionary, l *lexer) error { name, err := l.Next() if err != nil { return err } if _, ok := number(name); ok { return ErrWord } w, err := compile(dict, l) if err != nil { return err } dict.add(name, w...) return nil } func compile(dict dictionary, l *lexer) ([]word, error) { var words []word for { v, err := l.Next() if err == ErrEOL { return words, nil } // colon operator if v == ":" { if err := colon(dict, l); err != nil { return nil, err } continue } if v == ";" { return words, nil } // lookup dictionary first if w, ok := dict.find(v); ok { words = append(words, w...) continue } // try to parse literal if n, ok := number(v); ok { words = append(words, literal(n)) continue } return nil, ErrUnknown } } type lexer []string func NewLexer(line string) lexer { return strings.Fields(line) } func (l *lexer) Next() (string, error) { if len(*l) == 0 { return "", ErrEOL } s := (*l)[0] *l = (*l)[1:] return s, nil } func Forth(v []string) ([]int, error) { dict := make(dictionary) dict.add("+", add) dict.add("-", sub) dict.add("*", mul) dict.add("/", div) dict.add("dup", dup) dict.add("drop", drop) dict.add("swap", swap) dict.add("over", over) s := new(stack) for _, line := range v { // compile l := NewLexer(line) words, err := compile(dict, &l) if err != nil { return nil, err } // execute for _, w := range words { if err := w(s); err != nil { return nil, err } } } return s.values(), nil }