1160 lines
25 KiB
Go
1160 lines
25 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
|
|
e := list.Exprs[n]
|
|
list.Exprs = list.Exprs[:n]
|
|
push(list)
|
|
push(e)
|
|
} else {
|
|
return fmt.Errorf("cannot `split` not a list @%v", idx)
|
|
}
|
|
case CONS:
|
|
o, eo := pop()
|
|
if eo != nil {
|
|
return eo
|
|
}
|
|
list, e := pop()
|
|
if e != nil {
|
|
return e
|
|
}
|
|
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 MEASURE:
|
|
list, e := pop()
|
|
if e != nil {
|
|
return e
|
|
}
|
|
if list.Tok == LIST {
|
|
push(Expr{NUMBER, nil, float64(len(list.Exprs)), 0})
|
|
} else {
|
|
return fmt.Errorf("cannot `measure` 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:
|
|
if runtime.GOARCH == "wasm" {
|
|
// In WASM mode fetch does a fetch request from a foreign website
|
|
|
|
v1, e := pop()
|
|
if e != nil {
|
|
return e
|
|
}
|
|
|
|
if v1.Tok == STRING {
|
|
url := v1.Value.(string)
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
// we will get an error at this stage if the request fails, such as if the
|
|
// requested URL is not found, or if the server is not reachable.
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// print the response
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
push(Expr{STRING, nil, string(data), 0})
|
|
} else {
|
|
return fmt.Errorf("cannot `streq?` value is not a string @%v", idx)
|
|
}
|
|
} else {
|
|
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
|
|
}
|