1 Star 0 Fork 1

brucewang / qlang

forked from landy / qlang 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
code.go 9.90 KB
一键复制 编辑 原始数据 按行查看 历史
xushiwei 提交于 2016-06-16 16:37 . eql: error processing
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
package exec
import (
"errors"
"fmt"
"reflect"
"runtime/debug"
"sort"
"strings"
"qlang.io/qlang.spec.v1"
)
// -----------------------------------------------------------------------------
// type Stack
// A Stack represents a FILO container.
//
type Stack struct {
data []interface{}
}
// NewStack returns a new Stack.
//
func NewStack() *Stack {
data := make([]interface{}, 0, 16)
return &Stack{data}
}
// Push pushs a value into this stack.
//
func (p *Stack) Push(v interface{}) {
p.data = append(p.data, v)
}
// Top returns the last pushed value, if it exists.
//
func (p *Stack) Top() (v interface{}, ok bool) {
n := len(p.data)
if n > 0 {
v, ok = p.data[n-1], true
}
return
}
// Pop pops a value from this stack.
//
func (p *Stack) Pop() (v interface{}, ok bool) {
n := len(p.data)
if n > 0 {
v, ok = p.data[n-1], true
p.data = p.data[:n-1]
}
return
}
// PushRet pushs a function call result.
//
func (p *Stack) PushRet(ret []reflect.Value) error {
switch len(ret) {
case 0:
p.Push(nil)
case 1:
p.Push(ret[0].Interface())
default:
slice := make([]interface{}, len(ret))
for i, v := range ret {
slice[i] = v.Interface()
}
p.Push(slice)
}
return nil
}
// PopArgs pops arguments of a function call.
//
func (p *Stack) PopArgs(arity int) (args []reflect.Value, ok bool) {
pstk := p.data
n := len(pstk)
if n >= arity {
args, ok = make([]reflect.Value, arity), true
n -= arity
for i := 0; i < arity; i++ {
args[i] = reflect.ValueOf(pstk[n+i])
}
p.data = pstk[:n]
}
return
}
// PopNArgs pops arguments of a function call.
//
func (p *Stack) PopNArgs(arity int) []interface{} {
pstk := p.data
n := len(pstk)
if n >= arity {
args := make([]interface{}, arity)
n -= arity
for i := 0; i < arity; i++ {
args[i] = pstk[n+i]
}
p.data = pstk[:n]
return args
}
panic("unexpected argument count")
}
// PopFnArgs pops argument names of a function call.
//
func (p *Stack) PopFnArgs(arity int) []string {
ok := false
pstk := p.data
n := len(pstk)
if n >= arity {
args := make([]string, arity)
n -= arity
for i := 0; i < arity; i++ {
if args[i], ok = pstk[n+i].(string); !ok {
panic("function argument isn't a symbol?")
}
}
p.data = pstk[:n]
return args
}
panic("unexpected argument count")
}
// BaseFrame returns current stack size.
//
func (p *Stack) BaseFrame() int {
return len(p.data)
}
// SetFrame sets stack to new size.
//
func (p *Stack) SetFrame(n int) {
p.data = p.data[:n]
}
// -----------------------------------------------------------------------------
// type Context
type theDefer struct {
next *theDefer
start int
end int
}
// A Context represents the context of an executor.
//
type Context struct {
parent *Context
defers *theDefer
modmgr *moduleMgr
Stack *Stack
Code *Code
Recov interface{}
ret interface{}
vars map[string]interface{}
export []string
ip int
base int
onsel bool // on select
noextv bool // don't cache extern var
}
// NewContext returns a new context of an executor.
//
func NewContext() *Context {
vars := make(map[string]interface{})
mods := make(map[string]*importMod)
modmgr := &moduleMgr{
mods: mods,
}
return &Context{vars: vars, modmgr: modmgr}
}
// NewSimpleContext returns a new context of an executor, without module support.
//
func NewSimpleContext(vars map[string]interface{}, stk *Stack, code *Code, parent *Context) *Context {
return &Context{vars: vars, Stack: stk, Code: code, parent: parent, noextv: true}
}
// Exports returns a module exports.
//
func (p *Context) Exports() map[string]interface{} {
export := make(map[string]interface{}, len(p.export))
vars := p.vars
for _, name := range p.export {
export[name] = vars[name]
}
return export
}
// Vars is deprecated. please use `CopyVars` method.
//
func (p *Context) Vars() map[string]interface{} {
return p.vars
}
// CopyVars copies and returns all variables in executing context.
//
func (p *Context) CopyVars() map[string]interface{} {
vars := make(map[string]interface{})
for k, v := range p.vars {
vars[k] = v
}
return vars
}
// ResetVars resets all variables in executing context.
//
func (p *Context) ResetVars(vars map[string]interface{}) {
p.vars = make(map[string]interface{})
for k, v := range vars {
p.vars[k] = v
}
}
// Var returns a variable value in executing context.
//
func (p *Context) Var(name string) (v interface{}, ok bool) {
v, ok = p.vars[name]
return
}
// SetVar sets a variable value.
//
func (p *Context) SetVar(name string, v interface{}) {
p.vars[name] = v
}
// Unset deletes a variable.
//
func (p *Context) Unset(name string) {
delete(p.vars, name)
}
// ExecBlock executes an anonym function.
//
func (p *Context) ExecBlock(ip, ipEnd int) {
mod := NewFunction(nil, ip, ipEnd, nil, false)
mod.ExtCall(p)
}
// ExecDefers executes defer blocks.
//
func (p *Context) ExecDefers() {
d := p.defers
if d == nil {
return
}
p.defers = nil
code := p.Code
stk := p.Stack
for {
code.Exec(d.start, d.end, stk, p)
d = d.next
if d == nil {
break
}
}
}
// -----------------------------------------------------------------------------
// A Error represents a qlang runtime error.
//
type Error struct {
Err error
File string
Line int
Stack []byte
}
func (p *Error) Error() string {
var sep string
var stk []byte
if qlang.DumpStack {
stk = p.Stack
sep = "\n\n"
}
if p.Line == 0 {
return fmt.Sprintf("%v%s%s", p.Err, sep, stk)
}
if p.File == "" {
return fmt.Sprintf("line %d: %v%s%s", p.Line, p.Err, sep, stk)
}
return fmt.Sprintf("%s:%d: %v%s%s", p.File, p.Line, p.Err, sep, stk)
}
// -----------------------------------------------------------------------------
// type Code
// A Instr represents a instruction of the executor.
//
type Instr interface {
Exec(stk *Stack, ctx *Context)
}
// RefToVar converts a value reference instruction into a assignable variable instruction.
//
type RefToVar interface {
ToVar() Instr
}
type optimizableArityGetter interface {
OptimizableGetArity() int
}
type ipFileLine struct {
ip int
line int
file string
}
// A Code represents generated instructions to execute.
//
type Code struct {
data []Instr
lines []*ipFileLine // ip => (file,line)
}
// A ReservedInstr represents a reserved instruction to be assigned.
//
type ReservedInstr struct {
code *Code
idx int
}
// New returns a new Code object.
//
func New(data ...Instr) *Code {
return &Code{data, nil}
}
// CodeLine informs current file and line.
//
func (p *Code) CodeLine(file string, line int) {
p.lines = append(p.lines, &ipFileLine{ip: len(p.data), file: file, line: line})
}
// Line returns file line of a instruction position.
//
func (p *Code) Line(ip int) (file string, line int) {
idx := sort.Search(len(p.lines), func(i int) bool {
return ip < p.lines[i].ip
})
if idx < len(p.lines) {
t := p.lines[idx]
return t.file, t.line
}
return "", 0
}
// Len returns code length.
//
func (p *Code) Len() int {
return len(p.data)
}
// Reserve reserves an instruction and returns it.
//
func (p *Code) Reserve() ReservedInstr {
idx := len(p.data)
p.data = append(p.data, nil)
return ReservedInstr{p, idx}
}
// Set sets a reserved instruction.
//
func (p ReservedInstr) Set(code Instr) {
p.code.data[p.idx] = code
}
// Next returns next instruction position.
//
func (p ReservedInstr) Next() int {
return p.idx + 1
}
// Delta returns distance from b to p.
//
func (p ReservedInstr) Delta(b ReservedInstr) int {
return p.idx - b.idx
}
// CheckConst returns the value, if code[ip] is a const instruction.
//
func (p *Code) CheckConst(ip int) (v interface{}, ok bool) {
if instr, ok := p.data[ip].(*iPush); ok {
return instr.v, true
}
return
}
func appendInstrOptimized(data []Instr, instr Instr, arity int) []Instr {
n := len(data)
base := n - arity
for i := base; i < n; i++ {
if _, ok := data[i].(*iPush); !ok {
return append(data, instr)
}
}
args := make([]interface{}, arity)
for i := base; i < n; i++ {
args[i-base] = data[i].(*iPush).v
}
stk := &Stack{data: args}
instr.Exec(stk, nil)
return append(data[:base], Push(stk.data[0]))
}
// Block appends some instructions to code.
//
func (p *Code) Block(code ...Instr) int {
for _, instr := range code {
if g, ok := instr.(optimizableArityGetter); ok {
arity := g.OptimizableGetArity()
p.data = appendInstrOptimized(p.data, instr, arity)
} else {
p.data = append(p.data, instr)
}
}
return len(p.data)
}
// ToVar converts the last instruction from ref to var.
//
func (p *Code) ToVar() {
data := p.data
idx := len(data) - 1
if cvt, ok := data[idx].(RefToVar); ok {
data[idx] = cvt.ToVar()
} else {
panic("expr is not assignable")
}
}
// Exec executes a code block from ip to ipEnd.
//
func (p *Code) Exec(ip, ipEnd int, stk *Stack, ctx *Context) {
defer func() {
if e := recover(); e != nil {
if e == ErrReturn {
panic(e)
}
if err, ok := e.(*Error); ok {
panic(err)
}
err, ok := e.(error)
if !ok {
if s, ok := e.(string); ok {
err = errors.New(s)
} else {
panic(e)
}
}
file, line := p.Line(ctx.ip - 1)
err = &Error{
Err: err,
File: file,
Line: line,
Stack: debug.Stack(),
}
panic(err)
}
}()
ctx.ip = ip
data := p.data
for ctx.ip != ipEnd {
instr := data[ctx.ip]
ctx.ip++
instr.Exec(stk, ctx)
}
}
// Dump dumps code instructions within a range.
//
func (p *Code) Dump(ranges ...int) {
start := 0
end := len(p.data)
if len(ranges) > 0 {
start = ranges[0]
if len(ranges) > 1 {
end = ranges[1]
}
}
for i, instr := range p.data[start:end] {
fmt.Printf("==> %04d: %s %v\n", i+start, instrName(instr), instr)
}
}
func instrName(instr interface{}) string {
if instr == nil {
return "<nil>"
}
t := reflect.TypeOf(instr).String()
if strings.HasPrefix(t, "*") {
t = t[1:]
}
if strings.HasPrefix(t, "exec.") {
t = t[5:]
}
if strings.HasPrefix(t, "i") {
t = t[1:]
}
return t
}
// -----------------------------------------------------------------------------
Go
1
https://gitee.com/brucewangzhihua1/qlang.git
git@gitee.com:brucewangzhihua1/qlang.git
brucewangzhihua1
qlang
qlang
v2.9.60

搜索帮助

53164aa7 5694891 3bd8fe86 5694891