varaq-interpreter-go/varaq/interpreter.go

1124 lines
24 KiB
Go

package varaq
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"math"
"math/rand"
"net"
"net/http"
"os"
"runtime"
"strconv"
"strings"
"time"
)
var globals = map[string]Expr{}
var stack = make([]Expr, 0)
var s = bufio.NewScanner(os.Stdin)
func get(k string) Expr {
return globals[k]
}
func put(k string, v Expr) {
globals[k] = v
}
func push(o Expr) {
stack = append(stack, o)
}
func top() (Expr, error) {
n := len(stack) - 1
if n < 0 {
return Expr{}, fmt.Errorf("stack underflow")
}
return stack[n], nil
}
func pop() (Expr, error) {
res, e := top()
if e != nil {
return Expr{}, e
}
n := len(stack) - 1
stack = stack[:n]
return res, nil
}
func popExpr() (Expr, error) {
a, e := pop()
if e != nil {
return Expr{}, e
}
if a.Tok == IDENTIFIER {
a = get(a.Value.(string))
}
return a, nil
}
func pop2Expr() (Expr, Expr, error) {
b, e := popExpr()
if e != nil {
return Expr{}, Expr{}, e
}
a, e := popExpr()
if e != nil {
return Expr{}, Expr{}, e
}
return b, a, nil
}
func identifierToObj(c Expr, idx int, argv []string, w io.Writer) error {
k, k_ok := c.Value.(string)
if k_ok {
o := get(k)
if o.Tok == UNDEFINED {
return fmt.Errorf("identifier %s undefined @%v", k, idx)
} else if o.Tok == FUNCTION {
e := Interpret(o, argv, w)
if e != nil {
if e == fmt.Errorf("escape") {
return nil
}
return e
}
} else {
push(o)
}
} else {
return fmt.Errorf("idenifier not string @%v", idx)
}
return nil
}
func (c Expr) idToExpr(idx int, argv []string, w io.Writer) (Expr, error) {
e := identifierToObj(c, idx, argv, w)
if e != nil {
return Expr{UNDEFINED, nil, nil, 0}, e
}
vv, e2 := pop()
if e2 != nil {
return Expr{UNDEFINED, nil, nil, 0}, e2
}
return vv, nil
}
func (c Expr) idToS(idx int, argv []string, w io.Writer) (string, error) {
v, e := c.idToExpr(idx, argv, w)
if e != nil {
return "", e
}
return fmt.Sprintf("%b", v.Value), nil
}
func popArgs(idx int, argv []string, w io.Writer) (Expr, Expr, error) {
b, e := pop()
if e != nil {
return Expr{UNDEFINED, nil, nil, 0}, Expr{UNDEFINED, nil, nil, 0}, e
}
if b.Tok == IDENTIFIER {
b, e = b.idToExpr(idx, argv, w)
if e != nil {
return Expr{UNDEFINED, nil, nil, 0}, Expr{UNDEFINED, nil, nil, 0}, e
}
}
a, e := pop()
if e != nil {
return Expr{UNDEFINED, nil, nil, 0}, Expr{UNDEFINED, nil, nil, 0}, e
}
if a.Tok == IDENTIFIER {
a, e = a.idToExpr(idx, argv, w)
if e != nil {
return Expr{UNDEFINED, nil, nil, 0}, Expr{UNDEFINED, nil, nil, 0}, e
}
}
return a, b, nil
}
func Interpret(code Expr, argv []string, w io.Writer) error {
for idx, c := range code.Exprs {
switch c.Tok {
case NUMBER:
push(c)
case STRING:
push(c)
case LIST:
push(c)
case FUNCTION:
push(c)
case TRUE:
push(Expr{BOOLEAN, nil, true, 0})
case FALSE:
push(Expr{BOOLEAN, nil, false, 0})
case POP:
_, e := pop()
if e != nil {
return e
}
case DUP:
obj, e := pop()
if e != nil {
return e
}
push(obj)
push(obj)
case EXCH:
obj1, e1 := pop()
if e1 != nil {
return e1
}
obj2, e2 := pop()
if e2 != nil {
return e2
}
push(obj1)
push(obj2)
case OVER:
n := len(stack) - 1
if n-1 < 0 {
return fmt.Errorf("over: stack underflow")
}
push(stack[n-1])
case ROT:
n := len(stack) - 1
if n-3 < 0 {
return fmt.Errorf("stack underflow, must have 3 or more")
}
push(stack[n-2])
stack = append(stack[:n-2], stack[n-3:]...)
case DEPTH:
push(Expr{NUMBER, nil, float64(len(stack)), 0})
case CLEAR:
stack = nil
stack = make([]Expr, 0)
case IDENTIFIER:
if idx-1 >= 0 && code.Exprs[idx-1].Tok == TILDE {
push(c)
} else {
e := identifierToObj(c, idx, argv, w)
if e != nil {
return e
}
}
case REMEMBER:
push(Expr{REMEMBER, nil, "REMEMBER", 0})
case FORGET:
v, e := pop()
if e != nil {
return e
}
for {
if v.Tok == REMEMBER || len(stack) == 0 {
break
}
v, e = pop()
if e != nil {
return e
}
}
case DUMP:
for n := len(stack) - 1; n >= 0; n-- {
fmt.Fprintf(w, "%v\n", stack[n])
}
fmt.Fprintf(w, "=== end of dump ===\n")
case NAME:
fun, e := pop()
if e != nil {
return e
}
name, en := pop()
if en != nil {
return en
}
namev, n_ok := name.Value.(string)
if fun.Tok == FUNCTION && name.Tok == IDENTIFIER && n_ok {
put(namev, fun)
} else {
return fmt.Errorf("cannot bind name @%v", idx)
}
case SET:
name, en := pop()
if en != nil {
return en
}
o, e := pop()
if e != nil {
return e
}
n, n_ok := name.Value.(string)
if (name.Tok == IDENTIFIER || name.Tok == STRING) && n_ok {
put(n, o)
} else {
return fmt.Errorf("cannot set, name is not a string @%v", idx)
}
case IFYES:
fun, ef := pop()
if ef != nil {
return ef
}
logical, el := pop()
if el != nil {
return el
}
if fun.Tok == FUNCTION && logical.Tok == BOOLEAN {
if logical.Value.(bool) {
Interpret(fun, argv, w)
}
} else {
return fmt.Errorf("cannot `ifyes` not a function or bool @%v", idx)
}
case IFNO:
fun, ef := pop()
if ef != nil {
return ef
}
logical, el := pop()
if el != nil {
return el
}
if fun.Tok == FUNCTION && logical.Tok == BOOLEAN {
if !logical.Value.(bool) {
Interpret(fun, argv, w)
}
} else {
return fmt.Errorf("cannot `ifno` not a function or bool @%v", idx)
}
case CHOOSE:
b, e := pop()
if e != nil {
return e
}
if b.Tok == BOOLEAN {
push(b)
push(b)
} else {
return fmt.Errorf("cannot `choose` not a bool @%v", idx)
}
case EVAL:
fun, e := pop()
if e != nil {
return e
}
if fun.Tok == FUNCTION {
Interpret(fun, argv, w)
} else {
return fmt.Errorf("cannot `eval` not a function @%v", idx)
}
case ESCAPE:
return fmt.Errorf("escape")
case REPEAT:
fun, e := pop()
if e != nil {
return e
}
count, ec := pop()
if ec != nil {
return ec
}
if fun.Tok == FUNCTION && count.Tok == NUMBER {
for n := int64(0); n < int64(count.Value.(float64)); n++ {
err := Interpret(fun, argv, w)
if err != nil {
return err
}
}
} else {
return fmt.Errorf("cannot `repeat` not a function or int @%v", idx)
}
case SPLIT:
list, e := pop()
if e != nil {
return e
}
if list.Tok == LIST {
n := len(list.Exprs) - 1
push(list.Exprs[n])
list.Exprs = list.Exprs[:n]
push(list)
} else {
return fmt.Errorf("cannot `split` not a list @%v", idx)
}
case CONS:
list, e := pop()
if e != nil {
return e
}
o, eo := pop()
if eo != nil {
return eo
}
if list.Tok == LIST {
list.Exprs = append(list.Exprs, o)
push(list)
} else {
return fmt.Errorf("cannot `cons` not a list @%v", idx)
}
case SHATTER:
list, e := pop()
if e != nil {
return e
}
if list.Tok == LIST {
for n := 0; n < len(list.Exprs); n++ {
push(list.Exprs[n])
}
} else {
return fmt.Errorf("cannot `shatter` not a list @%v", idx)
}
case EMPTY:
list, e := pop()
if e != nil {
return e
}
if list.Tok == LIST {
push(Expr{BOOLEAN, nil, len(list.Exprs) == 0, 0})
} else {
return fmt.Errorf("cannot `empty` not a list @%v", idx)
}
case COMPOSE:
v, e := pop()
if e != nil {
return e
}
var sb strings.Builder
for {
if v.Tok == REMEMBER || len(stack) == 0 {
break
}
v, e := pop()
if e != nil {
return e
}
if v.Tok == STRING {
sb.WriteString(v.Value.(string))
} else {
return fmt.Errorf("cannot `compose` value is not a string @%v", idx)
}
}
push(Expr{STRING, nil, sb.String(), 0})
case STREQ:
v1, e := pop()
if e != nil {
return e
}
v2, e2 := pop()
if e2 != nil {
return e2
}
if v1.Tok == STRING && v2.Tok == STRING {
push(Expr{BOOLEAN, nil, v1.Value.(string) == v2.Value.(string), 0})
} else {
return fmt.Errorf("cannot `streq?` value is not a string @%v", idx)
}
case STRTIE:
var e error
v2, e := pop()
if e != nil {
return e
}
v1, e := pop()
if e != nil {
return e
}
var v1_s string
var v2_s string
if v1.Tok == IDENTIFIER {
v1_s, e = v1.idToS(idx, argv, w)
if e != nil {
return e
}
} else if v1.Tok == STRING {
v1_s = v1.Value.(string)
} else {
var d float64
err := false
switch v := v1.Value.(type) {
case float64:
d = float64(v)
default:
err = true
}
if err {
v1_s = fmt.Sprintf("%v", v1.Value)
} else if d == float64(int64(d)) {
v1_s = fmt.Sprintf("%d", int64(v1.Value.(float64)))
} else {
v1_s = fmt.Sprintf("%f", v1.Value.(float64))
}
}
if v2.Tok == IDENTIFIER {
v2_s, e = v2.idToS(idx, argv, w)
if e != nil {
return e
}
} else if v2.Tok == STRING {
v2_s = v2.Value.(string)
} else {
var d float64
err := false
switch v := v2.Value.(type) {
case float64:
d = float64(v)
default:
err = true
}
if err {
v2_s = fmt.Sprintf("%v", v2.Value)
} else if d == float64(int64(d)) {
v2_s = fmt.Sprintf("%d", int64(v2.Value.(float64)))
} else {
v2_s = fmt.Sprintf("%f", v2.Value.(float64))
}
}
push(Expr{STRING, nil, v1_s + v2_s, 0})
case STRCUT:
endval, e := pop()
if e != nil {
return e
}
startval, e := pop()
if e != nil {
return e
}
str, e := pop()
if e != nil {
return e
}
if str.Tok == STRING && startval.Tok == NUMBER && endval.Tok == NUMBER {
push(Expr{STRING, nil, str.Value.(string)[int64(startval.Value.(float64)):int64(endval.Value.(float64))], 0})
} else {
return fmt.Errorf("cannot `strcut` value is not a string or start/end val is not an int @%v", idx)
}
case STRMEASURE:
v, e := pop()
if e != nil {
return e
}
if v.Tok == STRING {
push(Expr{NUMBER, nil, float64(len(v.Value.(string))), 0})
} else {
return fmt.Errorf("cannot `strmeasure` value is not a string @%v", idx)
}
case EXPLODE:
str, e := pop()
if e != nil {
return e
}
if str.Tok == STRING {
words := strings.Fields(str.Value.(string))
for _, s := range words {
push(Expr{STRING, nil, s, 0})
}
}
case ADD:
a, b, e := popArgs(idx, argv, w)
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{NUMBER, nil, a.Value.(float64) + (b.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `add` values are not numbers @%v", idx)
}
case SUB:
a, b, e := popArgs(idx, argv, w)
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{NUMBER, nil, a.Value.(float64) - (b.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `sub` values are not numbers @%v", idx)
}
case MUL:
a, b, e := popArgs(idx, argv, w)
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{NUMBER, nil, a.Value.(float64) * (b.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `mul` values are not numbers @%v", idx)
}
case DIV:
a, b, e := popArgs(idx, argv, w)
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{NUMBER, nil, a.Value.(float64) / (b.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `div` values are not numbers @%v", idx)
}
case IDIV:
a, b, e := popArgs(idx, argv, w)
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Round(a.Value.(float64)) / math.Round(b.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `idiv` values are not numbers @%v", idx)
}
case MOD:
a, b, e := popArgs(idx, argv, w)
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{NUMBER, nil, float64(int64(a.Value.(float64)) % int64(b.Value.(float64))), 0})
} else {
return fmt.Errorf("cannot `mod` values are not numbers @%v", idx)
}
case POW:
a, b, e := popArgs(idx, argv, w)
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Pow(a.Value.(float64), b.Value.(float64)), 0})
}
case SQRT:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Sqrt(a.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `sqrt` value is not number or float @%v", idx)
}
case ADD1:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, a.Value.(float64) + 1.0, 0})
} else {
return fmt.Errorf("cannot `add1` value is not a number @%v", idx)
}
case SUB1:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, a.Value.(float64) - 1.0, 0})
} else {
return fmt.Errorf("cannot `sub1` value is not a number @%v", idx)
}
case SIN:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Sin(a.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `sin` values are not numbers or floats @%v", idx)
}
case COS:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Cos(a.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `cos` values are not numbers or floats @%v", idx)
}
case TAN:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Tan(a.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `tan` values are not numbers or floats @%v", idx)
}
case ATAN:
a, e := popExpr()
if e != nil {
return e
}
den, e := pop()
if e != nil {
return e
}
if den.Tok == IDENTIFIER {
den = get(den.Value.(string))
}
if a.Tok == NUMBER && den.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Atan(a.Value.(float64) / den.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `atan` values are not numbers or floats @%v", idx)
}
case LN:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Log(a.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `ln` values are not numbers or floats @%v", idx)
}
case LOG:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Log10(a.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `log` values are not numbers or floats @%v", idx)
}
case LOG3:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Log(a.Value.(float64)) / math.Log(3.0), 0})
} else {
return fmt.Errorf("cannot `log3` values are not numbers or floats @%v", idx)
}
case CLIP:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Floor(a.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `clip` values are not numbers or floats @%v", idx)
}
case SMOOTH:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Round(a.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `smooth` values are not numbers or floats @%v", idx)
}
case HOWMUCH:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, math.Abs(a.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `howmuch` values are not numbers or floats @%v", idx)
}
case SETRAND:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
rand.Seed(int64(a.Value.(float64)))
} else {
return fmt.Errorf("cannot `setrand` values are not numbers or floats @%v", idx)
}
case RAND:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
max := a.Value.(float64)
r := rand.Intn(int(max))
push(Expr{NUMBER, nil, float64(r), 0})
} else {
return fmt.Errorf("cannot `rand` values are not numbers or floats @%v", idx)
}
case PI:
push(Expr{NUMBER, nil, 3.14159265358979323846, 0})
case E:
push(Expr{NUMBER, nil, 2.71828182845904523536, 0})
case ISINT:
a, e := popExpr()
if e != nil {
return e
}
push(Expr{BOOLEAN, nil, a.Value.(float64) == float64(int64(a.Value.(float64))), 0})
case ISNUMBER:
a, e := popExpr()
if e != nil {
return e
}
push(Expr{BOOLEAN, nil, (a.Tok == NUMBER), 0})
case NUMBERIZE:
str, e := pop()
if e != nil {
return e
}
if str.Tok != STRING {
return fmt.Errorf("cannot `numberize` not a string @%v", idx)
}
num, err := strconv.ParseFloat(str.Value.(string), 64)
if err != nil {
return err
}
push(Expr{NUMBER, nil, num, 0})
case ISOLATE:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{NUMBER, nil, float64(int64(a.Value.(float64)) & int64(b.Value.(float64))), 0})
} else {
return fmt.Errorf("cannot `isolate` values are not numbers @%v", idx)
}
case MIX:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{NUMBER, nil, float64(int64(a.Value.(float64)) | int64(b.Value.(float64))), 0})
} else {
return fmt.Errorf("cannot `mix` values are not numbers @%v", idx)
}
case CONTRADICT:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{NUMBER, nil, float64(int64(a.Value.(float64)) ^ int64(b.Value.(float64))), 0})
} else {
return fmt.Errorf("cannot `contradict` values are not numbers @%v", idx)
}
case COMPL:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{NUMBER, nil, float64(^int64(a.Value.(float64))), 0})
} else {
return fmt.Errorf("cannot `compl` values are not numbers @%v", idx)
}
case SHIFTRIGHT:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{NUMBER, nil, float64(int64(a.Value.(float64)) >> int64(b.Value.(float64))), 0})
} else {
return fmt.Errorf("cannot `shiftright` values are not numbers @%v", idx)
}
case SHIFTLEFT:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{NUMBER, nil, float64(int64(a.Value.(float64)) << int64(b.Value.(float64))), 0})
} else {
return fmt.Errorf("cannot `shiftleft` values are not numbers @%v", idx)
}
case GT:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{BOOLEAN, nil, a.Value.(float64) > (b.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `gt` values are not numbers @%v", idx)
}
case LT:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{BOOLEAN, nil, a.Value.(float64) < (b.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `lt` values are not numbers @%v", idx)
}
case EQ:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{BOOLEAN, nil, a.Value.(float64) == b.Value.(float64), 0})
} else {
return fmt.Errorf("cannot `eq` values are not numbers @%v", idx)
}
case GE:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{BOOLEAN, nil, a.Value.(float64) >= (b.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `ge` values are not numbers @%v", idx)
}
case LE:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{BOOLEAN, nil, a.Value.(float64) <= (b.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `le` values are not numbers @%v", idx)
}
case NE:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == NUMBER && b.Tok == NUMBER {
push(Expr{BOOLEAN, nil, a.Value.(float64) != (b.Value.(float64)), 0})
} else {
return fmt.Errorf("cannot `ne` values are not numbers @%v", idx)
}
case ISNULL:
a, e := popExpr()
if e != nil {
return e
}
push(Expr{BOOLEAN, nil, (a.Tok == NULL || a.Value == nil), 0})
case NEGATIVE:
a, e := popExpr()
if e != nil {
return e
}
if a.Tok == NUMBER {
push(Expr{BOOLEAN, nil, a.Value.(float64) < 0.0, 0})
} else {
return fmt.Errorf("cannot `negative` values are not numbers @%v", idx)
}
case AND:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == BOOLEAN && b.Tok == BOOLEAN {
push(Expr{BOOLEAN, nil, (a.Value.(bool) && b.Value.(bool)), 0})
} else {
return fmt.Errorf("cannot `and` values are not numbers @%v", idx)
}
case OR:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == BOOLEAN && b.Tok == BOOLEAN {
push(Expr{BOOLEAN, nil, (a.Value.(bool) || b.Value.(bool)), 0})
} else {
return fmt.Errorf("cannot `or` values are not numbers @%v", idx)
}
case XOR:
b, a, e := pop2Expr()
if e != nil {
return e
}
if a.Tok == BOOLEAN && b.Tok == BOOLEAN {
push(Expr{BOOLEAN, nil, (a.Value.(bool) != b.Value.(bool)), 0})
} else {
return fmt.Errorf("cannot `xor` values are not numbers @%v", idx)
}
case DISP:
a, e := pop()
if e != nil {
return e
}
if a.Tok == IDENTIFIER {
fmt.Fprintf(w, "%v", get(a.Value.(string)))
} else if a.Tok == NUMBER {
if a.Value.(float64) == float64(int64(a.Value.(float64))) {
fmt.Fprintf(w, "%d", int64(a.Value.(float64)))
} else {
fmt.Fprintf(w, "%f", a.Value.(float64))
}
} else {
fmt.Fprintf(w, "%v", a.Value)
}
case LISTEN:
// FIXME: should there be a way to do this in wasm??
if runtime.GOARCH == "wasm" {
return nil
}
var str string
s.Scan()
str = s.Text()
if s.Err() != nil {
return s.Err()
}
push(Expr{STRING, nil, str, 0})
case COMPLAIN:
a, e := popExpr()
if e != nil {
return e
}
fmt.Fprintf(os.Stderr, "%v", a.Value)
case NEWLINE:
fmt.Fprintf(w, "\n")
case TAB:
fmt.Fprintf(w, "\t")
case WHEREAMI:
if runtime.GOARCH == "wasm" {
return nil
}
conn, e := net.Dial("udp", "8.8.8.8:80")
if e != nil {
return e
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
fmt.Fprintf(w, "%s\n", localAddr.IP.String())
case SERVEHTTP:
if runtime.GOARCH == "wasm" {
return nil
}
str, e := pop()
if e != nil {
return e
}
if str.Tok != STRING {
return fmt.Errorf("cannot `servehttp` exptected a string for the port @%v", idx)
}
fun, e := pop()
if e != nil {
return e
}
if fun.Tok != FUNCTION {
return fmt.Errorf("cannot `servehttp` exptected a function to serve @%v", idx)
}
http.ListenAndServe(str.Value.(string), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
req := make([]string, 0)
b, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println(err)
return
}
req = append(req, r.RequestURI)
req = append(req, string(b))
req = append(req, r.Method)
// maybe should add the headers here
// add the number of arguments for the header
err = Interpret(fun, req, w)
if err != nil {
fmt.Println(err)
}
}))
case READALL:
str, e := pop()
if e != nil {
return e
}
if str.Tok != STRING {
return fmt.Errorf("cannot `readall` @%v", idx)
}
filedata, err := ioutil.ReadFile(str.Value.(string)) // the file is inside the local directory
if err != nil {
return err
}
push(Expr{STRING, nil, string(filedata), 0})
case VERSION:
fmt.Fprintf(w, "var'aq -- 0.9.2 Martoq")
case ARGV:
arg := Expr{LIST, make([]Expr, 0), nil, 0}
for _, v := range argv {
arg.Exprs = append(arg.Exprs, Expr{STRING, nil, v, 0})
}
push(arg)
case TIME:
push(Expr{NUMBER, nil, float64(time.Now().Unix()), 0})
case TILDE:
default:
return fmt.Errorf("unknown opcode")
}
}
return nil
}