├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── README_QL.md ├── cl ├── builtin_types.go ├── class2.go ├── class2_test.go ├── ctrl2.go ├── ctrl_test.go ├── function2.go ├── function_test.go ├── goroutine2.go ├── goroutine_test.go ├── interpreter │ └── interpret2.go ├── module2.go ├── module_test.go ├── qlang2.go ├── qlang_test.go ├── stack.go ├── var2.go └── var_test.go ├── cmd ├── eql │ ├── README.md │ ├── eql_main.go │ ├── example.eql │ └── example │ │ ├── $set.v1 │ │ ├── README.md.eql │ │ └── set.go.eql │ │ ├── gen-set-types.sh │ │ ├── stringset.v1 │ │ ├── README.md │ │ └── set.go │ │ └── uint32set.v1 │ │ ├── README.md │ │ └── set.go ├── miniqlang │ └── mini.go ├── playground │ └── sandbox │ │ ├── play.go │ │ ├── play_test.go │ │ ├── sandbox.go │ │ └── sandbox_test.go ├── qexport │ ├── README.md │ ├── api.go │ ├── context.go │ ├── main.go │ ├── package.go │ ├── update.go │ └── util.go ├── qlang.safe │ └── main_safe.go ├── qlang │ └── main.go └── qshell │ └── qshell.go ├── engine2.go ├── exec ├── basic.go ├── basic_test.go ├── call.go ├── call_test.go ├── class.go ├── code.go ├── code_test.go ├── for.go ├── function.go ├── goroutine.go ├── module.go ├── types.go ├── var.go ├── var_new.go └── var_test.go ├── go.mod ├── go.sum ├── lib ├── bufio │ └── bufio.go ├── builtin │ ├── bits.go │ ├── boolean.go │ ├── builtin.go │ ├── exports.go │ ├── gentypes.ql │ ├── number.go │ ├── types.go │ └── types_builtin.go ├── bytes │ ├── bytes.go │ └── bytes_test.go ├── chan │ └── chan.go ├── compress │ └── lzw │ │ └── lzw.go ├── crypto │ ├── hmac │ │ └── hmac.go │ ├── md5 │ │ ├── md5_exports.go │ │ └── md5_test.go │ ├── sha1 │ │ └── sha1_exports.go │ └── sha256 │ │ └── sha256_exports.go ├── doc.go ├── encoding │ ├── hex │ │ └── hex.go │ └── json │ │ └── json.go ├── eqlang │ ├── eql.go │ ├── eql_test.go │ └── execute.go ├── errors │ └── errors.go ├── io │ ├── io-go16.go │ ├── io.go │ └── ioutil │ │ └── ioutil.go ├── math │ └── math.go ├── meta │ ├── README.md │ └── meta.go ├── net │ └── http │ │ └── http.go ├── os │ └── os.go ├── path │ └── path_exports.go ├── qlang.all │ └── all.go ├── reflect │ └── reflect.go ├── runtime │ ├── runtime-go16.go │ └── runtime.go ├── strconv │ └── strconv.go ├── strings │ ├── strings-go16.go │ └── strings.go ├── sync │ └── sync.go ├── terminal │ └── terminal.go ├── tpl │ └── extractor │ │ └── extract_exports.go └── version │ └── version.go ├── spec ├── spec.go └── types │ └── types.go └── tutorial ├── anonymfn └── anonym.ql ├── calc ├── calc.ql └── main.ql ├── chan └── chan.ql ├── closure └── closure.ql ├── defer └── file.ql ├── flowctrl └── flowctrl.ql ├── for_range └── for_range.ql ├── goroutine └── goroutine.ql ├── http └── httpserver.ql ├── if └── if.ql ├── include ├── a │ ├── foo.ql │ └── main.ql ├── b.ql ├── bar.ql └── run.sh ├── maxprime ├── maxprime.py └── maxprime.ql ├── module ├── c.ql ├── internal │ └── a │ │ ├── foo.ql │ │ └── main.ql └── run.sh ├── qlang ├── main.ql └── qlang.ql ├── shell └── shell.ql ├── sync └── sync.ql └── terminal ├── calc.ql └── qlang.ql /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | .DS_Store 6 | 7 | # Folders 8 | _obj 9 | _test 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | *.exe 24 | *.test 25 | *.prof 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | install: 3 | - export GO111MODULE="on" 4 | - go install -v ./... 5 | - go test ./... 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Q Language - A script language for Go 2 | ======== 3 | 4 | [![LICENSE](https://img.shields.io/github/license/xushiwei/qlang.svg)](https://github.com/xushiwei/qlang/blob/master/LICENSE) 5 | [![Build Status](https://travis-ci.org/xushiwei/qlang.png?branch=v1.5)](https://travis-ci.org/xushiwei/qlang) [![Go Report Card](https://goreportcard.com/badge/github.com/xushiwei/qlang)](https://goreportcard.com/report/github.com/xushiwei/qlang) 6 | [![GoDoc](https://godoc.org/github.com/xushiwei/qlang?status.svg)](https://godoc.org/github.com/xushiwei/qlang) 7 | 8 | [![Qiniu Logo](http://open.qiniudn.com/logo.png)](http://www.qiniu.com/) 9 | 10 | Q Language is moved to https://github.com/xushiwei/qlang 11 | -------------------------------------------------------------------------------- /cl/builtin_types.go: -------------------------------------------------------------------------------- 1 | package qlang 2 | 3 | import ( 4 | "github.com/xushiwei/qlang/exec" 5 | qlang "github.com/xushiwei/qlang/spec" 6 | ) 7 | 8 | // ----------------------------------------------------------------------------- 9 | 10 | func (p *Compiler) structInit() { 11 | 12 | arity := p.popArity() 13 | p.code.Block(exec.StructInit((arity << 1) + 1)) 14 | } 15 | 16 | func (p *Compiler) mapInit() { 17 | 18 | arity := p.popArity() 19 | p.code.Block(exec.MapInit((arity << 1) + 1)) 20 | } 21 | 22 | // ----------------------------------------------------------------------------- 23 | 24 | func (p *Compiler) tMap() { 25 | 26 | p.code.Block(exec.Map) 27 | } 28 | 29 | func (p *Compiler) vMap() { 30 | 31 | arity := p.popArity() 32 | p.code.Block(exec.Call(qlang.MapFrom, arity<<1)) 33 | } 34 | 35 | // ----------------------------------------------------------------------------- 36 | 37 | func (p *Compiler) tSlice() { 38 | 39 | p.code.Block(exec.Slice) 40 | } 41 | 42 | func (p *Compiler) vSlice() { 43 | 44 | hasSlice := p.popArity() 45 | hasInit := 0 46 | arityInit := 0 47 | if hasSlice > 0 { 48 | hasInit = p.popArity() 49 | if hasInit > 0 { 50 | arityInit = p.popArity() 51 | } 52 | } 53 | arity := p.popArity() 54 | 55 | if hasSlice > 0 { 56 | if arity > 0 { 57 | panic("must be []type") 58 | } 59 | if hasInit > 0 { // []T{a1, a2, ...} 60 | p.code.Block(exec.SliceFromTy(arityInit + 1)) 61 | } else { // []T 62 | p.code.Block(exec.Slice) 63 | } 64 | } else { // [a1, a2, ...] 65 | p.code.Block(exec.SliceFrom(arity)) 66 | } 67 | } 68 | 69 | // ----------------------------------------------------------------------------- 70 | -------------------------------------------------------------------------------- /cl/class2.go: -------------------------------------------------------------------------------- 1 | package qlang 2 | 3 | import ( 4 | "errors" 5 | 6 | ipt "github.com/qiniu/text/tpl/interpreter" 7 | "github.com/qiniu/text/tpl/interpreter.util" 8 | "github.com/xushiwei/qlang/exec" 9 | ) 10 | 11 | // ----------------------------------------------------------------------------- 12 | 13 | type functionInfo struct { 14 | args []string // args[0] => function name 15 | fnb interface{} 16 | variadic bool 17 | } 18 | 19 | // ----------------------------------------------------------------------------- 20 | 21 | func (p *Compiler) memberFuncDecl() { 22 | 23 | fnb, _ := p.gstk.Pop() 24 | variadic := p.popArity() 25 | arity := p.popArity() 26 | args := p.gstk.PopFnArgs(arity + 1) 27 | fn := &functionInfo{ 28 | args: args, 29 | fnb: fnb, 30 | variadic: variadic != 0, 31 | } 32 | p.gstk.Push(fn) 33 | } 34 | 35 | func (p *Compiler) addMethods(cls *exec.Class, e interpreter.Engine, parent *funcCtx, members []interface{}) { 36 | 37 | for _, val := range members { 38 | v := val.(*functionInfo) 39 | name := v.args[0] 40 | v.args[0] = "this" 41 | start, end, symtbl := p.clFunc(e, "doc", v.fnb, parent, v.args) 42 | fn := exec.NewFunction(cls, start, end, symtbl, v.args, v.variadic) 43 | fn.Parent = cls.Ctx 44 | cls.Fns[name] = fn 45 | } 46 | } 47 | 48 | func (p *Compiler) fnClass(e interpreter.Engine) { 49 | 50 | arity := p.popArity() 51 | members := p.gstk.PopNArgs(arity) 52 | instr := p.code.Reserve() 53 | fnctx := p.fnctx 54 | p.exits = append(p.exits, func() { 55 | cls := exec.IClass() 56 | p.addMethods(cls, e, fnctx, members) 57 | instr.Set(cls) 58 | }) 59 | } 60 | 61 | // InjectMethods injects some methods into a class. 62 | // 63 | func (p *Compiler) InjectMethods(cls *exec.Class, code []byte) (err error) { 64 | 65 | defer func() { 66 | if e := recover(); e != nil { 67 | switch v := e.(type) { 68 | case string: 69 | err = errors.New(v) 70 | case error: 71 | err = v 72 | default: 73 | panic(e) 74 | } 75 | } 76 | }() 77 | 78 | engine, err := ipt.New(p, p.Opts) 79 | if err != nil { 80 | return 81 | } 82 | p.ipt = engine 83 | 84 | src, err := engine.Tokenize(code, "") 85 | if err != nil { 86 | return 87 | } 88 | 89 | p.cl(engine, "methods", src) 90 | arity := p.popArity() 91 | members := p.gstk.PopNArgs(arity) 92 | p.addMethods(cls, engine, p.fnctx, members) 93 | p.Done() 94 | return 95 | } 96 | 97 | func (p *Compiler) fnNew() { 98 | 99 | nArgs := p.popArity() 100 | if nArgs != 0 { 101 | nArgs = p.popArity() 102 | } 103 | p.code.Block(exec.INew(nArgs)) 104 | } 105 | 106 | func (p *Compiler) memberRef(name string) { 107 | 108 | p.code.Block(exec.MemberRef(name)) 109 | } 110 | 111 | // ----------------------------------------------------------------------------- 112 | -------------------------------------------------------------------------------- /cl/class2_test.go: -------------------------------------------------------------------------------- 1 | package qlang_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/xushiwei/qlang" 7 | ) 8 | 9 | // ----------------------------------------------------------------------------- 10 | 11 | const testInjectCode = ` 12 | 13 | y = 3 14 | typ = class { 15 | fn foo() { 16 | return y 17 | } 18 | } 19 | 20 | a = new typ 21 | x = a.foo() 22 | ` 23 | 24 | const testInjectMethodsCode = ` 25 | 26 | fn bar(v) { 27 | return y + v 28 | } 29 | 30 | fn setbar(v) { 31 | y; y = v 32 | } 33 | ` 34 | 35 | const testInjectCheckCode = ` 36 | 37 | x2 = a.bar(1) 38 | 39 | a.setbar(10) 40 | x3 = a.bar(1) 41 | ` 42 | 43 | func TestInject(t *testing.T) { 44 | 45 | lang := qlang.New() 46 | err := lang.SafeExec([]byte(testInjectCode), "") 47 | if err != nil { 48 | t.Fatal("qlang.SafeExec:", err) 49 | } 50 | if v, ok := lang.GetVar("x"); !ok || v != 3 { 51 | t.Fatal("x != 3, x =", v) 52 | } 53 | 54 | err = lang.InjectMethods("typ", []byte(testInjectMethodsCode)) 55 | if err != nil { 56 | t.Fatal("InjectMethods failed:", err) 57 | } 58 | 59 | err = lang.SafeEval(testInjectCheckCode) 60 | if err != nil { 61 | t.Fatal("SafeEval failed:", err) 62 | } 63 | if v, ok := lang.GetVar("x2"); !ok || v != 4 { 64 | t.Fatal("x2 != 4, x2 =", v) 65 | } 66 | if v, ok := lang.GetVar("x3"); !ok || v != 11 { 67 | t.Fatal("x3 != 11, x3 =", v) 68 | } 69 | } 70 | 71 | // ----------------------------------------------------------------------------- 72 | 73 | type FooMemberRef struct { 74 | X int 75 | } 76 | 77 | func (p *FooMemberRef) Bar() int { 78 | return p.X 79 | } 80 | 81 | func (p FooMemberRef) Bar2() int { 82 | return p.X 83 | } 84 | 85 | func fooMemberPtr() *FooMemberRef { 86 | return &FooMemberRef{3} 87 | } 88 | 89 | func fooCall(v FooMemberRef) int { 90 | return v.X 91 | } 92 | 93 | const testMemberRefCode = ` 94 | 95 | foo = fooMemberPtr() 96 | x = foo.x 97 | y = foo.bar2() 98 | z = foo.bar() 99 | ret = fooCall(foo) 100 | ` 101 | 102 | func TestMemberRef(t *testing.T) { 103 | 104 | lang := qlang.New() 105 | qlang.Import("", map[string]interface{}{ 106 | "fooMemberPtr": fooMemberPtr, 107 | "fooCall": fooCall, 108 | }) 109 | 110 | err := lang.SafeExec([]byte(testMemberRefCode), "") 111 | if err != nil { 112 | t.Fatal("qlang.SafeExec:", err) 113 | } 114 | if v, ok := lang.GetVar("x"); !ok || v != 3 { 115 | t.Fatal("x != 3, x =", v) 116 | } 117 | if v, ok := lang.GetVar("y"); !ok || v != 3 { 118 | t.Fatal("y != 3, y =", v) 119 | } 120 | if v, ok := lang.GetVar("z"); !ok || v != 3 { 121 | t.Fatal("z != 3, z =", v) 122 | } 123 | if v, ok := lang.GetVar("ret"); !ok || v != 3 { 124 | t.Fatal("ret != 3, ret =", v) 125 | } 126 | } 127 | 128 | // ----------------------------------------------------------------------------- 129 | 130 | const testNewClassCode = ` 131 | 132 | t = new class { 133 | fn f() { 134 | return 2 135 | } 136 | } 137 | x = t.f() 138 | ` 139 | 140 | func TestNewClass(t *testing.T) { 141 | 142 | lang := qlang.New() 143 | err := lang.SafeExec([]byte(testNewClassCode), "") 144 | if err != nil { 145 | t.Fatal(err) 146 | } 147 | if v, ok := lang.GetVar("x"); !ok || v != 2 { 148 | t.Fatal("x != 2, x =", v) 149 | } 150 | } 151 | 152 | // ----------------------------------------------------------------------------- 153 | -------------------------------------------------------------------------------- /cl/function2.go: -------------------------------------------------------------------------------- 1 | package qlang 2 | 3 | import ( 4 | "github.com/qiniu/text/tpl/interpreter.util" 5 | "github.com/xushiwei/qlang/exec" 6 | ) 7 | 8 | // ----------------------------------------------------------------------------- 9 | 10 | func (p *Compiler) function(e interpreter.Engine) { 11 | 12 | fnb, _ := p.gstk.Pop() 13 | variadic := p.popArity() 14 | arity := p.popArity() 15 | args := p.gstk.PopFnArgs(arity) 16 | instr := p.code.Reserve() 17 | fnctx := p.fnctx 18 | p.exits = append(p.exits, func() { 19 | start, end, symtbl := p.clFunc(e, "doc", fnb, fnctx, args) 20 | instr.Set(exec.Func(nil, start, end, symtbl, args, variadic != 0)) 21 | }) 22 | } 23 | 24 | func (p *Compiler) anonymFn(e interpreter.Engine) { 25 | 26 | fnb, _ := p.gstk.Pop() 27 | instr := p.code.Reserve() 28 | fnctx := p.fnctx 29 | p.exits = append(p.exits, func() { 30 | start, end, symtbl := p.clFunc(e, "doc", fnb, fnctx, nil) 31 | instr.Set(exec.AnonymFn(start, end, symtbl)) 32 | }) 33 | } 34 | 35 | func (p *Compiler) fnReturn(e interpreter.Engine) { 36 | 37 | arity := p.popArity() 38 | p.code.Block(exec.Return(arity)) 39 | } 40 | 41 | // Done completes all exit functions generated by `Cl`. 42 | // 43 | func (p *Compiler) Done() { 44 | 45 | for { 46 | n := len(p.exits) 47 | if n == 0 { 48 | break 49 | } 50 | onExit := p.exits[n-1] 51 | p.exits = p.exits[:n-1] 52 | onExit() 53 | } 54 | } 55 | 56 | func (p *Compiler) fnDefer(e interpreter.Engine) { 57 | 58 | src, _ := p.gstk.Pop() 59 | instr := p.code.Reserve() 60 | fnctx := p.fnctx 61 | p.exits = append(p.exits, func() { 62 | start, end := p.clBlock(e, "expr", src, fnctx) 63 | p.codeLine(src) 64 | instr.Set(exec.Defer(start, end)) 65 | }) 66 | } 67 | 68 | func (p *Compiler) fnRecover() { 69 | 70 | p.code.Block(exec.Recover) 71 | } 72 | 73 | func (p *Compiler) clFunc( 74 | e interpreter.Engine, g string, src interface{}, 75 | parent *funcCtx, args []string) (start, end int, symtbl map[string]int) { 76 | 77 | ctx := newFuncCtx(parent, args) 78 | old := p.fnctx 79 | p.fnctx = ctx 80 | start, end = p.cl(e, g, src) 81 | symtbl = ctx.symtbl 82 | p.fnctx = old 83 | return 84 | } 85 | 86 | func (p *Compiler) clBlock( 87 | e interpreter.Engine, g string, src interface{}, parent *funcCtx) (start, end int) { 88 | 89 | old := p.fnctx 90 | p.fnctx = parent 91 | start, end = p.cl(e, g, src) 92 | p.fnctx = old 93 | return 94 | } 95 | 96 | func (p *Compiler) cl(e interpreter.Engine, g string, src interface{}) (start, end int) { 97 | 98 | start = p.code.Len() 99 | if src != nil { 100 | if err := e.EvalCode(p, g, src); err != nil { 101 | panic(err) 102 | } 103 | } 104 | end = p.code.Len() 105 | return 106 | } 107 | 108 | // ----------------------------------------------------------------------------- 109 | -------------------------------------------------------------------------------- /cl/goroutine2.go: -------------------------------------------------------------------------------- 1 | package qlang 2 | 3 | import ( 4 | "github.com/qiniu/text/tpl/interpreter.util" 5 | "github.com/xushiwei/qlang/exec" 6 | ) 7 | 8 | // ----------------------------------------------------------------------------- 9 | 10 | func (p *Compiler) fnGo(e interpreter.Engine) { 11 | 12 | src, _ := p.gstk.Pop() 13 | instr := p.code.Reserve() 14 | fnctx := p.fnctx 15 | p.exits = append(p.exits, func() { 16 | start, end := p.clBlock(e, "expr", src, fnctx) 17 | instr.Set(exec.Go(start, end)) 18 | }) 19 | } 20 | 21 | // ----------------------------------------------------------------------------- 22 | 23 | func (p *Compiler) chanIn() { 24 | 25 | p.code.Block(exec.ChanIn) 26 | } 27 | 28 | func (p *Compiler) chanOut() { 29 | 30 | p.code.Block(exec.ChanOut) 31 | } 32 | 33 | func (p *Compiler) tChan() { 34 | 35 | p.code.Block(exec.Chan) 36 | } 37 | 38 | // ----------------------------------------------------------------------------- 39 | -------------------------------------------------------------------------------- /cl/goroutine_test.go: -------------------------------------------------------------------------------- 1 | package qlang_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/xushiwei/qlang" 7 | _ "github.com/xushiwei/qlang/lib/builtin" 8 | _ "github.com/xushiwei/qlang/lib/chan" 9 | "github.com/xushiwei/qlang/lib/sync" 10 | ) 11 | 12 | func init() { 13 | qlang.Import("sync", sync.Exports) 14 | } 15 | 16 | // ----------------------------------------------------------------------------- 17 | 18 | const testChanCode = ` 19 | 20 | mutex = sync.mutex() 21 | ch = make(chan bool, 2) 22 | 23 | x = 1 24 | 25 | go fn { 26 | defer ch <- true 27 | 28 | mutex.lock() 29 | defer mutex.unlock() 30 | x; x++ 31 | } 32 | 33 | go fn { 34 | defer ch <- true 35 | 36 | mutex.lock() 37 | defer mutex.unlock() 38 | x; x++ 39 | } 40 | 41 | <-ch 42 | <-ch 43 | ` 44 | 45 | func TestChan(t *testing.T) { 46 | 47 | lang := qlang.New() 48 | err := lang.SafeExec([]byte(testChanCode), "") 49 | if err != nil { 50 | t.Fatal("qlang.SafeExec:", err) 51 | } 52 | if v, ok := lang.GetVar("x"); !ok || v != 3 { 53 | t.Fatal("x != 3, x =", v) 54 | } 55 | } 56 | 57 | // ----------------------------------------------------------------------------- 58 | 59 | const testGoroutineCode = ` 60 | 61 | mutex = sync.mutex() 62 | 63 | wg = sync.waitGroup() 64 | wg.add(2) 65 | 66 | x = 1 67 | 68 | go fn { 69 | defer wg.done() 70 | 71 | mutex.lock() 72 | defer mutex.unlock() 73 | x; x++ 74 | } 75 | 76 | go fn { 77 | defer wg.done() 78 | 79 | mutex.lock() 80 | defer mutex.unlock() 81 | x; x++ 82 | } 83 | 84 | wg.wait() 85 | ` 86 | 87 | func TestGoroutine(t *testing.T) { 88 | 89 | lang := qlang.New() 90 | err := lang.SafeExec([]byte(testGoroutineCode), "") 91 | if err != nil { 92 | t.Fatal("qlang.SafeExec:", err) 93 | } 94 | if v, ok := lang.GetVar("x"); !ok || v != 3 { 95 | t.Fatal("x != 3, x =", v) 96 | } 97 | } 98 | 99 | // ----------------------------------------------------------------------------- 100 | -------------------------------------------------------------------------------- /cl/module2.go: -------------------------------------------------------------------------------- 1 | package qlang 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path" 7 | "strconv" 8 | "strings" 9 | "syscall" 10 | 11 | "github.com/qiniu/text/tpl/interpreter" 12 | "github.com/xushiwei/qlang/exec" 13 | ) 14 | 15 | // ----------------------------------------------------------------------------- 16 | 17 | // An FindEntryError represents a FindEntry error. 18 | // 19 | type FindEntryError struct { 20 | Name string 21 | Err error 22 | } 23 | 24 | func (e *FindEntryError) Error() string { 25 | return strconv.Quote(e.Name) + ": " + e.Err.Error() 26 | } 27 | 28 | func findEntry(file string, libs []string) (string, error) { 29 | 30 | if strings.HasPrefix(file, "/") { 31 | _, err := os.Stat(file) 32 | if err == nil { 33 | return file, nil 34 | } 35 | return "", &FindEntryError{file, err} 36 | } 37 | for _, dir := range libs { 38 | if dir == "" { 39 | continue 40 | } 41 | path := dir + "/" + file 42 | if _, err := os.Stat(path); err == nil { 43 | return path, nil 44 | } 45 | } 46 | return "", &FindEntryError{file, syscall.ENOENT} 47 | } 48 | 49 | func resolvePath(file string, base string) string { 50 | 51 | if strings.HasPrefix(file, "/") { 52 | return file 53 | } 54 | return path.Join(base, file) 55 | } 56 | 57 | var ( 58 | // FindEntry specifies the policy how qlang searches library file. 59 | FindEntry = findEntry 60 | 61 | // ReadFile specifies the policy how qlang reads source file. 62 | ReadFile = ioutil.ReadFile 63 | ) 64 | 65 | // ----------------------------------------------------------------------------- 66 | 67 | const ( 68 | indexFile = "/main.ql" 69 | ) 70 | 71 | func qlangFile(file string) string { 72 | 73 | if path.Ext(file) == ".ql" { 74 | return file 75 | } 76 | return file + indexFile 77 | } 78 | 79 | func (p *Compiler) dir() string { 80 | 81 | if v, ok := p.gvars["__dir__"]; ok { 82 | if dir, ok := v.(string); ok { 83 | return dir 84 | } 85 | } 86 | panic("ident `__dir__` not found") 87 | } 88 | 89 | func (p *Compiler) compileModule(fname string, parent *funcCtx) (end int, symtbl map[string]int) { 90 | 91 | ctx := newFuncCtx(parent, nil) 92 | old := p.fnctx 93 | p.fnctx = ctx 94 | end = p.Compile(fname) 95 | symtbl = ctx.symtbl 96 | p.fnctx = old 97 | return 98 | } 99 | 100 | // Compile compiles a qlang source file. 101 | // 102 | func (p *Compiler) Compile(fname string) int { 103 | 104 | codeText, err := ReadFile(fname) 105 | if err != nil { 106 | panic(err) 107 | } 108 | return p.Cl(codeText, fname) 109 | } 110 | 111 | // Cl compiles a qlang source code. 112 | // 113 | func (p *Compiler) Cl(codeText []byte, fname string) int { 114 | 115 | engine, err := interpreter.New(p, p.Opts) 116 | if err != nil { 117 | panic(err) 118 | } 119 | 120 | old := p.ipt 121 | defer func() { // backup interpreter to restore 122 | p.ipt = old 123 | }() 124 | p.ipt = engine 125 | p.gvars["__dir__"] = path.Dir(fname) 126 | p.gvars["__file__"] = fname 127 | err = engine.MatchExactly(codeText, fname) 128 | if err != nil { 129 | panic(err) 130 | } 131 | return p.code.Len() 132 | } 133 | 134 | // ----------------------------------------------------------------------------- 135 | 136 | func (p *Compiler) include(lit string) { 137 | 138 | file, err := strconv.Unquote(lit) 139 | if err != nil { 140 | panic("invalid string `" + lit + "`: " + err.Error()) 141 | } 142 | 143 | fname := qlangFile(resolvePath(file, p.dir())) 144 | p.Compile(fname) 145 | } 146 | 147 | func requireModuleSym(fnctx *funcCtx, name string) int { 148 | 149 | if _, ok := fnctx.getSymbol(name); ok { 150 | panic("import `" + name + "` error: ident exists") 151 | } 152 | return fnctx.newSymbol(name) 153 | } 154 | 155 | func (p *Compiler) fnImport(lit string) { 156 | 157 | dir, err := strconv.Unquote(lit) 158 | if err != nil { 159 | panic("invalid string `" + lit + "`: " + err.Error()) 160 | } 161 | 162 | file, err := FindEntry(dir+indexFile, p.libs) 163 | if err != nil { 164 | panic(err) 165 | } 166 | 167 | var name string 168 | arity := p.popArity() 169 | if arity > 0 { 170 | name = p.popName() 171 | } else { 172 | name = path.Base(dir) 173 | if pos := strings.Index(name, "."); pos > 0 { 174 | name = name[:pos] 175 | } 176 | } 177 | 178 | code := p.code 179 | instr := code.Reserve() 180 | id := requireModuleSym(p.fnctx, name) 181 | code.Block(exec.As(id)) 182 | p.exits = append(p.exits, func() { 183 | file = path.Clean(file) 184 | mod, ok := p.mods[file] 185 | if !ok { 186 | start := code.Len() 187 | end, symtbl := p.compileModule(file, nil) 188 | mod = module{start, end, symtbl} 189 | p.mods[file] = mod 190 | } 191 | instr.Set(exec.Module(file, mod.start, mod.end, mod.symtbl)) 192 | }) 193 | } 194 | 195 | func (p *Compiler) export() { 196 | 197 | arity := p.popArity() 198 | names := p.gstk.PopFnArgs(arity) 199 | p.code.Block(exec.Export(names...)) 200 | } 201 | 202 | // ----------------------------------------------------------------------------- 203 | -------------------------------------------------------------------------------- /cl/module_test.go: -------------------------------------------------------------------------------- 1 | package qlang_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/xushiwei/qlang" 7 | _ "github.com/xushiwei/qlang/lib/builtin" 8 | ) 9 | 10 | // ----------------------------------------------------------------------------- 11 | 12 | const scriptA = ` 13 | 14 | println("in script A") 15 | 16 | a = 1 17 | b = 2 18 | 19 | foo = fn(a) { 20 | println("in func foo:", a, b) 21 | } 22 | 23 | export b, foo 24 | ` 25 | 26 | const scriptB = ` 27 | 28 | include "a.ql" 29 | 30 | println("in script B") 31 | foo(a) 32 | ` 33 | 34 | const scriptC = ` 35 | 36 | import "a" 37 | import "a" as g 38 | 39 | b = 3 40 | 41 | g.b = 4 42 | println("in script C:", a.b, g.b) 43 | a.foo(b) 44 | ` 45 | 46 | func TestInclude(t *testing.T) { 47 | 48 | lang := qlang.New() 49 | qlang.SetReadFile(func(file string) ([]byte, error) { 50 | return []byte(scriptA), nil 51 | }) 52 | 53 | err := lang.SafeExec([]byte(scriptB), "b.ql") 54 | if err != nil { 55 | t.Fatal(err) 56 | } 57 | if v, ok := lang.GetVar("b"); !ok || v != 2 { 58 | t.Fatal("b != 2, b =", v) 59 | } 60 | } 61 | 62 | func TestImport(t *testing.T) { 63 | 64 | lang := qlang.New() 65 | qlang.SetFindEntry(func(file string, libs []string) (string, error) { 66 | return file, nil 67 | }) 68 | 69 | qlang.SetReadFile(func(file string) ([]byte, error) { 70 | return []byte(scriptA), nil 71 | }) 72 | 73 | err := lang.SafeExec([]byte(scriptC), "c.ql") 74 | if err != nil { 75 | t.Fatal(err) 76 | } 77 | if v, ok := lang.GetVar("a"); !ok || v.(map[string]interface{})["b"] != 4 { 78 | t.Fatal("a.b != 4, a.b =", v) 79 | } 80 | } 81 | 82 | // ----------------------------------------------------------------------------- 83 | 84 | const scriptA1 = ` 85 | 86 | defer fn() { 87 | x; x = 2 88 | }() 89 | 90 | x = 1 91 | export x 92 | ` 93 | 94 | const scriptB1 = ` 95 | 96 | import "a" 97 | 98 | println("a.x:", a.x) 99 | ` 100 | 101 | func TestModuleDefer(t *testing.T) { 102 | 103 | lang := qlang.New() 104 | qlang.SetFindEntry(func(file string, libs []string) (string, error) { 105 | return file, nil 106 | }) 107 | 108 | qlang.SetReadFile(func(file string) ([]byte, error) { 109 | return []byte(scriptA1), nil 110 | }) 111 | 112 | err := lang.SafeExec([]byte(scriptB1), "b.ql") 113 | if err != nil { 114 | t.Fatal(err) 115 | } 116 | if v, ok := lang.GetVar("a"); !ok || v.(map[string]interface{})["x"] != 2 { 117 | t.Fatal("x != 2, x =", v) 118 | } 119 | } 120 | 121 | // ----------------------------------------------------------------------------- 122 | 123 | const scriptA2 = ` 124 | 125 | Foo = class { 126 | fn f() { 127 | return 2 128 | } 129 | } 130 | 131 | export Foo 132 | ` 133 | 134 | const scriptB2 = ` 135 | 136 | import "a" 137 | 138 | t = new a.Foo 139 | x = t.f() 140 | ` 141 | 142 | func TestModuleClass(t *testing.T) { 143 | 144 | lang := qlang.New() 145 | qlang.SetFindEntry(func(file string, libs []string) (string, error) { 146 | return file, nil 147 | }) 148 | 149 | qlang.SetReadFile(func(file string) ([]byte, error) { 150 | return []byte(scriptA2), nil 151 | }) 152 | 153 | err := lang.SafeExec([]byte(scriptB2), "b.ql") 154 | if err != nil { 155 | t.Fatal(err) 156 | } 157 | if v, ok := lang.GetVar("x"); !ok || v != 2 { 158 | t.Fatal("x != 2, x =", v) 159 | } 160 | } 161 | 162 | // ----------------------------------------------------------------------------- 163 | 164 | const scriptPi = `pi = 2` 165 | 166 | const scriptIncPi = ` 167 | 168 | include "pi.ql" 169 | b = pi 170 | ` 171 | 172 | func TestIncludePi(t *testing.T) { 173 | 174 | lang := qlang.New() 175 | qlang.SetReadFile(func(file string) ([]byte, error) { 176 | return []byte(scriptPi), nil 177 | }) 178 | 179 | err := lang.Exec([]byte(scriptIncPi), "incPi.ql") 180 | if err != nil { 181 | t.Fatal(err) 182 | } 183 | if v, ok := lang.GetVar("b"); !ok || v != 2 { 184 | t.Fatal("b != 2, b =", v) 185 | } 186 | } 187 | 188 | // ----------------------------------------------------------------------------- 189 | -------------------------------------------------------------------------------- /cl/qlang_test.go: -------------------------------------------------------------------------------- 1 | package qlang_test 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/xushiwei/qlang" 8 | "github.com/xushiwei/qlang/lib/math" 9 | qspec "github.com/xushiwei/qlang/spec" 10 | ) 11 | 12 | // ----------------------------------------------------------------------------- 13 | 14 | const testCastFloatCode = ` 15 | 16 | x = sin(0) 17 | ` 18 | 19 | func TestCastFloat(t *testing.T) { 20 | 21 | lang := qlang.New() 22 | err := lang.SafeExec([]byte(testCastFloatCode), "") 23 | if err != nil { 24 | t.Fatal("qlang.SafeExec:", err) 25 | } 26 | if v, ok := lang.GetVar("x"); !ok || v != 0.0 { 27 | t.Fatal("x != 0.0, x =", v) 28 | } 29 | } 30 | 31 | func init() { 32 | qlang.Import("", math.Exports) 33 | } 34 | 35 | // ----------------------------------------------------------------------------- 36 | 37 | const testTestByteSliceCode = ` 38 | 39 | a = ['a', 'b'] 40 | x = type(a) 41 | ` 42 | 43 | func TestByteSlice(t *testing.T) { 44 | 45 | lang := qlang.New() 46 | err := lang.SafeExec([]byte(testTestByteSliceCode), "") 47 | if err != nil { 48 | t.Fatal("qlang.SafeExec:", err) 49 | } 50 | if v, ok := lang.GetVar("x"); !ok || v != reflect.TypeOf([]byte(nil)) { 51 | t.Fatal("x != []byte, x =", v) 52 | } 53 | } 54 | 55 | // ----------------------------------------------------------------------------- 56 | 57 | const testNilSliceCode = ` 58 | 59 | a = [] 60 | a = append(a, 1) 61 | x = type(a) 62 | y = a[0] 63 | ` 64 | 65 | func TestNilSlice(t *testing.T) { 66 | 67 | lang := qlang.New() 68 | err := lang.SafeExec([]byte(testNilSliceCode), "") 69 | if err != nil { 70 | t.Fatal("qlang.SafeExec:", err) 71 | } 72 | if v, ok := lang.GetVar("x"); !ok || v != reflect.TypeOf([]interface{}(nil)) { 73 | t.Fatal("x != []interface{}, x =", v) 74 | } 75 | if v, ok := lang.GetVar("y"); !ok || v != 1 { 76 | t.Fatal("y != 1, y =", v) 77 | } 78 | } 79 | 80 | // ----------------------------------------------------------------------------- 81 | 82 | type fooMode uint 83 | 84 | func castFooMode(mode fooMode) int { 85 | return int(mode) 86 | } 87 | 88 | const testCastFooModeCode = ` 89 | 90 | y = castFooMode(1) 91 | ` 92 | 93 | func TestCastFooMode(t *testing.T) { 94 | 95 | lang := qlang.New() 96 | qlang.Import("", map[string]interface{}{ 97 | "castFooMode": castFooMode, 98 | }) 99 | 100 | err := lang.SafeExec([]byte(testCastFooModeCode), "") 101 | if err != nil { 102 | t.Fatal("qlang.SafeExec:", err) 103 | } 104 | if v, ok := lang.GetVar("y"); !ok || v != 1 { 105 | t.Fatal("y != 1, y =", v) 106 | } 107 | } 108 | 109 | // ----------------------------------------------------------------------------- 110 | 111 | type AImportType struct { 112 | X int 113 | } 114 | 115 | func (p *AImportType) GetX() int { 116 | return p.X 117 | } 118 | 119 | func init() { 120 | qlang.Import("fooMod", map[string]interface{}{ 121 | "AImportType": qspec.StructOf((*AImportType)(nil)), 122 | }) 123 | } 124 | 125 | const testImportTypeCode = ` 126 | 127 | a = new fooMod.AImportType 128 | a.x = 1 129 | y = a.getX() 130 | ` 131 | 132 | func TestImportType(t *testing.T) { 133 | 134 | lang := qlang.New() 135 | err := lang.SafeExec([]byte(testImportTypeCode), "") 136 | if err != nil { 137 | t.Fatal("qlang.SafeExec:", err) 138 | } 139 | if v, ok := lang.GetVar("y"); !ok || v != 1 { 140 | t.Fatal("y != 1, y =", v) 141 | } 142 | } 143 | 144 | // ----------------------------------------------------------------------------- 145 | 146 | const testStructInitCode = ` 147 | 148 | a = &fooMod.AImportType{x: 1} 149 | y = a.x 150 | ` 151 | 152 | func TestStructInit(t *testing.T) { 153 | 154 | lang := qlang.New() 155 | err := lang.SafeExec([]byte(testStructInitCode), "") 156 | if err != nil { 157 | t.Fatal("qlang.SafeExec:", err) 158 | } 159 | if v, ok := lang.GetVar("y"); !ok || v != 1 { 160 | t.Fatal("y != 1, y =", v) 161 | } 162 | } 163 | 164 | // ----------------------------------------------------------------------------- 165 | 166 | const testMapInitCode = ` 167 | 168 | a = map[string]int16{"x": 1} 169 | y = a.x 170 | ` 171 | 172 | func TestMapInit(t *testing.T) { 173 | 174 | lang := qlang.New() 175 | err := lang.SafeExec([]byte(testMapInitCode), "") 176 | if err != nil { 177 | t.Fatal("qlang.SafeExec:", err) 178 | } 179 | if v, ok := lang.GetVar("y"); !ok || v != int16(1) { 180 | t.Fatal("y != 1, y =", v) 181 | } 182 | } 183 | 184 | // ----------------------------------------------------------------------------- 185 | 186 | const testMapInit2Code = ` 187 | 188 | y = map[string]int(nil) 189 | ` 190 | 191 | func TestMapInit2(t *testing.T) { 192 | 193 | lang := qlang.New() 194 | err := lang.SafeExec([]byte(testMapInit2Code), "") 195 | if err != nil { 196 | t.Fatal("qlang.SafeExec:", err) 197 | } 198 | if v, ok := lang.GetVar("y"); !ok || v.(map[string]int) != nil { 199 | t.Fatal("y != nil, y =", v) 200 | } 201 | } 202 | 203 | // ----------------------------------------------------------------------------- 204 | 205 | const testMapInit3Code = ` 206 | 207 | a = map[string]var{"x": 1, "y": "hello"} 208 | y = a.x 209 | z = a.y 210 | ` 211 | 212 | func TestMapInit3(t *testing.T) { 213 | 214 | lang := qlang.New() 215 | err := lang.SafeExec([]byte(testMapInit3Code), "") 216 | if err != nil { 217 | t.Fatal("qlang.SafeExec:", err) 218 | } 219 | if v, ok := lang.GetVar("y"); !ok || v != 1 { 220 | t.Fatal("y != 1, y =", v) 221 | } 222 | if v, ok := lang.GetVar("z"); !ok || v != "hello" { 223 | t.Fatal("z != hello, z =", v) 224 | } 225 | } 226 | 227 | // ----------------------------------------------------------------------------- 228 | 229 | const testMapInit32Code = ` 230 | 231 | a = map[string]int16{"x": 1, "hello": 2} 232 | z = a.x 233 | ` 234 | 235 | func TestMapInit32(t *testing.T) { 236 | 237 | lang := qlang.New() 238 | err := lang.SafeExec([]byte(testMapInit32Code), "") 239 | if err != nil { 240 | t.Fatal("qlang.SafeExec:", err) 241 | } 242 | if v, ok := lang.GetVar("z"); !ok || v != int16(1) { 243 | t.Fatal("z != 1, z =", v) 244 | } 245 | } 246 | 247 | // ----------------------------------------------------------------------------- 248 | 249 | const testMapIDCode = ` 250 | 251 | map = 1 252 | y = map 253 | 254 | t = class { 255 | fn map() { 256 | return "hello" 257 | } 258 | } 259 | 260 | obj = new t 261 | z = obj.map() 262 | ` 263 | 264 | func TestMapID(t *testing.T) { 265 | 266 | lang := qlang.New() 267 | err := lang.SafeExec([]byte(testMapIDCode), "") 268 | if err != nil { 269 | t.Fatal("qlang.SafeExec:", err) 270 | } 271 | if v, ok := lang.GetVar("y"); !ok || v != 1 { 272 | t.Fatal("y != 1, y =", v) 273 | } 274 | if v, ok := lang.GetVar("z"); !ok || v != "hello" { 275 | t.Fatal("z != hello, z =", v) 276 | } 277 | } 278 | 279 | // ----------------------------------------------------------------------------- 280 | -------------------------------------------------------------------------------- /cl/stack.go: -------------------------------------------------------------------------------- 1 | package qlang 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/xushiwei/qlang/exec" 7 | ) 8 | 9 | // ----------------------------------------------------------------------------- 10 | 11 | func (p *Compiler) pushInt(v int) { 12 | 13 | p.code.Block(exec.Push(v)) 14 | } 15 | 16 | func (p *Compiler) pushFloat(v float64) { 17 | 18 | p.code.Block(exec.Push(v)) 19 | } 20 | 21 | func (p *Compiler) pushByte(lit string) { 22 | 23 | v, multibyte, tail, err := strconv.UnquoteChar(lit[1:len(lit)-1], '\'') 24 | if err != nil { 25 | panic("invalid char `" + lit + "`: " + err.Error()) 26 | } 27 | if tail != "" || multibyte { 28 | panic("invalid char: " + lit) 29 | } 30 | p.code.Block(exec.Push(byte(v))) 31 | } 32 | 33 | func (p *Compiler) pushString(lit string) { 34 | 35 | v, err := strconv.Unquote(lit) 36 | if err != nil { 37 | panic("invalid string `" + lit + "`: " + err.Error()) 38 | } 39 | p.code.Block(exec.Push(v)) 40 | } 41 | 42 | func (p *Compiler) pushID(name string) { 43 | 44 | p.code.Block(exec.Push(name)) 45 | } 46 | 47 | // ----------------------------------------------------------------------------- 48 | 49 | func (p *Compiler) popArity() int { 50 | 51 | if v, ok := p.gstk.Pop(); ok { 52 | if arity, ok := v.(int); ok { 53 | return arity 54 | } 55 | } 56 | panic("no arity") 57 | } 58 | 59 | func (p *Compiler) popName() string { 60 | 61 | if v, ok := p.gstk.Pop(); ok { 62 | if name, ok := v.(string); ok { 63 | return name 64 | } 65 | } 66 | panic("no ident name") 67 | } 68 | 69 | func (p *Compiler) pushCode(code interface{}) { 70 | 71 | p.gstk.Push(code) 72 | } 73 | 74 | func (p *Compiler) arity(arity int) { 75 | 76 | p.gstk.Push(arity) 77 | } 78 | 79 | func (p *Compiler) pushName(name string) { 80 | 81 | p.gstk.Push(name) 82 | } 83 | 84 | // ----------------------------------------------------------------------------- 85 | -------------------------------------------------------------------------------- /cl/var2.go: -------------------------------------------------------------------------------- 1 | package qlang 2 | 3 | import ( 4 | "github.com/xushiwei/qlang/exec" 5 | qlang "github.com/xushiwei/qlang/spec" 6 | ) 7 | 8 | // ----------------------------------------------------------------------------- 9 | 10 | func (p *Compiler) inc() { 11 | 12 | p.code.Block(exec.IncEx) 13 | } 14 | 15 | func (p *Compiler) dec() { 16 | 17 | p.code.Block(exec.DecEx) 18 | } 19 | 20 | func (p *Compiler) addAssign() { 21 | 22 | p.code.Block(exec.AddAssignEx) 23 | } 24 | 25 | func (p *Compiler) subAssign() { 26 | 27 | p.code.Block(exec.SubAssignEx) 28 | } 29 | 30 | func (p *Compiler) mulAssign() { 31 | 32 | p.code.Block(exec.MulAssignEx) 33 | } 34 | 35 | func (p *Compiler) quoAssign() { 36 | 37 | p.code.Block(exec.QuoAssignEx) 38 | } 39 | 40 | func (p *Compiler) modAssign() { 41 | 42 | p.code.Block(exec.ModAssignEx) 43 | } 44 | 45 | func (p *Compiler) xorAssign() { 46 | 47 | p.code.Block(exec.XorAssignEx) 48 | } 49 | 50 | func (p *Compiler) bitandAssign() { 51 | 52 | p.code.Block(exec.BitAndAssignEx) 53 | } 54 | 55 | func (p *Compiler) bitorAssign() { 56 | 57 | p.code.Block(exec.BitOrAssignEx) 58 | } 59 | 60 | func (p *Compiler) andnotAssign() { 61 | 62 | p.code.Block(exec.AndNotAssignEx) 63 | } 64 | 65 | func (p *Compiler) lshrAssign() { 66 | 67 | p.code.Block(exec.LshrAssignEx) 68 | } 69 | 70 | func (p *Compiler) rshrAssign() { 71 | 72 | p.code.Block(exec.RshrAssignEx) 73 | } 74 | 75 | func (p *Compiler) multiAssign() { 76 | 77 | nval := p.popArity() 78 | arity := p.popArity() + 1 79 | if nval == 1 { 80 | p.code.Block(exec.MultiAssignFromSliceEx(arity)) 81 | } else if arity != nval { 82 | panic("argument count of multi assignment doesn't match") 83 | } else { 84 | p.code.Block(exec.MultiAssignEx(arity)) 85 | } 86 | } 87 | 88 | func (p *Compiler) assign() { 89 | 90 | p.code.Block(exec.AssignEx) 91 | } 92 | 93 | func (p *funcCtx) requireSymbol(name string) (id int, fnew bool) { 94 | id, ok := p.getSymbol(name) 95 | if ok { 96 | return 97 | } 98 | return p.newSymbol(name), true 99 | } 100 | 101 | func (p *Compiler) ref(name string) { 102 | 103 | if val, ok := p.gvars[name]; ok { 104 | p.code.Block(exec.Push(val)) 105 | return 106 | } 107 | 108 | fnctx := p.fnctx 109 | id, ok := fnctx.getSymbol(name) 110 | if !ok { 111 | if val, ok := qlang.Fntable[name]; ok { 112 | p.code.Block(exec.GfnRef(val, func() exec.Instr { 113 | id := fnctx.newSymbol(name) 114 | return exec.Var(id) 115 | })) 116 | return 117 | } 118 | id = fnctx.newSymbol(name) 119 | } 120 | p.code.Block(exec.Ref(id)) 121 | } 122 | 123 | func (p *Compiler) index() { 124 | 125 | arity2 := p.popArity() 126 | arityMid := p.popArity() 127 | arity1 := p.popArity() 128 | 129 | if arityMid == 0 { 130 | if arity1 == 0 { 131 | panic("call operator[] without index") 132 | } 133 | p.code.Block(exec.Get) 134 | } else { 135 | p.code.Block(exec.Op3(qlang.SubSlice, arity1 != 0, arity2 != 0)) 136 | } 137 | } 138 | 139 | func (p *Compiler) toVar() { 140 | 141 | p.code.ToVar() 142 | } 143 | 144 | // ----------------------------------------------------------------------------- 145 | -------------------------------------------------------------------------------- /cl/var_test.go: -------------------------------------------------------------------------------- 1 | package qlang_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/xushiwei/qlang" 7 | _ "github.com/xushiwei/qlang/lib/builtin" 8 | ql "github.com/xushiwei/qlang/spec" 9 | ) 10 | 11 | // ----------------------------------------------------------------------------- 12 | 13 | const testGetCode = ` 14 | 15 | a = [1, 2, 3] 16 | x = a[2] 17 | b = "123" 18 | y = b[2] 19 | c = {"a": 1, "b": 2, "c": 3} 20 | z = c.a 21 | ` 22 | 23 | func TestGet(t *testing.T) { 24 | 25 | lang := qlang.New() 26 | err := lang.SafeExec([]byte(testGetCode), "") 27 | if err != nil { 28 | t.Fatal("qlang.SafeExec:", err) 29 | } 30 | if v, ok := lang.GetVar("x"); !ok || v != 3 { 31 | t.Fatal("x != 3, x =", v) 32 | } 33 | if v, ok := lang.GetVar("y"); !ok || v != byte('3') { 34 | t.Fatal("y != '3', y =", v) 35 | } 36 | if v, ok := lang.GetVar("z"); !ok || v != 1 { 37 | t.Fatal("z != 1, z =", v) 38 | } 39 | } 40 | 41 | // ----------------------------------------------------------------------------- 42 | 43 | const testGetUndefinedCode = ` 44 | 45 | c = {} 46 | x = c["a"]["b"] 47 | ` 48 | 49 | func TestGetUndefined(t *testing.T) { 50 | 51 | lang := qlang.New() 52 | err := lang.SafeExec([]byte(testGetUndefinedCode), "") 53 | if err != nil { 54 | t.Fatal("qlang.SafeExec:", err) 55 | } 56 | if v := lang.Var("x"); v != ql.Undefined { 57 | t.Fatal("x != undefined, x =", v) 58 | } 59 | } 60 | 61 | // ----------------------------------------------------------------------------- 62 | 63 | const testIncCode = ` 64 | 65 | a = [1, 100] 66 | a[0]++ 67 | x = a[0] 68 | x++ 69 | ` 70 | 71 | func TestInc(t *testing.T) { 72 | 73 | lang := qlang.New() 74 | err := lang.SafeExec([]byte(testIncCode), "") 75 | if err != nil { 76 | t.Fatal("qlang.SafeExec:", err) 77 | } 78 | if v, ok := lang.GetVar("x"); !ok || v != 3 { 79 | t.Fatal("x != 3, x =", v) 80 | } 81 | } 82 | 83 | // ----------------------------------------------------------------------------- 84 | 85 | const testMulAssignCode = ` 86 | 87 | t = {"a": 2, "b": 2} 88 | t["a"] *= 2 89 | t.a *= 2 90 | x = t.a 91 | x *= 2 92 | ` 93 | 94 | func TestMulAssign(t *testing.T) { 95 | 96 | lang := qlang.New() 97 | err := lang.SafeExec([]byte(testMulAssignCode), "") 98 | if err != nil { 99 | t.Fatal("qlang.SafeExec:", err) 100 | } 101 | if v, ok := lang.GetVar("x"); !ok || v != 16 { 102 | t.Fatal("x != 16, x =", v) 103 | } 104 | } 105 | 106 | // ----------------------------------------------------------------------------- 107 | 108 | const testSwapCode = ` 109 | 110 | t = {"a": 2, "b": 3} 111 | t.a, t.b = t.b, t.a 112 | x, y = t.a, t.b 113 | ` 114 | 115 | func TestSwap(t *testing.T) { 116 | 117 | lang := qlang.New() 118 | err := lang.SafeExec([]byte(testSwapCode), "") 119 | if err != nil { 120 | t.Fatal("qlang.SafeExec:", err) 121 | } 122 | if v, ok := lang.GetVar("x"); !ok || v != 3 { 123 | t.Fatal("x != 3, x =", v) 124 | } 125 | if v, ok := lang.GetVar("y"); !ok || v != 2 { 126 | t.Fatal("y != 2, y =", v) 127 | } 128 | } 129 | 130 | // ----------------------------------------------------------------------------- 131 | 132 | const testMultiAssignCode = ` 133 | 134 | x, y = 3, 2 135 | ` 136 | 137 | func TestMultiAssign(t *testing.T) { 138 | 139 | lang := qlang.New() 140 | err := lang.SafeExec([]byte(testMultiAssignCode), "") 141 | if err != nil { 142 | t.Fatal("qlang.SafeExec:", err) 143 | } 144 | if v, ok := lang.GetVar("x"); !ok || v != 3 { 145 | t.Fatal("x != 3, x =", v) 146 | } 147 | if v, ok := lang.GetVar("y"); !ok || v != 2 { 148 | t.Fatal("y != 2, y =", v) 149 | } 150 | } 151 | 152 | // ----------------------------------------------------------------------------- 153 | 154 | const testMultiAssignCode2 = ` 155 | 156 | x, y = [3, 2] 157 | ` 158 | 159 | func TestMultiAssign2(t *testing.T) { 160 | 161 | lang := qlang.New() 162 | err := lang.SafeExec([]byte(testMultiAssignCode2), "") 163 | if err != nil { 164 | t.Fatal("qlang.SafeExec:", err) 165 | } 166 | if v, ok := lang.GetVar("x"); !ok || v != 3 { 167 | t.Fatal("x != 3, x =", v) 168 | } 169 | if v, ok := lang.GetVar("y"); !ok || v != 2 { 170 | t.Fatal("y != 2, y =", v) 171 | } 172 | } 173 | 174 | // ----------------------------------------------------------------------------- 175 | 176 | const testErrMultiAssignCode = ` 177 | 178 | x, y = 3, 2, 1 179 | ` 180 | 181 | func TestErrMultiAssign(t *testing.T) { 182 | 183 | lang := qlang.New() 184 | err := lang.SafeExec([]byte(testErrMultiAssignCode), "") 185 | if err == nil { 186 | t.Fatal("qlang.SafeExec?") 187 | } 188 | if err.Error() != "line 3: runtime error: argument count of multi assignment doesn't match" { 189 | t.Fatal("testErrMultiAssignCode:", err) 190 | } 191 | } 192 | 193 | // ----------------------------------------------------------------------------- 194 | 195 | const testMultiAssignExCode = ` 196 | 197 | a = [1, 2] 198 | b = {"a": 1, "b": "hello"} 199 | a[1], b.b = 3, 2 200 | x, y = a[1], b.b 201 | ` 202 | 203 | func TestMultiAssignEx(t *testing.T) { 204 | 205 | lang := qlang.New() 206 | err := lang.SafeExec([]byte(testMultiAssignExCode), "") 207 | if err != nil { 208 | t.Fatal("qlang.SafeExec:", err) 209 | } 210 | if v, ok := lang.GetVar("x"); !ok || v != 3 { 211 | t.Fatal("x != 3, x =", v) 212 | } 213 | if v, ok := lang.GetVar("y"); !ok || v != 2 { 214 | t.Fatal("y != 2, y =", v) 215 | } 216 | } 217 | 218 | // ----------------------------------------------------------------------------- 219 | 220 | const testAssignExCode = ` 221 | 222 | Foo = class { 223 | fn _init(v) { 224 | this.foo = v 225 | } 226 | 227 | fn f() { 228 | return this.foo 229 | } 230 | } 231 | 232 | a = new Foo(3) 233 | x = a.f() 234 | ` 235 | 236 | func TestAssignEx(t *testing.T) { 237 | 238 | lang := qlang.New() 239 | err := lang.SafeExec([]byte(testAssignExCode), "") 240 | if err != nil { 241 | t.Fatal("qlang.SafeExec:", err) 242 | } 243 | if v, ok := lang.GetVar("x"); !ok || v != 3 { 244 | t.Fatal("x != 3, x =", v) 245 | } 246 | } 247 | 248 | // ----------------------------------------------------------------------------- 249 | 250 | const testClassAddAssignCode = ` 251 | 252 | Foo = class { 253 | fn _init() { 254 | this.foo = 1 255 | } 256 | 257 | fn f() { 258 | this.foo += 2 259 | } 260 | } 261 | 262 | a = new Foo 263 | a.f() 264 | x = a.foo 265 | ` 266 | 267 | func TestClassAddAssign(t *testing.T) { 268 | 269 | lang := qlang.New() 270 | err := lang.SafeExec([]byte(testClassAddAssignCode), "") 271 | if err != nil { 272 | t.Fatal("qlang.SafeExec:", err) 273 | } 274 | if v, ok := lang.GetVar("x"); !ok || v != 3 { 275 | t.Fatal("x != 3, x =", v) 276 | } 277 | } 278 | 279 | // ----------------------------------------------------------------------------- 280 | -------------------------------------------------------------------------------- /cmd/eql/README.md: -------------------------------------------------------------------------------- 1 | Embedded qlang (eql) 2 | ======== 3 | 4 | eql 全称 embedded qlang,是类似 erubis/erb 的东西。结合 go generate 可很方便地让 Go 支持模板(不是 html template,是指语言特性中的泛型)。 5 | 6 | ## eql 程序 7 | 8 | 命令行: 9 | 10 | ```bash 11 | eql [-i -j -o --key1=value1 --key2=value2 ...] 12 | eql [-i -j -o --key1=value1 --key2=value2 ...] 13 | ``` 14 | 15 | 其中 16 | 17 | * ``: 要解析的 eql 文件,也就是模板文件。 18 | * ``: 要解析的 template package,也就是整个目录是一个模板。 19 | * `-i`: 以 stdin 作为 json input。 20 | * `-j `: 输入 json input。 21 | * `-o `: 要生成的渲染后的文件。如果没有指定则为 stdout。 22 | * `-o `: 要生成的渲染后的目标目录。如果没有指定则为对 `` 进行渲染后的值。 23 | * `--key1=val1 --key2=val2 ...`: 重载 json input 涉及到的模板变量的值。 24 | 25 | 假设我们有 a.eql,内容如下: 26 | 27 | ``` 28 | Hello, $a! 29 | ``` 30 | 31 | 则我们可以有这样一些使用 `eql` 程序的方式: 32 | 33 | ```bash 34 | eql -j '{"a":"qlang"}' a.eql 35 | ``` 36 | 37 | 或者: 38 | 39 | ```bash 40 | echo '{"a":"qlang"}' | eql -i a.eql 41 | ``` 42 | 43 | 或者: 44 | 45 | ```bash 46 | # 这种方式看起来最为简洁,只是无法输入 string 类型之外的数据 47 | eql --a=qlang a.eql 48 | ``` 49 | 50 | ## 样例 51 | 52 | ### 单文件模板 53 | 54 | * [example.eql](example.eql): 展示 eql 语法的样例,下文将详细介绍“模板文法”。 55 | 56 | ### 目录模板 57 | 58 | * [$set.v1](example/$set.v1): 以集合类为例展示如何构建一个 template package。下文将详细介绍“目录模板规则”。 59 | 60 | 61 | ## 模板文法 62 | 63 | ### 插入 qlang 代码 64 | 65 | ```go 66 | <% 67 | // 在此插入 qlang 代码 68 | %> 69 | ``` 70 | 71 | ### 输出 qlang 表达式 72 | 73 | ```go 74 | <%= qlang_expr %> 75 | ``` 76 | 77 | 你可以理解为这只是插入 qlang 代码的一种简写手法。它等价于: 78 | 79 | ```go 80 | <% print(qlang_expr) %> 81 | ``` 82 | 83 | ### 输出一个变量 84 | 85 | ``` 86 | $var 87 | ``` 88 | 89 | 你可以理解为这只是插入 qlang 代码的一种简写手法。它等价于: 90 | 91 | ```go 92 | <% print(var) %> 93 | ``` 94 | 95 | 特别地,我们用 `$$` 表示普通字符 `$`。也就是说: 96 | 97 | ``` 98 | $$ 99 | ``` 100 | 101 | 等价于: 102 | 103 | ```go 104 | <% print('$') %> 105 | ``` 106 | 107 | ## 目录模板规则 108 | 109 | 目录模板的根目录名本身允许是模板,但是只有在用户没有指定 `-o ` 的情况下有效。例如上面例子中的 `$set.v1` 包含模板变量 `$set`。 110 | 111 | 目录模板的渲染过程(实例化过程)比较简单,就是对模板目录进行遍历,并执行如下规则: 112 | 113 | * 如果遇到子目录,创建出对应子目录,并递归本过程。 114 | * 如果是 `.eql` 后缀的文件,进行模板渲染并去掉 `.eql` 后缀进行存储。例如 `README.md.eql` 渲染后保存为 `README.md`。 115 | * 如果是其他后缀的文件,则进行简单复制。 116 | 117 | 118 | ## eql 函数 119 | 120 | ### eql.Imports() 121 | 122 | 这个函数解析 imports 变量(通常是用户通过命令行 `--imports=package1,package2,...` 传入的)并将 `,` 分隔风格改为符合 Go 语言中 import (...) 风格的字符串。 123 | 124 | 如果 imports 变量不存在,则该函数返回 "" (空字符串)。 125 | 126 | 例如,我们假设传入的 imports 变量为 "bufio,bytes",则 `eql.Imports()` 为如下风格的字符串: 127 | 128 | ```go 129 | "bufio" 130 | "bytes" 131 | ``` 132 | 133 | ### eql.Var("varname", defaultval) 134 | 135 | 这个函数主要解决 qlang 中目前还没有支持判断一个变量是否存在的缺陷。其语义看起来是这样的: 136 | 137 | ```go 138 | if varname != undefined { 139 | return varname 140 | } else { 141 | return defaultval 142 | } 143 | ``` 144 | 145 | 当然目前因为在 qlang 中如果 varname 不存在就会直接报错,所以以上代码仅仅是表达 `eql.Var("varname", defaultval)` 的逻辑语义。 146 | 147 | ## 用 eql 实现 Go 对泛型的支持 148 | 149 | 我们举例说明。假设我们现在实现了一个 Go 的模板类,文件名为 `example.eql`,内容如下: 150 | 151 | ```go 152 | package eql_test 153 | 154 | import ( 155 | <%= eql.Imports() %> 156 | "encoding/binary" 157 | ) 158 | 159 | // ----------------------------------------------------------------------------- 160 | 161 | type $module string 162 | 163 | func (p $module) write(out $Writer, b []byte) { 164 | 165 | _, err := out.Write(b) 166 | if err != nil { 167 | panic(err) 168 | } 169 | } 170 | 171 | <% if Writer == "*bytes.Buffer" { %> 172 | func (p $module) flush(out $Writer) { 173 | } 174 | <% } else { %> 175 | func (p $module) flush(out $Writer) { 176 | 177 | err := out.Flush() 178 | if err != nil { 179 | panic(err) 180 | } 181 | } 182 | <% } %> 183 | 184 | // ----------------------------------------------------------------------------- 185 | ``` 186 | 187 | 这个模板里面,有 3 个模板变量: 188 | 189 | * `imports`: 需要额外引入的 package 列表,用 `,` 分隔。 190 | * `module`: 模板类的类名。 191 | * `Writer`: 模板类的用到的参数类型。 192 | 193 | 有了这个模板,我们就可以用如下命令生成具体的类: 194 | 195 | ``` 196 | eql example.eql -o example_bytes.go --imports=bytes --module=modbytes --Writer="*bytes.Buffer" 197 | ``` 198 | 199 | 这会生成 example_bytes.go 文件,内容如下: 200 | 201 | ```go 202 | package eql_test 203 | 204 | import ( 205 | "bytes" 206 | "encoding/binary" 207 | ) 208 | 209 | // ----------------------------------------------------------------------------- 210 | 211 | type modbytes string 212 | 213 | func (p modbytes) write(out *bytes.Buffer, b []byte) { 214 | 215 | _, err := out.Write(b) 216 | if err != nil { 217 | panic(err) 218 | } 219 | } 220 | 221 | func (p modbytes) flush(out *bytes.Buffer) { 222 | } 223 | 224 | // ----------------------------------------------------------------------------- 225 | ``` 226 | 227 | 再试试换一个 Writer: 228 | 229 | ``` 230 | eql example.eql -o example_bufio.go --imports=bufio --module=modbufio --Writer="*bufio.Writer" 231 | ``` 232 | 233 | 我们得到 example_bufio.go,内容如下: 234 | 235 | ```go 236 | package eql_test 237 | 238 | import ( 239 | "bufio" 240 | "encoding/binary" 241 | ) 242 | 243 | // ----------------------------------------------------------------------------- 244 | 245 | type modbufio string 246 | 247 | func (p modbufio) write(out *bufio.Writer, b []byte) { 248 | 249 | _, err := out.Write(b) 250 | if err != nil { 251 | panic(err) 252 | } 253 | } 254 | 255 | func (p modbufio) flush(out *bufio.Writer) { 256 | 257 | err := out.Flush() 258 | if err != nil { 259 | panic(err) 260 | } 261 | } 262 | 263 | // ----------------------------------------------------------------------------- 264 | ``` 265 | 266 | ### 结合 go generate 267 | 268 | 结合 go generate 工具,我们就可以很好地支持 Go 泛型了。 269 | 270 | 例如假设我们在 foo.go 里面引用了 Writer = `*bufio.Writer` 版本的实现,则只需要在 foo.go 文件中插入以下代码: 271 | 272 | ```go 273 | //go:generate eql example.eql -o example_bufio.go --imports=bufio --module=module --Writer=*bufio.Writer 274 | ``` 275 | 276 | 如此,你只需要在 foo.go 所在的目录执行 go generate 就可以生成 example_bufio.go 文件了。 277 | 278 | 279 | ## 以库的方式使用 eql 280 | 281 | ### 初始化 282 | 283 | ```go 284 | ql = qlang.New() 285 | eql = eqlang.New(ql) 286 | 287 | ql.ResetVars(map[string]var{"name": "qlang"}) // 重置 ql 对象的变量集合 288 | ql.SetVar("a", 123) 289 | ``` 290 | 291 | ### eql.Subst("template text") 292 | 293 | 替换 $varname 为对应的值。例如: 294 | 295 | ```go 296 | output = eql.Subst("Hello, $name!") 297 | println(output) 298 | ``` 299 | 300 | 得到的结果是: 301 | 302 | ``` 303 | Hello, qlang! 304 | ``` 305 | 306 | ### eql.Execute("template script", fname, output) 307 | 308 | 执行 eql 模板。 309 | 310 | * fname: 为模板文件名。 311 | * output: 为输出文件,为 "" 表示输出到 stdout。 312 | 313 | 例如: 314 | 315 | ```go 316 | eql.Execute("Hello, <%= strings.ToUpper(name) %>!\n", "", "") 317 | ``` 318 | 319 | 输出为: 320 | 321 | ``` 322 | Hello, QLANG! 323 | ``` 324 | 325 | ### eql.ExecuteFile(source, output) 326 | 327 | 执行 eql 模板文件。 328 | 329 | * source: 为 eql 模板文件。 330 | * output: 为输出文件,为 "" 表示输出到 stdout。 331 | 332 | ### eql.ExecuteDir(vars, source, output) 333 | 334 | 执行 eql 模板目录。 335 | 336 | * vars: 是模板依赖的变量集。 337 | * source: 为 eql 模板目录。 338 | * output: 为输出目录,为 "" 表示输出的目录名是以 source 为模板的渲染结果。 339 | -------------------------------------------------------------------------------- /cmd/eql/eql_main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/xushiwei/qlang" 9 | "github.com/xushiwei/qlang/cl/interpreter" 10 | "github.com/xushiwei/qlang/exec" 11 | "github.com/xushiwei/qlang/lib/eqlang" 12 | "github.com/xushiwei/qlang/lib/qlang.all" 13 | ) 14 | 15 | // ----------------------------------------------------------------------------- 16 | 17 | const usage = ` 18 | Usage: 19 | eql [-i -j -o --key1=value1 --key2=value2 ...] 20 | eql [-i -j -o --key1=value1 --key2=value2 ...] 21 | 22 | : the template file to format. 23 | : the template dir to format. 24 | 25 | -i: use stdin as json input. 26 | -j : json input string. 27 | -o : output result into this file, default is stdout. 28 | -o : output result into this directory, default is format result. 29 | --key=value: (key, value) pair that overrides json input specified by -i or -j . 30 | ` 31 | 32 | func main() { 33 | 34 | qall.InitSafe(false) 35 | qlang.Import("", interpreter.Exports) 36 | qlang.SetDumpCode(os.Getenv("QLANG_DUMPCODE")) 37 | 38 | libs := os.Getenv("QLANG_PATH") 39 | if libs == "" { 40 | libs = os.Getenv("HOME") + "/qlang" 41 | } 42 | 43 | lang := qlang.New() 44 | lang.SetLibs(libs) 45 | 46 | vars = lang.Context 47 | eql := eqlang.New(lang) 48 | 49 | parseFlags() 50 | if source == "" { 51 | fmt.Fprintln(os.Stderr, usage) 52 | return 53 | } 54 | 55 | fi, err := os.Stat(source) 56 | if err != nil { 57 | fmt.Fprintln(os.Stderr, err) 58 | os.Exit(-2) 59 | } 60 | 61 | if fi.IsDir() { 62 | global := lang.CopyVars() 63 | err = eql.ExecuteDir(global, source, output) 64 | } else { 65 | err = eql.ExecuteFile(source, output) 66 | } 67 | if err != nil { 68 | fmt.Fprintln(os.Stderr, err) 69 | os.Exit(-3) 70 | } 71 | } 72 | 73 | // ----------------------------------------------------------------------------- 74 | 75 | var ( 76 | source string 77 | output string 78 | vars *exec.Context 79 | ) 80 | 81 | func parseFlags() { 82 | 83 | vars.SetVar("imports", "") 84 | for i := 1; i < len(os.Args); i++ { 85 | switch arg := os.Args[i]; arg { 86 | case "-o": 87 | if i+1 >= len(os.Args) { 88 | fmt.Fprintln(os.Stderr, "ERROR: switch -o doesn't have parameters, please use -o ") 89 | os.Exit(10) 90 | } 91 | output = os.Args[i+1] 92 | i++ 93 | case "-i": 94 | ret, err := eqlang.InputFile("-") 95 | if err != nil { 96 | fmt.Fprintf(os.Stderr, "ERROR: stdin isn't valid json - %v\n", err) 97 | os.Exit(12) 98 | } 99 | vars.ResetVars(ret) 100 | case "-j": 101 | if i+1 >= len(os.Args) { 102 | fmt.Fprintln(os.Stderr, "ERROR: switch -j doesn't have parameters, please use -j ") 103 | os.Exit(10) 104 | } 105 | ret, err := eqlang.Input(os.Args[i+1]) 106 | if err != nil { 107 | fmt.Fprintf(os.Stderr, "ERROR: invalid parameter `-j ` - %v\n", err) 108 | os.Exit(12) 109 | } 110 | vars.ResetVars(ret) 111 | i++ 112 | default: 113 | if strings.HasPrefix(arg, "--") { 114 | kv := arg[2:] 115 | pos := strings.Index(kv, "=") 116 | if pos < 0 { 117 | fmt.Fprintf(os.Stderr, "ERROR: invalid switch `%s`\n", arg) 118 | os.Exit(11) 119 | } 120 | vars.SetVar(kv[:pos], kv[pos+1:]) 121 | } else if arg[0] == '-' { 122 | fmt.Fprintf(os.Stderr, "ERROR: unknown switch `%s`\n", arg) 123 | os.Exit(12) 124 | } else { 125 | source = arg 126 | } 127 | } 128 | } 129 | } 130 | 131 | // ----------------------------------------------------------------------------- 132 | -------------------------------------------------------------------------------- /cmd/eql/example.eql: -------------------------------------------------------------------------------- 1 | <% 2 | // 3 | // eql example.eql -o example_bytes.go --imports=bytes --module=modbytes --Writer="*bytes.Buffer" 4 | // eql example.eql -o example_bufio.go --imports=bufio --module=modbufio --Writer="*bufio.Writer" 5 | // 6 | %> 7 | package eql_test 8 | 9 | import ( 10 | <%= eql.Imports() %> 11 | "encoding/binary" 12 | ) 13 | 14 | // ----------------------------------------------------------------------------- 15 | 16 | type $module string 17 | 18 | func (p $module) write(out $Writer, b []byte) { 19 | 20 | _, err := out.Write(b) 21 | if err != nil { 22 | panic(err) 23 | } 24 | } 25 | 26 | <% if Writer == "*bytes.Buffer" { %> 27 | func (p $module) flush(out $Writer) { 28 | } 29 | <% } else { %> 30 | func (p $module) flush(out $Writer) { 31 | 32 | err := out.Flush() 33 | if err != nil { 34 | panic(err) 35 | } 36 | } 37 | <% } %> 38 | 39 | // ----------------------------------------------------------------------------- 40 | -------------------------------------------------------------------------------- /cmd/eql/example/$set.v1/README.md.eql: -------------------------------------------------------------------------------- 1 | <% 2 | // 3 | // eql '$set.v1' --base=qlang.io/cmd/eql/example --set=uint32set --Set=Type --Item=uint32 4 | // eql '$set.v1' --base=qlang.io/cmd/eql/example --set=uintset --Set=Type --Item=uint 5 | // 6 | 7 | if Item == "string" { 8 | NewArgs, AddArgs, HasArgs = `"a", "b"`, `"c"`, `"d"` 9 | } else { 10 | NewArgs, AddArgs, HasArgs = `1, 2`, `3`, `4` 11 | } 12 | 13 | base = eql.Var("base", "") // if variable `base` is undefined, let it be "" 14 | if base != "" { 15 | base += "/" 16 | } 17 | package = base + set + ".v1" 18 | %> 19 | $package 20 | ====== 21 | 22 | $set.$Set is a set of $Item, implemented via `map[$Item]struct{}` for minimal memory consumption. 23 | 24 | Here is an example: 25 | 26 | ```go 27 | import ( 28 | "$package" 29 | ) 30 | 31 | set := $set.New($NewArgs) 32 | set.Add($AddArgs) 33 | 34 | if set.Has($HasArgs) { 35 | println("set has item `$HasArgs`") 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /cmd/eql/example/$set.v1/set.go.eql: -------------------------------------------------------------------------------- 1 | package $set 2 | 3 | // --------------------------------------------------------------------------- 4 | 5 | type empty struct{} 6 | 7 | // $Set is a set of $Item, implemented via map[$Item]struct{} for minimal memory consumption. 8 | type $Set map[$Item]empty 9 | 10 | // New creates a $Set from a list of values. 11 | func New(items ...$Item) $Set { 12 | ss := $Set{} 13 | ss.Insert(items...) 14 | return ss 15 | } 16 | 17 | // Add adds one item to the set. 18 | func (s $Set) Add(item $Item) { 19 | s[item] = empty{} 20 | } 21 | 22 | // Insert adds items to the set. 23 | func (s $Set) Insert(items ...$Item) { 24 | for _, item := range items { 25 | s[item] = empty{} 26 | } 27 | } 28 | 29 | // Delete removes all items from the set. 30 | func (s $Set) Delete(items ...$Item) { 31 | for _, item := range items { 32 | delete(s, item) 33 | } 34 | } 35 | 36 | // Has returns true iff item is contained in the set. 37 | func (s $Set) Has(item $Item) bool { 38 | _, contained := s[item] 39 | return contained 40 | } 41 | 42 | // HasAll returns true iff all items are contained in the set. 43 | func (s $Set) HasAll(items ...$Item) bool { 44 | for _, item := range items { 45 | if !s.Has(item) { 46 | return false 47 | } 48 | } 49 | return true 50 | } 51 | 52 | // IsSuperset returns true iff s1 is a superset of s2. 53 | func (s $Set) IsSuperset(s2 $Set) bool { 54 | for item := range s2 { 55 | if !s.Has(item) { 56 | return false 57 | } 58 | } 59 | return true 60 | } 61 | 62 | // Len returns the size of the set. 63 | func (s $Set) Len() int { 64 | return len(s) 65 | } 66 | 67 | // --------------------------------------------------------------------------- 68 | -------------------------------------------------------------------------------- /cmd/eql/example/gen-set-types.sh: -------------------------------------------------------------------------------- 1 | eql '$set.v1' --base=qlang.io/cmd/eql/example --set=uint32set --Set=Type --Item=uint32 2 | eql '$set.v1' --base=qlang.io/cmd/eql/example --set=stringset --Set=Type --Item=string 3 | -------------------------------------------------------------------------------- /cmd/eql/example/stringset.v1/README.md: -------------------------------------------------------------------------------- 1 | qlang.io/cmd/eql/example/stringset.v1 2 | ====== 3 | 4 | stringset.Type is a set of string, implemented via `map[string]struct{}` for minimal memory consumption. 5 | 6 | Here is an example: 7 | 8 | ```go 9 | import ( 10 | "qlang.io/cmd/eql/example/stringset.v1" 11 | ) 12 | 13 | set := stringset.New("a", "b") 14 | set.Add("c") 15 | 16 | if set.Has("d") { 17 | println("set has item `"d"`") 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /cmd/eql/example/stringset.v1/set.go: -------------------------------------------------------------------------------- 1 | package stringset 2 | 3 | // --------------------------------------------------------------------------- 4 | 5 | type empty struct{} 6 | 7 | // Type is a set of string, implemented via map[string]struct{} for minimal memory consumption. 8 | type Type map[string]empty 9 | 10 | // New creates a Type from a list of values. 11 | func New(items ...string) Type { 12 | ss := Type{} 13 | ss.Insert(items...) 14 | return ss 15 | } 16 | 17 | // Add adds one item to the set. 18 | func (s Type) Add(item string) { 19 | s[item] = empty{} 20 | } 21 | 22 | // Insert adds items to the set. 23 | func (s Type) Insert(items ...string) { 24 | for _, item := range items { 25 | s[item] = empty{} 26 | } 27 | } 28 | 29 | // Delete removes all items from the set. 30 | func (s Type) Delete(items ...string) { 31 | for _, item := range items { 32 | delete(s, item) 33 | } 34 | } 35 | 36 | // Has returns true iff item is contained in the set. 37 | func (s Type) Has(item string) bool { 38 | _, contained := s[item] 39 | return contained 40 | } 41 | 42 | // HasAll returns true iff all items are contained in the set. 43 | func (s Type) HasAll(items ...string) bool { 44 | for _, item := range items { 45 | if !s.Has(item) { 46 | return false 47 | } 48 | } 49 | return true 50 | } 51 | 52 | // IsSuperset returns true iff s1 is a superset of s2. 53 | func (s Type) IsSuperset(s2 Type) bool { 54 | for item := range s2 { 55 | if !s.Has(item) { 56 | return false 57 | } 58 | } 59 | return true 60 | } 61 | 62 | // Len returns the size of the set. 63 | func (s Type) Len() int { 64 | return len(s) 65 | } 66 | 67 | // --------------------------------------------------------------------------- 68 | -------------------------------------------------------------------------------- /cmd/eql/example/uint32set.v1/README.md: -------------------------------------------------------------------------------- 1 | qlang.io/cmd/eql/example/uint32set.v1 2 | ====== 3 | 4 | uint32set.Type is a set of uint32, implemented via `map[uint32]struct{}` for minimal memory consumption. 5 | 6 | Here is an example: 7 | 8 | ```go 9 | import ( 10 | "qlang.io/cmd/eql/example/uint32set.v1" 11 | ) 12 | 13 | set := uint32set.New(1, 2) 14 | set.Add(3) 15 | 16 | if set.Has(4) { 17 | println("set has item `4`") 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /cmd/eql/example/uint32set.v1/set.go: -------------------------------------------------------------------------------- 1 | package uint32set 2 | 3 | // --------------------------------------------------------------------------- 4 | 5 | type empty struct{} 6 | 7 | // Type is a set of uint32, implemented via map[uint32]struct{} for minimal memory consumption. 8 | type Type map[uint32]empty 9 | 10 | // New creates a Type from a list of values. 11 | func New(items ...uint32) Type { 12 | ss := Type{} 13 | ss.Insert(items...) 14 | return ss 15 | } 16 | 17 | // Add adds one item to the set. 18 | func (s Type) Add(item uint32) { 19 | s[item] = empty{} 20 | } 21 | 22 | // Insert adds items to the set. 23 | func (s Type) Insert(items ...uint32) { 24 | for _, item := range items { 25 | s[item] = empty{} 26 | } 27 | } 28 | 29 | // Delete removes all items from the set. 30 | func (s Type) Delete(items ...uint32) { 31 | for _, item := range items { 32 | delete(s, item) 33 | } 34 | } 35 | 36 | // Has returns true iff item is contained in the set. 37 | func (s Type) Has(item uint32) bool { 38 | _, contained := s[item] 39 | return contained 40 | } 41 | 42 | // HasAll returns true iff all items are contained in the set. 43 | func (s Type) HasAll(items ...uint32) bool { 44 | for _, item := range items { 45 | if !s.Has(item) { 46 | return false 47 | } 48 | } 49 | return true 50 | } 51 | 52 | // IsSuperset returns true iff s1 is a superset of s2. 53 | func (s Type) IsSuperset(s2 Type) bool { 54 | for item := range s2 { 55 | if !s.Has(item) { 56 | return false 57 | } 58 | } 59 | return true 60 | } 61 | 62 | // Len returns the size of the set. 63 | func (s Type) Len() int { 64 | return len(s) 65 | } 66 | 67 | // --------------------------------------------------------------------------- 68 | -------------------------------------------------------------------------------- /cmd/miniqlang/mini.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strings" 7 | 8 | "github.com/xushiwei/qlang" 9 | _ "github.com/xushiwei/qlang/lib/builtin" // 导入 builtin 包 10 | ) 11 | 12 | // ----------------------------------------------------------------------------- 13 | 14 | var strings_Exports = map[string]interface{}{ 15 | "replacer": strings.NewReplacer, 16 | } 17 | 18 | func main() { 19 | 20 | qlang.Import("strings", strings_Exports) // 导入一个自定义的包,叫 strings(和标准库同名) 21 | ql := qlang.New() 22 | 23 | err := ql.SafeEval(`x = strings.replacer("?", "!").replace("hello, world???")`) 24 | if err != nil { 25 | log.Fatal(err) 26 | return 27 | } 28 | 29 | fmt.Println("x:", ql.Var("x")) // 输出 x: hello, world!!! 30 | } 31 | 32 | // ----------------------------------------------------------------------------- 33 | -------------------------------------------------------------------------------- /cmd/playground/sandbox/play.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bytes" 9 | "encoding/binary" 10 | "errors" 11 | "fmt" 12 | "io" 13 | "sync" 14 | "time" 15 | "unicode/utf8" 16 | ) 17 | 18 | // When sandbox time begins. 19 | var epoch = time.Unix(1257894000, 0) 20 | 21 | // Recorder records the standard and error outputs of a sandbox program 22 | // (comprised of playback headers) and converts it to a sequence of Events. 23 | // It sanitizes each Event's Message to ensure it is valid UTF-8. 24 | // 25 | // Playground programs precede all their writes with a header (described 26 | // below) that describes the time the write occurred (in playground time) and 27 | // the length of the data that will be written. If a non-header is 28 | // encountered where a header is expected, the output is scanned for the next 29 | // header and the intervening text string is added to the sequence an event 30 | // occurring at the same time as the preceding event. 31 | // 32 | // A playback header has this structure: 33 | // 4 bytes: "\x00\x00PB", a magic header 34 | // 8 bytes: big-endian int64, unix time in nanoseconds 35 | // 4 bytes: big-endian int32, length of the next write 36 | // 37 | type Recorder struct { 38 | mu sync.Mutex 39 | writes []recorderWrite 40 | } 41 | 42 | type recorderWrite struct { 43 | b []byte 44 | kind string 45 | } 46 | 47 | func (r *Recorder) Stdout() io.Writer { return recorderWriter{r, "stdout"} } 48 | func (r *Recorder) Stderr() io.Writer { return recorderWriter{r, "stderr"} } 49 | 50 | type recorderWriter struct { 51 | r *Recorder 52 | kind string 53 | } 54 | 55 | func (w recorderWriter) Write(b []byte) (n int, err error) { 56 | w.r.mu.Lock() 57 | defer w.r.mu.Unlock() 58 | 59 | // Append this write to the previous one if it has the same kind. 60 | if len(w.r.writes) > 0 { 61 | prev := &w.r.writes[len(w.r.writes)-1] 62 | if prev.kind == w.kind { 63 | prev.b = append(prev.b, b...) 64 | return len(b), nil 65 | } 66 | } 67 | 68 | // Otherwise, append a new write. 69 | w.r.writes = append(w.r.writes, recorderWrite{ 70 | append([]byte(nil), b...), w.kind, 71 | }) 72 | 73 | return len(b), nil 74 | } 75 | 76 | type Event struct { 77 | Message string `json:"message"` 78 | Kind string `json:"kind"` // "stdout" or "stderr" 79 | Delay time.Duration `json:"delay"` // time to wait before printing Message 80 | } 81 | 82 | func (r *Recorder) Events() ([]Event, error) { 83 | r.mu.Lock() 84 | defer r.mu.Unlock() 85 | 86 | var ( 87 | out []Event 88 | now = epoch 89 | ) 90 | for _, w := range r.writes { 91 | events, err := decode(w.kind, w.b) 92 | if err != nil { 93 | return nil, err 94 | } 95 | for _, e := range events { 96 | delay := e.time.Sub(now) 97 | if delay < 0 { 98 | delay = 0 99 | } 100 | out = append(out, Event{ 101 | Message: string(sanitize(e.msg)), 102 | Kind: e.kind, 103 | Delay: delay, 104 | }) 105 | if delay > 0 { 106 | now = e.time 107 | } 108 | } 109 | } 110 | return out, nil 111 | } 112 | 113 | type event struct { 114 | msg []byte 115 | kind string 116 | time time.Time 117 | } 118 | 119 | func decode(kind string, output []byte) ([]event, error) { 120 | var ( 121 | magic = []byte{0, 0, 'P', 'B'} 122 | headerLen = 8 + 4 123 | last = epoch 124 | events []event 125 | ) 126 | add := func(t time.Time, b []byte) { 127 | var prev *event 128 | if len(events) > 0 { 129 | prev = &events[len(events)-1] 130 | } 131 | if prev != nil && t.Equal(prev.time) { 132 | // Merge this event with previous event, to avoid 133 | // sending a lot of events for a big output with no 134 | // significant timing information. 135 | prev.msg = append(prev.msg, b...) 136 | } else { 137 | e := event{msg: b, kind: kind, time: t} 138 | events = append(events, e) 139 | } 140 | last = t 141 | } 142 | for i := 0; i < len(output); { 143 | if !bytes.HasPrefix(output[i:], magic) { 144 | // Not a header; find next header. 145 | j := bytes.Index(output[i:], magic) 146 | if j < 0 { 147 | // No more headers; bail. 148 | add(last, output[i:]) 149 | break 150 | } 151 | add(last, output[i:i+j]) 152 | i += j 153 | } 154 | i += len(magic) 155 | 156 | // Decode header. 157 | if len(output)-i < headerLen { 158 | return nil, errors.New("short header") 159 | } 160 | header := output[i : i+headerLen] 161 | nanos := int64(binary.BigEndian.Uint64(header[0:])) 162 | t := time.Unix(0, nanos) 163 | if t.Before(last) { 164 | // Force timestamps to be monotonic. (This could 165 | // be an encoding error, which we ignore now but will 166 | // will likely be picked up when decoding the length.) 167 | t = last 168 | } 169 | n := int(binary.BigEndian.Uint32(header[8:])) 170 | if n < 0 { 171 | return nil, fmt.Errorf("bad length: %v", n) 172 | } 173 | i += headerLen 174 | 175 | // Slurp output. 176 | // Truncated output is OK (probably caused by sandbox limits). 177 | end := i + n 178 | if end > len(output) { 179 | end = len(output) 180 | } 181 | add(t, output[i:end]) 182 | i += n 183 | } 184 | return events, nil 185 | } 186 | 187 | // sanitize scans b for invalid utf8 code points. If found, it reconstructs 188 | // the slice replacing the invalid codes with \uFFFD, properly encoded. 189 | func sanitize(b []byte) []byte { 190 | if utf8.Valid(b) { 191 | return b 192 | } 193 | var buf bytes.Buffer 194 | for len(b) > 0 { 195 | r, size := utf8.DecodeRune(b) 196 | b = b[size:] 197 | buf.WriteRune(r) 198 | } 199 | return buf.Bytes() 200 | } 201 | -------------------------------------------------------------------------------- /cmd/playground/sandbox/play_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "encoding/binary" 9 | "reflect" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | func TestDecode(t *testing.T) { 15 | r := new(Recorder) 16 | stdout := r.Stdout() 17 | stderr := r.Stderr() 18 | 19 | stdout.Write([]byte("head")) 20 | stdout.Write(pbWrite(0, "one")) 21 | stdout.Write(pbWrite(0, "two")) 22 | stderr.Write(pbWrite(1*time.Second, "three")) 23 | stdout.Write(pbWrite(2*time.Second, "four")) 24 | stderr.Write(pbWrite(3*time.Second, "five")) 25 | stdout.Write([]byte("middle")) 26 | stderr.Write(pbWrite(4*time.Second, "six")) 27 | stdout.Write(pbWrite(4*time.Second, "seven")) 28 | stdout.Write([]byte("tail")) 29 | 30 | want := []Event{ 31 | {"headonetwo", "stdout", 0}, 32 | {"three", "stderr", time.Second}, 33 | {"four", "stdout", time.Second}, 34 | {"five", "stderr", time.Second}, 35 | {"middle", "stdout", 0}, 36 | {"six", "stderr", time.Second}, 37 | {"seventail", "stdout", 0}, 38 | } 39 | 40 | got, err := r.Events() 41 | if err != nil { 42 | t.Fatalf("Decode: %v", err) 43 | } 44 | if !reflect.DeepEqual(got, want) { 45 | t.Errorf("got %v, want %v", got, want) 46 | } 47 | } 48 | 49 | func pbWrite(offset time.Duration, s string) []byte { 50 | out := make([]byte, 16) 51 | out[2] = 'P' 52 | out[3] = 'B' 53 | binary.BigEndian.PutUint64(out[4:], uint64(epoch.Add(offset).UnixNano())) 54 | binary.BigEndian.PutUint32(out[12:], uint32(len(s))) 55 | return append(out, s...) 56 | } 57 | -------------------------------------------------------------------------------- /cmd/playground/sandbox/sandbox.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "net/http" 10 | "os" 11 | "os/exec" 12 | "path/filepath" 13 | "time" 14 | ) 15 | 16 | const maxRunTime = 2 * time.Second 17 | 18 | type Request struct { 19 | Body string `json:"body"` 20 | } 21 | 22 | type Response struct { 23 | Errors string `json:"errors"` 24 | Events []Event `json:"events"` 25 | } 26 | 27 | func main() { 28 | http.HandleFunc("/compile", compileHandler) 29 | log.Fatal(http.ListenAndServe(":8080", nil)) 30 | } 31 | 32 | func compileHandler(w http.ResponseWriter, r *http.Request) { 33 | var req Request 34 | if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 35 | http.Error(w, fmt.Sprintf("error decoding request: %v", err), http.StatusBadRequest) 36 | return 37 | } 38 | resp, err := compileAndRun(&req) 39 | if err != nil { 40 | http.Error(w, err.Error(), http.StatusInternalServerError) 41 | return 42 | } 43 | if err := json.NewEncoder(w).Encode(resp); err != nil { 44 | http.Error(w, fmt.Sprintf("error encoding response: %v", err), http.StatusInternalServerError) 45 | return 46 | } 47 | } 48 | 49 | func compileAndRun(req *Request) (res *Response, err error) { 50 | tmpDir, err := ioutil.TempDir("", "sandbox") 51 | if err != nil { 52 | return nil, fmt.Errorf("error creating temp directory: %v", err) 53 | } 54 | defer os.RemoveAll(tmpDir) 55 | 56 | in := filepath.Join(tmpDir, "main.qn") 57 | if err := ioutil.WriteFile(in, []byte(req.Body), 0400); err != nil { 58 | return nil, fmt.Errorf("error creating temp file %q: %v", in, err) 59 | } 60 | 61 | cmd := exec.Command("qlang.safe", in) 62 | rec := new(Recorder) 63 | cmd.Stdout = rec.Stdout() 64 | cmd.Stderr = rec.Stderr() 65 | 66 | if err := runTimeout(cmd, maxRunTime); err != nil { 67 | if err == timeoutErr { 68 | return &Response{Errors: "process took too long"}, nil 69 | } 70 | if _, ok := err.(*exec.ExitError); !ok { 71 | return nil, fmt.Errorf("error running sandbox: %v", err) 72 | } 73 | } 74 | events, err := rec.Events() 75 | if err != nil { 76 | return nil, fmt.Errorf("error decoding events: %v", err) 77 | } 78 | return &Response{Events: events}, nil 79 | } 80 | 81 | var timeoutErr = errors.New("process timed out") 82 | 83 | func runTimeout(cmd *exec.Cmd, d time.Duration) error { 84 | if err := cmd.Start(); err != nil { 85 | return err 86 | } 87 | errc := make(chan error, 1) 88 | go func() { 89 | errc <- cmd.Wait() 90 | }() 91 | t := time.NewTimer(d) 92 | select { 93 | case err := <-errc: 94 | t.Stop() 95 | return err 96 | case <-t.C: 97 | cmd.Process.Kill() 98 | return timeoutErr 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /cmd/playground/sandbox/sandbox_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | const fibCode = ` 8 | fib = fn() { 9 | a, b = [0, 1] 10 | f = fn() { 11 | a, b = [b, a+b] 12 | return a 13 | } 14 | return f 15 | } 16 | f = fib() 17 | println(f(), f(), f(), f(), f()) 18 | ` 19 | 20 | func _TestCompileAndRun(t *testing.T) { 21 | req := Request{ 22 | Body: fibCode, 23 | } 24 | res, err := compileAndRun(&req) 25 | if err != nil { 26 | t.Fatal("compileAndRun test error") 27 | } 28 | if res.Events[0].Message != "1 1 2 3 5\n" { 29 | t.Fatal("compileAndRun res test error") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /cmd/qexport/README.md: -------------------------------------------------------------------------------- 1 | The Q Language Export Tool 2 | ======== 3 | 4 | Export go package to qlang module 5 | 6 | 7 | The Q Language : https://github.com/xushiwei/qlang 8 | 9 | 10 | ### Usages: 11 | 12 | ``` 13 | Export go packages to qlang modules. 14 | 15 | Usage: 16 | qexport [option] packages 17 | 18 | The packages for go package list or std for golang all standard packages. 19 | 20 | -contexts string 21 | optional comma-separated list of -[-cgo] to override default contexts. 22 | -convnew 23 | optional convert NewType func to type func (default true) 24 | -defctx 25 | optional use default context for build, default use all contexts. 26 | -lowercase 27 | optional use qlang lower case style. (default true) 28 | -outpath string 29 | optional set export root path (default "./qlang") 30 | -skiperrimpl 31 | optional skip error interface implement struct. (default true) 32 | -updatepath string 33 | option set update qlang package root 34 | ``` 35 | 36 | ### Examples 37 | 38 | ``` 39 | export sync 40 | > qexport sync 41 | 42 | export html and html/template package 43 | > qexport html html/template 44 | 45 | export all package 46 | > qexport std 47 | 48 | export io and update from github.com/xushiwei/qlang/lib/io 49 | > qexport -updatepath github.com/xushiwei/qlang/lib io 50 | 51 | export all package and update from github.com/xushiwei/qlang/lib 52 | > qexport -updatepath github.com/xushiwei/qlang/lib std 53 | ``` 54 | 55 | ### 导出包 56 | 57 | ``` 58 | 导出一个包 59 | > qexport bufio 60 | 61 | 导出多个包 62 | > qexport bufio io io/ioutil 63 | 64 | 导出标准包 65 | > qexport std 66 | ``` 67 | 68 | ### 更新包 69 | ``` 70 | 导出bufio包,复制github.com/xushiwei/qlang/lib中bufio包到输出目录并更新。 71 | > qexport -updatepath github.com/xushiwei/qlang/lib bufio 72 | 73 | 导出多个包,复制github.com/xushiwei/qlang/lib中对应包到输出目录并作更新。 74 | > qexport -updatepath github.com/xushiwei/qlang/lib bufio io io/ioutil 75 | 76 | 导出标准包,复制github.com/xushiwei/qlang/lib中对应包到输出目录并作更新。 77 | > qexport -updatepath github.com/xushiwei/qlang/lib std 78 | 79 | ``` 80 | 81 | ### 导出和更新包原理和实现 82 | ``` 83 | 1. 导出pkg包,首先分析pkg包的所有函数和类型作导出准备 84 | 85 | 2. 如果需要更新,则先复制github.com/xushiwei/qlang/lib/pkg包到输出目录中 86 | 同时分析github.com/xushiwei/qlang/lib/pkg包中对原始pkg包的引用 87 | 所有引用的名称在做更新导出时不再输出。 88 | 89 | 3. 输出需要导出的函数到输出文件中,如果为更新包,则作合并处理 90 | 标准输出位置为 Exports 变量的定义处 91 | 特定Go版本输出 以go1.6版本为例 92 | 文件名为 pkg-go1.6.go 93 | 编译注释 // +build go1.6 94 | 在init函数中作输出 95 | ``` -------------------------------------------------------------------------------- /cmd/qexport/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "path/filepath" 7 | "regexp" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | func apipath(base string) string { 13 | return filepath.Join(os.Getenv("GOROOT"), "api", base) 14 | } 15 | 16 | //pkg syscall (windows-386), const CERT_E_CN_NO_MATCH = 2148204815 17 | var sym = regexp.MustCompile(`^pkg (\S+)\s?(.*)?, (?:(var|func|type|const)) ([A-Z]\w*)`) 18 | var num = regexp.MustCompile(`^\-?[0-9]+$`) 19 | 20 | type KeyType int 21 | 22 | const ( 23 | Normal KeyType = 1 24 | ConstInt64 KeyType = 2 25 | ConstUnit64 KeyType = 3 26 | ) 27 | 28 | type GoApi struct { 29 | Keys map[string]KeyType 30 | Ver string 31 | } 32 | 33 | func LoadApi(ver string) (*GoApi, error) { 34 | f, err := os.Open(apipath(ver + ".txt")) 35 | if err != nil { 36 | return nil, err 37 | } 38 | sc := bufio.NewScanner(f) 39 | keys := make(map[string]KeyType) 40 | for sc.Scan() { 41 | l := sc.Text() 42 | has := func(v string) bool { return strings.Contains(l, v) } 43 | if has("interface, ") || has(", method (") { 44 | continue 45 | } 46 | if m := sym.FindStringSubmatch(l); m != nil { 47 | // 1 pkgname 48 | // 2 os-arch-cgo 49 | // 3 var|func|type|const 50 | // 4 name 51 | key := m[1] + "." + m[4] 52 | if _, ok := keys[key]; ok { 53 | continue 54 | } 55 | keys[key] = Normal 56 | if m[3] == "const" { 57 | if pos := strings.LastIndex(l, "="); pos != -1 { 58 | value := strings.TrimSpace(l[pos+1:]) 59 | if num.MatchString(value) { 60 | _, err := strconv.ParseInt(l[pos+2:], 10, 32) 61 | if err != nil { 62 | if value[0] == '-' { 63 | keys[key] = ConstInt64 64 | } else { 65 | keys[key] = ConstUnit64 66 | } 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } 73 | return &GoApi{Ver: ver, Keys: keys}, nil 74 | } 75 | 76 | type ApiCheck struct { 77 | Base map[string]KeyType 78 | Apis []*GoApi 79 | } 80 | 81 | func NewApiCheck() *ApiCheck { 82 | ac := &ApiCheck{} 83 | ac.Base = make(map[string]KeyType) 84 | return ac 85 | } 86 | 87 | func (ac *ApiCheck) LoadBase(vers ...string) error { 88 | for _, ver := range vers { 89 | api, err := LoadApi(ver) 90 | if err != nil { 91 | return err 92 | } 93 | for k, v := range api.Keys { 94 | ac.Base[k] = v 95 | } 96 | } 97 | return nil 98 | } 99 | 100 | func (ac *ApiCheck) LoadApi(vers ...string) error { 101 | for _, ver := range vers { 102 | api, err := LoadApi(ver) 103 | if err != nil { 104 | return err 105 | } 106 | for k, _ := range api.Keys { 107 | if _, ok := ac.Base[k]; ok { 108 | delete(api.Keys, k) 109 | } 110 | } 111 | ac.Apis = append(ac.Apis, api) 112 | } 113 | return nil 114 | } 115 | 116 | func (ac *ApiCheck) FincApis(name string) (vers []string) { 117 | for _, api := range ac.Apis { 118 | if _, ok := api.Keys[name]; ok { 119 | vers = append(vers, api.Ver) 120 | } 121 | } 122 | return 123 | } 124 | 125 | func (ac *ApiCheck) ApiVers() (vers []string) { 126 | for _, api := range ac.Apis { 127 | vers = append(vers, api.Ver) 128 | } 129 | return 130 | } 131 | 132 | func (ac *ApiCheck) CheckConstType(name string) KeyType { 133 | if typ, ok := ac.Base[name]; ok { 134 | return typ 135 | } 136 | for _, api := range ac.Apis { 137 | if typ, ok := api.Keys[name]; ok { 138 | return typ 139 | } 140 | } 141 | return Normal 142 | } 143 | -------------------------------------------------------------------------------- /cmd/qexport/context.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go/build" 5 | "log" 6 | "strings" 7 | ) 8 | 9 | // contexts are the default contexts which are scanned, unless 10 | // overridden by the -contexts flag. 11 | var contexts = []*build.Context{ 12 | {GOOS: "linux", GOARCH: "386", CgoEnabled: true}, 13 | {GOOS: "linux", GOARCH: "amd64", CgoEnabled: true}, 14 | {GOOS: "linux", GOARCH: "arm", CgoEnabled: true}, 15 | {GOOS: "darwin", GOARCH: "386", CgoEnabled: true}, 16 | {GOOS: "darwin", GOARCH: "amd64", CgoEnabled: true}, 17 | {GOOS: "windows", GOARCH: "amd64", CgoEnabled: true}, 18 | {GOOS: "windows", GOARCH: "386", CgoEnabled: true}, 19 | {GOOS: "freebsd", GOARCH: "386", CgoEnabled: true}, 20 | {GOOS: "freebsd", GOARCH: "amd64", CgoEnabled: true}, 21 | {GOOS: "freebsd", GOARCH: "arm", CgoEnabled: true}, 22 | {GOOS: "netbsd", GOARCH: "386", CgoEnabled: true}, 23 | {GOOS: "netbsd", GOARCH: "amd64", CgoEnabled: true}, 24 | {GOOS: "netbsd", GOARCH: "arm", CgoEnabled: true}, 25 | {GOOS: "openbsd", GOARCH: "386", CgoEnabled: true}, 26 | {GOOS: "openbsd", GOARCH: "amd64", CgoEnabled: true}, 27 | } 28 | 29 | func contextName(c *build.Context) string { 30 | s := c.GOOS + "-" + c.GOARCH 31 | if c.CgoEnabled { 32 | return s + "-cgo" 33 | } 34 | return s 35 | } 36 | 37 | func osArchName(c *build.Context) string { 38 | return c.GOOS + "-" + c.GOARCH 39 | } 40 | 41 | func parseContext(c string) *build.Context { 42 | parts := strings.Split(c, "-") 43 | if len(parts) < 2 { 44 | log.Fatalf("bad context: %q", c) 45 | } 46 | bc := &build.Context{ 47 | GOOS: parts[0], 48 | GOARCH: parts[1], 49 | } 50 | if len(parts) == 3 { 51 | if parts[2] == "cgo" { 52 | bc.CgoEnabled = true 53 | } else { 54 | log.Fatalf("bad context: %q", c) 55 | } 56 | } 57 | return bc 58 | } 59 | 60 | func setCustomContexts(customctx string) { 61 | contexts = []*build.Context{} 62 | for _, c := range strings.Split(customctx, ",") { 63 | contexts = append(contexts, parseContext(c)) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /cmd/qexport/update.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "go/ast" 6 | "go/build" 7 | "go/format" 8 | "go/token" 9 | "go/types" 10 | "io/ioutil" 11 | "path/filepath" 12 | 13 | "github.com/visualfc/pkgwalk" 14 | ) 15 | 16 | type ObjectInfo struct { 17 | kind pkgwalk.ObjKind 18 | ident *ast.Ident 19 | obj types.Object 20 | } 21 | 22 | type InsertInfo struct { 23 | file *ast.File 24 | decl ast.Decl 25 | pos token.Position 26 | specImportName string 27 | importPos token.Position 28 | } 29 | 30 | type UpdateInfo struct { 31 | fileset *token.FileSet 32 | updataPkg string 33 | usesMap map[string]*ObjectInfo 34 | exportsInfo *InsertInfo 35 | initsInfo []*InsertInfo 36 | } 37 | 38 | func insertData(org []byte, data []byte, offset int) []byte { 39 | var out []byte 40 | out = append(out, org[:offset-1]...) 41 | out = append(out, data...) 42 | out = append(out, org[offset-1:]...) 43 | return out 44 | } 45 | 46 | func (i *InsertInfo) UpdateFile(outpath string, data []byte, hasTypeExport bool) error { 47 | file := filepath.Join(outpath, i.pos.Filename) 48 | all, err := ioutil.ReadFile(file) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | if hasTypeExport && i.specImportName == "spec" { 54 | data = bytes.Replace(data, []byte("qlang."), []byte("spec."), -1) 55 | } 56 | 57 | out := insertData(all, data, i.pos.Offset) 58 | if hasTypeExport && i.specImportName == "" { 59 | spec := []byte("\n\nqlang \"github.com/xushiwei/qlang/spec\"\n") 60 | out = insertData(out, spec, i.importPos.Offset+1) 61 | } 62 | 63 | // format 64 | fout, err := format.Source(out) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | ioutil.WriteFile(file, fout, 0777) 70 | return nil 71 | } 72 | 73 | //check has update package 74 | func CheckUpdateInfo(pkgname string, pkg string) (*UpdateInfo, error) { 75 | updatePkgPath := flagUpdatePath + "/" + pkg 76 | bp, err := build.Import(updatePkgPath, "", 0) 77 | if err != nil { 78 | return nil, err 79 | } 80 | CopyDir(bp.Dir, flagExportPath+"/"+pkg, false) 81 | 82 | conf := pkgwalk.DefaultPkgConfig() 83 | w := pkgwalk.NewPkgWalker(&build.Default) 84 | pkgx, err := pkgwalk.ImportPackage(w, updatePkgPath, conf) 85 | if err != nil { 86 | return nil, err 87 | } 88 | list := pkgwalk.LookupObjList(w, pkgx, conf) 89 | 90 | ui := &UpdateInfo{} 91 | ui.fileset = w.FileSet 92 | ui.updataPkg = bp.ImportPath 93 | ui.usesMap = make(map[string]*ObjectInfo) 94 | 95 | for ident, obj := range conf.Info.Uses { 96 | if obj != nil && obj.Pkg() != nil && obj.Pkg().Path() == pkg { 97 | kind, _ := pkgwalk.ParserObjectKind(ident, obj, conf) 98 | ui.usesMap[ident.Name] = &ObjectInfo{kind, ident, obj} 99 | } 100 | } 101 | fnMakeInsertInfo := func(ident *ast.Ident) *InsertInfo { 102 | file, decl := w.FindDeclForPos(ident.Pos()) 103 | if decl == nil { 104 | return nil 105 | } 106 | specImport := w.FindImportName(file, "github.com/xushiwei/qlang/spec") 107 | endImportPos := w.FindImportEndPos(file) 108 | pos := w.FileSet.Position(decl.End()) 109 | _, pos.Filename = filepath.Split(pos.Filename) 110 | ipos := w.FileSet.Position(endImportPos) 111 | _, ipos.Filename = filepath.Split(pos.Filename) 112 | return &InsertInfo{file, decl, pos, specImport, ipos} 113 | } 114 | for _, obj := range list { 115 | if obj != nil && obj.Obj != nil { 116 | if obj.Obj.Name() == "Exports" { 117 | v := fnMakeInsertInfo(obj.Ident) 118 | if v != nil { 119 | ui.exportsInfo = v 120 | } 121 | } else if obj.Obj.Name() == "init" { 122 | v := fnMakeInsertInfo(obj.Ident) 123 | if v != nil { 124 | ui.initsInfo = append(ui.initsInfo, v) 125 | } 126 | } 127 | } 128 | } 129 | 130 | return ui, nil 131 | } 132 | -------------------------------------------------------------------------------- /cmd/qexport/util.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | func CopyFile(source string, dest string) (err error) { 10 | sourcefile, err := os.Open(source) 11 | if err != nil { 12 | return err 13 | } 14 | defer sourcefile.Close() 15 | destfile, err := os.Create(dest) 16 | if err != nil { 17 | return err 18 | } 19 | defer destfile.Close() 20 | _, err = io.Copy(destfile, sourcefile) 21 | if err == nil { 22 | sourceinfo, err := os.Stat(source) 23 | if err != nil { 24 | err = os.Chmod(dest, sourceinfo.Mode()) 25 | } 26 | } 27 | return 28 | } 29 | 30 | func CopyDir(source string, dest string, subDir bool) (err error) { 31 | // get properties of source dir 32 | sourceinfo, err := os.Stat(source) 33 | if err != nil { 34 | return err 35 | } 36 | // create dest dir 37 | err = os.MkdirAll(dest, sourceinfo.Mode()) 38 | if err != nil { 39 | return err 40 | } 41 | directory, _ := os.Open(source) 42 | objects, err := directory.Readdir(-1) 43 | for _, obj := range objects { 44 | sourcefilepointer := source + "/" + obj.Name() 45 | destinationfilepointer := dest + "/" + obj.Name() 46 | if obj.IsDir() { 47 | if subDir { 48 | // create sub-directories - recursively 49 | err = CopyDir(sourcefilepointer, destinationfilepointer, subDir) 50 | if err != nil { 51 | fmt.Println(err) 52 | } 53 | } 54 | } else { 55 | // perform copy 56 | err = CopyFile(sourcefilepointer, destinationfilepointer) 57 | if err != nil { 58 | fmt.Println(err) 59 | } 60 | } 61 | } 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /cmd/qlang.safe/main_safe.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/xushiwei/qlang/cmd/qshell" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | func main() { 10 | 11 | qshell.Main(true) 12 | } 13 | 14 | // ----------------------------------------------------------------------------- 15 | -------------------------------------------------------------------------------- /cmd/qlang/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/xushiwei/qlang/cmd/qshell" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | func main() { 10 | 11 | qshell.Main(false) 12 | } 13 | 14 | // ----------------------------------------------------------------------------- 15 | -------------------------------------------------------------------------------- /cmd/qshell/qshell.go: -------------------------------------------------------------------------------- 1 | package qshell 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "os" 9 | "strings" 10 | 11 | "github.com/xushiwei/qlang" 12 | "github.com/xushiwei/qlang/lib/terminal" 13 | 14 | qipt "github.com/xushiwei/qlang/cl/interpreter" 15 | qall "github.com/xushiwei/qlang/lib/qlang.all" 16 | ) 17 | 18 | var ( 19 | historyFile = os.Getenv("HOME") + "/.qlang.history" 20 | notFound = interface{}(errors.New("not found")) 21 | ) 22 | 23 | // Main is the entry of qlang/qlang.safe shell 24 | // 25 | func Main(safeMode bool) { 26 | 27 | qall.InitSafe(safeMode) 28 | qlang.Import("", qipt.Exports) 29 | qlang.Import("qlang", qlang.Exports) 30 | qlang.SetDumpCode(os.Getenv("QLANG_DUMPCODE")) 31 | 32 | libs := os.Getenv("QLANG_PATH") 33 | if libs == "" { 34 | libs = os.Getenv("HOME") + "/qlang" 35 | } 36 | 37 | lang := qlang.New() 38 | lang.SetLibs(libs) 39 | 40 | // exec source 41 | // 42 | if len(os.Args) > 1 { 43 | fname := os.Args[1] 44 | b, err := ioutil.ReadFile(fname) 45 | if err != nil { 46 | fmt.Fprintln(os.Stderr, err) 47 | os.Exit(2) 48 | } 49 | err = lang.SafeExec(b, fname) 50 | if err != nil { 51 | fmt.Fprintln(os.Stderr, err) 52 | os.Exit(3) 53 | } 54 | return 55 | } 56 | 57 | // interpreter 58 | 59 | qall.Copyright() 60 | if safeMode { 61 | fmt.Printf("Use Ctrl-D (i.e. EOF) to exit.\n\n") 62 | } else { 63 | fmt.Printf("Use exit() or Ctrl-D (i.e. EOF) to exit.\n\n") 64 | } 65 | 66 | var ret interface{} 67 | qlang.SetOnPop(func(v interface{}) { 68 | ret = v 69 | }) 70 | 71 | var tokener tokener 72 | term := terminal.New(">>> ", "... ", tokener.ReadMore) 73 | term.SetWordCompleter(func(line string, pos int) (head string, completions []string, tail string) { 74 | return line[:pos], []string{" "}, line[pos:] 75 | }) 76 | 77 | term.LoadHistroy(historyFile) // load/save histroy 78 | defer term.SaveHistroy(historyFile) 79 | 80 | for { 81 | expr, err := term.Scan() 82 | if err != nil { 83 | if err == terminal.ErrPromptAborted { 84 | continue 85 | } else if err == io.EOF { 86 | fmt.Println("^D") 87 | break 88 | } 89 | fmt.Fprintln(os.Stderr, err) 90 | continue 91 | } 92 | expr = strings.TrimSpace(expr) 93 | if expr == "" { 94 | continue 95 | } 96 | ret = notFound 97 | err = lang.SafeEval(expr) 98 | if err != nil { 99 | fmt.Fprintln(os.Stderr, err) 100 | continue 101 | } 102 | if ret != notFound { 103 | fmt.Println(ret) 104 | } 105 | } 106 | } 107 | 108 | // ----------------------------------------------------------------------------- 109 | 110 | type tokener struct { 111 | level int 112 | instr bool 113 | } 114 | 115 | var dontReadMoreChars = "+-})];" 116 | var puncts = "([=,*/%|&<>^.:" 117 | 118 | func readMore(line string) bool { 119 | 120 | n := len(line) 121 | if n == 0 { 122 | return false 123 | } 124 | 125 | pos := strings.IndexByte(dontReadMoreChars, line[n-1]) 126 | if pos == 0 || pos == 1 { 127 | return n >= 2 && line[n-2] != dontReadMoreChars[pos] 128 | } 129 | return pos < 0 && strings.IndexByte(puncts, line[n-1]) >= 0 130 | } 131 | 132 | func findEnd(line string, c byte) int { 133 | 134 | for i := 0; i < len(line); i++ { 135 | switch line[i] { 136 | case c: 137 | return i 138 | case '\\': 139 | i++ 140 | } 141 | } 142 | return -1 143 | } 144 | 145 | func (p *tokener) ReadMore(expr string, line string) (string, bool) { // read more line check 146 | 147 | ret := expr + line + "\n" 148 | for { 149 | if p.instr { 150 | pos := strings.IndexByte(line, '`') 151 | if pos < 0 { 152 | return ret, true 153 | } 154 | line = line[pos+1:] 155 | p.instr = false 156 | } 157 | 158 | pos := strings.IndexAny(line, "{}`'\"") 159 | if pos < 0 { 160 | if p.level != 0 { 161 | return ret, true 162 | } 163 | line = strings.TrimRight(line, " \t") 164 | return ret, readMore(line) 165 | } 166 | switch c := line[pos]; c { 167 | case '{': 168 | p.level++ 169 | case '}': 170 | p.level-- 171 | case '`': 172 | p.instr = true 173 | default: 174 | line = line[pos+1:] 175 | pos = findEnd(line, c) 176 | if pos < 0 { 177 | return ret, p.level != 0 178 | } 179 | } 180 | line = line[pos+1:] 181 | } 182 | } 183 | 184 | // ----------------------------------------------------------------------------- 185 | -------------------------------------------------------------------------------- /engine2.go: -------------------------------------------------------------------------------- 1 | package qlang 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "reflect" 7 | 8 | "github.com/qiniu/text/tpl/interpreter" 9 | "github.com/xushiwei/qlang/exec" 10 | 11 | qcl "github.com/xushiwei/qlang/cl" 12 | qlang "github.com/xushiwei/qlang/spec" 13 | ) 14 | 15 | // Options represent interpreter options. 16 | // 17 | type Options interpreter.Options 18 | 19 | var ( 20 | // InsertSemis is interpreter options that means to insert semis smartly. 21 | InsertSemis = (*Options)(interpreter.InsertSemis) 22 | ) 23 | 24 | // SetReadFile sets the `ReadFile` function. 25 | // 26 | func SetReadFile(fn func(file string) ([]byte, error)) { 27 | 28 | qcl.ReadFile = fn 29 | } 30 | 31 | // SetFindEntry sets the `FindEntry` function. 32 | // 33 | func SetFindEntry(fn func(file string, libs []string) (string, error)) { 34 | 35 | qcl.FindEntry = fn 36 | } 37 | 38 | // SetOnPop sets OnPop callback. 39 | // 40 | func SetOnPop(fn func(v interface{})) { 41 | 42 | exec.OnPop = fn 43 | } 44 | 45 | // SetDumpCode sets dump code mode: 46 | // "1" - dump code with rem instruction. 47 | // "2" - dump code without rem instruction. 48 | // else - don't dump code. 49 | // 50 | func SetDumpCode(dumpCode string) { 51 | 52 | switch dumpCode { 53 | case "true", "1": 54 | qcl.DumpCode = 1 55 | case "2": 56 | qcl.DumpCode = 2 57 | default: 58 | qcl.DumpCode = 0 59 | } 60 | } 61 | 62 | // Debug sets dump code mode to "1" for debug. 63 | // 64 | func Debug(fn func()) { 65 | 66 | SetDumpCode("1") 67 | defer SetDumpCode("0") 68 | fn() 69 | } 70 | 71 | // ----------------------------------------------------------------------------- 72 | 73 | // A Qlang represents the qlang compiler and executor. 74 | // 75 | type Qlang struct { 76 | *exec.Context 77 | cl *qcl.Compiler 78 | } 79 | 80 | // New returns a new qlang instance. 81 | // 82 | func New() *Qlang { 83 | 84 | cl := qcl.New() 85 | stk := exec.NewStack() 86 | ctx := exec.NewContextEx(cl.GlobalSymbols()) 87 | ctx.Stack = stk 88 | ctx.Code = cl.Code() 89 | return &Qlang{ctx, cl} 90 | } 91 | 92 | // SetLibs sets lib paths for searching modules. 93 | // 94 | func (p *Qlang) SetLibs(libs string) { 95 | 96 | p.cl.SetLibs(libs) 97 | } 98 | 99 | // Cl compiles a source code. 100 | // 101 | func (p *Qlang) Cl(codeText []byte, fname string) (end int, err error) { 102 | 103 | end = p.cl.Cl(codeText, fname) 104 | p.cl.Done() 105 | p.ResizeVars() 106 | return 107 | } 108 | 109 | // SafeCl compiles a source code, without panic (will convert panic into an error). 110 | // 111 | func (p *Qlang) SafeCl(codeText []byte, fname string) (end int, err error) { 112 | 113 | defer func() { 114 | if e := recover(); e != nil { 115 | switch v := e.(type) { 116 | case string: 117 | err = errors.New(v) 118 | case error: 119 | err = v 120 | default: 121 | panic(e) 122 | } 123 | } 124 | }() 125 | 126 | return p.Cl(codeText, fname) 127 | } 128 | 129 | // Exec compiles and executes a source code. 130 | // 131 | func (p *Qlang) Exec(codeText []byte, fname string) (err error) { 132 | 133 | code := p.cl.Code() 134 | start := code.Len() 135 | end, err := p.Cl(codeText, fname) 136 | if err != nil { 137 | return 138 | } 139 | 140 | if qcl.DumpCode != 0 { 141 | code.Dump(start) 142 | } 143 | 144 | p.ExecBlock(start, end, p.cl.GlobalSymbols()) 145 | return 146 | } 147 | 148 | // Eval compiles and executes a source code. 149 | // 150 | func (p *Qlang) Eval(expr string) (err error) { 151 | 152 | return p.Exec([]byte(expr), "") 153 | } 154 | 155 | // SafeExec compiles and executes a source code, without panic (will convert panic into an error). 156 | // 157 | func (p *Qlang) SafeExec(code []byte, fname string) (err error) { 158 | 159 | defer func() { 160 | if e := recover(); e != nil { 161 | switch v := e.(type) { 162 | case string: 163 | err = errors.New(v) 164 | case error: 165 | err = v 166 | default: 167 | panic(e) 168 | } 169 | } 170 | }() 171 | 172 | err = p.Exec(code, fname) 173 | return 174 | } 175 | 176 | // SafeEval compiles and executes a source code, without panic (will convert panic into an error). 177 | // 178 | func (p *Qlang) SafeEval(expr string) (err error) { 179 | 180 | return p.SafeExec([]byte(expr), "") 181 | } 182 | 183 | // InjectMethods injects some methods into a class. 184 | // `pcls` can be a `*exec.Class` object or a `string` typed class name. 185 | // 186 | func (p *Qlang) InjectMethods(pcls interface{}, code []byte) (err error) { 187 | 188 | var cls *exec.Class 189 | switch v := pcls.(type) { 190 | case *exec.Class: 191 | cls = v 192 | case string: 193 | val, ok := p.GetVar(v) 194 | if !ok { 195 | return fmt.Errorf("class `%s` not exists", v) 196 | } 197 | if cls, ok = val.(*exec.Class); !ok { 198 | return fmt.Errorf("var `%s` not a class", v) 199 | } 200 | default: 201 | return fmt.Errorf("invalid cls argument type: %v", reflect.TypeOf(pcls)) 202 | } 203 | err = p.cl.InjectMethods(cls, code) 204 | p.ResizeVars() 205 | return 206 | } 207 | 208 | // Import imports a module written in Go. 209 | // 210 | func Import(mod string, table map[string]interface{}) { 211 | 212 | qlang.Import(mod, table) 213 | } 214 | 215 | // SetAutoCall is reserved for internal use. 216 | // 217 | func SetAutoCall(t reflect.Type) { 218 | 219 | qlang.SetAutoCall(t) 220 | } 221 | 222 | // ----------------------------------------------------------------------------- 223 | 224 | // Exports is the export table of this module. 225 | // 226 | var Exports = map[string]interface{}{ 227 | "new": New, 228 | "New": New, 229 | } 230 | 231 | // ----------------------------------------------------------------------------- 232 | -------------------------------------------------------------------------------- /exec/basic.go: -------------------------------------------------------------------------------- 1 | package exec 2 | 3 | import ( 4 | qlang "github.com/xushiwei/qlang/spec" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | // Rem 9 | 10 | type iRem struct { 11 | File string 12 | Line int 13 | Code string 14 | } 15 | 16 | func (p *iRem) Exec(stk *Stack, ctx *Context) {} 17 | 18 | func Rem(file string, line int, code string) Instr { 19 | 20 | return &iRem{file, line, code} 21 | } 22 | 23 | // ----------------------------------------------------------------------------- 24 | // Push/Pop 25 | 26 | var ( 27 | OnPop func(v interface{}) 28 | ) 29 | 30 | type iPush struct { 31 | v interface{} 32 | } 33 | 34 | type iPop int 35 | type iPopEx int 36 | 37 | func (p *iPush) Exec(stk *Stack, ctx *Context) { 38 | stk.Push(p.v) 39 | } 40 | 41 | func (p iPop) Exec(stk *Stack, ctx *Context) { 42 | stk.Pop() 43 | } 44 | 45 | func (p iPopEx) Exec(stk *Stack, ctx *Context) { 46 | v, _ := stk.Pop() 47 | OnPop(v) 48 | } 49 | 50 | func Push(v interface{}) Instr { 51 | return &iPush{v} 52 | } 53 | 54 | func PopEx() Instr { 55 | if OnPop != nil { 56 | return popEx 57 | } 58 | return Pop 59 | } 60 | 61 | var ( 62 | Nil Instr = Push(nil) 63 | Pop Instr = iPop(0) 64 | popEx Instr = iPopEx(0) 65 | ) 66 | 67 | // ----------------------------------------------------------------------------- 68 | // Clear 69 | 70 | type iClear int 71 | 72 | func (p iClear) Exec(stk *Stack, ctx *Context) { 73 | stk.data = stk.data[:ctx.base] 74 | } 75 | 76 | var ( 77 | Clear Instr = iClear(0) 78 | ) 79 | 80 | // ----------------------------------------------------------------------------- 81 | // Or/And 82 | 83 | type iOr int 84 | type iAnd int 85 | 86 | func (delta iOr) Exec(stk *Stack, ctx *Context) { 87 | a, _ := stk.Pop() 88 | if a1, ok := a.(bool); ok { 89 | if a1 { 90 | stk.Push(true) 91 | ctx.ip += int(delta) 92 | } 93 | } else { 94 | panic("left operand of || operator isn't a boolean expression") 95 | } 96 | } 97 | 98 | func (delta iAnd) Exec(stk *Stack, ctx *Context) { 99 | a, _ := stk.Pop() 100 | if a1, ok := a.(bool); ok { 101 | if !a1 { 102 | stk.Push(false) 103 | ctx.ip += int(delta) 104 | } 105 | } else { 106 | panic("left operand of && operator isn't a boolean expression") 107 | } 108 | } 109 | 110 | func Or(delta int) Instr { 111 | return iOr(delta) 112 | } 113 | 114 | func And(delta int) Instr { 115 | return iAnd(delta) 116 | } 117 | 118 | // ----------------------------------------------------------------------------- 119 | // Jmp 120 | 121 | type iJmp int 122 | 123 | func (delta iJmp) Exec(stk *Stack, ctx *Context) { 124 | ctx.ip += int(delta) 125 | } 126 | 127 | func Jmp(delta int) Instr { 128 | return iJmp(delta) 129 | } 130 | 131 | // ----------------------------------------------------------------------------- 132 | // JmpIfFalse 133 | 134 | type iJmpIfFalse int 135 | 136 | func (delta iJmpIfFalse) Exec(stk *Stack, ctx *Context) { 137 | a, _ := stk.Pop() 138 | if a1, ok := a.(bool); ok { 139 | if !a1 { 140 | ctx.ip += int(delta) 141 | } 142 | } else { 143 | panic("condition isn't a boolean expression") 144 | } 145 | } 146 | 147 | func JmpIfFalse(delta int) Instr { 148 | return iJmpIfFalse(delta) 149 | } 150 | 151 | // ----------------------------------------------------------------------------- 152 | // Case/Default 153 | 154 | type iCase int 155 | 156 | func (delta iCase) Exec(stk *Stack, ctx *Context) { 157 | b, _ := stk.Pop() 158 | a, _ := stk.Top() 159 | cond := qlang.EQ(a, b) 160 | if cond1, ok := cond.(bool); ok { 161 | if cond1 { 162 | stk.Pop() 163 | } else { 164 | ctx.ip += int(delta) 165 | } 166 | } else { 167 | panic("operator == return non-boolean value?") 168 | } 169 | } 170 | 171 | func Case(delta int) Instr { 172 | return iCase(delta) 173 | } 174 | 175 | var ( 176 | Default Instr = Pop 177 | ) 178 | 179 | // ----------------------------------------------------------------------------- 180 | // SubSlice 181 | 182 | type iOp3 struct { 183 | op func(v, a, b interface{}) interface{} 184 | arity int 185 | hasA bool 186 | hasB bool 187 | } 188 | 189 | func (p *iOp3) Exec(stk *Stack, ctx *Context) { 190 | var i = 1 191 | var a, b interface{} 192 | args := stk.PopNArgs(p.arity) 193 | if p.hasA { 194 | a = args[i] 195 | i++ 196 | } 197 | if p.hasB { 198 | b = args[i] 199 | } 200 | stk.Push(p.op(args[0], a, b)) 201 | } 202 | 203 | func Op3(op func(v, a, b interface{}) interface{}, hasA, hasB bool) Instr { 204 | n := 1 205 | if hasA { 206 | n++ 207 | } 208 | if hasB { 209 | n++ 210 | } 211 | return &iOp3{op, n, hasA, hasB} 212 | } 213 | 214 | // ----------------------------------------------------------------------------- 215 | -------------------------------------------------------------------------------- /exec/call_test.go: -------------------------------------------------------------------------------- 1 | package exec 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/xushiwei/qlang/lib/builtin" 7 | ) 8 | 9 | // ----------------------------------------------------------------------------- 10 | 11 | func TestMul(t *testing.T) { 12 | 13 | ctx := NewContext() 14 | stk := NewStack() 15 | 16 | code := New( 17 | Push(2), 18 | Push(3.0), 19 | Call(builtin.Mul), 20 | ) 21 | 22 | code.Exec(0, code.Len(), stk, ctx) 23 | if stk.BaseFrame() != 1 { 24 | t.Fatal("code.Exec failed") 25 | } 26 | if v, ok := stk.Pop(); !(ok && v == 6.0) { 27 | t.Fatal("mul != 6") 28 | } 29 | } 30 | 31 | func TestInvalidMax(t *testing.T) { 32 | 33 | defer func() { 34 | err := recover() 35 | if err != ErrArityRequired { 36 | t.Fatal("code.New:", err) 37 | } 38 | }() 39 | 40 | New( 41 | Push(2), 42 | Push(3.0), 43 | Push(5), 44 | Call(builtin.Max), 45 | ) 46 | } 47 | 48 | func TestMax(t *testing.T) { 49 | 50 | ctx := NewContext() 51 | stk := NewStack() 52 | 53 | code := New( 54 | Push(2), 55 | Push(3.0), 56 | Push(5), 57 | Call(builtin.Max, 3), 58 | ) 59 | 60 | code.Exec(0, code.Len(), stk, ctx) 61 | if stk.BaseFrame() != 1 { 62 | t.Fatal("code.Exec failed") 63 | } 64 | if v, ok := stk.Pop(); !(ok && v == 5.0) { 65 | t.Fatal("max != 5") 66 | } 67 | } 68 | 69 | func TestCallFn(t *testing.T) { 70 | 71 | ctx := NewContext() 72 | stk := NewStack() 73 | 74 | code := New( 75 | Push(builtin.Max), 76 | Push(2), 77 | Push(3.0), 78 | Push(5), 79 | CallFn(3), 80 | ) 81 | 82 | code.Exec(0, code.Len(), stk, ctx) 83 | if stk.BaseFrame() != 1 { 84 | t.Fatal("code.Exec failed") 85 | } 86 | if v, ok := stk.Pop(); !(ok && v == 5.0) { 87 | t.Fatal("max != 5") 88 | } 89 | } 90 | 91 | func TestCallFnv(t *testing.T) { 92 | 93 | ctx := NewContext() 94 | stk := NewStack() 95 | 96 | code := New( 97 | Push(builtin.Max), 98 | Push(2), 99 | Push(3.0), 100 | Push(5), 101 | Call(builtin.SliceFrom, 3), 102 | CallFnv(1), 103 | ) 104 | 105 | code.Exec(0, code.Len(), stk, ctx) 106 | if stk.BaseFrame() != 1 { 107 | t.Fatal("code.Exec failed") 108 | } 109 | if v, ok := stk.Pop(); !(ok && v == 5.0) { 110 | t.Fatal("max != 5") 111 | } 112 | } 113 | 114 | // ----------------------------------------------------------------------------- 115 | -------------------------------------------------------------------------------- /exec/code_test.go: -------------------------------------------------------------------------------- 1 | package exec 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | func NewContext() *Context { 10 | 11 | return NewContextEx(nil) 12 | } 13 | 14 | func TestCodeLine(t *testing.T) { 15 | 16 | code := New(Nil, Nil, Nil) 17 | code.CodeLine("", 1) 18 | code.Block(Nil, Nil, Nil) 19 | code.CodeLine("", 2) 20 | 21 | if _, line := code.Line(0); line != 1 { 22 | t.Fatal("code.Line(0):", line) 23 | } 24 | 25 | if _, line := code.Line(2); line != 1 { 26 | t.Fatal("code.Line(2):", line) 27 | } 28 | 29 | if _, line := code.Line(3); line != 2 { 30 | t.Fatal("code.Line(3):", line) 31 | } 32 | 33 | if _, line := code.Line(4); line != 2 { 34 | t.Fatal("code.Line(4):", line) 35 | } 36 | 37 | if _, line := code.Line(5); line != 2 { 38 | t.Fatal("code.Line(6):", line) 39 | } 40 | 41 | if _, line := code.Line(6); line != 0 { 42 | t.Fatal("code.Line(6):", line) 43 | } 44 | } 45 | 46 | // ----------------------------------------------------------------------------- 47 | -------------------------------------------------------------------------------- /exec/for.go: -------------------------------------------------------------------------------- 1 | package exec 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | qlang "github.com/xushiwei/qlang/spec" 8 | ) 9 | 10 | // ----------------------------------------------------------------------------- 11 | // ForRange 12 | 13 | type iForRange struct { 14 | args []int 15 | start int 16 | end int 17 | } 18 | 19 | const ( 20 | BreakForRange = -1 21 | ContinueForRange = -2 22 | ) 23 | 24 | func (p *iForRange) execBody(stk *Stack, ctx *Context) { 25 | 26 | data := ctx.Code.data 27 | ipEnd := p.end 28 | 29 | ctx.ip = p.start 30 | for ctx.ip != ipEnd { 31 | instr := data[ctx.ip] 32 | ctx.ip++ 33 | instr.Exec(stk, ctx) 34 | if ctx.ip < 0 { // continue or break? 35 | return 36 | } 37 | } 38 | } 39 | 40 | func (p *iForRange) Exec(stk *Stack, ctx *Context) { 41 | 42 | val, ok := stk.Pop() 43 | if !ok { 44 | panic("unexpected") 45 | } 46 | 47 | done := ctx.ip 48 | args := p.args 49 | narg := len(args) 50 | 51 | if ch, ok := val.(*qlang.Chan); ok { 52 | if narg > 1 { 53 | panic("too many variables in range") 54 | } 55 | v := ch.Data 56 | for { 57 | x, ok := v.Recv() 58 | if !ok { 59 | break 60 | } 61 | if narg > 0 { 62 | item := args[0] 63 | varRef := ctx.getVarRef(item) 64 | *varRef = x.Interface() 65 | } 66 | p.execBody(stk, ctx) 67 | if ctx.ip == BreakForRange { 68 | break 69 | } 70 | if ctx.ip == ContinueForRange { 71 | continue 72 | } 73 | } 74 | ctx.ip = done 75 | return 76 | } 77 | 78 | v := reflect.ValueOf(val) 79 | kind := v.Kind() 80 | switch kind { 81 | case reflect.Slice: 82 | n := v.Len() 83 | for i := 0; i < n; i++ { 84 | if narg > 0 { 85 | index := args[0] 86 | varRef := ctx.getVarRef(index) 87 | *varRef = i 88 | } 89 | if narg > 1 { 90 | item := args[1] 91 | varRef := ctx.getVarRef(item) 92 | *varRef = v.Index(i).Interface() 93 | } 94 | p.execBody(stk, ctx) 95 | if ctx.ip == BreakForRange { 96 | break 97 | } 98 | if ctx.ip == ContinueForRange { 99 | continue 100 | } 101 | } 102 | case reflect.Map: 103 | keys := v.MapKeys() 104 | for _, key := range keys { 105 | if narg > 0 { 106 | index := args[0] 107 | varRef := ctx.getVarRef(index) 108 | *varRef = key.Interface() 109 | } 110 | if narg > 1 { 111 | item := args[1] 112 | varRef := ctx.getVarRef(item) 113 | *varRef = v.MapIndex(key).Interface() 114 | } 115 | p.execBody(stk, ctx) 116 | if ctx.ip == BreakForRange { 117 | break 118 | } 119 | if ctx.ip == ContinueForRange { 120 | continue 121 | } 122 | } 123 | default: 124 | panic(fmt.Errorf("type `%v` doesn't support `range`", v.Type())) 125 | } 126 | ctx.ip = done 127 | } 128 | 129 | // ForRange returns an instruction that represents for..range 130 | // 131 | func ForRange(args []int, start, end int) Instr { 132 | 133 | return &iForRange{args, start, end} 134 | } 135 | 136 | // ----------------------------------------------------------------------------- 137 | -------------------------------------------------------------------------------- /exec/function.go: -------------------------------------------------------------------------------- 1 | package exec 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | var ( 10 | // ErrReturn is used for `panic(ErrReturn)` 11 | ErrReturn = errors.New("return") 12 | ) 13 | 14 | // ----------------------------------------------------------------------------- 15 | // Exit 16 | 17 | func doExit(ret interface{}) { 18 | 19 | code := 0 20 | if ret != nil { 21 | code = ret.(int) 22 | } 23 | os.Exit(code) 24 | } 25 | 26 | var ( 27 | // Exit is the instruction that executes `exit()`. 28 | Exit = Call(doExit) 29 | ) 30 | 31 | // ----------------------------------------------------------------------------- 32 | // Return 33 | 34 | type iReturn int 35 | 36 | func (arity iReturn) Exec(stk *Stack, ctx *Context) { 37 | 38 | if arity == 0 { 39 | ctx.ret = nil 40 | } else if arity == 1 { 41 | ctx.ret, _ = stk.Pop() 42 | } else { 43 | ctx.ret = stk.PopNArgs(int(arity)) 44 | } 45 | if ctx.parent != nil { 46 | panic(ErrReturn) // 利用 panic 来实现 return (正常退出) 47 | } 48 | 49 | ctx.ExecDefers() 50 | 51 | if ctx.ret == nil { 52 | os.Exit(0) 53 | } 54 | if v, ok := ctx.ret.(int); ok { 55 | os.Exit(v) 56 | } 57 | panic("must return `int` for main function") 58 | } 59 | 60 | // Return returns an instruction that means `return , ..., `. 61 | // 62 | func Return(arity int) Instr { 63 | return iReturn(arity) 64 | } 65 | 66 | // ----------------------------------------------------------------------------- 67 | // Defer 68 | 69 | type iDefer struct { 70 | start int 71 | end int 72 | } 73 | 74 | func (p *iDefer) Exec(stk *Stack, ctx *Context) { 75 | 76 | ctx.defers = &theDefer{ 77 | next: ctx.defers, 78 | start: p.start, 79 | end: p.end, 80 | } 81 | } 82 | 83 | // Defer returns an instruction that executes `defer `. 84 | // 85 | func Defer(start, end int) Instr { 86 | return &iDefer{start, end} 87 | } 88 | 89 | // ----------------------------------------------------------------------------- 90 | // Recover 91 | 92 | type iRecover int 93 | 94 | func (p iRecover) Exec(stk *Stack, ctx *Context) { 95 | 96 | if parent := ctx.parent; parent != nil { 97 | e := parent.Recov 98 | parent.Recov = nil 99 | stk.Push(e) 100 | } else { 101 | stk.Push(nil) 102 | } 103 | } 104 | 105 | var ( 106 | // Recover is the instruction that executes `recover()`. 107 | Recover Instr = iRecover(0) 108 | ) 109 | 110 | // ----------------------------------------------------------------------------- 111 | // NewFunction 112 | 113 | // A Function represents a function object. 114 | // 115 | type Function struct { 116 | Cls *Class 117 | Parent *Context 118 | start int 119 | end int 120 | symtbl map[string]int 121 | Args []string 122 | Variadic bool 123 | } 124 | 125 | // NewFunction creates a new function object. 126 | // 127 | func NewFunction(cls *Class, start, end int, symtbl map[string]int, args []string, variadic bool) *Function { 128 | 129 | return &Function{cls, nil, start, end, symtbl, args, variadic} 130 | } 131 | 132 | // Call calls this function with default context. 133 | // 134 | func (p *Function) Call(stk *Stack, args ...interface{}) (ret interface{}) { 135 | 136 | parent := p.Parent 137 | ctx := &Context{ 138 | parent: parent, 139 | Stack: stk, 140 | Code: parent.Code, 141 | modmgr: parent.modmgr, 142 | base: stk.BaseFrame(), 143 | } 144 | ctx.initVars(p.symtbl) 145 | return p.ExtCall(ctx, args...) 146 | } 147 | 148 | // ExtCall calls this function with a specified context. 149 | // 150 | func (p *Function) ExtCall(ctx *Context, args ...interface{}) (ret interface{}) { 151 | 152 | n := len(p.Args) 153 | if p.Variadic { 154 | if len(args) < n-1 { 155 | panic(fmt.Sprintf("function requires >= %d arguments, but we got %d", n-1, len(args))) 156 | } 157 | } else { 158 | if len(args) != n { 159 | panic(fmt.Sprintf("function requires %d arguments, but we got %d", n, len(args))) 160 | } 161 | } 162 | 163 | if p.start == p.end { 164 | return nil 165 | } 166 | 167 | stk := ctx.Stack 168 | vars := ctx.Vars() 169 | if p.Variadic { 170 | for i := 0; i < n-1; i++ { 171 | vars[i] = args[i] 172 | } 173 | vars[n-1] = args[n-1:] 174 | } else { 175 | for i, arg := range args { 176 | vars[i] = arg 177 | } 178 | } 179 | 180 | defer func() { 181 | if e := recover(); e != ErrReturn { // 正常 return 导致,见 (*iReturn).Exec 函数 182 | ctx.Recov = e 183 | } 184 | ret = ctx.ret 185 | ctx.ExecDefers() 186 | stk.SetFrame(ctx.base) 187 | if ctx.Recov != nil { 188 | panic(ctx.Recov) 189 | } 190 | }() 191 | ctx.Code.Exec(p.start, p.end, stk, ctx) 192 | return 193 | } 194 | 195 | // ----------------------------------------------------------------------------- 196 | 197 | type iFunc Function 198 | 199 | func (p *iFunc) Exec(stk *Stack, ctx *Context) { 200 | p.Parent = ctx 201 | stk.Push((*Function)(p)) 202 | } 203 | 204 | // Func returns an instruction that create a function object. 205 | // 206 | func Func(cls *Class, start, end int, symtbl map[string]int, args []string, variadic bool) Instr { 207 | f := NewFunction(cls, start, end, symtbl, args, variadic) 208 | return (*iFunc)(f) 209 | } 210 | 211 | // ----------------------------------------------------------------------------- 212 | -------------------------------------------------------------------------------- /exec/goroutine.go: -------------------------------------------------------------------------------- 1 | package exec 2 | 3 | import ( 4 | qlang "github.com/xushiwei/qlang/spec" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | type iGo struct { 10 | start int 11 | end int 12 | } 13 | 14 | func (p *iGo) Exec(stk *Stack, ctx *Context) { 15 | 16 | go func() { 17 | cloneCtx := *ctx // 每个goroutine需要自己的上下文,有自己的堆栈 18 | cloneCtx.Stack = NewStack() 19 | cloneCtx.Code.Exec(p.start, p.end, cloneCtx.Stack, &cloneCtx) 20 | }() 21 | } 22 | 23 | func Go(start, end int) Instr { 24 | 25 | return &iGo{start, end} 26 | } 27 | 28 | // ----------------------------------------------------------------------------- 29 | 30 | type iChanIn int 31 | type iChanOut int 32 | 33 | func (p iChanIn) Exec(stk *Stack, ctx *Context) { 34 | 35 | v, _ := stk.Pop() 36 | ch, _ := stk.Pop() 37 | ret := qlang.ChanIn(ch, v, ctx.onsel) 38 | stk.Push(ret) 39 | } 40 | 41 | func (p iChanOut) Exec(stk *Stack, ctx *Context) { 42 | 43 | ch, _ := stk.Pop() 44 | ret := qlang.ChanOut(ch, ctx.onsel) 45 | stk.Push(ret) 46 | } 47 | 48 | var ( 49 | ChanIn Instr = iChanIn(0) 50 | ChanOut Instr = iChanOut(0) 51 | ) 52 | 53 | // ----------------------------------------------------------------------------- 54 | -------------------------------------------------------------------------------- /exec/module.go: -------------------------------------------------------------------------------- 1 | package exec 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | // AnonymFn(匿名函数) 9 | 10 | type iAnonymFn struct { 11 | start int 12 | end int 13 | symtbl map[string]int 14 | } 15 | 16 | func (p *iAnonymFn) Exec(stk *Stack, ctx *Context) { 17 | 18 | fn := NewFunction(nil, p.start, p.end, p.symtbl, nil, false) 19 | fn.Parent = ctx 20 | stk.Push(fn.Call(stk)) 21 | } 22 | 23 | // AnonymFn returns an instruction that creates an anonymous function. 24 | // 25 | func AnonymFn(start, end int, symtbl map[string]int) Instr { 26 | 27 | return &iAnonymFn{start, end, symtbl} 28 | } 29 | 30 | // ----------------------------------------------------------------------------- 31 | // Module 32 | 33 | type importMod struct { 34 | exports map[string]interface{} 35 | sync.Mutex 36 | } 37 | 38 | func (p *importMod) Lock() (exports map[string]interface{}, uninited bool) { 39 | 40 | p.Mutex.Lock() 41 | if p.exports == nil { 42 | return nil, true 43 | } 44 | exports = p.exports 45 | p.Mutex.Unlock() 46 | return 47 | } 48 | 49 | type moduleMgr struct { 50 | mods map[string]*importMod 51 | mutex sync.Mutex 52 | } 53 | 54 | func (p *moduleMgr) get(id string) *importMod { 55 | 56 | p.mutex.Lock() 57 | mod, ok := p.mods[id] 58 | if !ok { 59 | mod = new(importMod) 60 | p.mods[id] = mod 61 | } 62 | p.mutex.Unlock() 63 | return mod 64 | } 65 | 66 | type iModule struct { 67 | start int 68 | end int 69 | symtbl map[string]int 70 | id string 71 | } 72 | 73 | func (p *iModule) Exec(stk *Stack, ctx *Context) { 74 | 75 | mod := ctx.modmgr.get(p.id) 76 | exports, uninited := mod.Lock() 77 | if uninited { 78 | defer mod.Unlock() 79 | modCtx := &Context{ 80 | Code: ctx.Code, 81 | Stack: ctx.Stack, 82 | modmgr: ctx.modmgr, 83 | } 84 | modCtx.initVars(p.symtbl) 85 | modFn := NewFunction(nil, p.start, p.end, p.symtbl, nil, false) 86 | modFn.ExtCall(modCtx) 87 | exports = modCtx.Exports() 88 | mod.exports = exports 89 | } 90 | stk.Push(exports) 91 | } 92 | 93 | // Module returns an instruction that creates a new module. 94 | // 95 | func Module(id string, start, end int, symtbl map[string]int) Instr { 96 | 97 | return &iModule{start, end, symtbl, id} 98 | } 99 | 100 | // ----------------------------------------------------------------------------- 101 | 102 | type iAs struct { 103 | name int 104 | } 105 | 106 | func (p *iAs) Exec(stk *Stack, ctx *Context) { 107 | 108 | v, ok := stk.Pop() 109 | if !ok { 110 | panic(ErrStackDamaged) 111 | } 112 | ctx.FastSetVar(p.name, v) 113 | } 114 | 115 | // As returns an instruction that specifies an alias name of a module. 116 | // 117 | func As(name int) Instr { 118 | 119 | return &iAs{name} 120 | } 121 | 122 | // ----------------------------------------------------------------------------- 123 | 124 | type iExport struct { 125 | names []string 126 | } 127 | 128 | func (p *iExport) Exec(stk *Stack, ctx *Context) { 129 | 130 | ctx.export = append(ctx.export, p.names...) 131 | } 132 | 133 | // Export returns an instruction that exports some module symbols. 134 | // 135 | func Export(names ...string) Instr { 136 | 137 | return &iExport{names} 138 | } 139 | 140 | // ----------------------------------------------------------------------------- 141 | -------------------------------------------------------------------------------- /exec/types.go: -------------------------------------------------------------------------------- 1 | package exec 2 | 3 | import ( 4 | qlang "github.com/xushiwei/qlang/spec" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | type tchan int 10 | 11 | func (p tchan) OptimizableGetArity() int { 12 | 13 | return 1 14 | } 15 | 16 | func (p tchan) Exec(stk *Stack, ctx *Context) { 17 | 18 | n := len(stk.data) - 1 19 | stk.data[n] = qlang.ChanOf(stk.data[n]) 20 | } 21 | 22 | // Chan is an instruction that returns chan T. 23 | // 24 | var Chan Instr = tchan(0) 25 | 26 | // ----------------------------------------------------------------------------- 27 | 28 | type slice int 29 | 30 | func (p slice) OptimizableGetArity() int { 31 | 32 | return 1 33 | } 34 | 35 | func (p slice) Exec(stk *Stack, ctx *Context) { 36 | 37 | n := len(stk.data) - 1 38 | stk.data[n] = qlang.Slice(stk.data[n]) 39 | } 40 | 41 | // Slice is an instruction that returns []T. 42 | // 43 | var Slice Instr = slice(0) 44 | 45 | // ----------------------------------------------------------------------------- 46 | 47 | type sliceFrom int 48 | type sliceFromTy int 49 | 50 | var nilVarSlice = make([]interface{}, 0) 51 | 52 | func (p sliceFrom) Exec(stk *Stack, ctx *Context) { 53 | 54 | if p == 0 { 55 | stk.data = append(stk.data, nilVarSlice) 56 | return 57 | } 58 | n := len(stk.data) - int(p) 59 | stk.data[n] = qlang.SliceFrom(stk.data[n:]...) 60 | stk.data = stk.data[:n+1] 61 | } 62 | 63 | func (p sliceFromTy) Exec(stk *Stack, ctx *Context) { 64 | 65 | n := len(stk.data) - int(p) 66 | stk.data[n] = qlang.SliceFromTy(stk.data[n:]...) 67 | stk.data = stk.data[:n+1] 68 | } 69 | 70 | // SliceFrom is an instruction that creates slice in [a1, a2, ...] form. 71 | // 72 | func SliceFrom(arity int) Instr { 73 | 74 | return sliceFrom(arity) 75 | } 76 | 77 | // SliceFromTy is an instruction that creates slice in []T{a1, a2, ...} form. 78 | // 79 | func SliceFromTy(arity int) Instr { 80 | 81 | return sliceFromTy(arity) 82 | } 83 | 84 | // ----------------------------------------------------------------------------- 85 | // StructInit 86 | 87 | type iStructInit int 88 | 89 | func (p iStructInit) Exec(stk *Stack, ctx *Context) { 90 | 91 | n := len(stk.data) - int(p) 92 | stk.data[n] = qlang.StructInit(stk.data[n:]...) 93 | stk.data = stk.data[:n+1] 94 | } 95 | 96 | // StructInit returns a StructInit instruction that means `&StructType{name1: expr1, name2: expr2, ...}`. 97 | // 98 | func StructInit(arity int) Instr { 99 | return iStructInit(arity) 100 | } 101 | 102 | // ----------------------------------------------------------------------------- 103 | // MapInit 104 | 105 | type iMapInit int 106 | 107 | func (p iMapInit) Exec(stk *Stack, ctx *Context) { 108 | 109 | n := len(stk.data) - int(p) 110 | stk.data[n] = qlang.MapInit(stk.data[n:]...) 111 | stk.data = stk.data[:n+1] 112 | } 113 | 114 | // MapInit returns a MapInit instruction that means `map[key]elem{key1: expr1, key2: expr2, ...}`. 115 | // 116 | func MapInit(arity int) Instr { 117 | return iMapInit(arity) 118 | } 119 | 120 | // ----------------------------------------------------------------------------- 121 | 122 | type tmap int 123 | 124 | func (p tmap) OptimizableGetArity() int { 125 | 126 | return 2 127 | } 128 | 129 | func (p tmap) Exec(stk *Stack, ctx *Context) { 130 | 131 | n := len(stk.data) - 1 132 | stk.data[n-1] = qlang.Map(stk.data[n-1], stk.data[n]) 133 | stk.data = stk.data[:n] 134 | } 135 | 136 | // Map is an instruction that returns `map[key]elem`. 137 | // 138 | var Map Instr = tmap(0) 139 | 140 | // ----------------------------------------------------------------------------- 141 | -------------------------------------------------------------------------------- /exec/var.go: -------------------------------------------------------------------------------- 1 | package exec 2 | 3 | import ( 4 | "errors" 5 | 6 | qlang "github.com/xushiwei/qlang/spec" 7 | ) 8 | 9 | var ( 10 | // ErrAssignWithoutVal is returned when variable assign without value 11 | ErrAssignWithoutVal = errors.New("variable assign without value") 12 | 13 | // ErrMultiAssignExprMustBeSlice is returned when expression of multi assignment must be a slice 14 | ErrMultiAssignExprMustBeSlice = errors.New("expression of multi assignment must be a slice") 15 | ) 16 | 17 | // ----------------------------------------------------------------------------- 18 | 19 | type iRef struct { 20 | name int 21 | } 22 | 23 | func (p *iRef) Exec(stk *Stack, ctx *Context) { 24 | 25 | stk.Push(ctx.getRef(p.name)) 26 | } 27 | 28 | func (p *iRef) ToVar() Instr { 29 | 30 | return &iVar{p.name} 31 | } 32 | 33 | func (p *Context) getRef(name int) interface{} { 34 | 35 | if name < symbolIndexMax { 36 | return p.FastGetVar(name) 37 | } 38 | scope := name >> symbolNameBits 39 | for scope > 0 { 40 | p = p.parent 41 | scope-- 42 | } 43 | return p.FastGetVar(name & (symbolIndexMax - 1)) 44 | } 45 | 46 | // SymbolIndex returns symbol index value. 47 | // 48 | func SymbolIndex(id, scope int) int { 49 | return id | (scope << symbolNameBits) 50 | } 51 | 52 | // Ref returns an instruction that refers a variable. 53 | // 54 | func Ref(name int) Instr { 55 | return &iRef{name} 56 | } 57 | 58 | // ----------------------------------------------------------------------------- 59 | // Var 60 | 61 | type iVar struct { 62 | name int 63 | } 64 | 65 | func (p *iVar) Exec(stk *Stack, ctx *Context) { 66 | 67 | stk.Push(&variable{Name: p.name}) 68 | } 69 | 70 | // Var returns a Var instruction. 71 | // 72 | func Var(name int) Instr { 73 | return &iVar{name} 74 | } 75 | 76 | // ----------------------------------------------------------------------------- 77 | 78 | type iGet int 79 | 80 | func (p iGet) Exec(stk *Stack, ctx *Context) { 81 | 82 | k, ok1 := stk.Pop() 83 | o, ok2 := stk.Pop() 84 | if !ok1 || !ok2 { 85 | panic("unexpected to call `Get` instruction") 86 | } 87 | stk.Push(qlang.Get(o, k)) 88 | } 89 | 90 | func (p iGet) ToVar() Instr { 91 | 92 | return GetVar 93 | } 94 | 95 | // Get is the Get instruction. 96 | // 97 | var Get Instr = iGet(0) 98 | 99 | // ----------------------------------------------------------------------------- 100 | 101 | type iGetVar int 102 | 103 | func (p iGetVar) Exec(stk *Stack, ctx *Context) { 104 | 105 | k, ok1 := stk.Pop() 106 | o, ok2 := stk.Pop() 107 | if !ok1 || !ok2 { 108 | panic("unexpected to call `GetVar` instruction") 109 | } 110 | stk.Push(&qlang.DataIndex{Data: o, Index: k}) 111 | } 112 | 113 | // GetVar is the Get instruction. 114 | // 115 | var GetVar Instr = iGetVar(0) 116 | 117 | // ----------------------------------------------------------------------------- 118 | 119 | type iGfnRef struct { 120 | val interface{} 121 | toVar func() Instr 122 | } 123 | 124 | func (p *iGfnRef) Exec(stk *Stack, ctx *Context) { 125 | 126 | stk.Push(p.val) 127 | } 128 | 129 | func (p *iGfnRef) ToVar() Instr { 130 | 131 | return p.toVar() 132 | } 133 | 134 | // GfnRef returns an instruction that refers a fntable item. 135 | // 136 | func GfnRef(v interface{}, toVar func() Instr) Instr { 137 | return &iGfnRef{v, toVar} 138 | } 139 | 140 | // ----------------------------------------------------------------------------- 141 | -------------------------------------------------------------------------------- /exec/var_new.go: -------------------------------------------------------------------------------- 1 | package exec 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | qlang "github.com/xushiwei/qlang/spec" 8 | ) 9 | 10 | // ----------------------------------------------------------------------------- 11 | // AssignEx 12 | 13 | type iAssignEx int 14 | 15 | func (p iAssignEx) Exec(stk *Stack, ctx *Context) { 16 | 17 | v, ok1 := stk.Pop() 18 | k, ok2 := stk.Pop() 19 | if !ok1 || !ok2 { 20 | panic(ErrAssignWithoutVal) 21 | } 22 | 23 | doAssign(k, v, ctx) 24 | } 25 | 26 | func doAssign(k, v interface{}, ctx *Context) { 27 | 28 | switch t := k.(type) { 29 | case *variable: 30 | varRef := ctx.getVarRef(t.Name) 31 | *varRef = v 32 | case *qlang.DataIndex: 33 | qlang.SetIndex(t.Data, t.Index, v) 34 | default: 35 | panic("invalid assignment statement") 36 | } 37 | } 38 | 39 | const ( 40 | symbolNameBits = 20 41 | symbolIndexMax = 1 << symbolNameBits 42 | ) 43 | 44 | func (p *Context) getVarRef(name int) (varRef *interface{}) { 45 | 46 | if name < symbolIndexMax { 47 | return p.FastRefVar(name) 48 | } 49 | scope := name >> symbolNameBits 50 | for scope > 0 { 51 | p = p.parent 52 | scope-- 53 | } 54 | return p.FastRefVar(name & (symbolIndexMax - 1)) 55 | } 56 | 57 | // AssignEx is qlang AssignEx instruction. 58 | // 59 | var AssignEx Instr = iAssignEx(0) 60 | 61 | // ----------------------------------------------------------------------------- 62 | 63 | type iMultiAssignFromSliceEx int 64 | 65 | func (p iMultiAssignFromSliceEx) Exec(stk *Stack, ctx *Context) { 66 | 67 | arity := int(p) 68 | args := stk.PopNArgs(arity + 1) 69 | 70 | v := reflect.ValueOf(args[arity]) 71 | if v.Kind() != reflect.Slice { 72 | panic(ErrMultiAssignExprMustBeSlice) 73 | } 74 | 75 | n := v.Len() 76 | if arity != n { 77 | panic(fmt.Errorf("multi assignment error: require %d variables, but we got %d", n, arity)) 78 | } 79 | 80 | for i := 0; i < arity; i++ { 81 | doAssign(args[i], v.Index(i).Interface(), ctx) 82 | } 83 | } 84 | 85 | // MultiAssignFromSliceEx returns a MultiAssignFromSliceEx instruction. 86 | // 87 | func MultiAssignFromSliceEx(arity int) Instr { 88 | return iMultiAssignFromSliceEx(arity) 89 | } 90 | 91 | // ----------------------------------------------------------------------------- 92 | 93 | type iMultiAssignEx int 94 | 95 | func (p iMultiAssignEx) Exec(stk *Stack, ctx *Context) { 96 | 97 | arity := int(p) 98 | args := stk.PopNArgs(arity << 1) 99 | 100 | for i := 0; i < arity; i++ { 101 | doAssign(args[i], args[arity+i], ctx) 102 | } 103 | } 104 | 105 | // MultiAssignEx returns a MultiAssignEx instruction. 106 | // 107 | func MultiAssignEx(arity int) Instr { 108 | return iMultiAssignEx(arity) 109 | } 110 | 111 | // ----------------------------------------------------------------------------- 112 | // AddAssign/SubAssign/MulAssign/QuoAssign/ModAssign/Inc/Dec 113 | 114 | func execOpAssignEx(p func(a, b interface{}) interface{}, stk *Stack, ctx *Context) { 115 | 116 | v, ok1 := stk.Pop() 117 | k, ok2 := stk.Pop() 118 | if !ok1 || !ok2 { 119 | panic(ErrAssignWithoutVal) 120 | } 121 | 122 | switch t := k.(type) { 123 | case *variable: 124 | name := t.Name 125 | varRef := ctx.getVarRef(name) 126 | *varRef = p(*varRef, v) 127 | case *qlang.DataIndex: 128 | val := qlang.Get(t.Data, t.Index) 129 | val = p(val, v) 130 | qlang.SetIndex(t.Data, t.Index, val) 131 | default: 132 | panic("invalid op assignment statement") 133 | } 134 | } 135 | 136 | func execOp1AssignEx(p func(a interface{}) interface{}, stk *Stack, ctx *Context) { 137 | 138 | k, ok := stk.Pop() 139 | if !ok { 140 | panic(ErrAssignWithoutVal) 141 | } 142 | 143 | switch t := k.(type) { 144 | case *variable: 145 | name := t.Name 146 | varRef := ctx.getVarRef(name) 147 | *varRef = p(*varRef) 148 | case *qlang.DataIndex: 149 | val := qlang.Get(t.Data, t.Index) 150 | val = p(val) 151 | qlang.SetIndex(t.Data, t.Index, val) 152 | default: 153 | panic("invalid op1 assignment statement") 154 | } 155 | } 156 | 157 | // ----------------------------------------------------------------------------- 158 | 159 | type iAddAssignEx int 160 | type iSubAssignEx int 161 | type iMulAssignEx int 162 | type iQuoAssignEx int 163 | type iModAssignEx int 164 | type iXorAssignEx int 165 | type iBitAndAssignEx int 166 | type iBitOrAssignEx int 167 | type iAndNotAssignEx int 168 | type iLshrAssignEx int 169 | type iRshrAssignEx int 170 | type iIncEx int 171 | type iDecEx int 172 | 173 | func (p iAddAssignEx) Exec(stk *Stack, ctx *Context) { 174 | execOpAssignEx(qlang.Add, stk, ctx) 175 | } 176 | 177 | func (p iSubAssignEx) Exec(stk *Stack, ctx *Context) { 178 | execOpAssignEx(qlang.Sub, stk, ctx) 179 | } 180 | 181 | func (p iMulAssignEx) Exec(stk *Stack, ctx *Context) { 182 | execOpAssignEx(qlang.Mul, stk, ctx) 183 | } 184 | 185 | func (p iQuoAssignEx) Exec(stk *Stack, ctx *Context) { 186 | execOpAssignEx(qlang.Quo, stk, ctx) 187 | } 188 | 189 | func (p iModAssignEx) Exec(stk *Stack, ctx *Context) { 190 | execOpAssignEx(qlang.Mod, stk, ctx) 191 | } 192 | 193 | func (p iXorAssignEx) Exec(stk *Stack, ctx *Context) { 194 | execOpAssignEx(qlang.Xor, stk, ctx) 195 | } 196 | 197 | func (p iBitAndAssignEx) Exec(stk *Stack, ctx *Context) { 198 | execOpAssignEx(qlang.BitAnd, stk, ctx) 199 | } 200 | 201 | func (p iBitOrAssignEx) Exec(stk *Stack, ctx *Context) { 202 | execOpAssignEx(qlang.BitOr, stk, ctx) 203 | } 204 | 205 | func (p iAndNotAssignEx) Exec(stk *Stack, ctx *Context) { 206 | execOpAssignEx(qlang.AndNot, stk, ctx) 207 | } 208 | 209 | func (p iLshrAssignEx) Exec(stk *Stack, ctx *Context) { 210 | execOpAssignEx(qlang.Lshr, stk, ctx) 211 | } 212 | 213 | func (p iRshrAssignEx) Exec(stk *Stack, ctx *Context) { 214 | execOpAssignEx(qlang.Rshr, stk, ctx) 215 | } 216 | 217 | func (p iIncEx) Exec(stk *Stack, ctx *Context) { 218 | execOp1AssignEx(qlang.Inc, stk, ctx) 219 | } 220 | 221 | func (p iDecEx) Exec(stk *Stack, ctx *Context) { 222 | execOp1AssignEx(qlang.Dec, stk, ctx) 223 | } 224 | 225 | var ( 226 | AddAssignEx Instr = iAddAssignEx(0) 227 | SubAssignEx Instr = iSubAssignEx(0) 228 | MulAssignEx Instr = iMulAssignEx(0) 229 | QuoAssignEx Instr = iQuoAssignEx(0) 230 | ModAssignEx Instr = iModAssignEx(0) 231 | XorAssignEx Instr = iXorAssignEx(0) 232 | BitAndAssignEx Instr = iBitAndAssignEx(0) 233 | BitOrAssignEx Instr = iBitOrAssignEx(0) 234 | AndNotAssignEx Instr = iAndNotAssignEx(0) 235 | LshrAssignEx Instr = iLshrAssignEx(0) 236 | RshrAssignEx Instr = iRshrAssignEx(0) 237 | IncEx Instr = iIncEx(0) 238 | DecEx Instr = iDecEx(0) 239 | ) 240 | 241 | // ----------------------------------------------------------------------------- 242 | -------------------------------------------------------------------------------- /exec/var_test.go: -------------------------------------------------------------------------------- 1 | package exec 2 | 3 | // ----------------------------------------------------------------------------- 4 | 5 | // ----------------------------------------------------------------------------- 6 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/xushiwei/qlang 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/peterh/liner v1.2.0 7 | github.com/qiniu/text v1.9.2 8 | github.com/visualfc/pkgwalk v1.0.0 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= 2 | github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 3 | github.com/peterh/liner v1.2.0 h1:w/UPXyl5GfahFxcTOz2j9wCIHNI+pUPr2laqpojKNCg= 4 | github.com/peterh/liner v1.2.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= 5 | github.com/qiniu/text v1.9.2 h1:1+aVZrM+LaDMf6PFL+U3q9N7zyj3YBqg2EnKeaHdLxU= 6 | github.com/qiniu/text v1.9.2/go.mod h1:v9y6inngR3QcljoFfgmBN+ukkI4WBItyzxMfyHao1C4= 7 | github.com/visualfc/pkgwalk v1.0.0 h1:WjGmokBfgJbmv5DU9+FDWK7A/tU2zyKpeWsNi/kKfSM= 8 | github.com/visualfc/pkgwalk v1.0.0/go.mod h1:1FBUxT6vBvYFqrUQVNrrmn1Nu30snrj2pTKlQMku3Tg= 9 | -------------------------------------------------------------------------------- /lib/bufio/bufio.go: -------------------------------------------------------------------------------- 1 | package bufio 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | ) 7 | 8 | // ----------------------------------------------------------------------------- 9 | 10 | // NewReader returns a new Reader with size (optional) or using default size. 11 | // 12 | func NewReader(rd io.Reader, size ...int) *bufio.Reader { 13 | 14 | if len(size) == 0 { 15 | return bufio.NewReader(rd) 16 | } 17 | return bufio.NewReaderSize(rd, size[0]) 18 | } 19 | 20 | // NewWriter returns a new Writer with size (optional) or using default size. 21 | // 22 | func NewWriter(w io.Writer, size ...int) *bufio.Writer { 23 | 24 | if len(size) == 0 { 25 | return bufio.NewWriter(w) 26 | } 27 | return bufio.NewWriterSize(w, size[0]) 28 | } 29 | 30 | // Exports is the export table of this module. 31 | // 32 | var Exports = map[string]interface{}{ 33 | "_name": "bufio", 34 | "reader": NewReader, 35 | "writer": NewWriter, 36 | "scanner": bufio.NewScanner, 37 | 38 | "NewReader": NewReader, 39 | "NewWriter": NewWriter, 40 | "NewScanner": bufio.NewScanner, 41 | } 42 | 43 | // ----------------------------------------------------------------------------- 44 | -------------------------------------------------------------------------------- /lib/builtin/bits.go: -------------------------------------------------------------------------------- 1 | package builtin 2 | 3 | // ----------------------------------------------------------------------------- 4 | 5 | // Lshr returns a << b 6 | // 7 | func Lshr(a, b interface{}) interface{} { 8 | 9 | switch a1 := a.(type) { 10 | case int: 11 | switch b1 := b.(type) { 12 | case int: 13 | return a1 << uint(b1) 14 | } 15 | } 16 | return panicUnsupportedOp2("<<", a, b) 17 | } 18 | 19 | // Rshr returns a >> b 20 | // 21 | func Rshr(a, b interface{}) interface{} { 22 | 23 | switch a1 := a.(type) { 24 | case int: 25 | switch b1 := b.(type) { 26 | case int: 27 | return a1 >> uint(b1) 28 | } 29 | } 30 | return panicUnsupportedOp2(">>", a, b) 31 | } 32 | 33 | // Xor returns a ^ b 34 | // 35 | func Xor(a, b interface{}) interface{} { 36 | 37 | switch a1 := a.(type) { 38 | case int: 39 | switch b1 := b.(type) { 40 | case int: 41 | return a1 ^ b1 42 | } 43 | } 44 | return panicUnsupportedOp2("^", a, b) 45 | } 46 | 47 | // BitAnd returns a & b 48 | // 49 | func BitAnd(a, b interface{}) interface{} { 50 | 51 | switch a1 := a.(type) { 52 | case int: 53 | switch b1 := b.(type) { 54 | case int: 55 | return a1 & b1 56 | } 57 | } 58 | return panicUnsupportedOp2("&", a, b) 59 | } 60 | 61 | // BitOr returns a | b 62 | // 63 | func BitOr(a, b interface{}) interface{} { 64 | 65 | switch a1 := a.(type) { 66 | case int: 67 | switch b1 := b.(type) { 68 | case int: 69 | return a1 | b1 70 | } 71 | } 72 | return panicUnsupportedOp2("|", a, b) 73 | } 74 | 75 | // BitNot returns ^a 76 | // 77 | func BitNot(a interface{}) interface{} { 78 | 79 | switch a1 := a.(type) { 80 | case int: 81 | return ^a1 82 | } 83 | return panicUnsupportedOp1("^", a) 84 | } 85 | 86 | // AndNot returns a &^ b 87 | // 88 | func AndNot(a, b interface{}) interface{} { 89 | 90 | switch a1 := a.(type) { 91 | case int: 92 | switch b1 := b.(type) { 93 | case int: 94 | return a1 &^ b1 95 | } 96 | } 97 | return panicUnsupportedOp2("&^", a, b) 98 | } 99 | 100 | // ----------------------------------------------------------------------------- 101 | -------------------------------------------------------------------------------- /lib/builtin/boolean.go: -------------------------------------------------------------------------------- 1 | package builtin 2 | 3 | // ----------------------------------------------------------------------------- 4 | 5 | // Not returns !a 6 | // 7 | func Not(a interface{}) interface{} { 8 | 9 | if a1, ok := a.(bool); ok { 10 | return !a1 11 | } 12 | return panicUnsupportedOp1("!", a) 13 | } 14 | 15 | // LT returns a < b 16 | // 17 | func LT(a, b interface{}) interface{} { 18 | 19 | switch a1 := a.(type) { 20 | case int: 21 | switch b1 := b.(type) { 22 | case int: 23 | return a1 < b1 24 | case float64: 25 | return float64(a1) < b1 26 | } 27 | case float64: 28 | switch b1 := b.(type) { 29 | case int: 30 | return a1 < float64(b1) 31 | case float64: 32 | return a1 < b1 33 | } 34 | case string: 35 | if b1, ok := b.(string); ok { 36 | return a1 < b1 37 | } 38 | } 39 | return panicUnsupportedOp2("<", a, b) 40 | } 41 | 42 | // GT returns a > b 43 | // 44 | func GT(a, b interface{}) interface{} { 45 | 46 | switch a1 := a.(type) { 47 | case int: 48 | switch b1 := b.(type) { 49 | case int: 50 | return a1 > b1 51 | case float64: 52 | return float64(a1) > b1 53 | } 54 | case float64: 55 | switch b1 := b.(type) { 56 | case int: 57 | return a1 > float64(b1) 58 | case float64: 59 | return a1 > b1 60 | } 61 | case string: 62 | if b1, ok := b.(string); ok { 63 | return a1 > b1 64 | } 65 | } 66 | return panicUnsupportedOp2(">", a, b) 67 | } 68 | 69 | // LE returns a <= b 70 | // 71 | func LE(a, b interface{}) interface{} { 72 | 73 | switch a1 := a.(type) { 74 | case int: 75 | switch b1 := b.(type) { 76 | case int: 77 | return a1 <= b1 78 | case float64: 79 | return float64(a1) <= b1 80 | } 81 | case float64: 82 | switch b1 := b.(type) { 83 | case int: 84 | return a1 <= float64(b1) 85 | case float64: 86 | return a1 <= b1 87 | } 88 | case string: 89 | if b1, ok := b.(string); ok { 90 | return a1 <= b1 91 | } 92 | } 93 | return panicUnsupportedOp2("<=", a, b) 94 | } 95 | 96 | // GE returns a >= b 97 | // 98 | func GE(a, b interface{}) interface{} { 99 | 100 | switch a1 := a.(type) { 101 | case int: 102 | switch b1 := b.(type) { 103 | case int: 104 | return a1 >= b1 105 | case float64: 106 | return float64(a1) >= b1 107 | } 108 | case float64: 109 | switch b1 := b.(type) { 110 | case int: 111 | return a1 >= float64(b1) 112 | case float64: 113 | return a1 >= b1 114 | } 115 | case string: 116 | if b1, ok := b.(string); ok { 117 | return a1 >= b1 118 | } 119 | } 120 | return panicUnsupportedOp2(">=", a, b) 121 | } 122 | 123 | // EQ returns a == b 124 | // 125 | func EQ(a, b interface{}) interface{} { 126 | 127 | return a == b 128 | } 129 | 130 | // NE returns a != b 131 | // 132 | func NE(a, b interface{}) interface{} { 133 | 134 | return a != b 135 | } 136 | 137 | // ----------------------------------------------------------------------------- 138 | -------------------------------------------------------------------------------- /lib/builtin/exports.go: -------------------------------------------------------------------------------- 1 | package builtin 2 | 3 | import ( 4 | "fmt" 5 | 6 | qlang "github.com/xushiwei/qlang/spec" 7 | ) 8 | 9 | // ----------------------------------------------------------------------------- 10 | 11 | var exports = map[string]interface{}{ 12 | "append": Append, 13 | "copy": Copy, 14 | "delete": Delete, 15 | "get": Get, 16 | "len": Len, 17 | "cap": Cap, 18 | "mkmap": Mkmap, 19 | "mapFrom": MapFrom, 20 | "mapOf": MapOf, 21 | "panic": Panic, 22 | "panicf": Panicf, 23 | "print": fmt.Print, 24 | "printf": fmt.Printf, 25 | "println": fmt.Println, 26 | "fprintln": fmt.Fprintln, 27 | "set": Set, 28 | "mkslice": Mkslice, 29 | "slice": Mkslice, 30 | "sliceFrom": sliceFrom, 31 | "sliceOf": SliceOf, 32 | "sub": SubSlice, 33 | "make": Make, 34 | 35 | "float": TyFloat64, 36 | "float64": TyFloat64, 37 | "float32": TyFloat32, 38 | "int8": TyInt8, 39 | "int16": TyInt16, 40 | "int32": TyInt32, 41 | "int64": TyInt64, 42 | "int": TyInt, 43 | "uint": TyUint, 44 | "byte": TyUint8, 45 | "uint8": TyUint8, 46 | "uint16": TyUint16, 47 | "uint32": TyUint32, 48 | "uint64": TyUint64, 49 | "string": TyString, 50 | "bool": TyBool, 51 | "var": TyVar, 52 | "type": typeOf, 53 | 54 | "max": Max, 55 | "min": Min, 56 | 57 | "undefined": qlang.Undefined, 58 | "nil": nil, 59 | "true": true, 60 | "false": false, 61 | 62 | "$elem": Elem, 63 | "$neg": Neg, 64 | "$mul": Mul, 65 | "$quo": Quo, 66 | "$mod": Mod, 67 | "$add": Add, 68 | "$sub": Sub, 69 | 70 | "$xor": Xor, 71 | "$lshr": Lshr, 72 | "$rshr": Rshr, 73 | "$bitand": BitAnd, 74 | "$bitor": BitOr, 75 | "$bitnot": BitNot, 76 | "$andnot": AndNot, 77 | 78 | "$lt": LT, 79 | "$gt": GT, 80 | "$le": LE, 81 | "$ge": GE, 82 | "$eq": EQ, 83 | "$ne": NE, 84 | "$not": Not, 85 | } 86 | 87 | func init() { 88 | qlang.SubSlice = SubSlice 89 | qlang.SliceFrom = SliceFrom 90 | qlang.SliceFromTy = SliceFromTy 91 | qlang.Slice = Slice 92 | qlang.Map = Map 93 | qlang.MapFrom = MapFrom 94 | qlang.MapInit = MapInit 95 | qlang.StructInit = StructInit 96 | qlang.EQ = EQ 97 | qlang.GetVar = GetVar 98 | qlang.Get = Get 99 | qlang.SetIndex = SetIndex 100 | qlang.Add = Add 101 | qlang.Sub = Sub 102 | qlang.Mul = Mul 103 | qlang.Quo = Quo 104 | qlang.Mod = Mod 105 | qlang.Xor = Xor 106 | qlang.Lshr = Lshr 107 | qlang.Rshr = Rshr 108 | qlang.BitAnd = BitAnd 109 | qlang.BitOr = BitOr 110 | qlang.AndNot = AndNot 111 | qlang.Inc = Inc 112 | qlang.Dec = Dec 113 | qlang.Import("", exports) 114 | } 115 | 116 | // ----------------------------------------------------------------------------- 117 | -------------------------------------------------------------------------------- /lib/builtin/gentypes.ql: -------------------------------------------------------------------------------- 1 | header = `package builtin 2 | 3 | // DON'T EDIT!!! THIS FILE IS GENERATED BY %%qlang gentypes.ql%% 4 | // 5 | 6 | import ( 7 | "reflect" 8 | ) 9 | ` 10 | 11 | template = `// ----------------------------------------------------------------------------- 12 | 13 | type ty{{Type}} int 14 | 15 | func (p ty{{Type}}) GoType() reflect.Type { 16 | 17 | return goty{{TypeTitle}} 18 | } 19 | 20 | // NewInstance creates a new instance of a qlang type. required by %%qlang type%% spec. 21 | // 22 | func (p ty{{Type}}) NewInstance(args ...interface{}) interface{} { 23 | 24 | ret := new({{TypeLower}}) 25 | if len(args) > 0 { 26 | *ret = {{Type}}(args[0]) 27 | } 28 | return ret 29 | } 30 | 31 | func (p ty{{Type}}) Call(a interface{}) {{TypeLower}} { 32 | 33 | return {{Type}}(a) 34 | } 35 | 36 | func (p ty{{Type}}) String() string { 37 | 38 | return "{{TypeLower}}" 39 | } 40 | 41 | // Ty{{Type}} represents the %%{{TypeLower}}%% type. 42 | // 43 | var Ty{{Type}} = ty{{Type}}(0) 44 | ` 45 | 46 | footer = `// -----------------------------------------------------------------------------` 47 | 48 | builtins = [ 49 | "float32", 50 | "float64", 51 | "int", 52 | "int8", 53 | "int16", 54 | "int32", 55 | "int64", 56 | "uint", 57 | "uint8", 58 | "uint16", 59 | "uint32", 60 | "uint64", 61 | "string", 62 | "bool", 63 | ] 64 | 65 | f, err = os.create("types_builtin.go") 66 | if err != nil { 67 | fprintln(os.stderr, err) 68 | return 1 69 | } 70 | defer f.close() 71 | 72 | fprintln(f, strings.replace(header, "%%", "`", -1)) 73 | 74 | for _, typLower = range builtins { 75 | typ = strings.title(typLower) 76 | typTitle = strings.title(typLower) 77 | replacer = strings.replacer( 78 | "%%", "`", "{{Type}}", typ, "{{TypeLower}}", typLower, "{{TypeTitle}}", typTitle) 79 | fprintln(f, replacer.replace(template)) 80 | } 81 | 82 | fprintln(f, footer) 83 | -------------------------------------------------------------------------------- /lib/builtin/types.go: -------------------------------------------------------------------------------- 1 | package builtin 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | qlang "github.com/xushiwei/qlang/spec" 8 | ) 9 | 10 | // ----------------------------------------------------------------------------- 11 | 12 | var ( 13 | gotyInt = reflect.TypeOf(int(0)) 14 | gotyInt8 = reflect.TypeOf(int8(0)) 15 | gotyInt16 = reflect.TypeOf(int16(0)) 16 | gotyInt32 = reflect.TypeOf(int32(0)) 17 | gotyInt64 = reflect.TypeOf(int64(0)) 18 | gotyUint = reflect.TypeOf(uint(0)) 19 | gotyUint8 = reflect.TypeOf(uint8(0)) 20 | gotyUint16 = reflect.TypeOf(uint16(0)) 21 | gotyUint32 = reflect.TypeOf(uint32(0)) 22 | gotyUint64 = reflect.TypeOf(uint64(0)) 23 | gotyFloat32 = reflect.TypeOf(float32(0)) 24 | gotyFloat64 = reflect.TypeOf(float64(0)) 25 | gotyString = reflect.TypeOf("") 26 | gotyBool = reflect.TypeOf(false) 27 | gotyInterface = reflect.TypeOf((*interface{})(nil)).Elem() 28 | ) 29 | 30 | // TyByte represents the `byte` type. 31 | // 32 | var TyByte = TyUint8 33 | 34 | // TyFloat represents the `float` type. 35 | // 36 | var TyFloat = TyFloat64 37 | 38 | // ----------------------------------------------------------------------------- 39 | 40 | type tyVar int 41 | 42 | func (p tyVar) GoType() reflect.Type { 43 | 44 | return gotyInterface 45 | } 46 | 47 | // NewInstance creates a new instance of a qlang type. required by `qlang type` spec. 48 | // 49 | func (p tyVar) NewInstance(args ...interface{}) interface{} { 50 | 51 | ret := new(interface{}) 52 | if len(args) > 0 { 53 | *ret = args[0] 54 | } 55 | return ret 56 | } 57 | 58 | func (p tyVar) Call(a interface{}) interface{} { 59 | 60 | return a 61 | } 62 | 63 | func (p tyVar) String() string { 64 | 65 | return "var" 66 | } 67 | 68 | // TyVar represents the `var` type. 69 | // 70 | var TyVar = tyVar(0) 71 | 72 | // ----------------------------------------------------------------------------- 73 | 74 | type goSliceFrom int 75 | type goTypeOf int 76 | 77 | func (p goSliceFrom) Call(a ...interface{}) interface{} { 78 | return SliceFrom(a...) 79 | } 80 | 81 | func (p goTypeOf) Call(a interface{}) reflect.Type { 82 | return reflect.TypeOf(a) 83 | } 84 | 85 | var sliceFrom = goSliceFrom(0) 86 | var typeOf = goTypeOf(0) 87 | 88 | func init() { 89 | t1 := reflect.TypeOf(TyVar) 90 | t2 := reflect.TypeOf(typeOf) 91 | t3 := reflect.TypeOf(sliceFrom) 92 | qlang.SetDontTyNormalize(t1) 93 | qlang.SetDontTyNormalize(t2) 94 | qlang.SetDontTyNormalize(t3) 95 | } 96 | 97 | // ----------------------------------------------------------------------------- 98 | 99 | // Elem returns *a 100 | // 101 | func Elem(a interface{}) interface{} { 102 | 103 | if t, ok := a.(qlang.GoTyper); ok { 104 | return qlang.TyPtrTo(t.GoType()) 105 | } 106 | return reflect.ValueOf(a).Elem().Interface() 107 | } 108 | 109 | // Slice returns []T 110 | // 111 | func Slice(elem interface{}) interface{} { 112 | 113 | if t, ok := elem.(qlang.GoTyper); ok { 114 | return qlang.TySliceOf(t.GoType()) 115 | } 116 | panic(fmt.Sprintf("invalid []T: `%v` isn't a qlang type", elem)) 117 | } 118 | 119 | // Map returns map[key]elem 120 | // 121 | func Map(key, elem interface{}) interface{} { 122 | 123 | tkey, ok := key.(qlang.GoTyper) 124 | if !ok { 125 | panic(fmt.Sprintf("invalid map[key]elem: key `%v` isn't a qlang type", key)) 126 | } 127 | telem, ok := elem.(qlang.GoTyper) 128 | if !ok { 129 | panic(fmt.Sprintf("invalid map[key]elem: elem `%v` isn't a qlang type", elem)) 130 | } 131 | return qlang.TyMapOf(tkey.GoType(), telem.GoType()) 132 | } 133 | 134 | // ----------------------------------------------------------------------------- 135 | 136 | // Make creates a instance of qlang builtin type (slice, map and chan) 137 | // 138 | func Make(typ qlang.GoTyper, args ...int) interface{} { 139 | 140 | t := typ.GoType() 141 | switch t.Kind() { 142 | case reflect.Slice: 143 | n, cap := 0, 0 144 | if len(args) == 1 { 145 | n = args[0] 146 | cap = n 147 | } else if len(args) > 1 { 148 | n, cap = args[0], args[1] 149 | } 150 | return reflect.MakeSlice(t, n, cap).Interface() 151 | case reflect.Map: 152 | return reflect.MakeMap(t).Interface() 153 | case reflect.Chan: 154 | return qlang.MakeChan(t, args...) 155 | } 156 | panic(fmt.Sprintf("cannot make type `%v`", typ)) 157 | } 158 | 159 | // ----------------------------------------------------------------------------- 160 | -------------------------------------------------------------------------------- /lib/bytes/bytes.go: -------------------------------------------------------------------------------- 1 | package bytes 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "reflect" 7 | ) 8 | 9 | // ----------------------------------------------------------------------------- 10 | 11 | func newBuffer(a ...interface{}) *bytes.Buffer { 12 | 13 | if len(a) == 0 { 14 | return new(bytes.Buffer) 15 | } 16 | switch v := a[0].(type) { 17 | case []byte: 18 | return bytes.NewBuffer(v) 19 | case string: 20 | return bytes.NewBufferString(v) 21 | } 22 | panic("bytes.buffer() - unsupported argument type") 23 | } 24 | 25 | func from(v interface{}) []byte { 26 | 27 | switch args := v.(type) { 28 | case []int: 29 | b := make([]byte, len(args)) 30 | for i, v := range args { 31 | b[i] = byte(v) 32 | } 33 | return b 34 | case string: 35 | return []byte(args) 36 | default: 37 | if v == nil { 38 | return nil 39 | } 40 | panic(fmt.Sprintf("can't convert from `%v` to []byte", reflect.TypeOf(v))) 41 | } 42 | } 43 | 44 | // Exports is the export table of this module. 45 | // 46 | var Exports = map[string]interface{}{ 47 | "_name": "bytes", 48 | "buffer": newBuffer, 49 | "from": from, 50 | "equal": bytes.Equal, 51 | "reader": bytes.NewReader, 52 | "contains": bytes.Contains, 53 | "index": bytes.Index, 54 | "indexAny": bytes.IndexAny, 55 | "join": bytes.Join, 56 | "title": bytes.Title, 57 | "toLower": bytes.ToLower, 58 | "toTitle": bytes.ToTitle, 59 | "toUpper": bytes.ToUpper, 60 | "trim": bytes.Trim, 61 | 62 | "NewBuffer": newBuffer, 63 | "From": from, 64 | "Equal": bytes.Equal, 65 | "NewReader": bytes.NewReader, 66 | "Contains": bytes.Contains, 67 | "Index": bytes.Index, 68 | "IndexAny": bytes.IndexAny, 69 | "Join": bytes.Join, 70 | "Title": bytes.Title, 71 | "ToLower": bytes.ToLower, 72 | "ToTitle": bytes.ToTitle, 73 | "ToUpper": bytes.ToUpper, 74 | "Trim": bytes.Trim, 75 | } 76 | 77 | // ----------------------------------------------------------------------------- 78 | -------------------------------------------------------------------------------- /lib/bytes/bytes_test.go: -------------------------------------------------------------------------------- 1 | package bytes 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestCastNil(t *testing.T) { 8 | 9 | if from(nil) != nil { 10 | t.Fatal("from(nil) != nil") 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/chan/chan.go: -------------------------------------------------------------------------------- 1 | package channel 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | qlang "github.com/xushiwei/qlang/spec" 8 | "github.com/xushiwei/qlang/spec/types" 9 | ) 10 | 11 | // ----------------------------------------------------------------------------- 12 | 13 | func TrySend(p *qlang.Chan, v interface{}) interface{} { 14 | 15 | if v == qlang.Undefined { 16 | panic("can't send `undefined` value to a channel") 17 | } 18 | if ok := p.Data.TrySend(reflect.ValueOf(v)); ok { 19 | return nil 20 | } 21 | return qlang.Undefined 22 | } 23 | 24 | func TryRecv(p *qlang.Chan) interface{} { 25 | 26 | v, _ := p.Data.TryRecv() 27 | if v.IsValid() { 28 | return v.Interface() 29 | } 30 | return qlang.Undefined 31 | } 32 | 33 | func Send(p *qlang.Chan, v interface{}) { 34 | 35 | if v == qlang.Undefined { 36 | panic("can't send `undefined` value to a channel") 37 | } 38 | p.Data.Send(reflect.ValueOf(v)) 39 | } 40 | 41 | func Recv(p *qlang.Chan) interface{} { 42 | 43 | v, ok := p.Data.Recv() 44 | if ok { 45 | return v.Interface() 46 | } 47 | return qlang.Undefined 48 | } 49 | 50 | // ----------------------------------------------------------------------------- 51 | 52 | func ChanIn(ch1, val interface{}, try bool) interface{} { 53 | 54 | ch := ch1.(*qlang.Chan) 55 | if try { 56 | return TrySend(ch, val) 57 | } 58 | Send(ch, val) 59 | return nil 60 | } 61 | 62 | func ChanOut(ch1 interface{}, try bool) interface{} { 63 | 64 | ch := ch1.(*qlang.Chan) 65 | if try { 66 | return TryRecv(ch) 67 | } 68 | return Recv(ch) 69 | } 70 | 71 | func ChanOf(typ interface{}) interface{} { 72 | 73 | return reflect.ChanOf(reflect.BothDir, types.Reflect(typ)) 74 | } 75 | 76 | func Mkchan(typ interface{}, buffer ...int) *qlang.Chan { 77 | 78 | n := 0 79 | if len(buffer) > 0 { 80 | n = buffer[0] 81 | } 82 | t := reflect.ChanOf(reflect.BothDir, types.Reflect(typ)) 83 | return &qlang.Chan{Data: reflect.MakeChan(t, n)} 84 | } 85 | 86 | func Close(ch1 interface{}) { 87 | 88 | ch := ch1.(*qlang.Chan) 89 | ch.Data.Close() 90 | } 91 | 92 | // ----------------------------------------------------------------------------- 93 | 94 | var exports = map[string]interface{}{ 95 | "chanOf": ChanOf, 96 | "mkchan": Mkchan, 97 | "close": Close, 98 | } 99 | 100 | func makeChan(typ reflect.Type, buffer ...int) interface{} { 101 | 102 | n := 0 103 | if len(buffer) > 0 { 104 | n = buffer[0] 105 | } 106 | return &qlang.Chan{Data: reflect.MakeChan(typ, n)} 107 | } 108 | 109 | func chanOf(elem interface{}) interface{} { 110 | 111 | if t, ok := elem.(qlang.GoTyper); ok { 112 | tchan := reflect.ChanOf(reflect.BothDir, t.GoType()) 113 | return qlang.NewType(tchan) 114 | } 115 | panic(fmt.Sprintf("invalid chan T: `%v` isn't a qlang type", elem)) 116 | } 117 | 118 | func init() { 119 | qlang.ChanIn = ChanIn 120 | qlang.ChanOut = ChanOut 121 | qlang.MakeChan = makeChan 122 | qlang.ChanOf = chanOf 123 | qlang.Import("", exports) 124 | } 125 | 126 | // ----------------------------------------------------------------------------- 127 | -------------------------------------------------------------------------------- /lib/compress/lzw/lzw.go: -------------------------------------------------------------------------------- 1 | package lzw 2 | 3 | import ( 4 | "compress/lzw" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | // Exports is the export table of this module. 10 | // 11 | var Exports = map[string]interface{}{ 12 | "_name": "compress/lzw", 13 | "reader": lzw.NewReader, 14 | "writer": lzw.NewWriter, 15 | "LSB": lzw.LSB, 16 | "MSB": lzw.MSB, 17 | 18 | "NewReader": lzw.NewReader, 19 | "NewWriter": lzw.NewWriter, 20 | } 21 | 22 | // ----------------------------------------------------------------------------- 23 | -------------------------------------------------------------------------------- /lib/crypto/hmac/hmac.go: -------------------------------------------------------------------------------- 1 | package hmac 2 | 3 | import "crypto/hmac" 4 | 5 | // ----------------------------------------------------------------------------- 6 | 7 | // Exports is the export table of this module. 8 | // 9 | var Exports = map[string]interface{}{ 10 | "_name": "crypto/hmac", 11 | "new": hmac.New, 12 | "New": hmac.New, 13 | } 14 | 15 | // ----------------------------------------------------------------------------- 16 | -------------------------------------------------------------------------------- /lib/crypto/md5/md5_exports.go: -------------------------------------------------------------------------------- 1 | package md5 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "io" 7 | ) 8 | 9 | // ----------------------------------------------------------------------------- 10 | 11 | // Hash returns md5 sum of (sep, args...) serialization. 12 | // 13 | func Hash(sep interface{}, args ...interface{}) string { 14 | 15 | h := md5.New() 16 | 17 | var bsep []byte 18 | switch v := sep.(type) { 19 | case []byte: 20 | bsep = v 21 | case string: 22 | bsep = []byte(v) 23 | default: 24 | panic("md5.Hash: invalid argument type, require []byte or string") 25 | } 26 | 27 | for i, arg := range args { 28 | if i > 0 { 29 | h.Write(bsep) 30 | } 31 | switch v := arg.(type) { 32 | case []byte: 33 | h.Write(v) 34 | case string: 35 | io.WriteString(h, v) 36 | case error: 37 | default: 38 | panic("md5.Hash: invalid argument type, require []byte or string") 39 | } 40 | } 41 | 42 | return hex.EncodeToString(h.Sum(nil)) 43 | } 44 | 45 | // Sumstr is hex.EncodeToString(md5.Sum(b)). 46 | // 47 | func Sumstr(b []byte) string { 48 | 49 | v := md5.Sum(b) 50 | return hex.EncodeToString(v[:]) 51 | } 52 | 53 | // Exports is the export table of this module. 54 | // 55 | var Exports = map[string]interface{}{ 56 | "_name": "crypto/md5", 57 | "new": md5.New, 58 | "sum": md5.Sum, 59 | "sumstr": Sumstr, 60 | "hash": Hash, 61 | 62 | "New": md5.New, 63 | "Sum": md5.Sum, 64 | "Sumstr": Sumstr, 65 | "Hash": Hash, 66 | 67 | "BlockSize": md5.BlockSize, 68 | "Size": md5.Size, 69 | } 70 | 71 | // ----------------------------------------------------------------------------- 72 | -------------------------------------------------------------------------------- /lib/crypto/md5/md5_test.go: -------------------------------------------------------------------------------- 1 | package md5 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | func Test(t *testing.T) { 10 | 11 | h1 := Hash(",", "a", "b", []byte("c")) 12 | h2 := Sumstr([]byte("a,b,c")) 13 | if h1 != h2 { 14 | t.Fatal("md5.Hash failed:", h1, h2) 15 | } 16 | } 17 | 18 | // ----------------------------------------------------------------------------- 19 | 20 | -------------------------------------------------------------------------------- /lib/crypto/sha1/sha1_exports.go: -------------------------------------------------------------------------------- 1 | package sha1 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/hex" 6 | ) 7 | 8 | // ----------------------------------------------------------------------------- 9 | 10 | // Sumstr is hex.EncodeToString(sha1.Sum(b)). 11 | // 12 | func Sumstr(b []byte) string { 13 | 14 | v := sha1.Sum(b) 15 | return hex.EncodeToString(v[:]) 16 | } 17 | 18 | // Exports is the export table of this module. 19 | // 20 | var Exports = map[string]interface{}{ 21 | "_name": "crypto/sha1", 22 | "new": sha1.New, 23 | "sum": sha1.Sum, 24 | "sumstr": Sumstr, 25 | 26 | "New": sha1.New, 27 | "Sum": sha1.Sum, 28 | "Sumstr": Sumstr, 29 | 30 | "BlockSize": sha1.BlockSize, 31 | "Size": sha1.Size, 32 | } 33 | 34 | // ----------------------------------------------------------------------------- 35 | -------------------------------------------------------------------------------- /lib/crypto/sha256/sha256_exports.go: -------------------------------------------------------------------------------- 1 | package sha256 2 | 3 | import "crypto/sha256" 4 | 5 | // ----------------------------------------------------------------------------- 6 | 7 | // Exports is the export table of this module. 8 | // 9 | var Exports = map[string]interface{}{ 10 | "_name": "crypto/sha256", 11 | "new": sha256.New, 12 | "new224": sha256.New224, 13 | 14 | "New": sha256.New, 15 | "New224": sha256.New224, 16 | 17 | "BlockSize": sha256.BlockSize, 18 | "Size": sha256.Size, 19 | } 20 | 21 | // ----------------------------------------------------------------------------- 22 | -------------------------------------------------------------------------------- /lib/doc.go: -------------------------------------------------------------------------------- 1 | package qlang 2 | 3 | import ( 4 | _ "github.com/xushiwei/qlang/cl" 5 | _ "github.com/xushiwei/qlang/lib/qlang.all" 6 | ) 7 | -------------------------------------------------------------------------------- /lib/encoding/hex/hex.go: -------------------------------------------------------------------------------- 1 | package hex 2 | 3 | import "encoding/hex" 4 | 5 | // ----------------------------------------------------------------------------- 6 | 7 | // Exports is the export table of this module. 8 | // 9 | var Exports = map[string]interface{}{ 10 | "_name": "encoding/hex", 11 | "encodedLen": hex.EncodedLen, 12 | "encode": hex.Encode, 13 | "decodedLen": hex.DecodedLen, 14 | "decode": hex.Decode, 15 | "encodeToString": hex.EncodeToString, 16 | "decodeString": hex.DecodeString, 17 | "dump": hex.Dump, 18 | "dumper": hex.Dumper, 19 | 20 | "EncodedLen": hex.EncodedLen, 21 | "Encode": hex.Encode, 22 | "DecodedLen": hex.DecodedLen, 23 | "Decode": hex.Decode, 24 | "EncodeToString": hex.EncodeToString, 25 | "DecodeString": hex.DecodeString, 26 | "Dump": hex.Dump, 27 | "Dumper": hex.Dumper, 28 | } 29 | 30 | // ----------------------------------------------------------------------------- 31 | -------------------------------------------------------------------------------- /lib/encoding/json/json.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "encoding/json" 5 | "strings" 6 | "syscall" 7 | ) 8 | 9 | // ----------------------------------------------------------------------------- 10 | 11 | // Pretty prints a value in pretty mode. 12 | // 13 | func Pretty(v interface{}) string { 14 | 15 | b, err := json.MarshalIndent(v, "", " ") 16 | if err != nil { 17 | return err.Error() 18 | } 19 | return string(b) 20 | } 21 | 22 | // Unmarshal unmarshals a []byte or string 23 | // 24 | func Unmarshal(b interface{}) (v interface{}, err error) { 25 | 26 | switch in := b.(type) { 27 | case []byte: 28 | err = json.Unmarshal(in, &v) 29 | case string: 30 | err = json.NewDecoder(strings.NewReader(in)).Decode(&v) 31 | default: 32 | err = syscall.EINVAL 33 | } 34 | return 35 | } 36 | 37 | // Exports is the export table of this module. 38 | // 39 | var Exports = map[string]interface{}{ 40 | "_name": "encoding/json", 41 | "decoder": json.NewDecoder, 42 | "encoder": json.NewEncoder, 43 | "marshal": json.Marshal, 44 | "marshalIndent": json.MarshalIndent, 45 | "pretty": Pretty, 46 | "unmarshal": Unmarshal, 47 | "compact": json.Compact, 48 | "indent": json.Indent, 49 | "htmlEscape": json.HTMLEscape, 50 | 51 | "NewDecoder": json.NewDecoder, 52 | "NewEncoder": json.NewEncoder, 53 | "Marshal": json.Marshal, 54 | "MarshalIndent": json.MarshalIndent, 55 | "Pretty": Pretty, 56 | "Unmarshal": Unmarshal, 57 | "Compact": json.Compact, 58 | "Indent": json.Indent, 59 | "HTMLEscape": json.HTMLEscape, 60 | } 61 | 62 | // ----------------------------------------------------------------------------- 63 | -------------------------------------------------------------------------------- /lib/eqlang/eql.go: -------------------------------------------------------------------------------- 1 | package eqlang 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "os" 9 | "reflect" 10 | "strings" 11 | "unicode" 12 | ) 13 | 14 | var ( 15 | // ErrEndRequired is returned if eql script doesn't end by `%>`. 16 | ErrEndRequired = errors.New("eql script requires `%>` to end") 17 | 18 | // ErrEndOfString is returned if string doesn't end. 19 | ErrEndOfString = errors.New("string doesn't end") 20 | ) 21 | 22 | // ----------------------------------------------------------------------------- 23 | 24 | // Parse parses eql source into qlang code. 25 | // 26 | func Parse(source string) (code []byte, err error) { 27 | 28 | var b bytes.Buffer 29 | for { 30 | pos := strings.Index(source, "<%") 31 | if pos < 0 { 32 | err = parseText(&b, source) 33 | break 34 | } 35 | if pos > 0 { 36 | err = parseText(&b, source[:pos]) 37 | if err != nil { 38 | return 39 | } 40 | } 41 | source, err = parseEql(&b, source[pos+2:]) 42 | if err != nil { 43 | return 44 | } 45 | } 46 | code = b.Bytes() 47 | return 48 | } 49 | 50 | func parseText(b *bytes.Buffer, source string) (err error) { 51 | 52 | b.WriteString("print(eql.Subst(`") 53 | for { 54 | pos := strings.IndexByte(source, '`') 55 | if pos < 0 { 56 | b.WriteString(source) 57 | break 58 | } 59 | b.WriteString(source[:pos]) 60 | pos2 := pos + 1 61 | for ; pos2 < len(source); pos2++ { 62 | if source[pos2] != '`' { 63 | break 64 | } 65 | } 66 | b.WriteString("` + \"") 67 | b.WriteString(source[pos:pos2]) 68 | b.WriteString("\" + `") 69 | source = source[pos2:] 70 | } 71 | b.WriteString("`)); ") 72 | return 73 | } 74 | 75 | func parseEql(b *bytes.Buffer, source string) (ret string, err error) { 76 | 77 | fexpr := strings.HasPrefix(source, "=") 78 | if fexpr { 79 | b.WriteString("print(") 80 | source = source[1:] 81 | } 82 | for { 83 | pos := strings.IndexAny(source, "%\"`") 84 | if pos < 0 { 85 | return "", ErrEndRequired 86 | } 87 | if c := source[pos]; c == '%' { 88 | if strings.HasPrefix(source[pos+1:], ">") { 89 | ret = source[pos+2:] 90 | b.WriteString(source[:pos]) 91 | if fexpr { 92 | b.WriteString("); ") 93 | } else if strings.HasPrefix(ret, "\n") { 94 | ret = ret[1:] 95 | b.WriteString("\n") 96 | } else { 97 | b.WriteString("; ") 98 | } 99 | return 100 | } 101 | b.WriteString(source[:pos+1]) 102 | source = source[pos+1:] 103 | } else { 104 | n := findEnd(source[pos+1:], c) 105 | if n < 0 { 106 | return "", ErrEndOfString 107 | } 108 | n += pos + 2 109 | b.WriteString(source[:n]) 110 | source = source[n:] 111 | } 112 | } 113 | } 114 | 115 | func findEnd(line string, c byte) int { 116 | 117 | for i := 0; i < len(line); i++ { 118 | switch line[i] { 119 | case c: 120 | return i 121 | case '\\': 122 | i++ 123 | } 124 | } 125 | return -1 126 | } 127 | 128 | // ----------------------------------------------------------------------------- 129 | 130 | // Variables represent how to get value of a variable. 131 | // 132 | type Variables interface { 133 | GetVar(name string) (v interface{}, ok bool) 134 | } 135 | 136 | type mapVars map[string]interface{} 137 | type mapStrings map[string]string 138 | 139 | func (vars mapVars) GetVar(name string) (v interface{}, ok bool) { 140 | v, ok = vars[name] 141 | return 142 | } 143 | 144 | func (vars mapStrings) GetVar(name string) (v interface{}, ok bool) { 145 | v, ok = vars[name] 146 | return 147 | } 148 | 149 | // Subst substs variables in text. 150 | // 151 | func Subst(text string, lang interface{}) string { 152 | 153 | var vars Variables 154 | switch v := lang.(type) { 155 | case map[string]interface{}: 156 | vars = mapVars(v) 157 | case map[string]string: 158 | vars = mapStrings(v) 159 | case Variables: 160 | vars = v 161 | default: 162 | panic(fmt.Sprintf("eql.Subst: unsupported lang type `%v`", reflect.TypeOf(lang))) 163 | } 164 | return subst(text, vars) 165 | } 166 | 167 | func subst(text string, vars Variables) string { 168 | 169 | var b bytes.Buffer 170 | for { 171 | pos := strings.IndexByte(text, '$') 172 | if pos < 0 || pos+1 >= len(text) { 173 | if b.Len() == 0 { 174 | return text 175 | } 176 | b.WriteString(text) 177 | break 178 | } 179 | switch c := text[pos+1]; { 180 | case c == '$': 181 | b.WriteString(text[:pos+1]) 182 | text = text[pos+2:] 183 | case (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'): 184 | b.WriteString(text[:pos]) 185 | pos1 := pos + 2 186 | n := strings.IndexFunc(text[pos1:], func(c rune) bool { 187 | return !unicode.IsLetter(c) && !unicode.IsDigit(c) 188 | }) 189 | if n < 0 { 190 | n = len(text) - pos1 191 | } 192 | pos2 := pos1 + n 193 | key := text[pos+1 : pos2] 194 | val, ok := vars.GetVar(key) 195 | if !ok { 196 | panic("variable not found: " + key) 197 | } 198 | b.WriteString(fmt.Sprint(val)) 199 | text = text[pos2:] 200 | default: 201 | b.WriteString(text[:pos+1]) 202 | text = text[pos+1:] 203 | } 204 | } 205 | return b.String() 206 | } 207 | 208 | // ----------------------------------------------------------------------------- 209 | 210 | // Input decodes a json string. 211 | // 212 | func Input(input string) (ret map[string]interface{}, err error) { 213 | 214 | err = json.NewDecoder(strings.NewReader(input)).Decode(&ret) 215 | return 216 | } 217 | 218 | // InputFile decodes a json file. 219 | // 220 | func InputFile(input string) (ret map[string]interface{}, err error) { 221 | 222 | in := os.Stdin 223 | if input != "-" { 224 | f, err1 := os.Open(input) 225 | if err1 != nil { 226 | return nil, err1 227 | } 228 | defer f.Close() 229 | in = f 230 | } 231 | err = json.NewDecoder(in).Decode(&ret) 232 | return 233 | } 234 | 235 | // ----------------------------------------------------------------------------- 236 | 237 | // Exports is the export table of this module. 238 | // 239 | var Exports = map[string]interface{}{ 240 | "new": New, 241 | "parse": Parse, 242 | "subst": Subst, 243 | 244 | "input": Input, 245 | "inputFile": InputFile, 246 | 247 | "New": New, 248 | "Parse": Parse, 249 | "Subst": Subst, 250 | 251 | "Input": Input, 252 | "InputFile": InputFile, 253 | 254 | "ErrEndRequired": ErrEndRequired, 255 | "ErrEndOfString": ErrEndOfString, 256 | } 257 | 258 | // ----------------------------------------------------------------------------- 259 | -------------------------------------------------------------------------------- /lib/eqlang/eql_test.go: -------------------------------------------------------------------------------- 1 | package eqlang 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | // ----------------------------------------------------------------------------- 9 | 10 | const eqlTestCode = `<% 11 | // 12 | // eql example.eql -o example_bytes.go --imports=bytes --module=modbytes --Writer="*bytes.Buffer" 13 | // eql example.eql -o example_bufio.go --imports=bufio --module=modbufio --Writer="*bufio.Writer" 14 | // 15 | %> 16 | package eql_test 17 | 18 | import ( 19 | <%= eql.Imports() %> 20 | "encoding/binary" 21 | ) 22 | 23 | // ----------------------------------------------------------------------------- 24 | 25 | type $module string 26 | 27 | func (p $module) write(out $Writer, b []byte) { 28 | 29 | _, err := out.Write(b) 30 | if err != nil { 31 | panic(err) 32 | } 33 | } 34 | 35 | <% if Writer == "*bytes.Buffer" { %> 36 | func (p $module) flush(out $Writer) { 37 | } 38 | <% } else { %> 39 | func (p $module) flush(out $Writer) { 40 | 41 | err := out.Flush() 42 | if err != nil { 43 | panic(err) 44 | } 45 | } 46 | <% } %> 47 | 48 | // ----------------------------------------------------------------------------- 49 | ` 50 | 51 | func TestEql(t *testing.T) { 52 | 53 | _, err := Parse(eqlTestCode) 54 | if err != nil { 55 | t.Fatal("Parse failed:", err) 56 | } 57 | } 58 | 59 | func TestSubst(t *testing.T) { 60 | 61 | out := Subst(`?$Writer!$`, map[string]interface{}{ 62 | "Writer": "abc", 63 | }) 64 | if out != "?abc!$" { 65 | t.Fatal("Subst failed:", out) 66 | } 67 | 68 | out = Subst(`$Writer!$$`, map[string]interface{}{ 69 | "Writer": "abc", 70 | }) 71 | if out != "abc!$" { 72 | t.Fatal("Subst failed:", out) 73 | } 74 | 75 | out = Subst(`$$$Writer!`, map[string]interface{}{ 76 | "Writer": 123, 77 | }) 78 | if out != "$123!" { 79 | t.Fatal("Subst failed:", out) 80 | } 81 | 82 | out = Subst(`$$$Writer`, map[string]interface{}{ 83 | "Writer": 123, 84 | }) 85 | if out != "$123" { 86 | t.Fatal("Subst failed:", out) 87 | } 88 | } 89 | 90 | func TestParseText(t *testing.T) { 91 | 92 | var b bytes.Buffer 93 | err := parseText(&b, "abc ``` def") 94 | if err != nil { 95 | t.Fatal("parseText failed:", err) 96 | } 97 | 98 | if b.String() != "print(eql.Subst(`abc ` + \"```\" + ` def`)); " { 99 | t.Fatal(b.String()) 100 | } 101 | } 102 | 103 | // ----------------------------------------------------------------------------- 104 | -------------------------------------------------------------------------------- /lib/eqlang/execute.go: -------------------------------------------------------------------------------- 1 | package eqlang 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "path" 8 | "strings" 9 | ) 10 | 11 | // ----------------------------------------------------------------------------- 12 | 13 | type qlang interface { 14 | Variables 15 | 16 | // SetVar sets a variable value. 17 | SetVar(name string, v interface{}) 18 | 19 | // ResetVars resets all variables in executing context. 20 | ResetVars(vars map[string]interface{}) 21 | 22 | // SafeExec compiles and executes a source code, without panic (will convert panic into an error). 23 | SafeExec(code []byte, fname string) (err error) 24 | } 25 | 26 | // ----------------------------------------------------------------------------- 27 | 28 | // A Type is a eql script engine. 29 | // 30 | type Type struct { 31 | Impl qlang 32 | } 33 | 34 | // New creates a new eql script engine. 35 | // 36 | func New(ql qlang) Type { 37 | 38 | return Type{ql} 39 | } 40 | 41 | // Var returns a variable value or defval if not found. 42 | // 43 | func (p Type) Var(name string, defval interface{}) interface{} { 44 | 45 | v, ok := p.Impl.GetVar(name) 46 | if !ok { 47 | v = defval 48 | } 49 | return v 50 | } 51 | 52 | // Imports returns import table. 53 | // 54 | func (p Type) Imports() string { 55 | 56 | imports := p.Var("imports", "").(string) 57 | if imports == "" { 58 | return "" 59 | } 60 | mods := strings.Split(imports, ",") 61 | return "\"" + strings.Join(mods, "\"\n\t\"") + "\"" 62 | } 63 | 64 | // Subst substs variables in text. 65 | // 66 | func (p Type) Subst(text string) string { 67 | 68 | return subst(text, p.Impl) 69 | } 70 | 71 | // ExecuteDir executes a eql template directory. 72 | // 73 | func (p Type) ExecuteDir(global map[string]interface{}, source, output string) (err error) { 74 | 75 | if output == "" { 76 | output = p.Subst(source) 77 | if output == source { 78 | panic(fmt.Sprintf("source `%s` doesn't have $var", source)) 79 | } 80 | } 81 | 82 | err = os.MkdirAll(output, 0755) 83 | if err != nil { 84 | return 85 | } 86 | 87 | fis, err := ioutil.ReadDir(source) 88 | if err != nil { 89 | return 90 | } 91 | 92 | source += "/" 93 | output += "/" 94 | for _, fi := range fis { 95 | name := fi.Name() 96 | if fi.IsDir() { 97 | err = p.ExecuteDir(global, source+name, output+name) 98 | } else if path.Ext(name) == ".eql" { 99 | p.Impl.ResetVars(global) 100 | newname := name[:len(name)-4] 101 | err = p.ExecuteFile(source+name, output+newname) 102 | } else { 103 | err = copyFile(source+name, output+name, fi.Mode()) 104 | } 105 | if err != nil { 106 | return 107 | } 108 | } 109 | return 110 | } 111 | 112 | // ExecuteFile executes a eql template file. 113 | // 114 | func (p Type) ExecuteFile(source, output string) (err error) { 115 | 116 | b, err := ioutil.ReadFile(source) 117 | if err != nil { 118 | return 119 | } 120 | 121 | return p.Execute(string(b), source, output) 122 | } 123 | 124 | // Execute executes a eql template string. 125 | // 126 | func (p Type) Execute(source string, fname string, output string) (err error) { 127 | 128 | p.Impl.SetVar("eql", p) 129 | code, err := Parse(source) 130 | if err != nil { 131 | return 132 | } 133 | 134 | if output != "" { 135 | f, err1 := os.Create(output) 136 | if err1 != nil { 137 | return err1 138 | } 139 | old := os.Stdout 140 | os.Stdout = f 141 | defer func() { 142 | os.Stdout = old 143 | f.Close() 144 | }() 145 | } 146 | 147 | err = p.Impl.SafeExec(code, fname) 148 | if err != nil && output != "" { 149 | os.Remove(output) 150 | } 151 | return 152 | } 153 | 154 | func copyFile(source, output string, perm os.FileMode) (err error) { 155 | 156 | b, err := ioutil.ReadFile(source) 157 | if err != nil { 158 | return 159 | } 160 | return ioutil.WriteFile(output, b, perm) 161 | } 162 | 163 | // ----------------------------------------------------------------------------- 164 | -------------------------------------------------------------------------------- /lib/errors/errors.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import "errors" 4 | 5 | // ----------------------------------------------------------------------------- 6 | 7 | // Exports is the export table of this module. 8 | // 9 | var Exports = map[string]interface{}{ 10 | "_name": "errors", 11 | "new": errors.New, 12 | "New": errors.New, 13 | } 14 | 15 | // ----------------------------------------------------------------------------- 16 | -------------------------------------------------------------------------------- /lib/io/io-go16.go: -------------------------------------------------------------------------------- 1 | // +build go1.6 2 | 3 | package io 4 | 5 | import ( 6 | "io" 7 | ) 8 | 9 | func init() { 10 | Exports["copyBuffer"] = io.CopyBuffer 11 | Exports["CopyBuffer"] = io.CopyBuffer 12 | } 13 | -------------------------------------------------------------------------------- /lib/io/io.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | // Exports is the export table of this module. 10 | // 11 | var Exports = map[string]interface{}{ 12 | "_name": "io", 13 | "copy": io.Copy, 14 | "copyN": io.CopyN, 15 | "readAtLeast": io.ReadAtLeast, 16 | "readFull": io.ReadFull, 17 | "writeString": io.WriteString, 18 | 19 | "Copy": io.Copy, 20 | "CopyN": io.CopyN, 21 | "ReadAtLeast": io.ReadAtLeast, 22 | "ReadFull": io.ReadFull, 23 | "WriteString": io.WriteString, 24 | 25 | "pipe": io.Pipe, 26 | "limitReader": io.LimitReader, 27 | "multiReader": io.MultiReader, 28 | "multiWriter": io.MultiWriter, 29 | "teeReader": io.TeeReader, 30 | "sectionReader": io.NewSectionReader, 31 | 32 | "Pipe": io.Pipe, 33 | "LimitReader": io.LimitReader, 34 | "MultiReader": io.MultiReader, 35 | "MultiWriter": io.MultiWriter, 36 | "TeeReader": io.TeeReader, 37 | "NewSectionReader": io.NewSectionReader, 38 | 39 | "EOF": io.EOF, 40 | "ErrClosedPipe": io.ErrClosedPipe, 41 | "ErrNoProgress": io.ErrNoProgress, 42 | "ErrShortBuffer": io.ErrShortBuffer, 43 | "ErrShortWrite": io.ErrShortWrite, 44 | "ErrUnexpectedEOF": io.ErrUnexpectedEOF, 45 | } 46 | 47 | // ----------------------------------------------------------------------------- 48 | -------------------------------------------------------------------------------- /lib/io/ioutil/ioutil.go: -------------------------------------------------------------------------------- 1 | package ioutil 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | qlang "github.com/xushiwei/qlang/spec" 7 | ) 8 | 9 | // ----------------------------------------------------------------------------- 10 | 11 | // Exports is the export table of this module. 12 | // 13 | var Exports = map[string]interface{}{ 14 | "_name": "io/ioutil", 15 | "_initSafe": _initSafe, 16 | "discard": ioutil.Discard, 17 | "Discard": ioutil.Discard, 18 | 19 | "nopCloser": ioutil.NopCloser, 20 | "readAll": ioutil.ReadAll, 21 | "readDir": ioutil.ReadDir, 22 | "readFile": ioutil.ReadFile, 23 | "tempDir": ioutil.TempDir, 24 | "tempFile": ioutil.TempFile, 25 | "writeFile": ioutil.WriteFile, 26 | 27 | "NopCloser": ioutil.NopCloser, 28 | "ReadAll": ioutil.ReadAll, 29 | "ReadDir": ioutil.ReadDir, 30 | "ReadFile": ioutil.ReadFile, 31 | "TempDir": ioutil.TempDir, 32 | "TempFile": ioutil.TempFile, 33 | "WriteFile": ioutil.WriteFile, 34 | } 35 | 36 | func _initSafe(mod qlang.Module) { 37 | 38 | mod.Disable("readDir", "readFile", "writeFile") 39 | mod.Disable("ReadDir", "ReadFile", "WriteFile") 40 | } 41 | 42 | // ----------------------------------------------------------------------------- 43 | -------------------------------------------------------------------------------- /lib/math/math.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import ( 4 | "math" 5 | "reflect" 6 | "strings" 7 | 8 | qlang "github.com/xushiwei/qlang/spec" 9 | ) 10 | 11 | // ----------------------------------------------------------------------------- 12 | 13 | func init() { 14 | 15 | fnt := qlang.Fntable 16 | 17 | fnt["e"] = math.E 18 | fnt["pi"] = math.Pi 19 | fnt["phi"] = math.Phi 20 | 21 | fnt["Inf"] = math.Inf(1) 22 | fnt["NaN"] = math.NaN() 23 | } 24 | 25 | // ----------------------------------------------------------------------------- 26 | 27 | // Exports is the export table of this module. 28 | // 29 | var Exports = map[string]interface{}{ 30 | "_name": "math", 31 | "abs": math.Abs, 32 | "acos": math.Acos, 33 | "acosh": math.Acosh, 34 | "asin": math.Asin, 35 | "asinh": math.Asinh, 36 | "atan": math.Atan, 37 | "atan2": math.Atan2, 38 | "atanh": math.Atanh, 39 | "cbrt": math.Cbrt, 40 | "ceil": math.Ceil, 41 | "copysign": math.Copysign, 42 | "cos": math.Cos, 43 | "cosh": math.Cosh, 44 | "dim": math.Dim, 45 | "erf": math.Erf, 46 | "erfc": math.Erfc, 47 | "exp": math.Exp, 48 | "exp2": math.Exp2, 49 | "expm1": math.Expm1, 50 | "floor": math.Floor, 51 | "gamma": math.Gamma, 52 | "hypot": math.Hypot, 53 | "inf": math.Inf, 54 | "j0": math.J0, 55 | "j1": math.J1, 56 | "jn": math.Jn, 57 | "ldexp": math.Ldexp, 58 | "ln": math.Log, 59 | "log": math.Log, 60 | "log10": math.Log10, 61 | "log1p": math.Log1p, 62 | "log2": math.Log2, 63 | "logb": math.Logb, 64 | "mod": mod, 65 | "nextafter": math.Nextafter, 66 | "pow": math.Pow, 67 | "pow10": math.Pow10, 68 | "remainder": math.Remainder, 69 | "sin": math.Sin, 70 | "sinh": math.Sinh, 71 | "sqrt": math.Sqrt, 72 | "tan": math.Tan, 73 | "tanh": math.Tanh, 74 | "trunc": math.Trunc, 75 | "y0": math.Y0, 76 | "y1": math.Y1, 77 | "yn": math.Yn, 78 | 79 | "Abs": math.Abs, 80 | "Acos": math.Acos, 81 | "Acosh": math.Acosh, 82 | "Asin": math.Asin, 83 | "Asinh": math.Asinh, 84 | "Atan": math.Atan, 85 | "Atan2": math.Atan2, 86 | "Atanh": math.Atanh, 87 | "Cbrt": math.Cbrt, 88 | "Ceil": math.Ceil, 89 | "Copysign": math.Copysign, 90 | "Cos": math.Cos, 91 | "Cosh": math.Cosh, 92 | "Dim": math.Dim, 93 | "Erf": math.Erf, 94 | "Erfc": math.Erfc, 95 | "Exp": math.Exp, 96 | "Exp2": math.Exp2, 97 | "Expm1": math.Expm1, 98 | "Floor": math.Floor, 99 | "Gamma": math.Gamma, 100 | "Hypot": math.Hypot, 101 | "J0": math.J0, 102 | "J1": math.J1, 103 | "Jn": math.Jn, 104 | "Ldexp": math.Ldexp, 105 | "Ln": math.Log, 106 | "Log": math.Log, 107 | "Log10": math.Log10, 108 | "Log1p": math.Log1p, 109 | "Log2": math.Log2, 110 | "Logb": math.Logb, 111 | "Mod": mod, 112 | "Nextafter": math.Nextafter, 113 | "Pow": math.Pow, 114 | "Pow10": math.Pow10, 115 | "Remainder": math.Remainder, 116 | "Sin": math.Sin, 117 | "Sinh": math.Sinh, 118 | "Sqrt": math.Sqrt, 119 | "Tan": math.Tan, 120 | "Tanh": math.Tanh, 121 | "Trunc": math.Trunc, 122 | "Y0": math.Y0, 123 | "Y1": math.Y1, 124 | "Yn": math.Yn, 125 | } 126 | 127 | // ----------------------------------------------------------------------------- 128 | 129 | func mod(a, b interface{}) interface{} { 130 | 131 | return math.Mod(castFloat(a), castFloat(b)) 132 | } 133 | 134 | func castFloat(a interface{}) float64 { 135 | 136 | switch a1 := a.(type) { 137 | case int: 138 | return float64(a1) 139 | case float64: 140 | return a1 141 | } 142 | panicUnsupportedFn("float", a) 143 | return 0 144 | } 145 | 146 | func panicUnsupportedFn(fn string, args ...interface{}) interface{} { 147 | 148 | targs := make([]string, len(args)) 149 | for i, a := range args { 150 | targs[i] = reflect.TypeOf(a).String() 151 | } 152 | panic("unsupported function: " + fn + "(" + strings.Join(targs, ",") + ")") 153 | } 154 | 155 | // ----------------------------------------------------------------------------- 156 | -------------------------------------------------------------------------------- /lib/meta/README.md: -------------------------------------------------------------------------------- 1 | qlang package meta 2 | ================== 3 | 4 | ### Usage 5 | 6 | ``` 7 | qlang.Import("", meta.Exports) 8 | ``` 9 | 10 | ### Function 11 | 12 | * pkgs `func() []string` 13 | 14 | ``` 15 | >>>pkgs() 16 | [bufio bytes md5 io ioutil hex json errors math os path http reflect runtime strconv strings sync] 17 | ``` 18 | 19 | * dir `func(interface {}) []string` 20 | 21 | ``` 22 | >>>dir(md5) 23 | [_name new sum sumstr hash BlockSize Size] 24 | ``` 25 | 26 | * doc `func(interface {}) string` 27 | 28 | ``` 29 | >>>doc(md5) 30 | package crypto/md5 31 | hash func(interface {}, ...interface {}) string 32 | BlockSize int 33 | Size int 34 | _name string 35 | new func() hash.Hash 36 | sum func([]uint8) [16]uint8 37 | sumstr func([]uint8) string 38 | ``` 39 | -------------------------------------------------------------------------------- /lib/net/http/http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net/http" 5 | 6 | qlang "github.com/xushiwei/qlang/spec" 7 | ) 8 | 9 | // ----------------------------------------------------------------------------- 10 | 11 | // Exports is the export table of this module. 12 | // 13 | var Exports = map[string]interface{}{ 14 | "_name": "net/http", 15 | "request": http.NewRequest, 16 | "readRequest": http.ReadRequest, 17 | "readResponse": http.ReadResponse, 18 | "parseHTTPVersion": http.ParseHTTPVersion, 19 | "parseTime": http.ParseTime, 20 | 21 | "NewRequest": http.NewRequest, 22 | "ReadRequest": http.ReadRequest, 23 | "ReadResponse": http.ReadResponse, 24 | "ParseHTTPVersion": http.ParseHTTPVersion, 25 | "ParseTime": http.ParseTime, 26 | 27 | "get": http.Get, 28 | "post": http.Post, 29 | "postForm": http.PostForm, 30 | "head": http.Head, 31 | 32 | "Get": http.Get, 33 | "Post": http.Post, 34 | "PostForm": http.PostForm, 35 | "Head": http.Head, 36 | 37 | "handle": http.Handle, 38 | "handleFunc": http.HandleFunc, 39 | "serveMux": http.NewServeMux, 40 | "serve": http.Serve, 41 | "listenAndServe": http.ListenAndServe, 42 | "listenAndServeTLS": http.ListenAndServeTLS, 43 | 44 | "Handle": http.Handle, 45 | "HandleFunc": http.HandleFunc, 46 | "NewServeMux": http.NewServeMux, 47 | "Serve": http.Serve, 48 | "ListenAndServe": http.ListenAndServe, 49 | "ListenAndServeTLS": http.ListenAndServeTLS, 50 | 51 | "error": http.Error, 52 | "notFound": http.NotFound, 53 | "notFoundHandler": http.NotFoundHandler, 54 | "redirect": http.Redirect, 55 | "redirectHandler": http.RedirectHandler, 56 | "fileServer": http.FileServer, 57 | "fileTransport": http.NewFileTransport, 58 | "serveContent": http.ServeContent, 59 | "serveFile": http.ServeFile, 60 | "setCookie": http.SetCookie, 61 | "stripPrefix": http.StripPrefix, 62 | "timeoutHandler": http.TimeoutHandler, 63 | 64 | "Error": http.Error, 65 | "NotFound": http.NotFound, 66 | "NotFoundHandler": http.NotFoundHandler, 67 | "Redirect": http.Redirect, 68 | "RedirectHandler": http.RedirectHandler, 69 | "FileServer": http.FileServer, 70 | "NewFileTransport": http.NewFileTransport, 71 | "ServeContent": http.ServeContent, 72 | "ServeFile": http.ServeFile, 73 | "SetCookie": http.SetCookie, 74 | "StripPrefix": http.StripPrefix, 75 | "TimeoutHandler": http.TimeoutHandler, 76 | 77 | "statusText": http.StatusText, 78 | "canonicalHeaderKey": http.CanonicalHeaderKey, 79 | "detectContentType": http.DetectContentType, 80 | "maxBytesReader": http.MaxBytesReader, 81 | "proxyFromEnvironment": http.ProxyFromEnvironment, 82 | "proxyURL": http.ProxyURL, 83 | 84 | "StatusText": http.StatusText, 85 | "CanonicalHeaderKey": http.CanonicalHeaderKey, 86 | "DetectContentType": http.DetectContentType, 87 | "MaxBytesReader": http.MaxBytesReader, 88 | "ProxyFromEnvironment": http.ProxyFromEnvironment, 89 | "ProxyURL": http.ProxyURL, 90 | 91 | "DefaultTransport": http.DefaultTransport, 92 | "DefaultClient": http.DefaultClient, 93 | "DefaultServeMux": http.DefaultServeMux, 94 | 95 | "Client": qlang.StructOf((*http.Client)(nil)), 96 | "Cookie": qlang.StructOf((*http.Cookie)(nil)), 97 | "Header": qlang.StructOf((*http.Header)(nil)), 98 | "Request": qlang.StructOf((*http.Request)(nil)), 99 | "Response": qlang.StructOf((*http.Response)(nil)), 100 | "Server": qlang.StructOf((*http.Server)(nil)), 101 | } 102 | 103 | // ----------------------------------------------------------------------------- 104 | -------------------------------------------------------------------------------- /lib/os/os.go: -------------------------------------------------------------------------------- 1 | package os 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | 7 | qlang "github.com/xushiwei/qlang/spec" 8 | ) 9 | 10 | // ----------------------------------------------------------------------------- 11 | 12 | // Exports is the export table of this module. 13 | // 14 | var Exports = map[string]interface{}{ 15 | "_name": "os", 16 | "_initSafe": _initSafe, 17 | "args": os.Args[1:], 18 | "stdin": os.Stdin, 19 | "stderr": os.Stderr, 20 | "stdout": os.Stdout, 21 | "getenv": os.Getenv, 22 | "open": os.Open, 23 | "create": os.Create, 24 | "exit": os.Exit, 25 | 26 | "Args": os.Args[1:], 27 | "Stdin": os.Stdin, 28 | "Stderr": os.Stderr, 29 | "Stdout": os.Stdout, 30 | "Getenv": os.Getenv, 31 | "Open": os.Open, 32 | "Create": os.Create, 33 | "Exit": os.Exit, 34 | } 35 | 36 | func _initSafe(mod qlang.Module) { 37 | 38 | mod.Disable("open") 39 | mod.Disable("getenv") 40 | mod.Exports["exit"] = SafeExit 41 | 42 | mod.Disable("Open") 43 | mod.Disable("Getenv") 44 | mod.Exports["Exit"] = SafeExit 45 | } 46 | 47 | // SafeExit is a safe way to quit qlang application. 48 | // 49 | func SafeExit(code int) { 50 | 51 | panic("exit " + strconv.Itoa(code)) 52 | } 53 | 54 | // ----------------------------------------------------------------------------- 55 | 56 | func exit() { 57 | os.Exit(0) 58 | } 59 | 60 | func safeExit() { 61 | panic("exit") 62 | } 63 | 64 | func _initSafe2(mod qlang.Module) { 65 | mod.Exports["exit"] = safeExit 66 | } 67 | 68 | // InlineExports is the export table of this module. 69 | // 70 | var InlineExports = map[string]interface{}{ 71 | "exit": exit, 72 | "_initSafe": _initSafe2, 73 | } 74 | 75 | // ----------------------------------------------------------------------------- 76 | -------------------------------------------------------------------------------- /lib/path/path_exports.go: -------------------------------------------------------------------------------- 1 | package path 2 | 3 | import ( 4 | "path" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | // Exports is the export table of this module. 10 | // 11 | var Exports = map[string]interface{}{ 12 | "_name": "path", 13 | "base": path.Base, 14 | "clean": path.Clean, 15 | "dir": path.Dir, 16 | "ext": path.Ext, 17 | "isAbs": path.IsAbs, 18 | "join": path.Join, 19 | "match": path.Match, 20 | "split": path.Split, 21 | 22 | "Base": path.Base, 23 | "Clean": path.Clean, 24 | "Dir": path.Dir, 25 | "Ext": path.Ext, 26 | "IsAbs": path.IsAbs, 27 | "Join": path.Join, 28 | "Match": path.Match, 29 | "Split": path.Split, 30 | 31 | "ErrBadPattern": path.ErrBadPattern, 32 | } 33 | 34 | // ----------------------------------------------------------------------------- 35 | -------------------------------------------------------------------------------- /lib/qlang.all/all.go: -------------------------------------------------------------------------------- 1 | package qall 2 | 3 | import ( 4 | "github.com/xushiwei/qlang/lib/bufio" 5 | "github.com/xushiwei/qlang/lib/bytes" 6 | "github.com/xushiwei/qlang/lib/crypto/md5" 7 | "github.com/xushiwei/qlang/lib/encoding/hex" 8 | "github.com/xushiwei/qlang/lib/encoding/json" 9 | "github.com/xushiwei/qlang/lib/eqlang" 10 | "github.com/xushiwei/qlang/lib/errors" 11 | "github.com/xushiwei/qlang/lib/io" 12 | "github.com/xushiwei/qlang/lib/io/ioutil" 13 | "github.com/xushiwei/qlang/lib/math" 14 | "github.com/xushiwei/qlang/lib/meta" 15 | "github.com/xushiwei/qlang/lib/net/http" 16 | "github.com/xushiwei/qlang/lib/os" 17 | "github.com/xushiwei/qlang/lib/path" 18 | "github.com/xushiwei/qlang/lib/reflect" 19 | "github.com/xushiwei/qlang/lib/runtime" 20 | "github.com/xushiwei/qlang/lib/strconv" 21 | "github.com/xushiwei/qlang/lib/strings" 22 | "github.com/xushiwei/qlang/lib/sync" 23 | "github.com/xushiwei/qlang/lib/terminal" 24 | "github.com/xushiwei/qlang/lib/tpl/extractor" 25 | "github.com/xushiwei/qlang/lib/version" 26 | qlang "github.com/xushiwei/qlang/spec" 27 | 28 | // qlang builtin modules 29 | _ "github.com/xushiwei/qlang/lib/builtin" 30 | _ "github.com/xushiwei/qlang/lib/chan" 31 | ) 32 | 33 | // ----------------------------------------------------------------------------- 34 | 35 | // Copyright prints qlang copyright information. 36 | // 37 | func Copyright() { 38 | version.Copyright() 39 | } 40 | 41 | // InitSafe inits qlang and imports modules. 42 | // 43 | func InitSafe(safeMode bool) { 44 | 45 | qlang.SafeMode = safeMode 46 | 47 | qlang.Import("", math.Exports) // import math as builtin package 48 | qlang.Import("", meta.Exports) // import meta package 49 | qlang.Import("bufio", bufio.Exports) 50 | qlang.Import("bytes", bytes.Exports) 51 | qlang.Import("md5", md5.Exports) 52 | qlang.Import("io", io.Exports) 53 | qlang.Import("ioutil", ioutil.Exports) 54 | qlang.Import("hex", hex.Exports) 55 | qlang.Import("json", json.Exports) 56 | qlang.Import("errors", errors.Exports) 57 | qlang.Import("eqlang", eqlang.Exports) 58 | qlang.Import("math", math.Exports) 59 | qlang.Import("os", os.Exports) 60 | qlang.Import("", os.InlineExports) 61 | qlang.Import("path", path.Exports) 62 | qlang.Import("http", http.Exports) 63 | qlang.Import("reflect", reflect.Exports) 64 | qlang.Import("runtime", runtime.Exports) 65 | qlang.Import("strconv", strconv.Exports) 66 | qlang.Import("strings", strings.Exports) 67 | qlang.Import("sync", sync.Exports) 68 | qlang.Import("terminal", terminal.Exports) 69 | qlang.Import("extractor", extractor.Exports) 70 | } 71 | 72 | // ----------------------------------------------------------------------------- 73 | -------------------------------------------------------------------------------- /lib/reflect/reflect.go: -------------------------------------------------------------------------------- 1 | package reflect 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | // Exports is the export table of this module. 10 | // 11 | var Exports = map[string]interface{}{ 12 | "_name": "reflect", 13 | "valueOf": reflect.ValueOf, 14 | "typeOf": reflect.TypeOf, 15 | "indirect": reflect.Indirect, 16 | "makeSlice": reflect.MakeSlice, 17 | "makeMap": reflect.MakeMap, 18 | "zero": reflect.Zero, 19 | 20 | "ValueOf": reflect.ValueOf, 21 | "TypeOf": reflect.TypeOf, 22 | "Indirect": reflect.Indirect, 23 | "MakeSlice": reflect.MakeSlice, 24 | "MakeMap": reflect.MakeMap, 25 | "Zero": reflect.Zero, 26 | 27 | "Map": reflect.Map, 28 | "Slice": reflect.Slice, 29 | "Interface": reflect.Interface, 30 | "Int": reflect.Int, 31 | } 32 | 33 | // ----------------------------------------------------------------------------- 34 | -------------------------------------------------------------------------------- /lib/runtime/runtime-go16.go: -------------------------------------------------------------------------------- 1 | // +build go1.6 2 | 3 | package runtime 4 | 5 | import "runtime" 6 | 7 | func init() { 8 | Exports["readTrace"] = runtime.ReadTrace 9 | Exports["startTrace"] = runtime.StartTrace 10 | Exports["stopTrace"] = runtime.StopTrace 11 | 12 | Exports["ReadTrace"] = runtime.ReadTrace 13 | Exports["StartTrace"] = runtime.StartTrace 14 | Exports["StopTrace"] = runtime.StopTrace 15 | } 16 | -------------------------------------------------------------------------------- /lib/runtime/runtime.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "runtime" 5 | ) 6 | 7 | func newBlockProfileRecords(n int) []runtime.BlockProfileRecord { 8 | return make([]runtime.BlockProfileRecord, n) 9 | } 10 | 11 | func newMemProfileRecords(n int) []runtime.MemProfileRecord { 12 | return make([]runtime.MemProfileRecord, n) 13 | } 14 | 15 | func newStackRecords(n int) []runtime.StackRecord { 16 | return make([]runtime.StackRecord, n) 17 | } 18 | 19 | // Exports is the export table of this module. 20 | // 21 | var Exports = map[string]interface{}{ 22 | "_name": "runtime", 23 | "memProfileRate": runtime.MemProfileRate, 24 | "MemProfileRate": runtime.MemProfileRate, 25 | 26 | "compiler": runtime.Compiler, 27 | "Compiler": runtime.Compiler, 28 | "GOARCH": runtime.GOARCH, 29 | "GOOS": runtime.GOOS, 30 | 31 | "blockProfileRecords": newBlockProfileRecords, 32 | "memProfileRecords": newMemProfileRecords, 33 | "stackRecords": newStackRecords, 34 | "blockProfile": runtime.BlockProfile, 35 | "memProfile": runtime.MemProfile, 36 | "CPUProfile": runtime.CPUProfile, 37 | "threadCreateProfile": runtime.ThreadCreateProfile, 38 | "goroutineProfile": runtime.GoroutineProfile, 39 | "setBlockProfileRate": runtime.SetBlockProfileRate, 40 | "setCPUProfileRate": runtime.SetCPUProfileRate, 41 | 42 | "NewBlockProfileRecords": newBlockProfileRecords, 43 | "NewMemProfileRecords": newMemProfileRecords, 44 | "NewStackRecords": newStackRecords, 45 | "BlockProfile": runtime.BlockProfile, 46 | "MemProfile": runtime.MemProfile, 47 | "ThreadCreateProfile": runtime.ThreadCreateProfile, 48 | "GoroutineProfile": runtime.GoroutineProfile, 49 | "SetBlockProfileRate": runtime.SetBlockProfileRate, 50 | "SetCPUProfileRate": runtime.SetCPUProfileRate, 51 | 52 | "breakpoint": runtime.Breakpoint, 53 | "caller": runtime.Caller, 54 | "callers": runtime.Callers, 55 | "funcForPC": runtime.FuncForPC, 56 | "GC": runtime.GC, 57 | "GOMAXPROCS": runtime.GOMAXPROCS, 58 | "GOROOT": runtime.GOROOT, 59 | "goexit": runtime.Goexit, 60 | "gosched": runtime.Gosched, 61 | "lockOSThread": runtime.LockOSThread, 62 | "numCPU": runtime.NumCPU, 63 | "numCgoCall": runtime.NumCgoCall, 64 | "numGoroutine": runtime.NumGoroutine, 65 | "readMemStats": runtime.ReadMemStats, 66 | "setFinalizer": runtime.SetFinalizer, 67 | "stack": runtime.Stack, 68 | "unlockOSThread": runtime.UnlockOSThread, 69 | "version": runtime.Version, 70 | 71 | "Breakpoint": runtime.Breakpoint, 72 | "Caller": runtime.Caller, 73 | "Callers": runtime.Callers, 74 | "FuncForPC": runtime.FuncForPC, 75 | "Goexit": runtime.Goexit, 76 | "Gosched": runtime.Gosched, 77 | "LockOSThread": runtime.LockOSThread, 78 | "NumCPU": runtime.NumCPU, 79 | "NumCgoCall": runtime.NumCgoCall, 80 | "NumGoroutine": runtime.NumGoroutine, 81 | "ReadMemStats": runtime.ReadMemStats, 82 | "SetFinalizer": runtime.SetFinalizer, 83 | "Stack": runtime.Stack, 84 | "UnlockOSThread": runtime.UnlockOSThread, 85 | "Version": runtime.Version, 86 | } 87 | -------------------------------------------------------------------------------- /lib/strconv/strconv.go: -------------------------------------------------------------------------------- 1 | package strconv 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | // Exports is the export table of this module. 10 | // 11 | var Exports = map[string]interface{}{ 12 | "_name": "strconv", 13 | "itoa": strconv.Itoa, 14 | "parseUint": strconv.ParseUint, 15 | "parseInt": strconv.ParseInt, 16 | "parseFloat": strconv.ParseFloat, 17 | 18 | "Itoa": strconv.Itoa, 19 | "ParseUint": strconv.ParseUint, 20 | "ParseInt": strconv.ParseInt, 21 | "ParseFloat": strconv.ParseFloat, 22 | 23 | "unquoteChar": strconv.UnquoteChar, 24 | "unquote": strconv.Unquote, 25 | 26 | "UnquoteChar": strconv.UnquoteChar, 27 | "Unquote": strconv.Unquote, 28 | } 29 | 30 | // ----------------------------------------------------------------------------- 31 | -------------------------------------------------------------------------------- /lib/strings/strings-go16.go: -------------------------------------------------------------------------------- 1 | // +build go1.6 2 | 3 | package strings 4 | 5 | import ( 6 | "strings" 7 | ) 8 | 9 | func init() { 10 | Exports["lastIndexByte"] = strings.LastIndexByte 11 | Exports["compare"] = strings.Compare 12 | 13 | Exports["LastIndexByte"] = strings.LastIndexByte 14 | Exports["Compare"] = strings.Compare 15 | } 16 | -------------------------------------------------------------------------------- /lib/strings/strings.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "strings" 5 | 6 | qlang "github.com/xushiwei/qlang/spec" 7 | ) 8 | 9 | // ----------------------------------------------------------------------------- 10 | 11 | var ( 12 | tyReader = qlang.StructOf((*strings.Reader)(nil)) 13 | tyReplacer = qlang.StructOf((*strings.Replacer)(nil)) 14 | ) 15 | 16 | // Exports is the export table of this module. 17 | // 18 | var Exports = map[string]interface{}{ 19 | "_name": "strings", 20 | "contains": strings.Contains, 21 | "containsAny": strings.ContainsAny, 22 | "containsRune": strings.ContainsRune, 23 | "count": strings.Count, 24 | "equalFold": strings.EqualFold, 25 | "fields": strings.Fields, 26 | "fieldsFunc": strings.FieldsFunc, 27 | "hasPrefix": strings.HasPrefix, 28 | "hasSuffix": strings.HasSuffix, 29 | "index": strings.Index, 30 | "indexAny": strings.IndexAny, 31 | "indexByte": strings.IndexByte, 32 | "indexFunc": strings.IndexFunc, 33 | "indexRune": strings.IndexRune, 34 | "join": strings.Join, 35 | "lastIndex": strings.LastIndex, 36 | "lastIndexAny": strings.LastIndexAny, 37 | "lastIndexFunc": strings.LastIndexFunc, 38 | "map": strings.Map, 39 | "repeat": strings.Repeat, 40 | "replace": strings.Replace, 41 | "split": strings.Split, 42 | "splitAfter": strings.SplitAfter, 43 | "splitAfterN": strings.SplitAfterN, 44 | "splitN": strings.SplitN, 45 | "title": strings.Title, 46 | "toLower": strings.ToLower, 47 | "toLowerSpecial": strings.ToLowerSpecial, 48 | "toTitle": strings.ToTitle, 49 | "toTitleSpecial": strings.ToTitleSpecial, 50 | "toUpper": strings.ToUpper, 51 | "trim": strings.Trim, 52 | "trimFunc": strings.TrimFunc, 53 | "trimLeft": strings.TrimLeft, 54 | "trimLeftFunc": strings.TrimLeftFunc, 55 | "trimPrefix": strings.TrimPrefix, 56 | "trimRight": strings.TrimRight, 57 | "trimRightFunc": strings.TrimRightFunc, 58 | "trimSpace": strings.TrimSpace, 59 | "trimSuffix": strings.TrimSuffix, 60 | 61 | "Contains": strings.Contains, 62 | "ContainsAny": strings.ContainsAny, 63 | "ContainsRune": strings.ContainsRune, 64 | "Count": strings.Count, 65 | "EqualFold": strings.EqualFold, 66 | "Fields": strings.Fields, 67 | "FieldsFunc": strings.FieldsFunc, 68 | "HasPrefix": strings.HasPrefix, 69 | "HasSuffix": strings.HasSuffix, 70 | "Index": strings.Index, 71 | "IndexAny": strings.IndexAny, 72 | "IndexByte": strings.IndexByte, 73 | "IndexFunc": strings.IndexFunc, 74 | "IndexRune": strings.IndexRune, 75 | "Join": strings.Join, 76 | "LastIndex": strings.LastIndex, 77 | "LastIndexAny": strings.LastIndexAny, 78 | "LastIndexFunc": strings.LastIndexFunc, 79 | "Map": strings.Map, 80 | "Repeat": strings.Repeat, 81 | "Replace": strings.Replace, 82 | "Split": strings.Split, 83 | "SplitAfter": strings.SplitAfter, 84 | "SplitAfterN": strings.SplitAfterN, 85 | "SplitN": strings.SplitN, 86 | "Title": strings.Title, 87 | "ToLower": strings.ToLower, 88 | "ToLowerSpecial": strings.ToLowerSpecial, 89 | "ToTitle": strings.ToTitle, 90 | "ToTitleSpecial": strings.ToTitleSpecial, 91 | "ToUpper": strings.ToUpper, 92 | "Trim": strings.Trim, 93 | "TrimFunc": strings.TrimFunc, 94 | "TrimLeft": strings.TrimLeft, 95 | "TrimLeftFunc": strings.TrimLeftFunc, 96 | "TrimPrefix": strings.TrimPrefix, 97 | "TrimRight": strings.TrimRight, 98 | "TrimRightFunc": strings.TrimRightFunc, 99 | "TrimSpace": strings.TrimSpace, 100 | "TrimSuffix": strings.TrimSuffix, 101 | 102 | "reader": strings.NewReader, 103 | "replacer": strings.NewReplacer, 104 | 105 | "NewReader": strings.NewReader, 106 | "NewReplacer": strings.NewReplacer, 107 | 108 | "Reader": tyReader, 109 | "Replacer": tyReplacer, 110 | } 111 | 112 | // ----------------------------------------------------------------------------- 113 | -------------------------------------------------------------------------------- /lib/sync/sync.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | func newMutex() *sync.Mutex { 10 | return new(sync.Mutex) 11 | } 12 | 13 | func newRWMutex() *sync.RWMutex { 14 | return new(sync.RWMutex) 15 | } 16 | 17 | func newWaitGroup() *sync.WaitGroup { 18 | return new(sync.WaitGroup) 19 | } 20 | 21 | // ----------------------------------------------------------------------------- 22 | 23 | // Exports is the export table of this module. 24 | // 25 | var Exports = map[string]interface{}{ 26 | "_name": "sync", 27 | "cond": sync.NewCond, 28 | "mutex": newMutex, 29 | "waitGroup": newWaitGroup, 30 | 31 | "NewCond": sync.NewCond, 32 | "NewMutex": newMutex, 33 | "NewWaitGroup": newWaitGroup, 34 | } 35 | 36 | // ----------------------------------------------------------------------------- 37 | -------------------------------------------------------------------------------- /lib/terminal/terminal.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "github.com/peterh/liner" 8 | ) 9 | 10 | type Terminal struct { 11 | *liner.State 12 | promptFirst string 13 | promptNext string 14 | fnReadMore func(expr string, line string) (string, bool) 15 | } 16 | 17 | func New(promptFirst, promptNext string, fnReadMore func(expr string, line string) (string, bool)) *Terminal { 18 | term := &Terminal{ 19 | State: liner.NewLiner(), 20 | promptFirst: promptFirst, 21 | promptNext: promptNext, 22 | fnReadMore: fnReadMore, 23 | } 24 | term.SetCtrlCAborts(true) 25 | return term 26 | } 27 | 28 | func (term *Terminal) LoadHistroy(historyFile string) error { 29 | f, err := os.Open(historyFile) 30 | if err != nil { 31 | return err 32 | } 33 | defer f.Close() 34 | term.ReadHistory(f) 35 | return nil 36 | } 37 | 38 | func (term *Terminal) SaveHistroy(historyFile string) error { 39 | f, err := os.Create(historyFile) 40 | if err != nil { 41 | return err 42 | } 43 | defer f.Close() 44 | term.WriteHistory(f) 45 | return nil 46 | } 47 | 48 | var ( 49 | ErrPromptAborted = liner.ErrPromptAborted 50 | ) 51 | 52 | func (term *Terminal) Scan() (string, error) { 53 | var all string 54 | var more bool 55 | prompt := term.promptFirst 56 | fnReadMore := term.fnReadMore 57 | for { 58 | line, err := term.Prompt(prompt) 59 | if err != nil { 60 | if err == liner.ErrPromptAborted { 61 | return all, ErrPromptAborted 62 | } 63 | return all, err 64 | } 65 | if strings.TrimSpace(line) != "" { 66 | term.AppendHistory(line) 67 | } 68 | if fnReadMore == nil { 69 | return line, nil 70 | } 71 | all, more = fnReadMore(all, line) 72 | if !more { 73 | break 74 | } 75 | prompt = term.promptNext 76 | } 77 | return all, nil 78 | } 79 | 80 | // Exports is the export table of this module. 81 | // 82 | var Exports = map[string]interface{}{ 83 | "new": New, 84 | "supported": liner.TerminalSupported, 85 | "mode": liner.TerminalMode, 86 | 87 | "New": New, 88 | "Supported": liner.TerminalSupported, 89 | "Mode": liner.TerminalMode, 90 | 91 | "ErrPromptAborted": ErrPromptAborted, 92 | } 93 | -------------------------------------------------------------------------------- /lib/tpl/extractor/extract_exports.go: -------------------------------------------------------------------------------- 1 | package extractor 2 | 3 | import ( 4 | "github.com/qiniu/text/tpl/extractor" 5 | ) 6 | 7 | // ----------------------------------------------------------------------------- 8 | 9 | // Exports is the export table of this module. 10 | // 11 | var Exports = map[string]interface{}{ 12 | "_name": "github.com/qiniu/text/tpl/extractor", 13 | "new": extractor.New, 14 | "New": extractor.New, 15 | } 16 | 17 | // ----------------------------------------------------------------------------- 18 | -------------------------------------------------------------------------------- /lib/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime" 7 | "strings" 8 | ) 9 | 10 | var version = "develop" 11 | 12 | func init() { 13 | version = strings.TrimRight(version, " ") 14 | if len(os.Args) > 1 && (os.Args[1] == "version" || os.Args[1] == "-version") { 15 | Copyright() 16 | os.Exit(0) 17 | } 18 | } 19 | 20 | // Copyright shows qlang copyright information. 21 | // 22 | func Copyright() { 23 | fmt.Printf("Q-language, version qlang-%s %s/%s\n", version, runtime.GOOS, runtime.GOARCH) 24 | fmt.Println("Copyright (C) 2015 Qiniu.com - Shanghai Qiniu Information Technologies Co., Ltd.") 25 | } 26 | 27 | // Version returns qlang version. 28 | // 29 | func Version() string { 30 | return version 31 | } 32 | -------------------------------------------------------------------------------- /spec/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | qlang "github.com/xushiwei/qlang/spec" 8 | ) 9 | 10 | // ----------------------------------------------------------------------------- 11 | 12 | var ( 13 | // Var is reflect.Type of interface{} 14 | Var = reflect.TypeOf((*interface{})(nil)).Elem() 15 | ) 16 | 17 | // Reflect returns reflect.Type of typ. 18 | // 19 | func Reflect(typ interface{}) reflect.Type { 20 | 21 | if t, ok := typ.(qlang.GoTyper); ok { 22 | return t.GoType() 23 | } 24 | if t, ok := typ.(string); ok { 25 | if v, ok := builtinTypes[t]; ok { 26 | return v 27 | } 28 | } else if t, ok := typ.(reflect.Type); ok { 29 | return t 30 | } 31 | panic(fmt.Errorf("unknown type: `%v`", typ)) 32 | } 33 | 34 | var builtinTypes = map[string]reflect.Type{ 35 | "int": reflect.TypeOf(0), 36 | "bool": reflect.TypeOf(false), 37 | "float": reflect.TypeOf(float64(0)), 38 | "string": reflect.TypeOf(""), 39 | "byte": reflect.TypeOf(byte(0)), 40 | "var": Var, 41 | "int:int": reflect.TypeOf(map[int]int(nil)), 42 | "int:float": reflect.TypeOf(map[int]float64(nil)), 43 | "int:string": reflect.TypeOf(map[int]string(nil)), 44 | "int:var": reflect.TypeOf(map[int]interface{}(nil)), 45 | "string:int": reflect.TypeOf(map[string]int(nil)), 46 | "string:float": reflect.TypeOf(map[string]float64(nil)), 47 | "string:string": reflect.TypeOf(map[string]string(nil)), 48 | "string:var": reflect.TypeOf(map[string]interface{}(nil)), 49 | } 50 | 51 | // ----------------------------------------------------------------------------- 52 | -------------------------------------------------------------------------------- /tutorial/anonymfn/anonym.ql: -------------------------------------------------------------------------------- 1 | defer fn { 2 | println("x:", x) 3 | } 4 | 5 | defer fn { 6 | x; x = 2 7 | } 8 | 9 | x = 1 -------------------------------------------------------------------------------- /tutorial/calc/calc.ql: -------------------------------------------------------------------------------- 1 | 2 | grammar = ` 3 | 4 | term = factor *('*' factor/mul | '/' factor/quo | '%' factor/mod) 5 | 6 | doc = term *('+' term/add | '-' term/sub) 7 | 8 | factor = 9 | FLOAT/pushFloat | 10 | '-' factor/neg | 11 | '(' doc ')' | 12 | (IDENT '(' doc %= ','/ARITY ')')/call 13 | ` 14 | 15 | Stack = class { 16 | 17 | fn _init() { 18 | this.stk = [] 19 | } 20 | 21 | fn clear() { 22 | this.stk = this.stk[:0] 23 | } 24 | 25 | fn pop() { 26 | n = len(this.stk) 27 | if n > 0 { 28 | v = this.stk[n-1] 29 | this.stk = this.stk[:n-1] 30 | return v, true 31 | } 32 | return nil, false 33 | } 34 | 35 | fn push(v) { 36 | this.stk = append(this.stk, v) 37 | } 38 | 39 | fn popArgs(arity) { 40 | n = len(this.stk) 41 | if n < arity { 42 | panic("Stack.popArgs: unexpected") 43 | } 44 | args = make([]var, arity) 45 | copy(args, this.stk[n-arity:]) 46 | this.stk = this.stk[:n-arity] 47 | return args 48 | } 49 | } 50 | 51 | Calculator = class { 52 | 53 | fn _init() { 54 | this.stk = new Stack 55 | } 56 | 57 | fn grammar() { 58 | return grammar 59 | } 60 | 61 | fn stack() { 62 | return this.stk 63 | } 64 | 65 | fn fntable() { 66 | return fntable 67 | } 68 | 69 | fn ret() { 70 | v, _ = this.stk.pop() 71 | this.stk.clear() 72 | return v 73 | } 74 | 75 | fn call(name) { 76 | f = fntable[name] 77 | if f == undefined { 78 | panic("function not found: " + name) 79 | } 80 | arity, _ = this.stk.pop() 81 | args = this.stk.popArgs(arity) 82 | ret = f(args...) 83 | this.stk.push(ret) 84 | } 85 | } 86 | 87 | fntable = { 88 | "sin": sin, 89 | "cos": cos, 90 | "pow": pow, 91 | "max": max, 92 | "min": min, 93 | 94 | "$mul": fn(a, b) { return a*b }, 95 | "$quo": fn(a, b) { return a/b }, 96 | "$mod": fn(a, b) { return a%b }, 97 | "$add": fn(a, b) { return a+b }, 98 | "$sub": fn(a, b) { return a-b }, 99 | "$neg": fn(a) { return -a }, 100 | 101 | "$call": Calculator.call, 102 | "$pushFloat": Stack.push, 103 | "$ARITY": Stack.push, 104 | } 105 | -------------------------------------------------------------------------------- /tutorial/calc/main.ql: -------------------------------------------------------------------------------- 1 | include "calc.ql" 2 | 3 | main { // 使用main关键字将主程序括起来,是为了避免其中用的局部变量比如 err 对其他函数造成影响 4 | 5 | calc = new Calculator 6 | engine, err = interpreter(calc, nil) 7 | if err != nil { 8 | fprintln(os.Stderr, err) 9 | return 1 10 | } 11 | 12 | scanner = bufio.NewScanner(os.Stdin) 13 | for scanner.Scan() { 14 | line = strings.Trim(scanner.Text(), " \t\r\n") 15 | if line != "" { 16 | err = engine.Eval(line) 17 | if err != nil { 18 | fprintln(os.Stderr, err) 19 | } else { 20 | printf("> %v\n\n", calc.ret()) 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tutorial/chan/chan.ql: -------------------------------------------------------------------------------- 1 | ch = make(chan bool, 2) 2 | x = 1 3 | 4 | go fn { 5 | defer ch <- true 6 | println("in goroutine1: x =", x) 7 | x; x++ 8 | } 9 | 10 | go fn { 11 | defer ch <- true 12 | println("in goroutine2: x =", x) 13 | x; x++ 14 | } 15 | 16 | <-ch 17 | <-ch 18 | 19 | // 结果并不一定是3,因为x++没有做到线程安全 20 | // 21 | println("in main routine: x =", x) 22 | printf("chan cap=%d, len=%d\n", cap(ch), len(ch)) 23 | 24 | -------------------------------------------------------------------------------- /tutorial/closure/closure.ql: -------------------------------------------------------------------------------- 1 | 2 | test = fn() { 3 | g = 1 4 | return class { 5 | fn f() { 6 | return g 7 | } 8 | } 9 | } 10 | 11 | main { 12 | Foo = test() 13 | foo = new Foo 14 | g = 2 15 | println("foo.f:", foo.f()) 16 | } 17 | -------------------------------------------------------------------------------- /tutorial/defer/file.ql: -------------------------------------------------------------------------------- 1 | 2 | f, err = os.Open("file.ql") 3 | if err != nil { 4 | fprintln(os.Stderr, err) 5 | return 1 6 | } 7 | defer println("exit!") 8 | defer f.Close() 9 | 10 | b = make([]byte, 8) 11 | n, err = f.Read(b) 12 | if err != nil { 13 | fprintln(os.Stderr, "Read failed:", err) 14 | return 2 15 | } 16 | 17 | println(string(b[:n])) 18 | -------------------------------------------------------------------------------- /tutorial/flowctrl/flowctrl.ql: -------------------------------------------------------------------------------- 1 | weekday = "Friday" 2 | 3 | switch weekday { 4 | case "Monday": 5 | v = 1 6 | case "Tuesday": 7 | v = 2 8 | case "Wednesday": 9 | v = 3 10 | case "Thursday": 11 | v = 4 12 | case "Friday": 13 | v = 5 14 | case "Saterday": 15 | v = 6 16 | case "Sunday": 17 | v = 7 18 | default: 19 | v = 0 20 | } 21 | println(weekday, "=>", v) 22 | 23 | a = 3 24 | b = 7 25 | if a < b { 26 | min = a 27 | } else { 28 | min = b 29 | } 30 | println("min", a, b, ":", min) 31 | 32 | -------------------------------------------------------------------------------- /tutorial/for_range/for_range.ql: -------------------------------------------------------------------------------- 1 | 2 | sumi = 0 3 | sumv = 0 4 | 5 | for i, v = range [10, 20, 30, 40, 50, 60, 70, 80, 90] { 6 | sumi += i 7 | sumv += v 8 | if i == 4 { 9 | break 10 | } 11 | } 12 | 13 | println("sumi:", sumi, "sumv:", sumv) 14 | 15 | -------------------------------------------------------------------------------- /tutorial/goroutine/goroutine.ql: -------------------------------------------------------------------------------- 1 | 2 | runtime.GOMAXPROCS(8) 3 | 4 | wg = sync.NewWaitGroup() 5 | wg.Add(2) 6 | 7 | x = 1 8 | 9 | go fn { 10 | defer wg.Done() 11 | println("in goroutine1: x =", x) 12 | x++ 13 | } 14 | 15 | go fn { 16 | defer wg.Done() 17 | println("in goroutine2: x =", x) 18 | x++ 19 | } 20 | 21 | wg.Wait() 22 | 23 | // 结果并不一定是3,因为x++没有做到线程安全 24 | // 25 | println("in main routine: x =", x) 26 | -------------------------------------------------------------------------------- /tutorial/http/httpserver.ql: -------------------------------------------------------------------------------- 1 | mux = http.NewServeMux() 2 | mux.Handle("/404", http.NotFoundHandler()) 3 | mux.HandleFunc("/", fn(w, req) { 4 | fprintln(w, "host:", req.Host, "path:", req.URL) 5 | }) 6 | 7 | err = http.ListenAndServe(":8888", mux) 8 | if err != nil { 9 | fprintln(os.Stderr, err) 10 | } 11 | -------------------------------------------------------------------------------- /tutorial/if/if.ql: -------------------------------------------------------------------------------- 1 | 2 | today = 6 3 | 4 | if today == 1 { 5 | today = "Mon" 6 | } elif today == 2 { 7 | today = "Tue" 8 | } elif today == 3 { 9 | today = "Wed" 10 | } 11 | 12 | println(today) 13 | -------------------------------------------------------------------------------- /tutorial/include/a/foo.ql: -------------------------------------------------------------------------------- 1 | foo = fn(a) { 2 | println("in func foo:", a, b) 3 | } 4 | 5 | -------------------------------------------------------------------------------- /tutorial/include/a/main.ql: -------------------------------------------------------------------------------- 1 | 2 | println("in script A") 3 | 4 | a = 1 5 | b = 2 6 | 7 | include "foo.ql" 8 | 9 | export b, foo 10 | 11 | -------------------------------------------------------------------------------- /tutorial/include/b.ql: -------------------------------------------------------------------------------- 1 | include "a" 2 | 3 | println("in script B:", a, b) 4 | foo(3) 5 | 6 | include "bar.ql" 7 | bar() 8 | 9 | -------------------------------------------------------------------------------- /tutorial/include/bar.ql: -------------------------------------------------------------------------------- 1 | bar = fn() { 2 | println("in func bar:", a, b) 3 | } 4 | 5 | -------------------------------------------------------------------------------- /tutorial/include/run.sh: -------------------------------------------------------------------------------- 1 | qlang b.ql 2 | 3 | -------------------------------------------------------------------------------- /tutorial/maxprime/maxprime.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | primes = [2, 3] 4 | n = 1 5 | limit = 9 6 | 7 | def isPrime(v): 8 | i = 0 9 | while i < n: 10 | if v % primes[i] == 0: 11 | return False 12 | i += 1 13 | return True 14 | 15 | def listPrimes(max): 16 | global n, limit 17 | v = 5 18 | while True: 19 | while v < limit: 20 | if isPrime(v): 21 | primes.append(v) 22 | if v * v >= max: 23 | return 24 | v += 2 25 | v += 2 26 | n += 1 27 | limit = primes[n] * primes[n] 28 | 29 | def maxPrimeOf(max): 30 | global n 31 | if max % 2 == 0: 32 | max -= 1 33 | listPrimes(max) 34 | n = len(primes) 35 | while True: 36 | if isPrime(max): 37 | return max 38 | max -= 2 39 | 40 | if len(sys.argv) < 2: 41 | print 'Usage: maxprime ' 42 | sys.exit(1) 43 | 44 | max = int(sys.argv[1]) 45 | if max < 8: 46 | sys.exit(2) 47 | 48 | max -= 1 49 | v = maxPrimeOf(max) 50 | print v 51 | 52 | -------------------------------------------------------------------------------- /tutorial/maxprime/maxprime.ql: -------------------------------------------------------------------------------- 1 | 2 | primes = [2, 3] 3 | n = 1 4 | limit = 9 5 | 6 | isPrime = fn(v) { 7 | for i = 0; i < n; i++ { 8 | if v % primes[i] == 0 { 9 | return false 10 | } 11 | } 12 | return true 13 | } 14 | 15 | listPrimes = fn(max) { 16 | 17 | v = 5 18 | for { 19 | for v < limit { 20 | if isPrime(v) { 21 | primes = append(primes, v) 22 | if v * v >= max { 23 | return 24 | } 25 | } 26 | v += 2 27 | } 28 | v += 2 29 | n; n++ 30 | limit = primes[n] * primes[n] 31 | } 32 | } 33 | 34 | maxPrimeOf = fn(max) { 35 | 36 | if max % 2 == 0 { 37 | max-- 38 | } 39 | 40 | listPrimes(max) 41 | n; n = len(primes) 42 | 43 | for { 44 | if isPrime(max) { 45 | return max 46 | } 47 | max -= 2 48 | } 49 | } 50 | 51 | // Usage: maxprime 52 | // 53 | if len(os.Args) < 2 { 54 | fprintln(os.Stderr, "Usage: maxprime ") 55 | return 56 | } 57 | 58 | max, err = strconv.ParseInt(os.Args[1], 10, 64) 59 | if err != nil { 60 | fprintln(os.Stderr, err) 61 | return 1 62 | } 63 | if max < 8 { // <8 的情况下,可直接建表,答案略 64 | return 65 | } 66 | 67 | max-- 68 | v = maxPrimeOf(max) 69 | println(v) 70 | -------------------------------------------------------------------------------- /tutorial/module/c.ql: -------------------------------------------------------------------------------- 1 | import "internal/a" 2 | import "internal/a" as g 3 | 4 | b = 3 5 | 6 | g.b = 4 7 | println("in script C:", a.b, g.b) 8 | a.foo(b) 9 | 10 | println("\nnow, we will panic") 11 | println(a.a) // 因为 module a 并没有导出变量 a 12 | -------------------------------------------------------------------------------- /tutorial/module/internal/a/foo.ql: -------------------------------------------------------------------------------- 1 | foo = fn(a) { 2 | println("in func foo:", a, b) 3 | } 4 | -------------------------------------------------------------------------------- /tutorial/module/internal/a/main.ql: -------------------------------------------------------------------------------- 1 | 2 | println("in script A") 3 | 4 | a = 1 5 | b = 2 6 | 7 | include "foo.ql" 8 | 9 | export b, foo 10 | 11 | -------------------------------------------------------------------------------- /tutorial/module/run.sh: -------------------------------------------------------------------------------- 1 | QLANG_PATH=`pwd` qlang c.ql 2 | 3 | -------------------------------------------------------------------------------- /tutorial/qlang/main.ql: -------------------------------------------------------------------------------- 1 | include "qlang.ql" 2 | 3 | main { // 使用main关键字将主程序括起来,是为了避免其中用的局部变量比如 err 对其他函数造成影响 4 | 5 | ipt = new Interpreter 6 | 7 | if len(os.Args) > 1 { 8 | engine, err = interpreter(ipt, insertSemis) 9 | if err != nil { 10 | fprintln(os.Stderr, err) 11 | return 1 12 | } 13 | fname = os.Args[1] 14 | b, err = ioutil.ReadFile(fname) 15 | if err != nil { 16 | fprintln(os.Stderr, err) 17 | return 2 18 | } 19 | err = engine.Exec(b, fname) 20 | if err != nil { 21 | fprintln(os.Stderr, err) 22 | return 3 23 | } 24 | return 25 | } 26 | 27 | engine, err = interpreter(ipt, nil) 28 | if err != nil { 29 | fprintln(os.Stderr, err) 30 | return 1 31 | } 32 | 33 | scanner = bufio.NewScanner(os.Stdin) 34 | for scanner.Scan() { 35 | line = strings.Trim(scanner.Text(), " \t\r\n") 36 | if line != "" { 37 | err = engine.Eval(line) 38 | if err != nil { 39 | fprintln(os.Stderr, err) 40 | } else { 41 | printf("> %v\n\n", ipt.ret()) 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tutorial/shell/shell.ql: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env qlang 2 | 3 | println("Command Line:", os.Args) 4 | -------------------------------------------------------------------------------- /tutorial/sync/sync.ql: -------------------------------------------------------------------------------- 1 | mutex = sync.NewMutex() 2 | 3 | n = 10 4 | x = 1 5 | 6 | wg = sync.NewWaitGroup() 7 | wg.Add(n) 8 | 9 | for i = 0; i < n; i++ { 10 | go fn { 11 | defer wg.Done() 12 | 13 | mutex.Lock() 14 | defer mutex.Unlock() 15 | x; x++ 16 | } 17 | } 18 | 19 | wg.Wait() 20 | 21 | println("x:", x) 22 | -------------------------------------------------------------------------------- /tutorial/terminal/calc.ql: -------------------------------------------------------------------------------- 1 | include "../calc/calc.ql" 2 | 3 | main { // 使用main关键字将主程序括起来,是为了避免其中用的局部变量比如 err 对其他函数造成影响 4 | 5 | calc = new Calculator 6 | engine, err = interpreter(calc, nil) 7 | if err != nil { 8 | fprintln(os.Stderr, err) 9 | return 1 10 | } 11 | 12 | historyFile = os.Getenv("HOME") + "/.qcalc.history" 13 | term = terminal.New(">>> ", "... ", nil) 14 | term.LoadHistroy(historyFile) 15 | defer term.SaveHistroy(historyFile) 16 | 17 | println(`Q-Calculator - http://qlang.io, version 1.0.00 18 | Copyright (C) 2015 Qiniu.com - Shanghai Qiniu Information Technologies Co., Ltd. 19 | Use Ctrl-D (i.e. EOF) to exit. 20 | `) 21 | 22 | for { 23 | expr, err = term.Scan() 24 | if err != nil { 25 | if err == terminal.ErrPromptAborted { 26 | continue 27 | } elif err == io.EOF { 28 | println("^D") 29 | break 30 | } 31 | fprintln(os.Stderr, err) 32 | continue 33 | } 34 | expr = strings.TrimSpace(expr) 35 | if expr == "" { 36 | continue 37 | } 38 | err = engine.Eval(expr) 39 | if err != nil { 40 | fprintln(os.Stderr, err) 41 | } else { 42 | println(calc.ret()) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tutorial/terminal/qlang.ql: -------------------------------------------------------------------------------- 1 | include "../qlang/qlang.ql" 2 | 3 | main { // 使用main关键字将主程序括起来,是为了避免其中用的局部变量比如 err 对其他函数造成影响 4 | 5 | ipt = new Interpreter 6 | engine, err = interpreter(ipt, nil) 7 | if err != nil { 8 | fprintln(os.Stderr, err) 9 | return 1 10 | } 11 | 12 | historyFile = os.Getenv("HOME") + "/.qlang.history" 13 | term = terminal.New(">>> ", "... ", nil) 14 | term.LoadHistroy(historyFile) 15 | defer term.SaveHistroy(historyFile) 16 | 17 | println(`Q-language - http://qlang.io, version 1.0.00 18 | Copyright (C) 2015 Qiniu.com - Shanghai Qiniu Information Technologies Co., Ltd. 19 | Use Ctrl-D (i.e. EOF) to exit. 20 | `) 21 | 22 | for { 23 | expr, err = term.Scan() 24 | if err != nil { 25 | if err == terminal.ErrPromptAborted { 26 | continue 27 | } elif err == io.EOF { 28 | println("^D") 29 | break 30 | } 31 | fprintln(os.Stderr, err) 32 | continue 33 | } 34 | expr = strings.TrimSpace(expr) 35 | if expr == "" { 36 | continue 37 | } 38 | err = engine.Eval(expr) 39 | if err != nil { 40 | fprintln(os.Stderr, err) 41 | } else { 42 | println(ipt.ret()) 43 | } 44 | } 45 | } 46 | --------------------------------------------------------------------------------