You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

135 lines
2.4 KiB
Go

package main
import (
"bufio"
"os"
"strconv"
"strings"
)
type forthfunc func(*[]int, *int, []string) string
type operation struct {
op forthfunc
readuntil string
}
type numberError string
type bufferError string
func (n numberError) Error() string { return string(n) }
func (n bufferError) Error() string { return string(n) }
var stack []int
var stackpointer int
var dictionary = make(map[string]operation)
func forth(code string) forthfunc {
op := func(st *[]int, stp *int, _ []string) string {
return eval(code, nil)
}
return forthfunc(op)
}
func getvalue(st *[]int, stp *int) (int, error) {
if *stp < 1 {
return 0, bufferError("buffer underflow")
}
r := (*st)[*stp-1]
*st = (*st)[:len(*st)-1]
*stp--
return r, nil
}
func putvalue(st *[]int, stp *int, n int) error {
if *stp == int(^uint(0)>>1) { // check for maxint
return bufferError("buffer overflow")
}
*st = append(*st, n)
*stp++
return nil
}
func read(r *bufio.Reader) (string, error) {
s, err := r.ReadString('\n')
if err != nil {
return "", err
}
return s[:len(s)-1], err
}
func pow(n, p int) int {
r := 1
for i := 0; i < p; i++ {
r *= n
}
return r
}
func number(st *[]int, stp *int, cmd string) error {
base := 6
if len(cmd) < 1 {
return numberError("tried parsing empty value")
}
switch {
case cmd[0] == 'd':
base = 10
case cmd[0] == 'x':
base = 16
case cmd[0] == '0':
base = 8
case cmd[0] == 'b':
base = 2
case cmd[0] == '#':
base = 36
}
if base != 6 {
cmd = cmd[1:]
}
val, err := strconv.ParseInt(cmd, base, 0)
if err != nil {
return err
}
return putvalue(st, stp, int(val))
}
func eval(code string, inerr error) string {
if inerr != nil {
if code == "" {
os.Exit(0)
}
return "Error before evaluating code: " + inerr.Error()
}
var value string
cmds := strings.Split(code, " ")
for i := 0; i < len(cmds); i++ {
cmd := cmds[i]
op, ok := dictionary[cmd]
if ok {
var read []string
if op.readuntil != "" {
for j, c := range cmds[i+1:] {
if c == op.readuntil {
read = cmds[i+1 : i+j+1]
i += j + 1
break
}
}
}
value = op.op(&stack, &stackpointer, read)
continue
}
err := number(&stack, &stackpointer, cmd)
if err != nil {
return "Error reading value: " + err.Error()
}
}
return value
}
func main() {
stdin := bufio.NewReader(os.Stdin)
prelude()
for {
println(eval(read(stdin)))
}
}