477 lines
12 KiB
Go
477 lines
12 KiB
Go
package vm
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/mattn/kinako/ast"
|
|
)
|
|
|
|
var (
|
|
NilValue = reflect.ValueOf((*interface{})(nil))
|
|
NilType = reflect.TypeOf((*interface{})(nil))
|
|
TrueValue = reflect.ValueOf(true)
|
|
FalseValue = reflect.ValueOf(false)
|
|
)
|
|
|
|
// Error provides a convenient interface for handling runtime error.
|
|
// It can be Error interface with type cast which can call Pos().
|
|
type Error struct {
|
|
Message string
|
|
}
|
|
|
|
var (
|
|
BreakError = errors.New("Unexpected break statement")
|
|
ContinueError = errors.New("Unexpected continue statement")
|
|
ReturnError = errors.New("Unexpected return statement")
|
|
InterruptError = errors.New("Execution interrupted")
|
|
)
|
|
|
|
// Error returns the error message.
|
|
func (e *Error) Error() string {
|
|
return e.Message
|
|
}
|
|
|
|
// Func is function interface to reflect functions internaly.
|
|
type Func func(args ...reflect.Value) (reflect.Value, error)
|
|
|
|
// Run executes statements in the specified environment.
|
|
func Run(stmts []ast.Stmt, env *Env) (reflect.Value, error) {
|
|
rv := NilValue
|
|
var err error
|
|
for _, stmt := range stmts {
|
|
rv, err = RunSingleStmt(stmt, env)
|
|
if err != nil {
|
|
return rv, err
|
|
}
|
|
}
|
|
return rv, nil
|
|
}
|
|
|
|
// Interrupts the execution of any running statements in the specified environment.
|
|
//
|
|
// Note that the execution is not instantly aborted: after a call to Interrupt,
|
|
// the current running statement will finish, but the next statement will not run,
|
|
// and instead will return a NilValue and an InterruptError.
|
|
func Interrupt(env *Env) {
|
|
env.Lock()
|
|
*(env.interrupt) = true
|
|
env.Unlock()
|
|
}
|
|
|
|
// RunSingleStmt executes one statement in the specified environment.
|
|
func RunSingleStmt(stmt ast.Stmt, env *Env) (reflect.Value, error) {
|
|
env.Lock()
|
|
if *(env.interrupt) {
|
|
*(env.interrupt) = false
|
|
env.Unlock()
|
|
|
|
return NilValue, InterruptError
|
|
}
|
|
env.Unlock()
|
|
|
|
switch stmt := stmt.(type) {
|
|
case *ast.ExprStmt:
|
|
rv, err := invokeExpr(stmt.Expr, env)
|
|
if err != nil {
|
|
return rv, err
|
|
}
|
|
return rv, nil
|
|
case *ast.LetStmt:
|
|
rv := NilValue
|
|
var err error
|
|
rv, err = invokeExpr(stmt.Rhs, env)
|
|
if err != nil {
|
|
return rv, err
|
|
}
|
|
_, err = invokeLetExpr(stmt.Lhs, rv, env)
|
|
if err != nil {
|
|
return rv, err
|
|
}
|
|
return rv, nil
|
|
default:
|
|
return NilValue, errors.New("unknown statement")
|
|
}
|
|
}
|
|
|
|
// toString converts all reflect.Value-s into string.
|
|
func toString(v reflect.Value) string {
|
|
if v.Kind() == reflect.Interface {
|
|
v = v.Elem()
|
|
}
|
|
if v.Kind() == reflect.String {
|
|
return v.String()
|
|
}
|
|
if !v.IsValid() {
|
|
return "nil"
|
|
}
|
|
return fmt.Sprint(v.Interface())
|
|
}
|
|
|
|
// toBool converts all reflect.Value-s into bool.
|
|
func toBool(v reflect.Value) bool {
|
|
if v.Kind() == reflect.Interface {
|
|
v = v.Elem()
|
|
}
|
|
|
|
switch v.Kind() {
|
|
case reflect.Float32, reflect.Float64:
|
|
return v.Float() != 0.0
|
|
case reflect.Int, reflect.Int32, reflect.Int64:
|
|
return v.Int() != 0
|
|
case reflect.Bool:
|
|
return v.Bool()
|
|
case reflect.String:
|
|
if v.String() == "true" {
|
|
return true
|
|
}
|
|
if toInt64(v) != 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// toFloat64 converts all reflect.Value-s into float64.
|
|
func toFloat64(v reflect.Value) float64 {
|
|
if v.Kind() == reflect.Interface {
|
|
v = v.Elem()
|
|
}
|
|
switch v.Kind() {
|
|
case reflect.Float32, reflect.Float64:
|
|
return v.Float()
|
|
case reflect.Int, reflect.Int32, reflect.Int64:
|
|
return float64(v.Int())
|
|
}
|
|
return 0.0
|
|
}
|
|
|
|
func isNil(v reflect.Value) bool {
|
|
if !v.IsValid() || v.Kind().String() == "unsafe.Pointer" {
|
|
return true
|
|
}
|
|
if (v.Kind() == reflect.Interface || v.Kind() == reflect.Ptr) && v.IsNil() {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isNum(v reflect.Value) bool {
|
|
switch v.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// equal returns true when lhsV and rhsV is same value.
|
|
func equal(lhsV, rhsV reflect.Value) bool {
|
|
lhsIsNil, rhsIsNil := isNil(lhsV), isNil(rhsV)
|
|
if lhsIsNil && rhsIsNil {
|
|
return true
|
|
}
|
|
if (!lhsIsNil && rhsIsNil) || (lhsIsNil && !rhsIsNil) {
|
|
return false
|
|
}
|
|
if lhsV.Kind() == reflect.Interface || lhsV.Kind() == reflect.Ptr {
|
|
lhsV = lhsV.Elem()
|
|
}
|
|
if rhsV.Kind() == reflect.Interface || rhsV.Kind() == reflect.Ptr {
|
|
rhsV = rhsV.Elem()
|
|
}
|
|
if !lhsV.IsValid() || !rhsV.IsValid() {
|
|
return true
|
|
}
|
|
if isNum(lhsV) && isNum(rhsV) {
|
|
if rhsV.Type().ConvertibleTo(lhsV.Type()) {
|
|
rhsV = rhsV.Convert(lhsV.Type())
|
|
}
|
|
}
|
|
if lhsV.CanInterface() && rhsV.CanInterface() {
|
|
return reflect.DeepEqual(lhsV.Interface(), rhsV.Interface())
|
|
}
|
|
return reflect.DeepEqual(lhsV, rhsV)
|
|
}
|
|
|
|
// toInt64 converts all reflect.Value-s into int64.
|
|
func toInt64(v reflect.Value) int64 {
|
|
if v.Kind() == reflect.Interface {
|
|
v = v.Elem()
|
|
}
|
|
switch v.Kind() {
|
|
case reflect.Float32, reflect.Float64:
|
|
return int64(v.Float())
|
|
case reflect.Int, reflect.Int32, reflect.Int64:
|
|
return v.Int()
|
|
case reflect.String:
|
|
s := v.String()
|
|
var i int64
|
|
var err error
|
|
if strings.HasPrefix(s, "0x") {
|
|
i, err = strconv.ParseInt(s, 16, 64)
|
|
} else {
|
|
i, err = strconv.ParseInt(s, 10, 64)
|
|
}
|
|
if err == nil {
|
|
return int64(i)
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func invokeLetExpr(expr ast.Expr, rv reflect.Value, env *Env) (reflect.Value, error) {
|
|
switch lhs := expr.(type) {
|
|
case *ast.IdentExpr:
|
|
if env.Set(lhs.Lit, rv) != nil {
|
|
if strings.Contains(lhs.Lit, ".") {
|
|
return NilValue, fmt.Errorf("Undefined symbol '%s'", lhs.Lit)
|
|
}
|
|
env.Define(lhs.Lit, rv)
|
|
}
|
|
return rv, nil
|
|
}
|
|
return NilValue, errors.New("Invalid operation")
|
|
}
|
|
|
|
// invokeExpr evaluates one expression.
|
|
func invokeExpr(expr ast.Expr, env *Env) (reflect.Value, error) {
|
|
switch e := expr.(type) {
|
|
case *ast.NumberExpr:
|
|
if strings.Contains(e.Lit, ".") || strings.Contains(e.Lit, "e") {
|
|
v, err := strconv.ParseFloat(e.Lit, 64)
|
|
if err != nil {
|
|
return NilValue, err
|
|
}
|
|
return reflect.ValueOf(float64(v)), nil
|
|
}
|
|
var i int64
|
|
var err error
|
|
if strings.HasPrefix(e.Lit, "0x") {
|
|
i, err = strconv.ParseInt(e.Lit[2:], 16, 64)
|
|
} else {
|
|
i, err = strconv.ParseInt(e.Lit, 10, 64)
|
|
}
|
|
if err != nil {
|
|
return NilValue, err
|
|
}
|
|
return reflect.ValueOf(i), nil
|
|
case *ast.IdentExpr:
|
|
return env.Get(e.Lit)
|
|
case *ast.StringExpr:
|
|
return reflect.ValueOf(e.Lit), nil
|
|
case *ast.UnaryExpr:
|
|
v, err := invokeExpr(e.Expr, env)
|
|
if err != nil {
|
|
return v, err
|
|
}
|
|
switch e.Operator {
|
|
case "-":
|
|
if v.Kind() == reflect.Float64 {
|
|
return reflect.ValueOf(-v.Float()), nil
|
|
}
|
|
return reflect.ValueOf(-v.Int()), nil
|
|
case "^":
|
|
return reflect.ValueOf(^toInt64(v)), nil
|
|
case "!":
|
|
return reflect.ValueOf(!toBool(v)), nil
|
|
default:
|
|
return NilValue, errors.New("Unknown operator ''")
|
|
}
|
|
case *ast.ParenExpr:
|
|
v, err := invokeExpr(e.SubExpr, env)
|
|
if err != nil {
|
|
return v, err
|
|
}
|
|
return v, nil
|
|
case *ast.BinOpExpr:
|
|
lhsV := NilValue
|
|
rhsV := NilValue
|
|
var err error
|
|
|
|
lhsV, err = invokeExpr(e.Lhs, env)
|
|
if err != nil {
|
|
return lhsV, err
|
|
}
|
|
if lhsV.Kind() == reflect.Interface {
|
|
lhsV = lhsV.Elem()
|
|
}
|
|
if e.Rhs != nil {
|
|
rhsV, err = invokeExpr(e.Rhs, env)
|
|
if err != nil {
|
|
return rhsV, err
|
|
}
|
|
if rhsV.Kind() == reflect.Interface {
|
|
rhsV = rhsV.Elem()
|
|
}
|
|
}
|
|
switch e.Operator {
|
|
case "+":
|
|
if lhsV.Kind() == reflect.String || rhsV.Kind() == reflect.String {
|
|
return reflect.ValueOf(toString(lhsV) + toString(rhsV)), nil
|
|
}
|
|
if (lhsV.Kind() == reflect.Array || lhsV.Kind() == reflect.Slice) && (rhsV.Kind() != reflect.Array && rhsV.Kind() != reflect.Slice) {
|
|
return reflect.Append(lhsV, rhsV), nil
|
|
}
|
|
if (lhsV.Kind() == reflect.Array || lhsV.Kind() == reflect.Slice) && (rhsV.Kind() == reflect.Array || rhsV.Kind() == reflect.Slice) {
|
|
return reflect.AppendSlice(lhsV, rhsV), nil
|
|
}
|
|
if lhsV.Kind() == reflect.Float64 || rhsV.Kind() == reflect.Float64 {
|
|
return reflect.ValueOf(toFloat64(lhsV) + toFloat64(rhsV)), nil
|
|
}
|
|
return reflect.ValueOf(toInt64(lhsV) + toInt64(rhsV)), nil
|
|
case "-":
|
|
if lhsV.Kind() == reflect.Float64 || rhsV.Kind() == reflect.Float64 {
|
|
return reflect.ValueOf(toFloat64(lhsV) - toFloat64(rhsV)), nil
|
|
}
|
|
return reflect.ValueOf(toInt64(lhsV) - toInt64(rhsV)), nil
|
|
case "*":
|
|
if lhsV.Kind() == reflect.String && (rhsV.Kind() == reflect.Int || rhsV.Kind() == reflect.Int32 || rhsV.Kind() == reflect.Int64) {
|
|
return reflect.ValueOf(strings.Repeat(toString(lhsV), int(toInt64(rhsV)))), nil
|
|
}
|
|
if lhsV.Kind() == reflect.Float64 || rhsV.Kind() == reflect.Float64 {
|
|
return reflect.ValueOf(toFloat64(lhsV) * toFloat64(rhsV)), nil
|
|
}
|
|
return reflect.ValueOf(toInt64(lhsV) * toInt64(rhsV)), nil
|
|
case "/":
|
|
return reflect.ValueOf(toFloat64(lhsV) / toFloat64(rhsV)), nil
|
|
case "%":
|
|
return reflect.ValueOf(toInt64(lhsV) % toInt64(rhsV)), nil
|
|
case "==":
|
|
return reflect.ValueOf(equal(lhsV, rhsV)), nil
|
|
case "!=":
|
|
return reflect.ValueOf(equal(lhsV, rhsV) == false), nil
|
|
case ">":
|
|
return reflect.ValueOf(toFloat64(lhsV) > toFloat64(rhsV)), nil
|
|
case ">=":
|
|
return reflect.ValueOf(toFloat64(lhsV) >= toFloat64(rhsV)), nil
|
|
case "<":
|
|
return reflect.ValueOf(toFloat64(lhsV) < toFloat64(rhsV)), nil
|
|
case "<=":
|
|
return reflect.ValueOf(toFloat64(lhsV) <= toFloat64(rhsV)), nil
|
|
case "|":
|
|
return reflect.ValueOf(toInt64(lhsV) | toInt64(rhsV)), nil
|
|
case "||":
|
|
if toBool(lhsV) {
|
|
return lhsV, nil
|
|
}
|
|
return rhsV, nil
|
|
case "&":
|
|
return reflect.ValueOf(toInt64(lhsV) & toInt64(rhsV)), nil
|
|
case "&&":
|
|
if toBool(lhsV) {
|
|
return rhsV, nil
|
|
}
|
|
return lhsV, nil
|
|
case "**":
|
|
if lhsV.Kind() == reflect.Float64 {
|
|
return reflect.ValueOf(math.Pow(toFloat64(lhsV), toFloat64(rhsV))), nil
|
|
}
|
|
return reflect.ValueOf(int64(math.Pow(toFloat64(lhsV), toFloat64(rhsV)))), nil
|
|
case ">>":
|
|
return reflect.ValueOf(toInt64(lhsV) >> uint64(toInt64(rhsV))), nil
|
|
case "<<":
|
|
return reflect.ValueOf(toInt64(lhsV) << uint64(toInt64(rhsV))), nil
|
|
default:
|
|
return NilValue, errors.New("Unknown operator")
|
|
}
|
|
case *ast.CallExpr:
|
|
f, err := env.Get(e.Name)
|
|
if err != nil {
|
|
return f, err
|
|
}
|
|
|
|
args := []reflect.Value{}
|
|
for i, expr := range e.SubExprs {
|
|
arg, err := invokeExpr(expr, env)
|
|
if err != nil {
|
|
return arg, err
|
|
}
|
|
|
|
if i < f.Type().NumIn() {
|
|
if !f.Type().IsVariadic() {
|
|
it := f.Type().In(i)
|
|
if arg.Kind().String() == "unsafe.Pointer" {
|
|
arg = reflect.New(it).Elem()
|
|
}
|
|
if arg.Kind() != it.Kind() && arg.IsValid() && arg.Type().ConvertibleTo(it) {
|
|
arg = arg.Convert(it)
|
|
} else if arg.Kind() == reflect.Func {
|
|
if _, isFunc := arg.Interface().(Func); isFunc {
|
|
rfunc := arg
|
|
arg = reflect.MakeFunc(it, func(args []reflect.Value) []reflect.Value {
|
|
for i := range args {
|
|
args[i] = reflect.ValueOf(args[i])
|
|
}
|
|
return rfunc.Call(args)[:it.NumOut()]
|
|
})
|
|
}
|
|
} else if !arg.IsValid() {
|
|
arg = reflect.Zero(it)
|
|
}
|
|
}
|
|
}
|
|
if !arg.IsValid() {
|
|
arg = NilValue
|
|
}
|
|
|
|
args = append(args, arg)
|
|
}
|
|
ret := NilValue
|
|
fnc := func() {
|
|
defer func() {
|
|
if os.Getenv("KINAKO_DEBUG") == "" {
|
|
if ex := recover(); ex != nil {
|
|
if e, ok := ex.(error); ok {
|
|
err = e
|
|
} else {
|
|
err = errors.New(fmt.Sprint(ex))
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
if f.Kind() == reflect.Interface {
|
|
f = f.Elem()
|
|
}
|
|
rets := f.Call(args)
|
|
if f.Type().NumOut() == 1 {
|
|
ret = rets[0]
|
|
} else {
|
|
var result []interface{}
|
|
for _, r := range rets {
|
|
result = append(result, r.Interface())
|
|
}
|
|
ret = reflect.ValueOf(result)
|
|
}
|
|
}
|
|
fnc()
|
|
if err != nil {
|
|
return ret, err
|
|
}
|
|
return ret, nil
|
|
case *ast.TernaryOpExpr:
|
|
rv, err := invokeExpr(e.Expr, env)
|
|
if err != nil {
|
|
return rv, err
|
|
}
|
|
if toBool(rv) {
|
|
lhsV, err := invokeExpr(e.Lhs, env)
|
|
if err != nil {
|
|
return lhsV, err
|
|
}
|
|
return lhsV, nil
|
|
}
|
|
rhsV, err := invokeExpr(e.Rhs, env)
|
|
if err != nil {
|
|
return rhsV, err
|
|
}
|
|
return rhsV, nil
|
|
default:
|
|
return NilValue, errors.New("Unknown expression")
|
|
}
|
|
}
|