Skip to content

Instantly share code, notes, and snippets.

@w7dup
Last active December 16, 2015 08:19
Show Gist options
  • Save w7dup/5405173 to your computer and use it in GitHub Desktop.
Save w7dup/5405173 to your computer and use it in GitHub Desktop.
Exercise with Go. A super simple forth/four function integer RPN calculator.
package main
import (
"bytes"
"fmt"
"io"
"strconv"
"strings"
)
type Stack struct {
top *Element
size int
}
type Element struct {
value int
next *Element
}
// Return the stack's length
func (s *Stack) Len() int {
return s.size
}
// Push a new element onto the stack
func (s *Stack) Push(value int) {
s.top = &Element{value, s.top}
s.size++
}
// Remove the top element from the stack and return it's value
// If the stack is empty, return nil
func (s *Stack) Pop() (value int) {
if s.size > 0 {
value, s.top = s.top.value, s.top.next
s.size--
return
}
return 0
}
// Print representation of Stack
func (s *Stack) String() (value string) {
if s.size == 0 {
return "<0>"
}
values := make([]string, s.size)
el := s.top
for i := s.size; i > 0; i-- {
values[i-1] = strconv.Itoa(el.value)
el = el.next
}
value = fmt.Sprintf("<%v> %v", s.size, strings.Join(values, " "))
return
}
func nextWord(r *strings.Reader) (string, error) {
whitespace := " \t\n\r"
var value bytes.Buffer
rune, _, err := r.ReadRune()
if err == io.EOF {
return "", io.EOF
}
for err != io.EOF {
if strings.ContainsRune(whitespace, rune) {
if value.Len() > 0 {
return value.String(), nil
} else {
continue
}
}
value.WriteRune(rune)
rune, _, err = r.ReadRune()
}
return value.String(), nil
}
func eval(stack *Stack, s string) error {
reader := strings.NewReader(s)
word, err := nextWord(reader)
for err != io.EOF {
if word == "swap" {
a := stack.Pop()
b := stack.Pop()
stack.Push(a)
stack.Push(b)
} else if word == "dup" {
v := stack.Pop()
stack.Push(v)
stack.Push(v)
} else if word == "over" {
a := stack.Pop()
b := stack.Pop()
stack.Push(a)
stack.Push(b)
stack.Push(a)
} else if word == "rot" {
a := stack.Pop()
b := stack.Pop()
c := stack.Pop()
stack.Push(a)
stack.Push(c)
stack.Push(b)
} else if word == "drop" {
stack.Pop()
} else if word == "+" {
a := stack.Pop()
b := stack.Pop()
v := a + b
stack.Push(v)
} else if word == "-" {
a := stack.Pop()
b := stack.Pop()
v := b - a
stack.Push(v)
} else if word == "*" {
a := stack.Pop()
b := stack.Pop()
v := a * b
stack.Push(v)
} else if word == "/" {
a := stack.Pop()
b := stack.Pop()
v := b / a
stack.Push(v)
} else if word == "mod" {
a := stack.Pop()
b := stack.Pop()
v := b % a
stack.Push(v)
} else if word == "/mod" {
a := stack.Pop()
b := stack.Pop()
q := b / a
r := b % a
stack.Push(r)
stack.Push(q)
} else if word == "." {
a := stack.Pop()
fmt.Printf("%v ", a)
} else if word == ".S" {
fmt.Printf("%v ", stack.String())
} else {
i, err := strconv.ParseInt(word, 0, 0)
if err != nil {
return err
} else {
stack.Push(int(i))
}
}
word, err = nextWord(reader)
}
return nil
}
func main() {
stack := new(Stack)
inputs := []string{
"1 2 3 + +",
"1 2 3 rot"}
for _, in := range inputs {
fmt.Printf("%v↵ ", in)
err := eval(stack, in)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%v ok\n", stack.String())
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment