├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── test.yaml ├── .gitignore ├── LICENSE ├── Makefile ├── README.rst ├── _glua-tests ├── base.lua ├── coroutine.lua ├── db.lua ├── goto.lua ├── issues.lua ├── math.lua ├── os.lua ├── strings.lua ├── table.lua └── vm.lua ├── _lua5.1-tests ├── README ├── all.lua ├── api.lua ├── attrib.lua ├── big.lua ├── calls.lua ├── checktable.lua ├── closure.lua ├── code.lua ├── constructs.lua ├── db.lua ├── errors.lua ├── events.lua ├── files.lua ├── gc.lua ├── libs │ ├── .gitignore │ └── P1 │ │ └── .gitignore ├── literals.lua ├── locals.lua ├── main.lua ├── math.lua ├── nextvar.lua ├── pm.lua ├── sort.lua ├── strings.lua ├── vararg.lua └── verybig.lua ├── _state.go ├── _tools └── go-inline ├── _vm.go ├── alloc.go ├── ast ├── ast.go ├── expr.go ├── misc.go ├── stmt.go └── token.go ├── auxlib.go ├── auxlib_test.go ├── baselib.go ├── baselib_test.go ├── channellib.go ├── channellib_test.go ├── cmd └── glua │ └── glua.go ├── compile.go ├── config.go ├── coroutinelib.go ├── debuglib.go ├── function.go ├── go.mod ├── go.sum ├── iolib.go ├── linit.go ├── loadlib.go ├── mathlib.go ├── opcode.go ├── oslib.go ├── oslib_test.go ├── package.go ├── parse ├── Makefile ├── lexer.go ├── parser.go └── parser.go.y ├── pm └── pm.go ├── script_test.go ├── state.go ├── state_test.go ├── stringlib.go ├── table.go ├── table_test.go ├── tablelib.go ├── testutils_test.go ├── utils.go ├── value.go └── vm.go /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | Any kind of contributions are welcome. 3 | 4 | ## Building GopherLua 5 | 6 | GopherLua uses simple inlining tool for generate efficient codes. This tool requires python interpreter. Files name of which starts with `_` genarate files name of which does not starts with `_` . For instance, `_state.go` generate `state.go` . You do not edit generated sources. 7 | To generate sources, some make target is available. 8 | 9 | ```bash 10 | make build 11 | make glua 12 | make test 13 | ``` 14 | 15 | You have to run `make build` before committing to the repository. 16 | 17 | ## Pull requests 18 | Our workflow is based on the [github-flow](https://guides.github.com/introduction/flow/>) . 19 | 20 | 1. Create a new issue. 21 | 2. Fork the project. 22 | 3. Clone your fork and add the upstream. 23 | ```bash 24 | git remote add upstream https://github.com/yuin/gopher-lua.git 25 | ``` 26 | 27 | 4. Pull new changes from the upstream. 28 | ```bash 29 | git checkout master 30 | git fetch upstream 31 | git merge upstream/master 32 | ``` 33 | 34 | 5. Create a feature branch 35 | ```bash 36 | git checkout -b 37 | ``` 38 | 39 | 6. Commit your changes and reference the issue number in your comment. 40 | ```bash 41 | git commit -m "Issue # : " 42 | ``` 43 | 44 | 7. Push the feature branch to your remote repository. 45 | ```bash 46 | git push origin 47 | ``` 48 | 49 | 8. Open new pull request. 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | You **must** post issues only here. Questions, ideas **must** be posted in [discussions](https://github.com/yuin/gopher-lua/discussions). 2 | 3 | - [ ] GopherLua is a Lua5.1 implementation. You should be familiar with Lua programming language. Have you read [Lua 5.1 reference manual](http://www.lua.org/manual/5.1/) carefully? 4 | - [ ] GopherLua is a Lua5.1 implementation. In Lua, to keep it simple, **it is more important to remove functionalities rather than to add functionalities unlike other languages** . If you are going to introduce some new cool functionalities into the GopherLua code base and the functionalities can be implemented by existing APIs, It should be implemented as a library. 5 | 6 | Please answer the following before submitting your issue: 7 | 8 | 1. What version of GopherLua are you using? : 9 | 2. What version of Go are you using? : 10 | 3. What operating system and processor architecture are you using? : 11 | 4. What did you do? : 12 | 5. What did you expect to see? : 13 | 6. What did you see instead? : 14 | 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # . 2 | 3 | Changes proposed in this pull request: 4 | 5 | - a 6 | - b 7 | - c 8 | - d 9 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: test 3 | jobs: 4 | test: 5 | strategy: 6 | fail-fast: false 7 | matrix: 8 | go-version: [1.18.x, 1.19.x] 9 | platform: [ubuntu-latest, macos-latest, windows-latest] 10 | runs-on: ${{ matrix.platform }} 11 | steps: 12 | - name: Install Go 13 | uses: actions/setup-go@v1 14 | with: 15 | go-version: ${{ matrix.go-version }} 16 | - name: Checkout code 17 | uses: actions/checkout@v1 18 | - name: Run tests 19 | run: ./_tools/go-inline *.go && go fmt . && go test -v ./... -covermode=count -coverprofile=coverage.out -coverpkg=$(go list ./... | sed 's/\n/,/g') 20 | - name: Send coverage 21 | if: "matrix.platform == 'ubuntu-latest'" 22 | env: 23 | COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | run: | 25 | GO111MODULE=off go get github.com/mattn/goveralls 26 | ./_tools/go-inline *.go && go fmt . 27 | $(go env GOPATH)/bin/goveralls -coverprofile=coverage.out -service=github 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Yusuke Inuzuka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build test glua 2 | 3 | build: 4 | ./_tools/go-inline *.go && go fmt . && go build 5 | 6 | glua: *.go pm/*.go cmd/glua/glua.go 7 | ./_tools/go-inline *.go && go fmt . && go build cmd/glua/glua.go 8 | 9 | test: 10 | ./_tools/go-inline *.go && go fmt . && go test 11 | -------------------------------------------------------------------------------- /_glua-tests/base.lua: -------------------------------------------------------------------------------- 1 | local ok, msg = pcall(function() 2 | dofile("notexist") 3 | end) 4 | assert(not ok and string.find(msg, ".*notexist.*")) 5 | 6 | local ok, msg = pcall(function() 7 | assert(getfenv(2) == _G) 8 | end) 9 | assert(ok) 10 | 11 | local i = 1 12 | local fn = assert(load(function() 13 | local tbl = {"return ", "1", "+", "1"} 14 | local v = tbl[i] 15 | i = i + 1 16 | return v 17 | end)) 18 | assert(fn() == 2) 19 | 20 | local fn, msg = load(function() 21 | return {} 22 | end) 23 | assert(not fn and string.find(msg, "must return a string")) 24 | 25 | local i = 1 26 | local fn, msg = load(function() 27 | if i == 1 then 28 | i = i + 1 29 | return "returna" 30 | end 31 | end) 32 | assert(not fn and string.find(string.lower(msg), "eof")) 33 | 34 | local ok, a, b = xpcall(function() 35 | return "a", "b" 36 | end, 37 | function(err) 38 | assert(nil) 39 | end) 40 | assert(ok and a == "a" and b == "b") 41 | 42 | local ok, a, b = xpcall(function() 43 | error("error!") 44 | end, 45 | function(err) 46 | return err .. "!", "b" 47 | end) 48 | assert(not ok and string.find(a, "error!!") and b == nil) 49 | -------------------------------------------------------------------------------- /_glua-tests/coroutine.lua: -------------------------------------------------------------------------------- 1 | co = coroutine.wrap(function() 2 | co() 3 | end) 4 | 5 | local ok, msg = pcall(function() 6 | co() 7 | end) 8 | assert(not ok and string.find(msg, "can not resume a running thread")) 9 | 10 | co = coroutine.wrap(function() 11 | return 1 12 | end) 13 | assert(co() == 1) 14 | local ok, msg = pcall(function() 15 | co() 16 | end) 17 | assert(not ok and string.find(msg, "can not resume a dead thread")) 18 | -------------------------------------------------------------------------------- /_glua-tests/db.lua: -------------------------------------------------------------------------------- 1 | -- debug lib tests 2 | -- debug stuff are partially implemented; hooks are not supported. 3 | 4 | local function f1() 5 | end 6 | local env = {} 7 | local mt = {} 8 | debug.setfenv(f1, env) 9 | assert(debug.getfenv(f1) == env) 10 | debug.setmetatable(f1, mt) 11 | assert(debug.getmetatable(f1) == mt) 12 | 13 | local function f2() 14 | local info = debug.getinfo(1, "Slunf") 15 | assert(info.currentline == 14) 16 | assert(info.linedefined == 13) 17 | assert(info.func == f2) 18 | assert(info.lastlinedefined == 25) 19 | assert(info.nups == 1) 20 | assert(info.name == "f2") 21 | assert(info.what == "Lua") 22 | if string.find(_VERSION, "GopherLua") then 23 | assert(info.source == "db.lua") 24 | end 25 | end 26 | f2() 27 | 28 | local function f3() 29 | end 30 | local info = debug.getinfo(f3) 31 | assert(info.currentline == -1) 32 | assert(info.linedefined == 28) 33 | assert(info.func == f3) 34 | assert(info.lastlinedefined == 29) 35 | assert(info.nups == 0) 36 | assert(info.name == nil) 37 | assert(info.what == "Lua") 38 | if string.find(_VERSION, "GopherLua") then 39 | assert(info.source == "db.lua") 40 | end 41 | 42 | local function f4() 43 | local a,b,c = 1,2,3 44 | local function f5() 45 | local name, value = debug.getlocal(2, 2) 46 | assert(debug.getlocal(2, 10) == nil) 47 | assert(name == "b") 48 | assert(value == 2) 49 | name = debug.setlocal(2, 2, 10) 50 | assert(debug.setlocal(2, 10, 10) == nil) 51 | assert(name == "b") 52 | 53 | local d = a 54 | local e = c 55 | 56 | local tb = debug.traceback("--msg--") 57 | assert(string.find(tb, "\\-\\-msg\\-\\-")) 58 | assert(string.find(tb, "in.*f5")) 59 | assert(string.find(tb, "in.*f4")) 60 | end 61 | f5() 62 | local name, value = debug.getupvalue(f5, 1) 63 | assert(debug.getupvalue(f5, 10) == nil) 64 | assert(name == "a") 65 | assert(value == 1) 66 | name = debug.setupvalue(f5, 1, 11) 67 | assert(debug.setupvalue(f5, 10, 11) == nil) 68 | assert(name == "a") 69 | assert(a == 11) 70 | 71 | assert(b == 10) -- changed by debug.setlocal in f4 72 | end 73 | f4() 74 | 75 | local ok, msg = pcall(function() 76 | debug.getlocal(10, 1) 77 | end) 78 | assert(not ok and string.find(msg, "level out of range")) 79 | 80 | local ok, msg = pcall(function() 81 | debug.setlocal(10, 1, 1) 82 | end) 83 | assert(not ok and string.find(msg, "level out of range")) 84 | 85 | assert(debug.getinfo(100) == nil) 86 | assert(debug.getinfo(1, "a") == nil) 87 | -------------------------------------------------------------------------------- /_glua-tests/goto.lua: -------------------------------------------------------------------------------- 1 | local function errmsg (code, m) 2 | local st, msg = loadstring(code) 3 | assert(not st and string.find(msg, m)) 4 | end 5 | 6 | -- cannot see label inside block 7 | errmsg([[ goto l1; do ::l1:: end ]], "label 'l1'") 8 | errmsg([[ do ::l1:: end goto l1; ]], "label 'l1'") 9 | 10 | -- repeated label 11 | errmsg([[ ::l1:: ::l1:: ]], "label 'l1'") 12 | 13 | 14 | -- undefined label 15 | errmsg([[ goto l1; local aa ::l1:: ::l2:: print(3) ]], "local 'aa'") 16 | 17 | -- jumping over variable definition 18 | errmsg([[ 19 | do local bb, cc; goto l1; end 20 | local aa 21 | ::l1:: print(3) 22 | ]], "local 'aa'") 23 | 24 | -- jumping into a block 25 | errmsg([[ do ::l1:: end goto l1 ]], "label 'l1'") 26 | errmsg([[ goto l1 do ::l1:: end ]], "label 'l1'") 27 | 28 | -- cannot continue a repeat-until with variables 29 | errmsg([[ 30 | repeat 31 | if x then goto cont end 32 | local xuxu = 10 33 | ::cont:: 34 | until xuxu < x 35 | ]], "local 'xuxu'") 36 | 37 | -- simple gotos 38 | local x 39 | do 40 | local y = 12 41 | goto l1 42 | ::l2:: x = x + 1; goto l3 43 | ::l1:: x = y; goto l2 44 | end 45 | ::l3:: ::l3_1:: assert(x == 13) 46 | 47 | 48 | -- long labels 49 | do 50 | local prog = [[ 51 | do 52 | local a = 1 53 | goto l%sa; a = a + 1 54 | ::l%sa:: a = a + 10 55 | goto l%sb; a = a + 2 56 | ::l%sb:: a = a + 20 57 | return a 58 | end 59 | ]] 60 | local label = string.rep("0123456789", 40) 61 | prog = string.format(prog, label, label, label, label) 62 | assert(assert(loadstring(prog))() == 31) 63 | end 64 | 65 | -- goto to correct label when nested 66 | do goto l3; ::l3:: end -- does not loop jumping to previous label 'l3' 67 | 68 | -- ok to jump over local dec. to end of block 69 | do 70 | goto l1 71 | local a = 23 72 | x = a 73 | ::l1::; 74 | end 75 | 76 | while true do 77 | goto l4 78 | goto l1 -- ok to jump over local dec. to end of block 79 | goto l1 -- multiple uses of same label 80 | local x = 45 81 | ::l1:: ;;; 82 | end 83 | ::l4:: assert(x == 13) 84 | 85 | if print then 86 | goto l1 -- ok to jump over local dec. to end of block 87 | error("should not be here") 88 | goto l2 -- ok to jump over local dec. to end of block 89 | local x 90 | ::l1:: ; ::l2:: ;; 91 | else end 92 | 93 | -- to repeat a label in a different function is OK 94 | local function foo () 95 | local a = {} 96 | goto l3 97 | ::l1:: a[#a + 1] = 1; goto l2; 98 | ::l2:: a[#a + 1] = 2; goto l5; 99 | ::l3:: 100 | ::l3a:: a[#a + 1] = 3; goto l1; 101 | ::l4:: a[#a + 1] = 4; goto l6; 102 | ::l5:: a[#a + 1] = 5; goto l4; 103 | ::l6:: assert(a[1] == 3 and a[2] == 1 and a[3] == 2 and 104 | a[4] == 5 and a[5] == 4) 105 | if not a[6] then a[6] = true; goto l3a end -- do it twice 106 | end 107 | 108 | ::l6:: foo() 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- 113 | -- testing closing of upvalues 114 | 115 | local function foo () 116 | local a = {} 117 | do 118 | local i = 1 119 | local k = 0 120 | a[0] = function (y) k = y end 121 | ::l1:: do 122 | local x 123 | if i > 2 then goto l2 end 124 | a[i] = function (y) if y then x = y else return x + k end end 125 | i = i + 1 126 | goto l1 127 | end 128 | end 129 | ::l2:: return a 130 | end 131 | 132 | local a = foo() 133 | a[1](10); a[2](20) 134 | assert(a[1]() == 10 and a[2]() == 20 and a[3] == nil) 135 | a[0](13) 136 | assert(a[1]() == 23 and a[2]() == 33) 137 | 138 | -------------------------------------------------------------------------------- 139 | -- testing if x goto optimizations 140 | 141 | local function testG (a) 142 | if a == 1 then 143 | goto l1 144 | error("should never be here!") 145 | elseif a == 2 then goto l2 146 | elseif a == 3 then goto l3 147 | elseif a == 4 then 148 | goto l1 -- go to inside the block 149 | error("should never be here!") 150 | ::l1:: a = a + 1 -- must go to 'if' end 151 | else 152 | goto l4 153 | ::l4a:: a = a * 2; goto l4b 154 | error("should never be here!") 155 | ::l4:: goto l4a 156 | error("should never be here!") 157 | ::l4b:: 158 | end 159 | do return a end 160 | ::l2:: do return "2" end 161 | ::l3:: do return "3" end 162 | ::l1:: return "1" 163 | end 164 | 165 | assert(testG(1) == "1") 166 | assert(testG(2) == "2") 167 | assert(testG(3) == "3") 168 | assert(testG(4) == 5) 169 | assert(testG(5) == 10) 170 | -------------------------------------------------------------------------------- 171 | 172 | 173 | print'OK' 174 | -------------------------------------------------------------------------------- /_glua-tests/math.lua: -------------------------------------------------------------------------------- 1 | assert(math.fmod(13.5, 2) == 1.5) 2 | assert(math.pow(7, 2) == 49) 3 | 4 | local ok, msg = pcall(function() 5 | math.max() 6 | end) 7 | assert(not ok and string.find(msg, "wrong number of arguments")) 8 | 9 | local ok, msg = pcall(function() 10 | math.min() 11 | end) 12 | assert(not ok and string.find(msg, "wrong number of arguments")) 13 | -------------------------------------------------------------------------------- /_glua-tests/os.lua: -------------------------------------------------------------------------------- 1 | local osname = "linux" 2 | if string.find(os.getenv("OS") or "", "Windows") then 3 | osname = "windows" 4 | end 5 | 6 | if osname == "linux" then 7 | -- travis ci failed to start date command? 8 | -- assert(os.execute("date") == 0) 9 | assert(os.execute("date -a") == 1) 10 | else 11 | assert(os.execute("date /T") == 0) 12 | assert(os.execute("md") == 1) 13 | end 14 | 15 | assert(os.getenv("PATH") ~= "") 16 | assert(os.getenv("_____GLUATEST______") == nil) 17 | assert(os.setenv("_____GLUATEST______", "1")) 18 | assert(os.getenv("_____GLUATEST______") == "1") 19 | -------------------------------------------------------------------------------- /_glua-tests/strings.lua: -------------------------------------------------------------------------------- 1 | 2 | local ok, msg = pcall(function() 3 | string.dump() 4 | end) 5 | assert(not ok and string.find(msg, "GopherLua does not support the string.dump")) 6 | assert(string.find("","aaa") == nil) 7 | assert(string.gsub("hello world", "(%w+)", "%1 %1 %c") == "hello hello %c world world %c") 8 | 9 | local ret1, ret2, ret3, ret4 = string.find("aaa bbb", "(%w+())") 10 | assert(ret1 == 1) 11 | assert(ret2 == 3) 12 | assert(ret3 == "aaa") 13 | assert(ret4 == 4) 14 | -------------------------------------------------------------------------------- /_glua-tests/table.lua: -------------------------------------------------------------------------------- 1 | local a = {} 2 | assert(table.maxn(a) == 0) 3 | a["key"] = 1 4 | assert(table.maxn(a) == 0) 5 | table.insert(a, 10) 6 | table.insert(a, 3, 10) 7 | assert(table.maxn(a) == 3) 8 | 9 | local ok, msg = pcall(function() 10 | table.insert(a) 11 | end) 12 | assert(not ok and string.find(msg, "wrong number of arguments")) 13 | 14 | a = {} 15 | a["key0"] = "0" 16 | a["key1"] = "1" 17 | a[1] = 1 18 | a[2] = 2 19 | a[true] = "true" 20 | a[false] = "false" 21 | for k, v in pairs(a) do 22 | if k == "key0" then 23 | assert(v == "0") 24 | elseif k == "key1" then 25 | assert(v == "1") 26 | elseif k == 1 then 27 | assert(v == 1) 28 | elseif k == 2 then 29 | assert(v == 2) 30 | elseif k == true then 31 | assert(v == "true") 32 | elseif k == false then 33 | assert(v == "false") 34 | else 35 | error("unexpected key:" .. tostring(k)) 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /_glua-tests/vm.lua: -------------------------------------------------------------------------------- 1 | for i, v in ipairs({"hoge", {}, function() end, true, nil}) do 2 | local ok, msg = pcall(function() 3 | print(-v) 4 | end) 5 | assert(not ok and string.find(msg, "__unm undefined")) 6 | end 7 | 8 | assert(#"abc" == 3) 9 | local tbl = {1,2,3} 10 | setmetatable(tbl, {__len = function(self) 11 | return 10 12 | end}) 13 | assert(#tbl == 10) 14 | 15 | setmetatable(tbl, nil) 16 | assert(#tbl == 3) 17 | 18 | local ok, msg = pcall(function() 19 | return 1 < "hoge" 20 | end) 21 | assert(not ok and string.find(msg, "attempt to compare number with string")) 22 | 23 | local ok, msg = pcall(function() 24 | return {} < (function() end) 25 | end) 26 | assert(not ok and string.find(msg, "attempt to compare table with function")) 27 | 28 | local ok, msg = pcall(function() 29 | for n = nil,1 do 30 | print(1) 31 | end 32 | end) 33 | assert(not ok and string.find(msg, "for statement init must be a number")) 34 | 35 | local ok, msg = pcall(function() 36 | for n = 1,nil do 37 | print(1) 38 | end 39 | end) 40 | assert(not ok and string.find(msg, "for statement limit must be a number")) 41 | 42 | local ok, msg = pcall(function() 43 | for n = 1,10,nil do 44 | print(1) 45 | end 46 | end) 47 | assert(not ok and string.find(msg, "for statement step must be a number")) 48 | 49 | local ok, msg = pcall(function() 50 | return {} + (function() end) 51 | end) 52 | assert(not ok and string.find(msg, "cannot perform add operation between table and function")) 53 | 54 | local ok, msg = pcall(function() 55 | return {} .. (function() end) 56 | end) 57 | assert(not ok and string.find(msg, "cannot perform concat operation between table and function")) 58 | 59 | -- test table with initial elements over 511 60 | local bigtable = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 61 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 62 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 63 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 64 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 65 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 66 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 67 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 68 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 69 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 70 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 71 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 72 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 73 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 74 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 75 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 76 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 77 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 78 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 79 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 80 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 81 | 1,1,1,1,1,1,1,1,1,1,1,1,10} 82 | assert(bigtable[601] == 10) 83 | 84 | local ok, msg = loadstring([[ 85 | function main( 86 | a1, a2, a3, a4, a5, a6, a7, a8, 87 | a9, a10, a11, a12, a13, a14, a15, a16, 88 | a17, a18, a19, a20, a21, a22, a23, a24, 89 | a25, a26, a27, a28, a29, a30, a31, a32, 90 | a33, a34, a35, a36, a37, a38, a39, a40, 91 | a41, a42, a43, a44, a45, a46, a47, a48, 92 | a49, a50, a51, a52, a53, a54, a55, a56, 93 | a57, a58, a59, a60, a61, a62, a63, a64, 94 | a65, a66, a67, a68, a69, a70, a71, a72, 95 | a73, a74, a75, a76, a77, a78, a79, a80, 96 | a81, a82, a83, a84, a85, a86, a87, a88, 97 | a89, a90, a91, a92, a93, a94, a95, a96, 98 | a97, a98, a99, a100, a101, a102, a103, 99 | a104, a105, a106, a107, a108, a109, a110, 100 | a111, a112, a113, a114, a115, a116, a117, 101 | a118, a119, a120, a121, a122, a123, a124, 102 | a125, a126, a127, a128, a129, a130, a131, 103 | a132, a133, a134, a135, a136, a137, a138, 104 | a139, a140, a141, a142, a143, a144, a145, 105 | a146, a147, a148, a149, a150, a151, a152, 106 | a153, a154, a155, a156, a157, a158, a159, 107 | a160, a161, a162, a163, a164, a165, a166, 108 | a167, a168, a169, a170, a171, a172, a173, 109 | a174, a175, a176, a177, a178, a179, a180, 110 | a181, a182, a183, a184, a185, a186, a187, 111 | a188, a189, a190, a191, a192, a193, a194, 112 | a195, a196, a197, a198, a199, a200, a201, 113 | a202, a203, a204, a205, a206, a207, a208, 114 | a209, a210, a211, a212, a213, a214, a215, 115 | a216, a217, a218, a219, a220, a221, a222, 116 | a223, a224, a225, a226, a227, a228, a229, 117 | a230, a231, a232, a233, a234, a235, a236, 118 | a237, a238, a239, a240, a241, a242, a243, 119 | a244, a245, a246, a247, a248, a249, a250, 120 | a251, a252, a253, a254, a255, a256, a257, 121 | a258, a259, a260, a261, a262, a263, a264, 122 | a265, a266, a267, a268, a269, a270, a271, 123 | a272, a273, a274, a275, a276, a277, a278, 124 | a279, a280, a281, a282, a283, a284, a285, 125 | a286, a287, a288, a289, a290, a291, a292, 126 | a293, a294, a295, a296, a297, a298, a299, 127 | a300, a301, a302) end 128 | ]]) 129 | assert(not ok and string.find(msg, "register overflow")) 130 | 131 | local ok, msg = loadstring([[ 132 | function main() 133 | local a = {...} 134 | end 135 | ]]) 136 | assert(not ok and string.find(msg, "cannot use '...' outside a vararg function")) 137 | -------------------------------------------------------------------------------- /_lua5.1-tests/README: -------------------------------------------------------------------------------- 1 | This tarball contains the official test scripts for Lua 5.1. 2 | Unlike Lua itself, these tests do not aim portability, small footprint, 3 | or easy of use. (Their main goal is to try to crash Lua.) They are not 4 | intended for general use. You are wellcome to use them, but expect to 5 | have to "dirt your hands". 6 | 7 | The tarball should expand in the following contents: 8 | - several .lua scripts with the tests 9 | - a main "all.lua" Lua script that invokes all the other scripts 10 | - a subdirectory "libs" with an empty subdirectory "libs/P1", 11 | to be used by the scripts 12 | - a subdirectory "etc" with some extra files 13 | 14 | To run the tests, do as follows: 15 | 16 | - go to the test directory 17 | 18 | - set LUA_PATH to "?;./?.lua" (or, better yet, set LUA_PATH to "./?.lua;;" 19 | and LUA_INIT to "package.path = '?;'..package.path") 20 | 21 | - run "lua all.lua" 22 | 23 | 24 | -------------------------------------------- 25 | Internal tests 26 | -------------------------------------------- 27 | 28 | Some tests need a special library, "testC", that gives access to 29 | several internal structures in Lua. 30 | This library is only available when Lua is compiled in debug mode. 31 | The scripts automatically detect its absence and skip those tests. 32 | 33 | If you want to run these tests, move etc/ltests.c and etc/ltests.h to 34 | the directory with the source Lua files, and recompile Lua with 35 | the option -DLUA_USER_H='"ltests.h"' (or its equivalent to define 36 | LUA_USER_H as the string "ltests.h", including the quotes). This 37 | option not only adds the testC library, but it adds several other 38 | internal tests as well. After the recompilation, run the tests 39 | as before. 40 | 41 | 42 | -------------------------------------------------------------------------------- /_lua5.1-tests/all.lua: -------------------------------------------------------------------------------- 1 | #!../lua 2 | 3 | math.randomseed(0) 4 | 5 | collectgarbage("setstepmul", 180) 6 | collectgarbage("setpause", 190) 7 | 8 | 9 | --[=[ 10 | example of a long [comment], 11 | [[spanning several [lines]]] 12 | 13 | ]=] 14 | 15 | print("current path:\n " .. string.gsub(package.path, ";", "\n ")) 16 | 17 | 18 | local msgs = {} 19 | function Message (m) 20 | print(m) 21 | msgs[#msgs+1] = string.sub(m, 3, -3) 22 | end 23 | 24 | 25 | local c = os.clock() 26 | 27 | assert(os.setlocale"C") 28 | 29 | local T,print,gcinfo,format,write,assert,type = 30 | T,print,gcinfo,string.format,io.write,assert,type 31 | 32 | local function formatmem (m) 33 | if m < 1024 then return m 34 | else 35 | m = m/1024 - m/1024%1 36 | if m < 1024 then return m.."K" 37 | else 38 | m = m/1024 - m/1024%1 39 | return m.."M" 40 | end 41 | end 42 | end 43 | 44 | local showmem = function () 45 | if not T then 46 | print(format(" ---- total memory: %s ----\n", formatmem(gcinfo()))) 47 | else 48 | T.checkmemory() 49 | local a,b,c = T.totalmem() 50 | local d,e = gcinfo() 51 | print(format( 52 | "\n ---- total memory: %s (%dK), max use: %s, blocks: %d\n", 53 | formatmem(a), d, formatmem(c), b)) 54 | end 55 | end 56 | 57 | 58 | -- 59 | -- redefine dofile to run files through dump/undump 60 | -- 61 | dofile = function (n) 62 | showmem() 63 | local f = assert(loadfile(n)) 64 | local b = string.dump(f) 65 | f = assert(loadstring(b)) 66 | return f() 67 | end 68 | 69 | dofile('main.lua') 70 | 71 | do 72 | local u = newproxy(true) 73 | local newproxy, stderr = newproxy, io.stderr 74 | getmetatable(u).__gc = function (o) 75 | stderr:write'.' 76 | newproxy(o) 77 | end 78 | end 79 | 80 | local f = assert(loadfile('gc.lua')) 81 | f() 82 | dofile('db.lua') 83 | assert(dofile('calls.lua') == deep and deep) 84 | dofile('strings.lua') 85 | dofile('literals.lua') 86 | assert(dofile('attrib.lua') == 27) 87 | assert(dofile('locals.lua') == 5) 88 | dofile('constructs.lua') 89 | dofile('code.lua') 90 | do 91 | local f = coroutine.wrap(assert(loadfile('big.lua'))) 92 | assert(f() == 'b') 93 | assert(f() == 'a') 94 | end 95 | dofile('nextvar.lua') 96 | dofile('pm.lua') 97 | dofile('api.lua') 98 | assert(dofile('events.lua') == 12) 99 | dofile('vararg.lua') 100 | dofile('closure.lua') 101 | dofile('errors.lua') 102 | dofile('math.lua') 103 | dofile('sort.lua') 104 | assert(dofile('verybig.lua') == 10); collectgarbage() 105 | dofile('files.lua') 106 | 107 | if #msgs > 0 then 108 | print("\ntests not performed:") 109 | for i=1,#msgs do 110 | print(msgs[i]) 111 | end 112 | print() 113 | end 114 | 115 | print("final OK !!!") 116 | print('cleaning all!!!!') 117 | 118 | debug.sethook(function (a) assert(type(a) == 'string') end, "cr") 119 | 120 | local _G, collectgarbage, showmem, print, format, clock = 121 | _G, collectgarbage, showmem, print, format, os.clock 122 | 123 | local a={} 124 | for n in pairs(_G) do a[n] = 1 end 125 | a.tostring = nil 126 | a.___Glob = nil 127 | for n in pairs(a) do _G[n] = nil end 128 | 129 | a = nil 130 | collectgarbage() 131 | collectgarbage() 132 | collectgarbage() 133 | collectgarbage() 134 | collectgarbage() 135 | collectgarbage();showmem() 136 | 137 | print(format("\n\ntotal time: %.2f\n", clock()-c)) 138 | -------------------------------------------------------------------------------- /_lua5.1-tests/attrib.lua: -------------------------------------------------------------------------------- 1 | do --[ 2 | 3 | print "testing require" 4 | 5 | assert(require"string" == string) 6 | assert(require"math" == math) 7 | assert(require"table" == table) 8 | assert(require"io" == io) 9 | assert(require"os" == os) 10 | assert(require"debug" == debug) 11 | assert(require"coroutine" == coroutine) 12 | 13 | assert(type(package.path) == "string") 14 | assert(type(package.cpath) == "string") 15 | assert(type(package.loaded) == "table") 16 | assert(type(package.preload) == "table") 17 | 18 | 19 | local DIR = "libs/" 20 | 21 | local function createfiles (files, preextras, posextras) 22 | for n,c in pairs(files) do 23 | io.output(DIR..n) 24 | io.write(string.format(preextras, n)) 25 | io.write(c) 26 | io.write(string.format(posextras, n)) 27 | io.close(io.output()) 28 | end 29 | end 30 | 31 | function removefiles (files) 32 | for n in pairs(files) do 33 | os.remove(DIR..n) 34 | end 35 | end 36 | 37 | local files = { 38 | ["A.lua"] = "", 39 | ["B.lua"] = "assert(...=='B');require 'A'", 40 | ["A.lc"] = "", 41 | ["A"] = "", 42 | ["L"] = "", 43 | ["XXxX"] = "", 44 | ["C.lua"] = "package.loaded[...] = 25; require'C'" 45 | } 46 | 47 | AA = nil 48 | local extras = [[ 49 | NAME = '%s' 50 | REQUIRED = ... 51 | return AA]] 52 | 53 | createfiles(files, "", extras) 54 | 55 | 56 | local oldpath = package.path 57 | 58 | package.path = string.gsub("D/?.lua;D/?.lc;D/?;D/??x?;D/L", "D/", DIR) 59 | 60 | local try = function (p, n, r) 61 | NAME = nil 62 | local rr = require(p) 63 | assert(NAME == n) 64 | assert(REQUIRED == p) 65 | assert(rr == r) 66 | end 67 | 68 | assert(require"C" == 25) 69 | assert(require"C" == 25) 70 | AA = nil 71 | try('B', 'B.lua', true) 72 | assert(package.loaded.B) 73 | assert(require"B" == true) 74 | assert(package.loaded.A) 75 | package.loaded.A = nil 76 | try('B', nil, true) -- should not reload package 77 | try('A', 'A.lua', true) 78 | package.loaded.A = nil 79 | os.remove(DIR..'A.lua') 80 | AA = {} 81 | try('A', 'A.lc', AA) -- now must find second option 82 | assert(require("A") == AA) 83 | AA = false 84 | try('K', 'L', false) -- default option 85 | try('K', 'L', false) -- default option (should reload it) 86 | assert(rawget(_G, "_REQUIREDNAME") == nil) 87 | 88 | AA = "x" 89 | try("X", "XXxX", AA) 90 | 91 | 92 | removefiles(files) 93 | 94 | 95 | -- testing require of sub-packages 96 | 97 | package.path = string.gsub("D/?.lua;D/?/init.lua", "D/", DIR) 98 | 99 | files = { 100 | ["P1/init.lua"] = "AA = 10", 101 | ["P1/xuxu.lua"] = "AA = 20", 102 | } 103 | 104 | createfiles(files, "module(..., package.seeall)\n", "") 105 | AA = 0 106 | 107 | local m = assert(require"P1") 108 | assert(m == P1 and m._NAME == "P1" and AA == 0 and m.AA == 10) 109 | assert(require"P1" == P1 and P1 == m) 110 | assert(require"P1" == P1) 111 | assert(P1._PACKAGE == "") 112 | 113 | local m = assert(require"P1.xuxu") 114 | assert(m == P1.xuxu and m._NAME == "P1.xuxu" and AA == 0 and m.AA == 20) 115 | assert(require"P1.xuxu" == P1.xuxu and P1.xuxu == m) 116 | assert(require"P1.xuxu" == P1.xuxu) 117 | assert(require"P1" == P1) 118 | assert(P1.xuxu._PACKAGE == "P1.") 119 | assert(P1.AA == 10 and P1._PACKAGE == "") 120 | assert(P1._G == _G and P1.xuxu._G == _G) 121 | 122 | 123 | 124 | removefiles(files) 125 | 126 | 127 | package.path = "" 128 | assert(not pcall(require, "file_does_not_exist")) 129 | package.path = "??\0?" 130 | assert(not pcall(require, "file_does_not_exist1")) 131 | 132 | package.path = oldpath 133 | 134 | -- check 'require' error message 135 | -- local fname = "file_does_not_exist2" 136 | -- local m, err = pcall(require, fname) 137 | -- for t in string.gmatch(package.path..";"..package.cpath, "[^;]+") do 138 | -- t = string.gsub(t, "?", fname) 139 | -- print(t, err) 140 | -- assert(string.find(err, t, 1, true)) 141 | -- end 142 | 143 | 144 | local function import(...) 145 | local f = {...} 146 | return function (m) 147 | for i=1, #f do m[f[i]] = _G[f[i]] end 148 | end 149 | end 150 | 151 | local assert, module, package = assert, module, package 152 | X = nil; x = 0; assert(_G.x == 0) -- `x' must be a global variable 153 | module"X"; x = 1; assert(_M.x == 1) 154 | module"X.a.b.c"; x = 2; assert(_M.x == 2) 155 | module("X.a.b", package.seeall); x = 3 156 | assert(X._NAME == "X" and X.a.b.c._NAME == "X.a.b.c" and X.a.b._NAME == "X.a.b") 157 | assert(X._M == X and X.a.b.c._M == X.a.b.c and X.a.b._M == X.a.b) 158 | assert(X.x == 1 and X.a.b.c.x == 2 and X.a.b.x == 3) 159 | assert(X._PACKAGE == "" and X.a.b.c._PACKAGE == "X.a.b." and 160 | X.a.b._PACKAGE == "X.a.") 161 | assert(_PACKAGE.."c" == "X.a.c") 162 | assert(X.a._NAME == nil and X.a._M == nil) 163 | module("X.a", import("X")) ; x = 4 164 | assert(X.a._NAME == "X.a" and X.a.x == 4 and X.a._M == X.a) 165 | module("X.a.b", package.seeall); assert(x == 3); x = 5 166 | assert(_NAME == "X.a.b" and X.a.b.x == 5) 167 | 168 | assert(X._G == nil and X.a._G == nil and X.a.b._G == _G and X.a.b.c._G == nil) 169 | 170 | setfenv(1, _G) 171 | assert(x == 0) 172 | 173 | assert(not pcall(module, "x")) 174 | assert(not pcall(module, "math.sin")) 175 | 176 | 177 | -- testing C libraries 178 | 179 | 180 | local p = "" -- On Mac OS X, redefine this to "_" 181 | 182 | -- assert(loadlib == package.loadlib) -- only for compatibility 183 | -- local f, err, when = package.loadlib("libs/lib1.so", p.."luaopen_lib1") 184 | local f = nil 185 | if not f then 186 | (Message or print)('\a\n >>> cannot load dynamic library <<<\n\a') 187 | print(err, when) 188 | else 189 | f() -- open library 190 | assert(require("lib1") == lib1) 191 | collectgarbage() 192 | assert(lib1.id("x") == "x") 193 | f = assert(package.loadlib("libs/lib1.so", p.."anotherfunc")) 194 | assert(f(10, 20) == "1020\n") 195 | f, err, when = package.loadlib("libs/lib1.so", p.."xuxu") 196 | assert(not f and type(err) == "string" and when == "init") 197 | package.cpath = "libs/?.so" 198 | require"lib2" 199 | assert(lib2.id("x") == "x") 200 | local fs = require"lib1.sub" 201 | assert(fs == lib1.sub and next(lib1.sub) == nil) 202 | module("lib2", package.seeall) 203 | f = require"-lib2" 204 | assert(f.id("x") == "x" and _M == f and _NAME == "lib2") 205 | module("lib1.sub", package.seeall) 206 | assert(_M == fs) 207 | setfenv(1, _G) 208 | 209 | end 210 | -- f, err, when = package.loadlib("donotexist", p.."xuxu") 211 | -- assert(not f and type(err) == "string" and (when == "open" or when == "absent")) 212 | 213 | 214 | -- testing preload 215 | 216 | do 217 | local p = package 218 | package = {} 219 | p.preload.pl = function (...) 220 | module(...) 221 | function xuxu (x) return x+20 end 222 | end 223 | 224 | require"pl" 225 | assert(require"pl" == pl) 226 | assert(pl.xuxu(10) == 30) 227 | 228 | package = p 229 | assert(type(package.path) == "string") 230 | end 231 | 232 | 233 | 234 | end --] 235 | 236 | print('+') 237 | 238 | print("testing assignments, logical operators, and constructors") 239 | 240 | local res, res2 = 27 241 | 242 | a, b = 1, 2+3 243 | assert(a==1 and b==5) 244 | a={} 245 | function f() return 10, 11, 12 end 246 | a.x, b, a[1] = 1, 2, f() 247 | assert(a.x==1 and b==2 and a[1]==10) 248 | a[f()], b, a[f()+3] = f(), a, 'x' 249 | assert(a[10] == 10 and b == a and a[13] == 'x') 250 | 251 | do 252 | local f = function (n) local x = {}; for i=1,n do x[i]=i end; 253 | return unpack(x) end; 254 | local a,b,c 255 | a,b = 0, f(1) 256 | assert(a == 0 and b == 1) 257 | A,b = 0, f(1) 258 | assert(A == 0 and b == 1) 259 | a,b,c = 0,5,f(4) 260 | assert(a==0 and b==5 and c==1) 261 | a,b,c = 0,5,f(0) 262 | assert(a==0 and b==5 and c==nil) 263 | end 264 | 265 | 266 | a, b, c, d = 1 and nil, 1 or nil, (1 and (nil or 1)), 6 267 | assert(not a and b and c and d==6) 268 | 269 | d = 20 270 | a, b, c, d = f() 271 | assert(a==10 and b==11 and c==12 and d==nil) 272 | a,b = f(), 1, 2, 3, f() 273 | assert(a==10 and b==1) 274 | 275 | assert(ab == true) 276 | assert((10 and 2) == 2) 277 | assert((10 or 2) == 10) 278 | assert((10 or assert(nil)) == 10) 279 | assert(not (nil and assert(nil))) 280 | assert((nil or "alo") == "alo") 281 | assert((nil and 10) == nil) 282 | assert((false and 10) == false) 283 | assert((true or 10) == true) 284 | assert((false or 10) == 10) 285 | assert(false ~= nil) 286 | assert(nil ~= false) 287 | assert(not nil == true) 288 | assert(not not nil == false) 289 | assert(not not 1 == true) 290 | assert(not not a == true) 291 | assert(not not (6 or nil) == true) 292 | assert(not not (nil and 56) == false) 293 | assert(not not (nil and true) == false) 294 | print('+') 295 | 296 | a = {} 297 | a[true] = 20 298 | a[false] = 10 299 | assert(a[1<2] == 20 and a[1>2] == 10) 300 | 301 | function f(a) return a end 302 | 303 | local a = {} 304 | for i=3000,-3000,-1 do a[i] = i; end 305 | a[10e30] = "alo"; a[true] = 10; a[false] = 20 306 | assert(a[10e30] == 'alo' and a[not 1] == 20 and a[10<20] == 10) 307 | for i=3000,-3000,-1 do assert(a[i] == i); end 308 | a[print] = assert 309 | a[f] = print 310 | a[a] = a 311 | assert(a[a][a][a][a][print] == assert) 312 | a[print](a[a[f]] == a[print]) 313 | a = nil 314 | 315 | a = {10,9,8,7,6,5,4,3,2; [-3]='a', [f]=print, a='a', b='ab'} 316 | a, a.x, a.y = a, a[-3] 317 | assert(a[1]==10 and a[-3]==a.a and a[f]==print and a.x=='a' and not a.y) 318 | a[1], f(a)[2], b, c = {['alo']=assert}, 10, a[1], a[f], 6, 10, 23, f(a), 2 319 | a[1].alo(a[2]==10 and b==10 and c==print) 320 | 321 | a[2^31] = 10; a[2^31+1] = 11; a[-2^31] = 12; 322 | a[2^32] = 13; a[-2^32] = 14; a[2^32+1] = 15; a[10^33] = 16; 323 | 324 | assert(a[2^31] == 10 and a[2^31+1] == 11 and a[-2^31] == 12 and 325 | a[2^32] == 13 and a[-2^32] == 14 and a[2^32+1] == 15 and 326 | a[10^33] == 16) 327 | 328 | a = nil 329 | 330 | 331 | -- do 332 | -- local a,i,j,b 333 | -- a = {'a', 'b'}; i=1; j=2; b=a 334 | -- i, a[i], a, j, a[j], a[i+j] = j, i, i, b, j, i 335 | -- assert(i == 2 and b[1] == 1 and a == 1 and j == b and b[2] == 2 and 336 | -- b[3] == 1) 337 | -- end 338 | 339 | print('OK') 340 | 341 | return res 342 | -------------------------------------------------------------------------------- /_lua5.1-tests/big.lua: -------------------------------------------------------------------------------- 1 | print "testing string length overflow" 2 | 3 | local longs = string.rep("\0", 2^25) 4 | local function catter (i) 5 | return assert(loadstring( 6 | string.format("return function(a) return a%s end", 7 | string.rep("..a", i-1))))() 8 | end 9 | rep129 = catter(129) 10 | local a, b = pcall(rep129, longs) 11 | print(b) 12 | assert(not a and string.find(b, "overflow")) 13 | print('+') 14 | 15 | 16 | require "checktable" 17 | 18 | --[[ lots of empty lines (to force SETLINEW) 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | --]] 223 | 224 | 225 | a,b = nil,nil 226 | while not b do 227 | if a then 228 | b = { -- lots of strings (to force JMPW and PUSHCONSTANTW) 229 | "n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", 230 | "n11", "n12", "j301", "j302", "j303", "j304", "j305", "j306", "j307", "j308", 231 | "j309", "a310", "n311", "n312", "n313", "n314", "n315", "n316", "n317", "n318", 232 | "n319", "n320", "n321", "n322", "n323", "n324", "n325", "n326", "n327", "n328", 233 | "a329", "n330", "n331", "n332", "n333", "n334", "n335", "n336", "n337", "n338", 234 | "n339", "n340", "n341", "z342", "n343", "n344", "n345", "n346", "n347", "n348", 235 | "n349", "n350", "n351", "n352", "r353", "n354", "n355", "n356", "n357", "n358", 236 | "n359", "n360", "n361", "n362", "n363", "n364", "n365", "n366", "z367", "n368", 237 | "n369", "n370", "n371", "n372", "n373", "n374", "n375", "a376", "n377", "n378", 238 | "n379", "n380", "n381", "n382", "n383", "n384", "n385", "n386", "n387", "n388", 239 | "n389", "n390", "n391", "n392", "n393", "n394", "n395", "n396", "n397", "n398", 240 | "n399", "n400", "n13", "n14", "n15", "n16", "n17", "n18", "n19", "n20", 241 | "n21", "n22", "n23", "a24", "n25", "n26", "n27", "n28", "n29", "j30", 242 | "n31", "n32", "n33", "n34", "n35", "n36", "n37", "n38", "n39", "n40", 243 | "n41", "n42", "n43", "n44", "n45", "n46", "n47", "n48", "n49", "n50", 244 | "n51", "n52", "n53", "n54", "n55", "n56", "n57", "n58", "n59", "n60", 245 | "n61", "n62", "n63", "n64", "n65", "a66", "z67", "n68", "n69", "n70", 246 | "n71", "n72", "n73", "n74", "n75", "n76", "n77", "n78", "n79", "n80", 247 | "n81", "n82", "n83", "n84", "n85", "n86", "n87", "n88", "n89", "n90", 248 | "n91", "n92", "n93", "n94", "n95", "n96", "n97", "n98", "n99", "n100", 249 | "n201", "n202", "n203", "n204", "n205", "n206", "n207", "n208", "n209", "n210", 250 | "n211", "n212", "n213", "n214", "n215", "n216", "n217", "n218", "n219", "n220", 251 | "n221", "n222", "n223", "n224", "n225", "n226", "n227", "n228", "n229", "n230", 252 | "n231", "n232", "n233", "n234", "n235", "n236", "n237", "n238", "n239", "a240", 253 | "a241", "a242", "a243", "a244", "a245", "a246", "a247", "a248", "a249", "n250", 254 | "n251", "n252", "n253", "n254", "n255", "n256", "n257", "n258", "n259", "n260", 255 | "n261", "n262", "n263", "n264", "n265", "n266", "n267", "n268", "n269", "n270", 256 | "n271", "n272", "n273", "n274", "n275", "n276", "n277", "n278", "n279", "n280", 257 | "n281", "n282", "n283", "n284", "n285", "n286", "n287", "n288", "n289", "n290", 258 | "n291", "n292", "n293", "n294", "n295", "n296", "n297", "n298", "n299" 259 | ; x=23} 260 | else a = 1 end 261 | 262 | 263 | end 264 | 265 | assert(b.x == 23) 266 | print('+') 267 | 268 | stat(b) 269 | 270 | repeat 271 | a = { 272 | n1 = 1.5, n2 = 2.5, n3 = 3.5, n4 = 4.5, n5 = 5.5, n6 = 6.5, n7 = 7.5, 273 | n8 = 8.5, n9 = 9.5, n10 = 10.5, n11 = 11.5, n12 = 12.5, 274 | j301 = 301.5, j302 = 302.5, j303 = 303.5, j304 = 304.5, j305 = 305.5, 275 | j306 = 306.5, j307 = 307.5, j308 = 308.5, j309 = 309.5, a310 = 310.5, 276 | n311 = 311.5, n312 = 312.5, n313 = 313.5, n314 = 314.5, n315 = 315.5, 277 | n316 = 316.5, n317 = 317.5, n318 = 318.5, n319 = 319.5, n320 = 320.5, 278 | n321 = 321.5, n322 = 322.5, n323 = 323.5, n324 = 324.5, n325 = 325.5, 279 | n326 = 326.5, n327 = 327.5, n328 = 328.5, a329 = 329.5, n330 = 330.5, 280 | n331 = 331.5, n332 = 332.5, n333 = 333.5, n334 = 334.5, n335 = 335.5, 281 | n336 = 336.5, n337 = 337.5, n338 = 338.5, n339 = 339.5, n340 = 340.5, 282 | n341 = 341.5, z342 = 342.5, n343 = 343.5, n344 = 344.5, n345 = 345.5, 283 | n346 = 346.5, n347 = 347.5, n348 = 348.5, n349 = 349.5, n350 = 350.5, 284 | n351 = 351.5, n352 = 352.5, r353 = 353.5, n354 = 354.5, n355 = 355.5, 285 | n356 = 356.5, n357 = 357.5, n358 = 358.5, n359 = 359.5, n360 = 360.5, 286 | n361 = 361.5, n362 = 362.5, n363 = 363.5, n364 = 364.5, n365 = 365.5, 287 | n366 = 366.5, z367 = 367.5, n368 = 368.5, n369 = 369.5, n370 = 370.5, 288 | n371 = 371.5, n372 = 372.5, n373 = 373.5, n374 = 374.5, n375 = 375.5, 289 | a376 = 376.5, n377 = 377.5, n378 = 378.5, n379 = 379.5, n380 = 380.5, 290 | n381 = 381.5, n382 = 382.5, n383 = 383.5, n384 = 384.5, n385 = 385.5, 291 | n386 = 386.5, n387 = 387.5, n388 = 388.5, n389 = 389.5, n390 = 390.5, 292 | n391 = 391.5, n392 = 392.5, n393 = 393.5, n394 = 394.5, n395 = 395.5, 293 | n396 = 396.5, n397 = 397.5, n398 = 398.5, n399 = 399.5, n400 = 400.5, 294 | n13 = 13.5, n14 = 14.5, n15 = 15.5, n16 = 16.5, n17 = 17.5, 295 | n18 = 18.5, n19 = 19.5, n20 = 20.5, n21 = 21.5, n22 = 22.5, 296 | n23 = 23.5, a24 = 24.5, n25 = 25.5, n26 = 26.5, n27 = 27.5, 297 | n28 = 28.5, n29 = 29.5, j30 = 30.5, n31 = 31.5, n32 = 32.5, 298 | n33 = 33.5, n34 = 34.5, n35 = 35.5, n36 = 36.5, n37 = 37.5, 299 | n38 = 38.5, n39 = 39.5, n40 = 40.5, n41 = 41.5, n42 = 42.5, 300 | n43 = 43.5, n44 = 44.5, n45 = 45.5, n46 = 46.5, n47 = 47.5, 301 | n48 = 48.5, n49 = 49.5, n50 = 50.5, n51 = 51.5, n52 = 52.5, 302 | n53 = 53.5, n54 = 54.5, n55 = 55.5, n56 = 56.5, n57 = 57.5, 303 | n58 = 58.5, n59 = 59.5, n60 = 60.5, n61 = 61.5, n62 = 62.5, 304 | n63 = 63.5, n64 = 64.5, n65 = 65.5, a66 = 66.5, z67 = 67.5, 305 | n68 = 68.5, n69 = 69.5, n70 = 70.5, n71 = 71.5, n72 = 72.5, 306 | n73 = 73.5, n74 = 74.5, n75 = 75.5, n76 = 76.5, n77 = 77.5, 307 | n78 = 78.5, n79 = 79.5, n80 = 80.5, n81 = 81.5, n82 = 82.5, 308 | n83 = 83.5, n84 = 84.5, n85 = 85.5, n86 = 86.5, n87 = 87.5, 309 | n88 = 88.5, n89 = 89.5, n90 = 90.5, n91 = 91.5, n92 = 92.5, 310 | n93 = 93.5, n94 = 94.5, n95 = 95.5, n96 = 96.5, n97 = 97.5, 311 | n98 = 98.5, n99 = 99.5, n100 = 100.5, n201 = 201.5, n202 = 202.5, 312 | n203 = 203.5, n204 = 204.5, n205 = 205.5, n206 = 206.5, n207 = 207.5, 313 | n208 = 208.5, n209 = 209.5, n210 = 210.5, n211 = 211.5, n212 = 212.5, 314 | n213 = 213.5, n214 = 214.5, n215 = 215.5, n216 = 216.5, n217 = 217.5, 315 | n218 = 218.5, n219 = 219.5, n220 = 220.5, n221 = 221.5, n222 = 222.5, 316 | n223 = 223.5, n224 = 224.5, n225 = 225.5, n226 = 226.5, n227 = 227.5, 317 | n228 = 228.5, n229 = 229.5, n230 = 230.5, n231 = 231.5, n232 = 232.5, 318 | n233 = 233.5, n234 = 234.5, n235 = 235.5, n236 = 236.5, n237 = 237.5, 319 | n238 = 238.5, n239 = 239.5, a240 = 240.5, a241 = 241.5, a242 = 242.5, 320 | a243 = 243.5, a244 = 244.5, a245 = 245.5, a246 = 246.5, a247 = 247.5, 321 | a248 = 248.5, a249 = 249.5, n250 = 250.5, n251 = 251.5, n252 = 252.5, 322 | n253 = 253.5, n254 = 254.5, n255 = 255.5, n256 = 256.5, n257 = 257.5, 323 | n258 = 258.5, n259 = 259.5, n260 = 260.5, n261 = 261.5, n262 = 262.5, 324 | n263 = 263.5, n264 = 264.5, n265 = 265.5, n266 = 266.5, n267 = 267.5, 325 | n268 = 268.5, n269 = 269.5, n270 = 270.5, n271 = 271.5, n272 = 272.5, 326 | n273 = 273.5, n274 = 274.5, n275 = 275.5, n276 = 276.5, n277 = 277.5, 327 | n278 = 278.5, n279 = 279.5, n280 = 280.5, n281 = 281.5, n282 = 282.5, 328 | n283 = 283.5, n284 = 284.5, n285 = 285.5, n286 = 286.5, n287 = 287.5, 329 | n288 = 288.5, n289 = 289.5, n290 = 290.5, n291 = 291.5, n292 = 292.5, 330 | n293 = 293.5, n294 = 294.5, n295 = 295.5, n296 = 296.5, n297 = 297.5, 331 | n298 = 298.5, n299 = 299.5, j300 = 300} or 1 332 | until 1 333 | 334 | assert(a.n299 == 299.5) 335 | xxx = 1 336 | assert(xxx == 1) 337 | 338 | stat(a) 339 | 340 | function a:findfield (f) 341 | local i,v = next(self, nil) 342 | while i ~= f do 343 | if not i then return end 344 | i,v = next(self, i) 345 | end 346 | return v 347 | end 348 | 349 | local ii = 0 350 | i = 1 351 | while b[i] do 352 | local r = a:findfield(b[i]); 353 | assert(a[b[i]] == r) 354 | ii = math.max(ii,i) 355 | i = i+1 356 | end 357 | 358 | assert(ii == 299) 359 | 360 | function xxxx (x) coroutine.yield('b'); return ii+x end 361 | 362 | assert(xxxx(10) == 309) 363 | 364 | a = nil 365 | b = nil 366 | a1 = nil 367 | 368 | print("tables with table indices:") 369 | i = 1; a={} 370 | while i <= 1023 do a[{}] = i; i=i+1 end 371 | stat(a) 372 | a = nil 373 | 374 | print("tables with function indices:") 375 | a={} 376 | for i=1,511 do local x; a[function () return x end] = i end 377 | stat(a) 378 | a = nil 379 | 380 | print'OK' 381 | 382 | return 'a' 383 | -------------------------------------------------------------------------------- /_lua5.1-tests/calls.lua: -------------------------------------------------------------------------------- 1 | print("testing functions and calls") 2 | 3 | -- get the opportunity to test 'type' too ;) 4 | 5 | assert(type(1<2) == 'boolean') 6 | assert(type(true) == 'boolean' and type(false) == 'boolean') 7 | assert(type(nil) == 'nil' and type(-3) == 'number' and type'x' == 'string' and 8 | type{} == 'table' and type(type) == 'function') 9 | 10 | assert(type(assert) == type(print)) 11 | f = nil 12 | function f (x) return a:x (x) end 13 | assert(type(f) == 'function') 14 | 15 | 16 | -- testing local-function recursion 17 | fact = false 18 | do 19 | local res = 1 20 | local function fact (n) 21 | if n==0 then return res 22 | else return n*fact(n-1) 23 | end 24 | end 25 | assert(fact(5) == 120) 26 | end 27 | assert(fact == false) 28 | 29 | -- testing declarations 30 | a = {i = 10} 31 | self = 20 32 | function a:x (x) return x+self.i end 33 | function a.y (x) return x+self end 34 | 35 | assert(a:x(1)+10 == a.y(1)) 36 | 37 | a.t = {i=-100} 38 | a["t"].x = function (self, a,b) return self.i+a+b end 39 | 40 | assert(a.t:x(2,3) == -95) 41 | 42 | do 43 | local a = {x=0} 44 | function a:add (x) self.x, a.y = self.x+x, 20; return self end 45 | assert(a:add(10):add(20):add(30).x == 60 and a.y == 20) 46 | end 47 | 48 | local a = {b={c={}}} 49 | 50 | function a.b.c.f1 (x) return x+1 end 51 | function a.b.c:f2 (x,y) self[x] = y end 52 | assert(a.b.c.f1(4) == 5) 53 | a.b.c:f2('k', 12); assert(a.b.c.k == 12) 54 | 55 | print('+') 56 | 57 | t = nil -- 'declare' t 58 | function f(a,b,c) local d = 'a'; t={a,b,c,d} end 59 | 60 | f( -- this line change must be valid 61 | 1,2) 62 | assert(t[1] == 1 and t[2] == 2 and t[3] == nil and t[4] == 'a') 63 | f(1,2, -- this one too 64 | 3,4) 65 | assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 'a') 66 | 67 | function fat(x) 68 | if x <= 1 then return 1 69 | else return x*loadstring("return fat(" .. x-1 .. ")")() 70 | end 71 | end 72 | 73 | assert(loadstring "loadstring 'assert(fat(6)==720)' () ")() 74 | a = loadstring('return fat(5), 3') 75 | a,b = a() 76 | assert(a == 120 and b == 3) 77 | print('+') 78 | 79 | function err_on_n (n) 80 | if n==0 then error(); exit(1); 81 | else err_on_n (n-1); exit(1); 82 | end 83 | end 84 | 85 | do 86 | function dummy (n) 87 | if n > 0 then 88 | assert(not pcall(err_on_n, n)) 89 | dummy(n-1) 90 | end 91 | end 92 | end 93 | 94 | dummy(10) 95 | 96 | function deep (n) 97 | if n>0 then deep(n-1) end 98 | end 99 | deep(10) 100 | deep(200) 101 | 102 | -- testing tail call 103 | function deep (n) if n>0 then return deep(n-1) else return 101 end end 104 | assert(deep(30000) == 101) 105 | a = {} 106 | function a:deep (n) if n>0 then return self:deep(n-1) else return 101 end end 107 | assert(a:deep(30000) == 101) 108 | 109 | print('+') 110 | 111 | 112 | a = nil 113 | (function (x) a=x end)(23) 114 | assert(a == 23 and (function (x) return x*2 end)(20) == 40) 115 | 116 | 117 | local x,y,z,a 118 | a = {}; lim = 2000 119 | for i=1, lim do a[i]=i end 120 | assert(select(lim, unpack(a)) == lim and select('#', unpack(a)) == lim) 121 | x = unpack(a) 122 | assert(x == 1) 123 | x = {unpack(a)} 124 | assert(table.getn(x) == lim and x[1] == 1 and x[lim] == lim) 125 | x = {unpack(a, lim-2)} 126 | assert(table.getn(x) == 3 and x[1] == lim-2 and x[3] == lim) 127 | x = {unpack(a, 10, 6)} 128 | assert(next(x) == nil) -- no elements 129 | x = {unpack(a, 11, 10)} 130 | assert(next(x) == nil) -- no elements 131 | x,y = unpack(a, 10, 10) 132 | assert(x == 10 and y == nil) 133 | x,y,z = unpack(a, 10, 11) 134 | assert(x == 10 and y == 11 and z == nil) 135 | a,x = unpack{1} 136 | assert(a==1 and x==nil) 137 | a,x = unpack({1,2}, 1, 1) 138 | assert(a==1 and x==nil) 139 | 140 | 141 | -- testing closures 142 | 143 | -- fixed-point operator 144 | Y = function (le) 145 | local function a (f) 146 | return le(function (x) return f(f)(x) end) 147 | end 148 | return a(a) 149 | end 150 | 151 | 152 | -- non-recursive factorial 153 | 154 | F = function (f) 155 | return function (n) 156 | if n == 0 then return 1 157 | else return n*f(n-1) end 158 | end 159 | end 160 | 161 | fat = Y(F) 162 | 163 | assert(fat(0) == 1 and fat(4) == 24 and Y(F)(5)==5*Y(F)(4)) 164 | 165 | local function g (z) 166 | local function f (a,b,c,d) 167 | return function (x,y) return a+b+c+d+a+x+y+z end 168 | end 169 | return f(z,z+1,z+2,z+3) 170 | end 171 | 172 | f = g(10) 173 | assert(f(9, 16) == 10+11+12+13+10+9+16+10) 174 | 175 | Y, F, f = nil 176 | print('+') 177 | 178 | -- testing multiple returns 179 | 180 | function unlpack (t, i) 181 | i = i or 1 182 | if (i <= table.getn(t)) then 183 | return t[i], unlpack(t, i+1) 184 | end 185 | end 186 | 187 | function equaltab (t1, t2) 188 | assert(table.getn(t1) == table.getn(t2)) 189 | for i,v1 in ipairs(t1) do 190 | assert(v1 == t2[i]) 191 | end 192 | end 193 | 194 | local function pack (...) 195 | local x = {...} 196 | x.n = select('#', ...) 197 | return x 198 | end 199 | 200 | function f() return 1,2,30,4 end 201 | function ret2 (a,b) return a,b end 202 | 203 | local a,b,c,d = unlpack{1,2,3} 204 | assert(a==1 and b==2 and c==3 and d==nil) 205 | a = {1,2,3,4,false,10,'alo',false,assert} 206 | equaltab(pack(unlpack(a)), a) 207 | equaltab(pack(unlpack(a), -1), {1,-1}) 208 | a,b,c,d = ret2(f()), ret2(f()) 209 | assert(a==1 and b==1 and c==2 and d==nil) 210 | a,b,c,d = unlpack(pack(ret2(f()), ret2(f()))) 211 | assert(a==1 and b==1 and c==2 and d==nil) 212 | a,b,c,d = unlpack(pack(ret2(f()), (ret2(f())))) 213 | assert(a==1 and b==1 and c==nil and d==nil) 214 | 215 | a = ret2{ unlpack{1,2,3}, unlpack{3,2,1}, unlpack{"a", "b"}} 216 | assert(a[1] == 1 and a[2] == 3 and a[3] == "a" and a[4] == "b") 217 | 218 | 219 | -- testing calls with 'incorrect' arguments 220 | rawget({}, "x", 1) 221 | rawset({}, "x", 1, 2) 222 | assert(math.sin(1,2) == math.sin(1)) 223 | table.sort({10,9,8,4,19,23,0,0}, function (a,b) return a" then 18 | assert(val==nil) 19 | else 20 | assert(t[key] == val) 21 | local mp = T.hash(key, t) 22 | if l[i] then 23 | assert(l[i] == mp) 24 | elseif mp ~= i then 25 | l[i] = mp 26 | else -- list head 27 | l[mp] = {mp} -- first element 28 | while next do 29 | assert(ff <= next and next < hsize) 30 | if l[next] then assert(l[next] == mp) else l[next] = mp end 31 | table.insert(l[mp], next) 32 | key,val,next = T.querytab(t, next) 33 | assert(key) 34 | end 35 | end 36 | end 37 | end 38 | l.asize = asize; l.hsize = hsize; l.ff = ff 39 | return l 40 | end 41 | 42 | function mostra (t) 43 | local asize, hsize, ff = T.querytab(t) 44 | print(asize, hsize, ff) 45 | print'------' 46 | for i=0,asize-1 do 47 | local _, v = T.querytab(t, i) 48 | print(string.format("[%d] -", i), v) 49 | end 50 | print'------' 51 | for i=0,hsize-1 do 52 | print(i, T.querytab(t, i+asize)) 53 | end 54 | print'-------------' 55 | end 56 | 57 | function stat (t) 58 | t = checktable(t) 59 | local nelem, nlist = 0, 0 60 | local maxlist = {} 61 | for i=0,t.hsize-1 do 62 | if type(t[i]) == 'table' then 63 | local n = table.getn(t[i]) 64 | nlist = nlist+1 65 | nelem = nelem + n 66 | if not maxlist[n] then maxlist[n] = 0 end 67 | maxlist[n] = maxlist[n]+1 68 | end 69 | end 70 | print(string.format("hsize=%d elements=%d load=%.2f med.len=%.2f (asize=%d)", 71 | t.hsize, nelem, nelem/t.hsize, nelem/nlist, t.asize)) 72 | for i=1,table.getn(maxlist) do 73 | local n = maxlist[i] or 0 74 | print(string.format("%5d %10d %.2f%%", i, n, n*100/nlist)) 75 | end 76 | end 77 | 78 | -------------------------------------------------------------------------------- /_lua5.1-tests/code.lua: -------------------------------------------------------------------------------- 1 | 2 | if T==nil then 3 | (Message or print)('\a\n >>> testC not active: skipping opcode tests <<<\n\a') 4 | return 5 | end 6 | print "testing code generation and optimizations" 7 | 8 | 9 | -- this code gave an error for the code checker 10 | do 11 | local function f (a) 12 | for k,v,w in a do end 13 | end 14 | end 15 | 16 | 17 | function check (f, ...) 18 | local c = T.listcode(f) 19 | for i=1, arg.n do 20 | -- print(arg[i], c[i]) 21 | assert(string.find(c[i], '- '..arg[i]..' *%d')) 22 | end 23 | assert(c[arg.n+2] == nil) 24 | end 25 | 26 | 27 | function checkequal (a, b) 28 | a = T.listcode(a) 29 | b = T.listcode(b) 30 | for i = 1, table.getn(a) do 31 | a[i] = string.gsub(a[i], '%b()', '') -- remove line number 32 | b[i] = string.gsub(b[i], '%b()', '') -- remove line number 33 | assert(a[i] == b[i]) 34 | end 35 | end 36 | 37 | 38 | -- some basic instructions 39 | check(function () 40 | (function () end){f()} 41 | end, 'CLOSURE', 'NEWTABLE', 'GETGLOBAL', 'CALL', 'SETLIST', 'CALL', 'RETURN') 42 | 43 | 44 | -- sequence of LOADNILs 45 | check(function () 46 | local a,b,c 47 | local d; local e; 48 | a = nil; d=nil 49 | end, 'RETURN') 50 | 51 | 52 | -- single return 53 | check (function (a,b,c) return a end, 'RETURN') 54 | 55 | 56 | -- infinite loops 57 | check(function () while true do local a = -1 end end, 58 | 'LOADK', 'JMP', 'RETURN') 59 | 60 | check(function () while 1 do local a = -1 end end, 61 | 'LOADK', 'JMP', 'RETURN') 62 | 63 | check(function () repeat local x = 1 until false end, 64 | 'LOADK', 'JMP', 'RETURN') 65 | 66 | check(function () repeat local x until nil end, 67 | 'LOADNIL', 'JMP', 'RETURN') 68 | 69 | check(function () repeat local x = 1 until true end, 70 | 'LOADK', 'RETURN') 71 | 72 | 73 | -- concat optimization 74 | check(function (a,b,c,d) return a..b..c..d end, 75 | 'MOVE', 'MOVE', 'MOVE', 'MOVE', 'CONCAT', 'RETURN') 76 | 77 | -- not 78 | check(function () return not not nil end, 'LOADBOOL', 'RETURN') 79 | check(function () return not not false end, 'LOADBOOL', 'RETURN') 80 | check(function () return not not true end, 'LOADBOOL', 'RETURN') 81 | check(function () return not not 1 end, 'LOADBOOL', 'RETURN') 82 | 83 | -- direct access to locals 84 | check(function () 85 | local a,b,c,d 86 | a = b*2 87 | c[4], a[b] = -((a + d/-20.5 - a[b]) ^ a.x), b 88 | end, 89 | 'MUL', 90 | 'DIV', 'ADD', 'GETTABLE', 'SUB', 'GETTABLE', 'POW', 91 | 'UNM', 'SETTABLE', 'SETTABLE', 'RETURN') 92 | 93 | 94 | -- direct access to constants 95 | check(function () 96 | local a,b 97 | a.x = 0 98 | a.x = b 99 | a[b] = 'y' 100 | a = 1 - a 101 | b = 1/a 102 | b = 5+4 103 | a[true] = false 104 | end, 105 | 'SETTABLE', 'SETTABLE', 'SETTABLE', 'SUB', 'DIV', 'LOADK', 106 | 'SETTABLE', 'RETURN') 107 | 108 | local function f () return -((2^8 + -(-1)) % 8)/2 * 4 - 3 end 109 | 110 | check(f, 'LOADK', 'RETURN') 111 | assert(f() == -5) 112 | 113 | check(function () 114 | local a,b,c 115 | b[c], a = c, b 116 | b[a], a = c, b 117 | a, b = c, a 118 | a = a 119 | end, 120 | 'MOVE', 'MOVE', 'SETTABLE', 121 | 'MOVE', 'MOVE', 'MOVE', 'SETTABLE', 122 | 'MOVE', 'MOVE', 'MOVE', 123 | -- no code for a = a 124 | 'RETURN') 125 | 126 | 127 | -- x == nil , x ~= nil 128 | checkequal(function () if (a==nil) then a=1 end; if a~=nil then a=1 end end, 129 | function () if (a==9) then a=1 end; if a~=9 then a=1 end end) 130 | 131 | check(function () if a==nil then a=1 end end, 132 | 'GETGLOBAL', 'EQ', 'JMP', 'LOADK', 'SETGLOBAL', 'RETURN') 133 | 134 | -- de morgan 135 | checkequal(function () local a; if not (a or b) then b=a end end, 136 | function () local a; if (not a and not b) then b=a end end) 137 | 138 | checkequal(function (l) local a; return 0 <= a and a <= l end, 139 | function (l) local a; return not (not(a >= 0) or not(a <= l)) end) 140 | 141 | 142 | print 'OK' 143 | 144 | -------------------------------------------------------------------------------- /_lua5.1-tests/constructs.lua: -------------------------------------------------------------------------------- 1 | print "testing syntax" 2 | 3 | -- testing priorities 4 | 5 | assert(2^3^2 == 2^(3^2)); 6 | assert(2^3*4 == (2^3)*4); 7 | assert(2^-2 == 1/4 and -2^- -2 == - - -4); 8 | assert(not nil and 2 and not(2>3 or 3<2)); 9 | assert(-3-1-5 == 0+0-9); 10 | assert(-2^2 == -4 and (-2)^2 == 4 and 2*2-3-1 == 0); 11 | assert(2*1+3/3 == 3 and 1+2 .. 3*1 == "33"); 12 | assert(not(2+1 > 3*1) and "a".."b" > "a"); 13 | 14 | assert(not ((true or false) and nil)) 15 | assert( true or false and nil) 16 | 17 | local a,b = 1,nil; 18 | assert(-(1 or 2) == -1 and (1 and 2)+(-1.25 or -4) == 0.75); 19 | x = ((b or a)+1 == 2 and (10 or a)+1 == 11); assert(x); 20 | x = (((2<3) or 1) == true and (2<3 and 4) == 4); assert(x); 21 | 22 | x,y=1,2; 23 | assert((x>y) and x or y == 2); 24 | x,y=2,1; 25 | assert((x>y) and x or y == 2); 26 | 27 | assert(1234567890 == tonumber('1234567890') and 1234567890+1 == 1234567891) 28 | 29 | 30 | -- silly loops 31 | repeat until 1; repeat until true; 32 | while false do end; while nil do end; 33 | 34 | do -- test old bug (first name could not be an `upvalue') 35 | local a; function f(x) x={a=1}; x={x=1}; x={G=1} end 36 | end 37 | 38 | function f (i) 39 | if type(i) ~= 'number' then return i,'jojo'; end; 40 | if i > 0 then return i, f(i-1); end; 41 | end 42 | 43 | x = {f(3), f(5), f(10);}; 44 | assert(x[1] == 3 and x[2] == 5 and x[3] == 10 and x[4] == 9 and x[12] == 1); 45 | assert(x[nil] == nil) 46 | x = {f'alo', f'xixi', nil}; 47 | assert(x[1] == 'alo' and x[2] == 'xixi' and x[3] == nil); 48 | x = {f'alo'..'xixi'}; 49 | assert(x[1] == 'aloxixi') 50 | x = {f{}} 51 | assert(x[2] == 'jojo' and type(x[1]) == 'table') 52 | 53 | 54 | local f = function (i) 55 | if i < 10 then return 'a'; 56 | elseif i < 20 then return 'b'; 57 | elseif i < 30 then return 'c'; 58 | end; 59 | end 60 | 61 | assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == nil) 62 | 63 | for i=1,1000 do break; end; 64 | n=100; 65 | i=3; 66 | t = {}; 67 | a=nil 68 | while not a do 69 | a=0; for i=1,n do for i=i,1,-1 do a=a+1; t[i]=1; end; end; 70 | end 71 | assert(a == n*(n+1)/2 and i==3); 72 | assert(t[1] and t[n] and not t[0] and not t[n+1]) 73 | 74 | function f(b) 75 | local x = 1; 76 | repeat 77 | local a; 78 | if b==1 then local b=1; x=10; break 79 | elseif b==2 then x=20; break; 80 | elseif b==3 then x=30; 81 | else local a,b,c,d=math.sin(1); x=x+1; 82 | end 83 | until x>=12; 84 | return x; 85 | end; 86 | 87 | assert(f(1) == 10 and f(2) == 20 and f(3) == 30 and f(4)==12) 88 | 89 | 90 | local f = function (i) 91 | if i < 10 then return 'a' 92 | elseif i < 20 then return 'b' 93 | elseif i < 30 then return 'c' 94 | else return 8 95 | end 96 | end 97 | 98 | assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == 8) 99 | 100 | local a, b = nil, 23 101 | x = {f(100)*2+3 or a, a or b+2} 102 | assert(x[1] == 19 and x[2] == 25) 103 | x = {f=2+3 or a, a = b+2} 104 | assert(x.f == 5 and x.a == 25) 105 | 106 | a={y=1} 107 | x = {a.y} 108 | assert(x[1] == 1) 109 | 110 | function f(i) 111 | while 1 do 112 | if i>0 then i=i-1; 113 | else return; end; 114 | end; 115 | end; 116 | 117 | function g(i) 118 | while 1 do 119 | if i>0 then i=i-1 120 | else return end 121 | end 122 | end 123 | 124 | f(10); g(10); 125 | 126 | do 127 | function f () return 1,2,3; end 128 | local a, b, c = f(); 129 | assert(a==1 and b==2 and c==3) 130 | a, b, c = (f()); 131 | assert(a==1 and b==nil and c==nil) 132 | end 133 | 134 | local a,b = 3 and f(); 135 | assert(a==1 and b==nil) 136 | 137 | function g() f(); return; end; 138 | assert(g() == nil) 139 | function g() return nil or f() end 140 | a,b = g() 141 | assert(a==1 and b==nil) 142 | 143 | print'+'; 144 | 145 | 146 | f = [[ 147 | return function ( a , b , c , d , e ) 148 | local x = a >= b or c or ( d and e ) or nil 149 | return x 150 | end , { a = 1 , b = 2 >= 1 , } or { 1 }; 151 | ]] 152 | f = string.gsub(f, "%s+", "\n"); -- force a SETLINE between opcodes 153 | f,a = loadstring(f)(); 154 | assert(a.a == 1 and a.b) 155 | 156 | function g (a,b,c,d,e) 157 | if not (a>=b or c or d and e or nil) then return 0; else return 1; end; 158 | end 159 | 160 | function h (a,b,c,d,e) 161 | while (a>=b or c or (d and e) or nil) do return 1; end; 162 | return 0; 163 | end; 164 | 165 | assert(f(2,1) == true and g(2,1) == 1 and h(2,1) == 1) 166 | assert(f(1,2,'a') == 'a' and g(1,2,'a') == 1 and h(1,2,'a') == 1) 167 | assert(f(1,2,'a') 168 | ~= -- force SETLINE before nil 169 | nil, "") 170 | assert(f(1,2,'a') == 'a' and g(1,2,'a') == 1 and h(1,2,'a') == 1) 171 | assert(f(1,2,nil,1,'x') == 'x' and g(1,2,nil,1,'x') == 1 and 172 | h(1,2,nil,1,'x') == 1) 173 | assert(f(1,2,nil,nil,'x') == nil and g(1,2,nil,nil,'x') == 0 and 174 | h(1,2,nil,nil,'x') == 0) 175 | assert(f(1,2,nil,1,nil) == nil and g(1,2,nil,1,nil) == 0 and 176 | h(1,2,nil,1,nil) == 0) 177 | 178 | assert(1 and 2<3 == true and 2<3 and 'a'<'b' == true) 179 | x = 2<3 and not 3; assert(x==false) 180 | x = 2<1 or (2>1 and 'a'); assert(x=='a') 181 | 182 | 183 | do 184 | local a; if nil then a=1; else a=2; end; -- this nil comes as PUSHNIL 2 185 | assert(a==2) 186 | end 187 | 188 | function F(a) 189 | assert(debug.getinfo(1, "n").name == 'F') 190 | return a,2,3 191 | end 192 | 193 | a,b = F(1)~=nil; assert(a == true and b == nil); 194 | a,b = F(nil)==nil; assert(a == true and b == nil) 195 | 196 | ---------------------------------------------------------------- 197 | -- creates all combinations of 198 | -- [not] ([not] arg op [not] (arg op [not] arg )) 199 | -- and tests each one 200 | 201 | function ID(x) return x end 202 | 203 | function f(t, i) 204 | local b = t.n 205 | local res = math.mod(math.floor(i/c), b)+1 206 | c = c*b 207 | return t[res] 208 | end 209 | 210 | local arg = {" ( 1 < 2 ) ", " ( 1 >= 2 ) ", " F ( ) ", " nil "; n=4} 211 | 212 | local op = {" and ", " or ", " == ", " ~= "; n=4} 213 | 214 | local neg = {" ", " not "; n=2} 215 | 216 | local i = 0 217 | repeat 218 | c = 1 219 | local s = f(neg, i)..'ID('..f(neg, i)..f(arg, i)..f(op, i).. 220 | f(neg, i)..'ID('..f(arg, i)..f(op, i)..f(neg, i)..f(arg, i)..'))' 221 | local s1 = string.gsub(s, 'ID', '') 222 | K,X,NX,WX1,WX2 = nil 223 | s = string.format([[ 224 | local a = %s 225 | local b = not %s 226 | K = b 227 | local xxx; 228 | if %s then X = a else X = b end 229 | if %s then NX = b else NX = a end 230 | while %s do WX1 = a; break end 231 | while %s do WX2 = a; break end 232 | repeat if (%s) then break end; assert(b) until not(%s) 233 | ]], s1, s, s1, s, s1, s, s1, s, s) 234 | assert(loadstring(s))() 235 | assert(X and not NX and not WX1 == K and not WX2 == K) 236 | if math.mod(i,4000) == 0 then print('+') end 237 | i = i+1 238 | until i==c 239 | 240 | print'OK' 241 | -------------------------------------------------------------------------------- /_lua5.1-tests/db.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuin/gopher-lua/ccacf662c9d24821ea6cb1abaa515cc3b6aad7c6/_lua5.1-tests/db.lua -------------------------------------------------------------------------------- /_lua5.1-tests/errors.lua: -------------------------------------------------------------------------------- 1 | print("testing errors") 2 | 3 | function doit (s) 4 | local f, msg = loadstring(s) 5 | if f == nil then return msg end 6 | local cond, msg = pcall(f) 7 | return (not cond) and msg 8 | end 9 | 10 | 11 | function checkmessage (prog, msg) 12 | assert(string.find(doit(prog), msg, 1, true)) 13 | end 14 | 15 | function checksyntax (prog, extra, token, line) 16 | local msg = doit(prog) 17 | token = string.gsub(token, "(%p)", "%%%1") 18 | local pt = string.format([[^%%[string ".*"%%]:%d: .- near '%s'$]], 19 | line, token) 20 | assert(string.find(msg, pt)) 21 | assert(string.find(msg, msg, 1, true)) 22 | end 23 | 24 | 25 | -- test error message with no extra info 26 | assert(doit("error('hi', 0)") == 'hi') 27 | 28 | -- test error message with no info 29 | assert(doit("error()") == nil) 30 | 31 | 32 | -- test common errors/errors that crashed in the past 33 | assert(doit("unpack({}, 1, n=2^30)")) 34 | assert(doit("a=math.sin()")) 35 | assert(not doit("tostring(1)") and doit("tostring()")) 36 | assert(doit"tonumber()") 37 | assert(doit"repeat until 1; a") 38 | checksyntax("break label", "", "label", 1) 39 | assert(doit";") 40 | assert(doit"a=1;;") 41 | assert(doit"return;;") 42 | assert(doit"assert(false)") 43 | assert(doit"assert(nil)") 44 | assert(doit"a=math.sin\n(3)") 45 | assert(doit("function a (... , ...) end")) 46 | assert(doit("function a (, ...) end")) 47 | 48 | checksyntax([[ 49 | local a = {4 50 | 51 | ]], "'}' expected (to close '{' at line 1)", "", 3) 52 | 53 | 54 | -- tests for better error messages 55 | 56 | checkmessage("a=1; bbbb=2; a=math.sin(3)+bbbb(3)", "global 'bbbb'") 57 | checkmessage("a=1; local a,bbbb=2,3; a = math.sin(1) and bbbb(3)", 58 | "local 'bbbb'") 59 | checkmessage("a={}; do local a=1 end a:bbbb(3)", "method 'bbbb'") 60 | checkmessage("local a={}; a.bbbb(3)", "field 'bbbb'") 61 | assert(not string.find(doit"a={13}; local bbbb=1; a[bbbb](3)", "'bbbb'")) 62 | checkmessage("a={13}; local bbbb=1; a[bbbb](3)", "number") 63 | 64 | aaa = nil 65 | checkmessage("aaa.bbb:ddd(9)", "global 'aaa'") 66 | checkmessage("local aaa={bbb=1}; aaa.bbb:ddd(9)", "field 'bbb'") 67 | checkmessage("local aaa={bbb={}}; aaa.bbb:ddd(9)", "method 'ddd'") 68 | checkmessage("local a,b,c; (function () a = b+1 end)()", "upvalue 'b'") 69 | assert(not doit"local aaa={bbb={ddd=next}}; aaa.bbb:ddd(nil)") 70 | 71 | checkmessage("b=1; local aaa='a'; x=aaa+b", "local 'aaa'") 72 | checkmessage("aaa={}; x=3/aaa", "global 'aaa'") 73 | checkmessage("aaa='2'; b=nil;x=aaa*b", "global 'b'") 74 | checkmessage("aaa={}; x=-aaa", "global 'aaa'") 75 | assert(not string.find(doit"aaa={}; x=(aaa or aaa)+(aaa and aaa)", "'aaa'")) 76 | assert(not string.find(doit"aaa={}; (aaa or aaa)()", "'aaa'")) 77 | 78 | checkmessage([[aaa=9 79 | repeat until 3==3 80 | local x=math.sin(math.cos(3)) 81 | if math.sin(1) == x then return math.sin(1) end -- tail call 82 | local a,b = 1, { 83 | {x='a'..'b'..'c', y='b', z=x}, 84 | {1,2,3,4,5} or 3+3<=3+3, 85 | 3+1>3+1, 86 | {d = x and aaa[x or y]}} 87 | ]], "global 'aaa'") 88 | 89 | checkmessage([[ 90 | local x,y = {},1 91 | if math.sin(1) == 0 then return 3 end -- return 92 | x.a()]], "field 'a'") 93 | 94 | checkmessage([[ 95 | prefix = nil 96 | insert = nil 97 | while 1 do 98 | local a 99 | if nil then break end 100 | insert(prefix, a) 101 | end]], "global 'insert'") 102 | 103 | checkmessage([[ -- tail call 104 | return math.sin("a") 105 | ]], "'sin'") 106 | 107 | checkmessage([[collectgarbage("nooption")]], "invalid option") 108 | 109 | checkmessage([[x = print .. "a"]], "concatenate") 110 | 111 | checkmessage("getmetatable(io.stdin).__gc()", "no value") 112 | 113 | print'+' 114 | 115 | 116 | -- testing line error 117 | 118 | function lineerror (s) 119 | local err,msg = pcall(loadstring(s)) 120 | local line = string.match(msg, ":(%d+):") 121 | return line and line+0 122 | end 123 | 124 | assert(lineerror"local a\n for i=1,'a' do \n print(i) \n end" == 2) 125 | assert(lineerror"\n local a \n for k,v in 3 \n do \n print(k) \n end" == 3) 126 | assert(lineerror"\n\n for k,v in \n 3 \n do \n print(k) \n end" == 4) 127 | assert(lineerror"function a.x.y ()\na=a+1\nend" == 1) 128 | 129 | local p = [[ 130 | function g() f() end 131 | function f(x) error('a', X) end 132 | g() 133 | ]] 134 | X=3;assert(lineerror(p) == 3) 135 | X=0;assert(lineerror(p) == nil) 136 | X=1;assert(lineerror(p) == 2) 137 | X=2;assert(lineerror(p) == 1) 138 | 139 | lineerror = nil 140 | 141 | C = 0 142 | local l = debug.getinfo(1, "l").currentline; function y () C=C+1; y() end 143 | 144 | local function checkstackmessage (m) 145 | return (string.find(m, "^.-:%d+: stack overflow")) 146 | end 147 | assert(checkstackmessage(doit('y()'))) 148 | assert(checkstackmessage(doit('y()'))) 149 | assert(checkstackmessage(doit('y()'))) 150 | -- teste de linhas em erro 151 | C = 0 152 | local l1 153 | local function g() 154 | l1 = debug.getinfo(1, "l").currentline; y() 155 | end 156 | local _, stackmsg = xpcall(g, debug.traceback) 157 | local stack = {} 158 | for line in string.gmatch(stackmsg, "[^\n]*") do 159 | local curr = string.match(line, ":(%d+):") 160 | if curr then table.insert(stack, tonumber(curr)) end 161 | end 162 | local i=1 163 | while stack[i] ~= l1 do 164 | assert(stack[i] == l) 165 | i = i+1 166 | end 167 | assert(i > 15) 168 | 169 | 170 | -- error in error handling 171 | local res, msg = xpcall(error, error) 172 | assert(not res and type(msg) == 'string') 173 | 174 | local function f (x) 175 | if x==0 then error('a\n') 176 | else 177 | local aux = function () return f(x-1) end 178 | local a,b = xpcall(aux, aux) 179 | return a,b 180 | end 181 | end 182 | f(3) 183 | 184 | -- non string messages 185 | function f() error{msg='x'} end 186 | res, msg = xpcall(f, function (r) return {msg=r.msg..'y'} end) 187 | assert(msg.msg == 'xy') 188 | 189 | print('+') 190 | checksyntax("syntax error", "", "error", 1) 191 | checksyntax("1.000", "", "1.000", 1) 192 | checksyntax("[[a]]", "", "[[a]]", 1) 193 | checksyntax("'aa'", "", "'aa'", 1) 194 | 195 | -- test 255 as first char in a chunk 196 | checksyntax("\255a = 1", "", "\255", 1) 197 | 198 | doit('I = loadstring("a=9+"); a=3') 199 | assert(a==3 and I == nil) 200 | print('+') 201 | 202 | lim = 1000 203 | if rawget(_G, "_soft") then lim = 100 end 204 | for i=1,lim do 205 | doit('a = ') 206 | doit('a = 4+nil') 207 | end 208 | 209 | 210 | -- testing syntax limits 211 | local function testrep (init, rep) 212 | local s = "local a; "..init .. string.rep(rep, 400) 213 | local a,b = loadstring(s) 214 | assert(not a and string.find(b, "syntax levels")) 215 | end 216 | testrep("a=", "{") 217 | testrep("a=", "(") 218 | testrep("", "a(") 219 | testrep("", "do ") 220 | testrep("", "while a do ") 221 | testrep("", "if a then else ") 222 | testrep("", "function foo () ") 223 | testrep("a=", "a..") 224 | testrep("a=", "a^") 225 | 226 | 227 | -- testing other limits 228 | -- upvalues 229 | local s = "function foo ()\n local " 230 | for j = 1,70 do 231 | s = s.."a"..j..", " 232 | end 233 | s = s.."b\n" 234 | for j = 1,70 do 235 | s = s.."function foo"..j.." ()\n a"..j.."=3\n" 236 | end 237 | local a,b = loadstring(s) 238 | assert(string.find(b, "line 3")) 239 | 240 | -- local variables 241 | s = "\nfunction foo ()\n local " 242 | for j = 1,300 do 243 | s = s.."a"..j..", " 244 | end 245 | s = s.."b\n" 246 | local a,b = loadstring(s) 247 | assert(string.find(b, "line 2")) 248 | 249 | 250 | print('OK') 251 | -------------------------------------------------------------------------------- /_lua5.1-tests/files.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuin/gopher-lua/ccacf662c9d24821ea6cb1abaa515cc3b6aad7c6/_lua5.1-tests/files.lua -------------------------------------------------------------------------------- /_lua5.1-tests/gc.lua: -------------------------------------------------------------------------------- 1 | print('testing garbage collection') 2 | 3 | collectgarbage() 4 | 5 | _G["while"] = 234 6 | 7 | limit = 5000 8 | 9 | 10 | 11 | contCreate = 0 12 | 13 | print('tables') 14 | while contCreate <= limit do 15 | local a = {}; a = nil 16 | contCreate = contCreate+1 17 | end 18 | 19 | a = "a" 20 | 21 | contCreate = 0 22 | print('strings') 23 | while contCreate <= limit do 24 | a = contCreate .. "b"; 25 | a = string.gsub(a, '(%d%d*)', string.upper) 26 | a = "a" 27 | contCreate = contCreate+1 28 | end 29 | 30 | 31 | contCreate = 0 32 | 33 | a = {} 34 | 35 | print('functions') 36 | function a:test () 37 | while contCreate <= limit do 38 | loadstring(string.format("function temp(a) return 'a%d' end", contCreate))() 39 | assert(temp() == string.format('a%d', contCreate)) 40 | contCreate = contCreate+1 41 | end 42 | end 43 | 44 | a:test() 45 | 46 | -- collection of functions without locals, globals, etc. 47 | do local f = function () end end 48 | 49 | 50 | print("functions with errors") 51 | prog = [[ 52 | do 53 | a = 10; 54 | function foo(x,y) 55 | a = sin(a+0.456-0.23e-12); 56 | return function (z) return sin(%x+z) end 57 | end 58 | local x = function (w) a=a+w; end 59 | end 60 | ]] 61 | do 62 | local step = 1 63 | if rawget(_G, "_soft") then step = 13 end 64 | for i=1, string.len(prog), step do 65 | for j=i, string.len(prog), step do 66 | pcall(loadstring(string.sub(prog, i, j))) 67 | end 68 | end 69 | end 70 | 71 | print('long strings') 72 | x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789" 73 | assert(string.len(x)==80) 74 | s = '' 75 | n = 0 76 | k = 300 77 | while n < k do s = s..x; n=n+1; j=tostring(n) end 78 | assert(string.len(s) == k*80) 79 | s = string.sub(s, 1, 20000) 80 | s, i = string.gsub(s, '(%d%d%d%d)', math.sin) 81 | assert(i==20000/4) 82 | s = nil 83 | x = nil 84 | 85 | assert(_G["while"] == 234) 86 | 87 | 88 | local bytes = gcinfo() 89 | while 1 do 90 | local nbytes = gcinfo() 91 | if nbytes < bytes then break end -- run until gc 92 | bytes = nbytes 93 | a = {} 94 | end 95 | 96 | 97 | local function dosteps (siz) 98 | collectgarbage() 99 | collectgarbage"stop" 100 | local a = {} 101 | for i=1,100 do a[i] = {{}}; local b = {} end 102 | local x = gcinfo() 103 | local i = 0 104 | repeat 105 | i = i+1 106 | until collectgarbage("step", siz) 107 | assert(gcinfo() < x) 108 | return i 109 | end 110 | 111 | assert(dosteps(0) > 10) 112 | assert(dosteps(6) < dosteps(2)) 113 | assert(dosteps(10000) == 1) 114 | assert(collectgarbage("step", 1000000) == true) 115 | assert(collectgarbage("step", 1000000)) 116 | 117 | 118 | do 119 | local x = gcinfo() 120 | collectgarbage() 121 | collectgarbage"stop" 122 | repeat 123 | local a = {} 124 | until gcinfo() > 1000 125 | collectgarbage"restart" 126 | repeat 127 | local a = {} 128 | until gcinfo() < 1000 129 | end 130 | 131 | lim = 15 132 | a = {} 133 | -- fill a with `collectable' indices 134 | for i=1,lim do a[{}] = i end 135 | b = {} 136 | for k,v in pairs(a) do b[k]=v end 137 | -- remove all indices and collect them 138 | for n in pairs(b) do 139 | a[n] = nil 140 | assert(type(n) == 'table' and next(n) == nil) 141 | collectgarbage() 142 | end 143 | b = nil 144 | collectgarbage() 145 | for n in pairs(a) do error'cannot be here' end 146 | for i=1,lim do a[i] = i end 147 | for i=1,lim do assert(a[i] == i) end 148 | 149 | 150 | print('weak tables') 151 | a = {}; setmetatable(a, {__mode = 'k'}); 152 | -- fill a with some `collectable' indices 153 | for i=1,lim do a[{}] = i end 154 | -- and some non-collectable ones 155 | for i=1,lim do local t={}; a[t]=t end 156 | for i=1,lim do a[i] = i end 157 | for i=1,lim do local s=string.rep('@', i); a[s] = s..'#' end 158 | collectgarbage() 159 | local i = 0 160 | for k,v in pairs(a) do assert(k==v or k..'#'==v); i=i+1 end 161 | assert(i == 3*lim) 162 | 163 | a = {}; setmetatable(a, {__mode = 'v'}); 164 | a[1] = string.rep('b', 21) 165 | collectgarbage() 166 | assert(a[1]) -- strings are *values* 167 | a[1] = nil 168 | -- fill a with some `collectable' values (in both parts of the table) 169 | for i=1,lim do a[i] = {} end 170 | for i=1,lim do a[i..'x'] = {} end 171 | -- and some non-collectable ones 172 | for i=1,lim do local t={}; a[t]=t end 173 | for i=1,lim do a[i+lim]=i..'x' end 174 | collectgarbage() 175 | local i = 0 176 | for k,v in pairs(a) do assert(k==v or k-lim..'x' == v); i=i+1 end 177 | assert(i == 2*lim) 178 | 179 | a = {}; setmetatable(a, {__mode = 'vk'}); 180 | local x, y, z = {}, {}, {} 181 | -- keep only some items 182 | a[1], a[2], a[3] = x, y, z 183 | a[string.rep('$', 11)] = string.rep('$', 11) 184 | -- fill a with some `collectable' values 185 | for i=4,lim do a[i] = {} end 186 | for i=1,lim do a[{}] = i end 187 | for i=1,lim do local t={}; a[t]=t end 188 | collectgarbage() 189 | assert(next(a) ~= nil) 190 | local i = 0 191 | for k,v in pairs(a) do 192 | assert((k == 1 and v == x) or 193 | (k == 2 and v == y) or 194 | (k == 3 and v == z) or k==v); 195 | i = i+1 196 | end 197 | assert(i == 4) 198 | x,y,z=nil 199 | collectgarbage() 200 | assert(next(a) == string.rep('$', 11)) 201 | 202 | 203 | -- testing userdata 204 | collectgarbage("stop") -- stop collection 205 | local u = newproxy(true) 206 | local s = 0 207 | local a = {[u] = 0}; setmetatable(a, {__mode = 'vk'}) 208 | for i=1,10 do a[newproxy(u)] = i end 209 | for k in pairs(a) do assert(getmetatable(k) == getmetatable(u)) end 210 | local a1 = {}; for k,v in pairs(a) do a1[k] = v end 211 | for k,v in pairs(a1) do a[v] = k end 212 | for i =1,10 do assert(a[i]) end 213 | getmetatable(u).a = a1 214 | getmetatable(u).u = u 215 | do 216 | local u = u 217 | getmetatable(u).__gc = function (o) 218 | assert(a[o] == 10-s) 219 | assert(a[10-s] == nil) -- udata already removed from weak table 220 | assert(getmetatable(o) == getmetatable(u)) 221 | assert(getmetatable(o).a[o] == 10-s) 222 | s=s+1 223 | end 224 | end 225 | a1, u = nil 226 | assert(next(a) ~= nil) 227 | collectgarbage() 228 | assert(s==11) 229 | collectgarbage() 230 | assert(next(a) == nil) -- finalized keys are removed in two cycles 231 | 232 | 233 | -- __gc x weak tables 234 | local u = newproxy(true) 235 | setmetatable(getmetatable(u), {__mode = "v"}) 236 | getmetatable(u).__gc = function (o) os.exit(1) end -- cannot happen 237 | collectgarbage() 238 | 239 | local u = newproxy(true) 240 | local m = getmetatable(u) 241 | m.x = {[{0}] = 1; [0] = {1}}; setmetatable(m.x, {__mode = "kv"}); 242 | m.__gc = function (o) 243 | assert(next(getmetatable(o).x) == nil) 244 | m = 10 245 | end 246 | u, m = nil 247 | collectgarbage() 248 | assert(m==10) 249 | 250 | 251 | -- errors during collection 252 | u = newproxy(true) 253 | getmetatable(u).__gc = function () error "!!!" end 254 | u = nil 255 | assert(not pcall(collectgarbage)) 256 | 257 | 258 | if not rawget(_G, "_soft") then 259 | print("deep structures") 260 | local a = {} 261 | for i = 1,200000 do 262 | a = {next = a} 263 | end 264 | collectgarbage() 265 | end 266 | 267 | -- create many threads with self-references and open upvalues 268 | local thread_id = 0 269 | local threads = {} 270 | 271 | function fn(thread) 272 | local x = {} 273 | threads[thread_id] = function() 274 | thread = x 275 | end 276 | coroutine.yield() 277 | end 278 | 279 | while thread_id < 1000 do 280 | local thread = coroutine.create(fn) 281 | coroutine.resume(thread, thread) 282 | thread_id = thread_id + 1 283 | end 284 | 285 | 286 | 287 | -- create a userdata to be collected when state is closed 288 | do 289 | local newproxy,assert,type,print,getmetatable = 290 | newproxy,assert,type,print,getmetatable 291 | local u = newproxy(true) 292 | local tt = getmetatable(u) 293 | ___Glob = {u} -- avoid udata being collected before program end 294 | tt.__gc = function (o) 295 | assert(getmetatable(o) == tt) 296 | -- create new objects during GC 297 | local a = 'xuxu'..(10+3)..'joao', {} 298 | ___Glob = o -- ressurect object! 299 | newproxy(o) -- creates a new one with same metatable 300 | print(">>> closing state " .. "<<<\n") 301 | end 302 | end 303 | 304 | -- create several udata to raise errors when collected while closing state 305 | do 306 | local u = newproxy(true) 307 | getmetatable(u).__gc = function (o) return o + 1 end 308 | table.insert(___Glob, u) -- preserve udata until the end 309 | for i = 1,10 do table.insert(___Glob, newproxy(u)) end 310 | end 311 | 312 | print('OK') 313 | -------------------------------------------------------------------------------- /_lua5.1-tests/libs/.gitignore: -------------------------------------------------------------------------------- 1 | *.* 2 | -------------------------------------------------------------------------------- /_lua5.1-tests/libs/P1/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /_lua5.1-tests/literals.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuin/gopher-lua/ccacf662c9d24821ea6cb1abaa515cc3b6aad7c6/_lua5.1-tests/literals.lua -------------------------------------------------------------------------------- /_lua5.1-tests/locals.lua: -------------------------------------------------------------------------------- 1 | print('testing local variables plus some extra stuff') 2 | 3 | do 4 | local i = 10 5 | do local i = 100; assert(i==100) end 6 | do local i = 1000; assert(i==1000) end 7 | assert(i == 10) 8 | if i ~= 10 then 9 | local i = 20 10 | else 11 | local i = 30 12 | assert(i == 30) 13 | end 14 | end 15 | 16 | 17 | 18 | f = nil 19 | 20 | local f 21 | x = 1 22 | 23 | a = nil 24 | loadstring('local a = {}')() 25 | assert(type(a) ~= 'table') 26 | 27 | function f (a) 28 | local _1, _2, _3, _4, _5 29 | local _6, _7, _8, _9, _10 30 | local x = 3 31 | local b = a 32 | local c,d = a,b 33 | if (d == b) then 34 | local x = 'q' 35 | x = b 36 | assert(x == 2) 37 | else 38 | assert(nil) 39 | end 40 | assert(x == 3) 41 | local f = 10 42 | end 43 | 44 | local b=10 45 | local a; repeat local b; a,b=1,2; assert(a+1==b); until a+b==3 46 | 47 | 48 | assert(x == 1) 49 | 50 | f(2) 51 | assert(type(f) == 'function') 52 | 53 | 54 | -- testing globals ;-) 55 | do 56 | local f = {} 57 | local _G = _G 58 | for i=1,10 do f[i] = function (x) A=A+1; return A, _G.getfenv(x) end end 59 | A=10; assert(f[1]() == 11) 60 | for i=1,10 do assert(setfenv(f[i], {A=i}) == f[i]) end 61 | assert(f[3]() == 4 and A == 11) 62 | local a,b = f[8](1) 63 | assert(b.A == 9) 64 | a,b = f[8](0) 65 | assert(b.A == 11) -- `real' global 66 | local g 67 | local function f () assert(setfenv(2, {a='10'}) == g) end 68 | g = function () f(); _G.assert(_G.getfenv(1).a == '10') end 69 | g(); assert(getfenv(g).a == '10') 70 | end 71 | 72 | -- test for global table of loaded chunks 73 | local function foo (s) 74 | return loadstring(s) 75 | end 76 | 77 | assert(getfenv(foo("")) == _G) 78 | local a = {loadstring = loadstring} 79 | setfenv(foo, a) 80 | assert(getfenv(foo("")) == _G) 81 | setfenv(0, a) -- change global environment 82 | assert(getfenv(foo("")) == a) 83 | setfenv(0, _G) 84 | 85 | 86 | -- testing limits for special instructions 87 | 88 | local a 89 | local p = 4 90 | for i=2,31 do 91 | for j=-3,3 do 92 | assert(loadstring(string.format([[local a=%s;a=a+ 93 | %s; 94 | assert(a 95 | ==2^%s)]], j, p-j, i))) () 96 | assert(loadstring(string.format([[local a=%s; 97 | a=a-%s; 98 | assert(a==-2^%s)]], -j, p-j, i))) () 99 | assert(loadstring(string.format([[local a,b=0,%s; 100 | a=b-%s; 101 | assert(a==-2^%s)]], -j, p-j, i))) () 102 | end 103 | p =2*p 104 | end 105 | 106 | print'+' 107 | 108 | 109 | if rawget(_G, "querytab") then 110 | -- testing clearing of dead elements from tables 111 | collectgarbage("stop") -- stop GC 112 | local a = {[{}] = 4, [3] = 0, alo = 1, 113 | a1234567890123456789012345678901234567890 = 10} 114 | 115 | local t = querytab(a) 116 | 117 | for k,_ in pairs(a) do a[k] = nil end 118 | collectgarbage() -- restore GC and collect dead fiels in `a' 119 | for i=0,t-1 do 120 | local k = querytab(a, i) 121 | assert(k == nil or type(k) == 'number' or k == 'alo') 122 | end 123 | end 124 | 125 | print('OK') 126 | 127 | return 5,f 128 | -------------------------------------------------------------------------------- /_lua5.1-tests/main.lua: -------------------------------------------------------------------------------- 1 | # testing special comment on first line 2 | 3 | print ("testing lua.c options") 4 | 5 | assert(os.execute() ~= 0) -- machine has a system command 6 | 7 | prog = os.tmpname() 8 | otherprog = os.tmpname() 9 | out = os.tmpname() 10 | 11 | do 12 | local i = 0 13 | while arg[i] do i=i-1 end 14 | progname = '"'..arg[i+1]..'"' 15 | end 16 | print(progname) 17 | 18 | local prepfile = function (s, p) 19 | p = p or prog 20 | io.output(p) 21 | io.write(s) 22 | assert(io.close()) 23 | end 24 | 25 | function checkout (s) 26 | io.input(out) 27 | local t = io.read("*a") 28 | io.input():close() 29 | assert(os.remove(out)) 30 | if s ~= t then print(string.format("'%s' - '%s'\n", s, t)) end 31 | assert(s == t) 32 | return t 33 | end 34 | 35 | function auxrun (...) 36 | local s = string.format(...) 37 | s = string.gsub(s, "lua", progname, 1) 38 | return os.execute(s) 39 | end 40 | 41 | function RUN (...) 42 | assert(auxrun(...) == 0) 43 | end 44 | 45 | function NoRun (...) 46 | print("\n(the next error is expected by the test)") 47 | assert(auxrun(...) ~= 0) 48 | end 49 | 50 | -- test 2 files 51 | prepfile("print(1); a=2") 52 | prepfile("print(a)", otherprog) 53 | RUN("lua -l %s -l%s -lstring -l io %s > %s", prog, otherprog, otherprog, out) 54 | checkout("1\n2\n2\n") 55 | 56 | local a = [[ 57 | assert(table.getn(arg) == 3 and arg[1] == 'a' and 58 | arg[2] == 'b' and arg[3] == 'c') 59 | assert(arg[-1] == '--' and arg[-2] == "-e " and arg[-3] == %s) 60 | assert(arg[4] == nil and arg[-4] == nil) 61 | local a, b, c = ... 62 | assert(... == 'a' and a == 'a' and b == 'b' and c == 'c') 63 | ]] 64 | a = string.format(a, progname) 65 | prepfile(a) 66 | RUN('lua "-e " -- %s a b c', prog) 67 | 68 | prepfile"assert(arg==nil)" 69 | prepfile("assert(arg)", otherprog) 70 | RUN("lua -l%s - < %s", prog, otherprog) 71 | 72 | prepfile"" 73 | RUN("lua - < %s > %s", prog, out) 74 | checkout("") 75 | 76 | -- test many arguments 77 | prepfile[[print(({...})[30])]] 78 | RUN("lua %s %s > %s", prog, string.rep(" a", 30), out) 79 | checkout("a\n") 80 | 81 | RUN([[lua "-eprint(1)" -ea=3 -e "print(a)" > %s]], out) 82 | checkout("1\n3\n") 83 | 84 | prepfile[[ 85 | print( 86 | 1, a 87 | ) 88 | ]] 89 | RUN("lua - < %s > %s", prog, out) 90 | checkout("1\tnil\n") 91 | 92 | prepfile[[ 93 | = (6*2-6) -- === 94 | a 95 | = 10 96 | print(a) 97 | = a]] 98 | RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) 99 | checkout("6\n10\n10\n\n") 100 | 101 | prepfile("a = [[b\nc\nd\ne]]\n=a") 102 | print(prog) 103 | RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) 104 | checkout("b\nc\nd\ne\n\n") 105 | 106 | prompt = "alo" 107 | prepfile[[ -- 108 | a = 2 109 | ]] 110 | RUN([[lua "-e_PROMPT='%s'" -i < %s > %s]], prompt, prog, out) 111 | checkout(string.rep(prompt, 3).."\n") 112 | 113 | s = [=[ -- 114 | function f ( x ) 115 | local a = [[ 116 | xuxu 117 | ]] 118 | local b = "\ 119 | xuxu\n" 120 | if x == 11 then return 1 , 2 end --[[ test multiple returns ]] 121 | return x + 1 122 | --\\ 123 | end 124 | =( f( 10 ) ) 125 | assert( a == b ) 126 | =f( 11 ) ]=] 127 | s = string.gsub(s, ' ', '\n\n') 128 | prepfile(s) 129 | RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) 130 | checkout("11\n1\t2\n\n") 131 | 132 | prepfile[[#comment in 1st line without \n at the end]] 133 | RUN("lua %s", prog) 134 | 135 | prepfile("#comment with a binary file\n"..string.dump(loadstring("print(1)"))) 136 | RUN("lua %s > %s", prog, out) 137 | checkout("1\n") 138 | 139 | prepfile("#comment with a binary file\r\n"..string.dump(loadstring("print(1)"))) 140 | RUN("lua %s > %s", prog, out) 141 | checkout("1\n") 142 | 143 | -- close Lua with an open file 144 | prepfile(string.format([[io.output(%q); io.write('alo')]], out)) 145 | RUN("lua %s", prog) 146 | checkout('alo') 147 | 148 | assert(os.remove(prog)) 149 | assert(os.remove(otherprog)) 150 | assert(not os.remove(out)) 151 | 152 | RUN("lua -v") 153 | 154 | NoRun("lua -h") 155 | NoRun("lua -e") 156 | NoRun("lua -e a") 157 | NoRun("lua -f") 158 | 159 | print("OK") 160 | -------------------------------------------------------------------------------- /_lua5.1-tests/math.lua: -------------------------------------------------------------------------------- 1 | print("testing numbers and math lib") 2 | 3 | do 4 | local a,b,c = "2", " 3e0 ", " 10 " 5 | assert(a+b == 5 and -b == -3 and b+"2" == 5 and "10"-c == 0) 6 | assert(type(a) == 'string' and type(b) == 'string' and type(c) == 'string') 7 | assert(a == "2" and b == " 3e0 " and c == " 10 " and -c == -" 10 ") 8 | assert(c%a == 0 and a^b == 8) 9 | end 10 | 11 | 12 | do 13 | local a,b = math.modf(3.5) 14 | assert(a == 3 and b == 0.5) 15 | assert(math.huge > 10e30) 16 | assert(-math.huge < -10e30) 17 | end 18 | 19 | function f(...) 20 | if select('#', ...) == 1 then 21 | return (...) 22 | else 23 | return "***" 24 | end 25 | end 26 | 27 | assert(tonumber{} == nil) 28 | assert(tonumber'+0.01' == 1/100 and tonumber'+.01' == 0.01 and 29 | tonumber'.01' == 0.01 and tonumber'-1.' == -1 and 30 | tonumber'+1.' == 1) 31 | assert(tonumber'+ 0.01' == nil and tonumber'+.e1' == nil and 32 | tonumber'1e' == nil and tonumber'1.0e+' == nil and 33 | tonumber'.' == nil) 34 | assert(tonumber('-12') == -10-2) 35 | assert(tonumber('-1.2e2') == - - -120) 36 | assert(f(tonumber('1 a')) == nil) 37 | assert(f(tonumber('e1')) == nil) 38 | assert(f(tonumber('e 1')) == nil) 39 | assert(f(tonumber(' 3.4.5 ')) == nil) 40 | assert(f(tonumber('')) == nil) 41 | assert(f(tonumber('', 8)) == nil) 42 | assert(f(tonumber(' ')) == nil) 43 | assert(f(tonumber(' ', 9)) == nil) 44 | assert(f(tonumber('99', 8)) == nil) 45 | assert(tonumber(' 1010 ', 2) == 10) 46 | assert(tonumber('10', 36) == 36) 47 | assert(tonumber('\n -10 \n', 36) == -36) 48 | assert(tonumber('-fFfa', 16) == -(10+(16*(15+(16*(15+(16*15))))))) 49 | assert(tonumber('fFfa', 15) == nil) 50 | assert(tonumber(string.rep('1', 42), 2) + 1 == 2^42) 51 | assert(tonumber(string.rep('1', 32), 2) + 1 == 2^32) 52 | assert(tonumber('-fffffFFFFF', 16)-1 == -2^40) 53 | assert(tonumber('ffffFFFF', 16)+1 == 2^32) 54 | assert(tonumber('0xF') == 15) 55 | 56 | assert(1.1 == 1.+.1) 57 | print(100.0, 1E2, .01) 58 | assert(100.0 == 1E2 and .01 == 1e-2) 59 | assert(1111111111111111-1111111111111110== 1000.00e-03) 60 | -- 1234567890123456 61 | assert(1.1 == '1.'+'.1') 62 | assert('1111111111111111'-'1111111111111110' == tonumber" +0.001e+3 \n\t") 63 | 64 | function eq (a,b,limit) 65 | if not limit then limit = 10E-10 end 66 | return math.abs(a-b) <= limit 67 | end 68 | 69 | assert(0.1e-30 > 0.9E-31 and 0.9E30 < 0.1e31) 70 | 71 | assert(0.123456 > 0.123455) 72 | 73 | assert(tonumber('+1.23E30') == 1.23*10^30) 74 | 75 | -- testing order operators 76 | assert(not(1<1) and (1<2) and not(2<1)) 77 | assert(not('a'<'a') and ('a'<'b') and not('b'<'a')) 78 | assert((1<=1) and (1<=2) and not(2<=1)) 79 | assert(('a'<='a') and ('a'<='b') and not('b'<='a')) 80 | assert(not(1>1) and not(1>2) and (2>1)) 81 | assert(not('a'>'a') and not('a'>'b') and ('b'>'a')) 82 | assert((1>=1) and not(1>=2) and (2>=1)) 83 | assert(('a'>='a') and not('a'>='b') and ('b'>='a')) 84 | 85 | -- testing mod operator 86 | assert(-4%3 == 2) 87 | assert(4%-3 == -2) 88 | assert(math.pi - math.pi % 1 == 3) 89 | assert(math.pi - math.pi % 0.001 == 3.141) 90 | 91 | local function testbit(a, n) 92 | return a/2^n % 2 >= 1 93 | end 94 | 95 | assert(eq(math.sin(-9.8)^2 + math.cos(-9.8)^2, 1)) 96 | assert(eq(math.tan(math.pi/4), 1)) 97 | assert(eq(math.sin(math.pi/2), 1) and eq(math.cos(math.pi/2), 0)) 98 | assert(eq(math.atan(1), math.pi/4) and eq(math.acos(0), math.pi/2) and 99 | eq(math.asin(1), math.pi/2)) 100 | assert(eq(math.deg(math.pi/2), 90) and eq(math.rad(90), math.pi/2)) 101 | assert(math.abs(-10) == 10) 102 | assert(eq(math.atan2(1,0), math.pi/2)) 103 | assert(math.ceil(4.5) == 5.0) 104 | assert(math.floor(4.5) == 4.0) 105 | assert(math.mod(10,3) == 1) 106 | assert(eq(math.sqrt(10)^2, 10)) 107 | assert(eq(math.log10(2), math.log(2)/math.log(10))) 108 | assert(eq(math.exp(0), 1)) 109 | assert(eq(math.sin(10), math.sin(10%(2*math.pi)))) 110 | local v,e = math.frexp(math.pi) 111 | assert(eq(math.ldexp(v,e), math.pi)) 112 | 113 | assert(eq(math.tanh(3.5), math.sinh(3.5)/math.cosh(3.5))) 114 | 115 | assert(tonumber(' 1.3e-2 ') == 1.3e-2) 116 | assert(tonumber(' -1.00000000000001 ') == -1.00000000000001) 117 | 118 | -- testing constant limits 119 | -- 2^23 = 8388608 120 | assert(8388609 + -8388609 == 0) 121 | assert(8388608 + -8388608 == 0) 122 | assert(8388607 + -8388607 == 0) 123 | 124 | if rawget(_G, "_soft") then return end 125 | 126 | f = io.tmpfile() 127 | assert(f) 128 | f:write("a = {") 129 | i = 1 130 | repeat 131 | f:write("{", math.sin(i), ", ", math.cos(i), ", ", i/3, "},\n") 132 | i=i+1 133 | until i > 1000 134 | f:write("}") 135 | f:seek("set", 0) 136 | assert(loadstring(f:read('*a')))() 137 | assert(f:close()) 138 | 139 | assert(eq(a[300][1], math.sin(300))) 140 | assert(eq(a[600][1], math.sin(600))) 141 | assert(eq(a[500][2], math.cos(500))) 142 | assert(eq(a[800][2], math.cos(800))) 143 | assert(eq(a[200][3], 200/3)) 144 | assert(eq(a[1000][3], 1000/3, 0.001)) 145 | print('+') 146 | 147 | do -- testing NaN 148 | local NaN = 10e500 - 10e400 149 | assert(NaN ~= NaN) 150 | assert(not (NaN < NaN)) 151 | assert(not (NaN <= NaN)) 152 | assert(not (NaN > NaN)) 153 | assert(not (NaN >= NaN)) 154 | assert(not (0 < NaN)) 155 | assert(not (NaN < 0)) 156 | local a = {} 157 | assert(not pcall(function () a[NaN] = 1 end)) 158 | assert(a[NaN] == nil) 159 | a[1] = 1 160 | assert(not pcall(function () a[NaN] = 1 end)) 161 | assert(a[NaN] == nil) 162 | end 163 | 164 | require "checktable" 165 | stat(a) 166 | 167 | a = nil 168 | 169 | -- testing implicit convertions 170 | 171 | local a,b = '10', '20' 172 | assert(a*b == 200 and a+b == 30 and a-b == -10 and a/b == 0.5 and -b == -20) 173 | assert(a == '10' and b == '20') 174 | 175 | 176 | math.randomseed(0) 177 | 178 | local i = 0 179 | local Max = 0 180 | local Min = 2 181 | repeat 182 | local t = math.random() 183 | Max = math.max(Max, t) 184 | Min = math.min(Min, t) 185 | i=i+1 186 | flag = eq(Max, 1, 0.001) and eq(Min, 0, 0.001) 187 | until flag or i>10000 188 | assert(0 <= Min and Max<1) 189 | assert(flag); 190 | 191 | for i=1,10 do 192 | local t = math.random(5) 193 | assert(1 <= t and t <= 5) 194 | end 195 | 196 | i = 0 197 | Max = -200 198 | Min = 200 199 | repeat 200 | local t = math.random(-10,0) 201 | Max = math.max(Max, t) 202 | Min = math.min(Min, t) 203 | i=i+1 204 | flag = (Max == 0 and Min == -10) 205 | until flag or i>10000 206 | assert(-10 <= Min and Max<=0) 207 | assert(flag); 208 | 209 | 210 | print('OK') 211 | -------------------------------------------------------------------------------- /_lua5.1-tests/pm.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuin/gopher-lua/ccacf662c9d24821ea6cb1abaa515cc3b6aad7c6/_lua5.1-tests/pm.lua -------------------------------------------------------------------------------- /_lua5.1-tests/sort.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuin/gopher-lua/ccacf662c9d24821ea6cb1abaa515cc3b6aad7c6/_lua5.1-tests/sort.lua -------------------------------------------------------------------------------- /_lua5.1-tests/strings.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuin/gopher-lua/ccacf662c9d24821ea6cb1abaa515cc3b6aad7c6/_lua5.1-tests/strings.lua -------------------------------------------------------------------------------- /_lua5.1-tests/vararg.lua: -------------------------------------------------------------------------------- 1 | print('testing vararg') 2 | 3 | _G.arg = nil 4 | 5 | function f(a, ...) 6 | assert(type(arg) == 'table') 7 | assert(type(arg.n) == 'number') 8 | for i=1,arg.n do assert(a[i]==arg[i]) end 9 | return arg.n 10 | end 11 | 12 | function c12 (...) 13 | assert(arg == nil) 14 | local x = {...}; x.n = table.getn(x) 15 | local res = (x.n==2 and x[1] == 1 and x[2] == 2) 16 | if res then res = 55 end 17 | return res, 2 18 | end 19 | 20 | function vararg (...) return arg end 21 | 22 | local call = function (f, args) return f(unpack(args, 1, args.n)) end 23 | 24 | assert(f() == 0) 25 | assert(f({1,2,3}, 1, 2, 3) == 3) 26 | assert(f({"alo", nil, 45, f, nil}, "alo", nil, 45, f, nil) == 5) 27 | 28 | assert(c12(1,2)==55) 29 | a,b = assert(call(c12, {1,2})) 30 | assert(a == 55 and b == 2) 31 | a = call(c12, {1,2;n=2}) 32 | assert(a == 55 and b == 2) 33 | a = call(c12, {1,2;n=1}) 34 | assert(not a) 35 | assert(c12(1,2,3) == false) 36 | --[[ 37 | local a = vararg(call(next, {_G,nil;n=2})) 38 | local b,c = next(_G) 39 | assert(a[1] == b and a[2] == c and a.n == 2) 40 | a = vararg(call(call, {c12, {1,2}})) 41 | assert(a.n == 2 and a[1] == 55 and a[2] == 2) 42 | a = call(print, {'+'}) 43 | assert(a == nil) 44 | --]] 45 | 46 | local t = {1, 10} 47 | function t:f (...) return self[arg[1]]+arg.n end 48 | assert(t:f(1,4) == 3 and t:f(2) == 11) 49 | print('+') 50 | 51 | lim = 20 52 | local i, a = 1, {} 53 | while i <= lim do a[i] = i+0.3; i=i+1 end 54 | 55 | function f(a, b, c, d, ...) 56 | local more = {...} 57 | assert(a == 1.3 and more[1] == 5.3 and 58 | more[lim-4] == lim+0.3 and not more[lim-3]) 59 | end 60 | 61 | function g(a,b,c) 62 | assert(a == 1.3 and b == 2.3 and c == 3.3) 63 | end 64 | 65 | call(f, a) 66 | call(g, a) 67 | 68 | a = {} 69 | i = 1 70 | while i <= lim do a[i] = i; i=i+1 end 71 | assert(call(math.max, a) == lim) 72 | 73 | print("+") 74 | 75 | 76 | -- new-style varargs 77 | 78 | function oneless (a, ...) return ... end 79 | 80 | function f (n, a, ...) 81 | local b 82 | assert(arg == nil) 83 | if n == 0 then 84 | local b, c, d = ... 85 | return a, b, c, d, oneless(oneless(oneless(...))) 86 | else 87 | n, b, a = n-1, ..., a 88 | assert(b == ...) 89 | return f(n, a, ...) 90 | end 91 | end 92 | 93 | a,b,c,d,e = assert(f(10,5,4,3,2,1)) 94 | assert(a==5 and b==4 and c==3 and d==2 and e==1) 95 | 96 | a,b,c,d,e = f(4) 97 | assert(a==nil and b==nil and c==nil and d==nil and e==nil) 98 | 99 | 100 | -- varargs for main chunks 101 | f = loadstring[[ return {...} ]] 102 | x = f(2,3) 103 | assert(x[1] == 2 and x[2] == 3 and x[3] == nil) 104 | 105 | 106 | f = loadstring[[ 107 | local x = {...} 108 | for i=1,select('#', ...) do assert(x[i] == select(i, ...)) end 109 | assert(x[select('#', ...)+1] == nil) 110 | return true 111 | ]] 112 | 113 | assert(f("a", "b", nil, {}, assert)) 114 | assert(f()) 115 | 116 | a = {select(3, unpack{10,20,30,40})} 117 | assert(table.getn(a) == 2 and a[1] == 30 and a[2] == 40) 118 | a = {select(1)} 119 | assert(next(a) == nil) 120 | a = {select(-1, 3, 5, 7)} 121 | assert(a[1] == 7 and a[2] == nil) 122 | a = {select(-2, 3, 5, 7)} 123 | assert(a[1] == 5 and a[2] == 7 and a[3] == nil) 124 | pcall(select, 10000) 125 | pcall(select, -10000) 126 | 127 | print('OK') 128 | 129 | -------------------------------------------------------------------------------- /_lua5.1-tests/verybig.lua: -------------------------------------------------------------------------------- 1 | if rawget(_G, "_soft") then return 10 end 2 | 3 | print "testing large programs (>64k)" 4 | 5 | -- template to create a very big test file 6 | prog = [[$ 7 | 8 | local a,b 9 | 10 | b = {$1$ 11 | b30009 = 65534, 12 | b30010 = 65535, 13 | b30011 = 65536, 14 | b30012 = 65537, 15 | b30013 = 16777214, 16 | b30014 = 16777215, 17 | b30015 = 16777216, 18 | b30016 = 16777217, 19 | b30017 = 4294967294, 20 | b30018 = 4294967295, 21 | b30019 = 4294967296, 22 | b30020 = 4294967297, 23 | b30021 = -65534, 24 | b30022 = -65535, 25 | b30023 = -65536, 26 | b30024 = -4294967297, 27 | b30025 = 15012.5, 28 | $2$ 29 | }; 30 | 31 | assert(b.a50008 == 25004 and b["a11"] == 5.5) 32 | assert(b.a33007 == 16503.5 and b.a50009 == 25004.5) 33 | assert(b["b"..30024] == -4294967297) 34 | 35 | function b:xxx (a,b) return a+b end 36 | assert(b:xxx(10, 12) == 22) -- pushself with non-constant index 37 | b.xxx = nil 38 | 39 | s = 0; n=0 40 | for a,b in pairs(b) do s=s+b; n=n+1 end 41 | assert(s==13977183656.5 and n==70001) 42 | 43 | require "checktable" 44 | stat(b) 45 | 46 | a = nil; b = nil 47 | print'+' 48 | 49 | function f(x) b=x end 50 | 51 | a = f{$3$} or 10 52 | 53 | assert(a==10) 54 | assert(b[1] == "a10" and b[2] == 5 and b[table.getn(b)-1] == "a50009") 55 | 56 | 57 | function xxxx (x) return b[x] end 58 | 59 | assert(xxxx(3) == "a11") 60 | 61 | a = nil; b=nil 62 | xxxx = nil 63 | 64 | return 10 65 | 66 | ]] 67 | 68 | -- functions to fill in the $n$ 69 | F = { 70 | function () -- $1$ 71 | for i=10,50009 do 72 | io.write('a', i, ' = ', 5+((i-10)/2), ',\n') 73 | end 74 | end, 75 | 76 | function () -- $2$ 77 | for i=30026,50009 do 78 | io.write('b', i, ' = ', 15013+((i-30026)/2), ',\n') 79 | end 80 | end, 81 | 82 | function () -- $3$ 83 | for i=10,50009 do 84 | io.write('"a', i, '", ', 5+((i-10)/2), ',\n') 85 | end 86 | end, 87 | } 88 | 89 | file = os.tmpname() 90 | io.output(file) 91 | for s in string.gmatch(prog, "$([^$]+)") do 92 | local n = tonumber(s) 93 | if not n then io.write(s) else F[n]() end 94 | end 95 | io.close() 96 | result = dofile(file) 97 | assert(os.remove(file)) 98 | print'OK' 99 | return result 100 | 101 | -------------------------------------------------------------------------------- /_tools/go-inline: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Simple and indolent text processor to do inline go function calling 3 | 4 | import sys, re 5 | 6 | 7 | files = sys.argv 8 | class inline(object): 9 | def __init__(self, name): 10 | self.name = name 11 | self.receiver = "" 12 | self.args = [] 13 | self.contents = [] 14 | self.file = "" 15 | self.original = "" 16 | 17 | inlines = {} 18 | for file in sorted(files, reverse=True): 19 | contents = open(file).read().splitlines() 20 | i = 0 21 | name = "" 22 | while i < len(contents): 23 | line = contents[i] 24 | m = re.match(".*\/\/ \+inline-start", line) 25 | if m: 26 | m2 = re.match("^func\s*(\([\*\s\w]+\))?\s*(\w+)\(([^\)]*)\)", line) 27 | name = m2.group(2) 28 | tinline = inline(name) 29 | tinline.original = line.split("//")[0].strip().rstrip("{") 30 | tinline.file = file 31 | if m2.group(1): 32 | tinline.receiver = m2.group(1).split("(")[1].split(" ")[0] 33 | tinline.args = [arg.strip().split(" ")[0] for arg in m2.group(3).split(",")] 34 | inlines[name] = tinline 35 | else: 36 | if re.match(".*\/\/\s*\+inline-end", line): 37 | inlines[name].contents = "\n".join(inlines[name].contents) 38 | name = "" 39 | elif len(name) > 0: 40 | inlines[name].contents.append(line) 41 | i += 1 42 | 43 | def do_inlining(text): 44 | contents = text.splitlines() 45 | buf = [] 46 | i = 0 47 | while i < len(contents): 48 | line = contents[i] 49 | m = re.match("\s*\/\/\s*\+inline-call\s+([\w\.]+)\s+(.*)", line) 50 | if m: 51 | inlinet = inlines[m.group(1).split(".")[-1]] 52 | buf.append("// this section is inlined by go-inline") 53 | buf.append("// source function is '{}' in '{}'".format(inlinet.original, inlinet.file)) 54 | buf.append("{") 55 | if len(inlinet.receiver) > 0 and inlinet.receiver != m.group(1).split(".")[0]: 56 | buf.append("{} := {}".format(inlinet.receiver, ".".join(m.group(1).split(".")[0:-1]))) 57 | 58 | callargs = [arg.strip() for arg in m.group(2).split(" ")] 59 | for j in range(len(callargs)): 60 | if inlinet.args[j] != callargs[j]: 61 | buf.append("{} := {}".format(inlinet.args[j], callargs[j])) 62 | buf.append(do_inlining(inlinet.contents)) 63 | buf.append("}") 64 | else: 65 | buf.append(line) 66 | i += 1 67 | return "\n".join(buf) 68 | 69 | for file in files: 70 | if not file.startswith("_"): 71 | continue 72 | contents = open(file).read() 73 | with open(file.lstrip("_"), "w") as io: 74 | inlined = do_inlining(contents).split("\n") 75 | for i in range(len(inlined)): 76 | if i == 1: 77 | io.write("////////////////////////////////////////////////////////\n") 78 | io.write("// This file was generated by go-inline. DO NOT EDIT. //\n") 79 | io.write("////////////////////////////////////////////////////////\n") 80 | io.write(inlined[i]) 81 | io.write("\n") 82 | io.write("\n") 83 | -------------------------------------------------------------------------------- /alloc.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | // iface is an internal representation of the go-interface. 9 | type iface struct { 10 | itab unsafe.Pointer 11 | word unsafe.Pointer 12 | } 13 | 14 | const preloadLimit LNumber = 128 15 | 16 | var _fv float64 17 | var _uv uintptr 18 | 19 | var preloads [int(preloadLimit)]LValue 20 | 21 | func init() { 22 | for i := 0; i < int(preloadLimit); i++ { 23 | preloads[i] = LNumber(i) 24 | } 25 | } 26 | 27 | // allocator is a fast bulk memory allocator for the LValue. 28 | type allocator struct { 29 | size int 30 | fptrs []float64 31 | fheader *reflect.SliceHeader 32 | 33 | scratchValue LValue 34 | scratchValueP *iface 35 | } 36 | 37 | func newAllocator(size int) *allocator { 38 | al := &allocator{ 39 | size: size, 40 | fptrs: make([]float64, 0, size), 41 | fheader: nil, 42 | } 43 | al.fheader = (*reflect.SliceHeader)(unsafe.Pointer(&al.fptrs)) 44 | al.scratchValue = LNumber(0) 45 | al.scratchValueP = (*iface)(unsafe.Pointer(&al.scratchValue)) 46 | 47 | return al 48 | } 49 | 50 | // LNumber2I takes a number value and returns an interface LValue representing the same number. 51 | // Converting an LNumber to a LValue naively, by doing: 52 | // `var val LValue = myLNumber` 53 | // will result in an individual heap alloc of 8 bytes for the float value. LNumber2I amortizes the cost and memory 54 | // overhead of these allocs by allocating blocks of floats instead. 55 | // The downside of this is that all of the floats on a given block have to become eligible for gc before the block 56 | // as a whole can be gc-ed. 57 | func (al *allocator) LNumber2I(v LNumber) LValue { 58 | // first check for shared preloaded numbers 59 | if v >= 0 && v < preloadLimit && float64(v) == float64(int64(v)) { 60 | return preloads[int(v)] 61 | } 62 | 63 | // check if we need a new alloc page 64 | if cap(al.fptrs) == len(al.fptrs) { 65 | al.fptrs = make([]float64, 0, al.size) 66 | al.fheader = (*reflect.SliceHeader)(unsafe.Pointer(&al.fptrs)) 67 | } 68 | 69 | // alloc a new float, and store our value into it 70 | al.fptrs = append(al.fptrs, float64(v)) 71 | fptr := &al.fptrs[len(al.fptrs)-1] 72 | 73 | // hack our scratch LValue to point to our allocated value 74 | // this scratch lvalue is copied when this function returns meaning the scratch value can be reused 75 | // on the next call 76 | al.scratchValueP.word = unsafe.Pointer(fptr) 77 | 78 | return al.scratchValue 79 | } 80 | -------------------------------------------------------------------------------- /ast/ast.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | type PositionHolder interface { 4 | Line() int 5 | SetLine(int) 6 | LastLine() int 7 | SetLastLine(int) 8 | } 9 | 10 | type Node struct { 11 | line int 12 | lastline int 13 | } 14 | 15 | func (self *Node) Line() int { 16 | return self.line 17 | } 18 | 19 | func (self *Node) SetLine(line int) { 20 | self.line = line 21 | } 22 | 23 | func (self *Node) LastLine() int { 24 | return self.lastline 25 | } 26 | 27 | func (self *Node) SetLastLine(line int) { 28 | self.lastline = line 29 | } 30 | -------------------------------------------------------------------------------- /ast/expr.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | type Expr interface { 4 | PositionHolder 5 | exprMarker() 6 | } 7 | 8 | type ExprBase struct { 9 | Node 10 | } 11 | 12 | func (expr *ExprBase) exprMarker() {} 13 | 14 | /* ConstExprs {{{ */ 15 | 16 | type ConstExpr interface { 17 | Expr 18 | constExprMarker() 19 | } 20 | 21 | type ConstExprBase struct { 22 | ExprBase 23 | } 24 | 25 | func (expr *ConstExprBase) constExprMarker() {} 26 | 27 | type TrueExpr struct { 28 | ConstExprBase 29 | } 30 | 31 | type FalseExpr struct { 32 | ConstExprBase 33 | } 34 | 35 | type NilExpr struct { 36 | ConstExprBase 37 | } 38 | 39 | type NumberExpr struct { 40 | ConstExprBase 41 | 42 | Value string 43 | } 44 | 45 | type StringExpr struct { 46 | ConstExprBase 47 | 48 | Value string 49 | } 50 | 51 | /* ConstExprs }}} */ 52 | 53 | type Comma3Expr struct { 54 | ExprBase 55 | AdjustRet bool 56 | } 57 | 58 | type IdentExpr struct { 59 | ExprBase 60 | 61 | Value string 62 | } 63 | 64 | type AttrGetExpr struct { 65 | ExprBase 66 | 67 | Object Expr 68 | Key Expr 69 | } 70 | 71 | type TableExpr struct { 72 | ExprBase 73 | 74 | Fields []*Field 75 | } 76 | 77 | type FuncCallExpr struct { 78 | ExprBase 79 | 80 | Func Expr 81 | Receiver Expr 82 | Method string 83 | Args []Expr 84 | AdjustRet bool 85 | } 86 | 87 | type LogicalOpExpr struct { 88 | ExprBase 89 | 90 | Operator string 91 | Lhs Expr 92 | Rhs Expr 93 | } 94 | 95 | type RelationalOpExpr struct { 96 | ExprBase 97 | 98 | Operator string 99 | Lhs Expr 100 | Rhs Expr 101 | } 102 | 103 | type StringConcatOpExpr struct { 104 | ExprBase 105 | 106 | Lhs Expr 107 | Rhs Expr 108 | } 109 | 110 | type ArithmeticOpExpr struct { 111 | ExprBase 112 | 113 | Operator string 114 | Lhs Expr 115 | Rhs Expr 116 | } 117 | 118 | type UnaryMinusOpExpr struct { 119 | ExprBase 120 | Expr Expr 121 | } 122 | 123 | type UnaryNotOpExpr struct { 124 | ExprBase 125 | Expr Expr 126 | } 127 | 128 | type UnaryLenOpExpr struct { 129 | ExprBase 130 | Expr Expr 131 | } 132 | 133 | type FunctionExpr struct { 134 | ExprBase 135 | 136 | ParList *ParList 137 | Stmts []Stmt 138 | } 139 | -------------------------------------------------------------------------------- /ast/misc.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | type Field struct { 4 | Key Expr 5 | Value Expr 6 | } 7 | 8 | type ParList struct { 9 | HasVargs bool 10 | Names []string 11 | } 12 | 13 | type FuncName struct { 14 | Func Expr 15 | Receiver Expr 16 | Method string 17 | } 18 | -------------------------------------------------------------------------------- /ast/stmt.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | type Stmt interface { 4 | PositionHolder 5 | stmtMarker() 6 | } 7 | 8 | type StmtBase struct { 9 | Node 10 | } 11 | 12 | func (stmt *StmtBase) stmtMarker() {} 13 | 14 | type AssignStmt struct { 15 | StmtBase 16 | 17 | Lhs []Expr 18 | Rhs []Expr 19 | } 20 | 21 | type LocalAssignStmt struct { 22 | StmtBase 23 | 24 | Names []string 25 | Exprs []Expr 26 | } 27 | 28 | type FuncCallStmt struct { 29 | StmtBase 30 | 31 | Expr Expr 32 | } 33 | 34 | type DoBlockStmt struct { 35 | StmtBase 36 | 37 | Stmts []Stmt 38 | } 39 | 40 | type WhileStmt struct { 41 | StmtBase 42 | 43 | Condition Expr 44 | Stmts []Stmt 45 | } 46 | 47 | type RepeatStmt struct { 48 | StmtBase 49 | 50 | Condition Expr 51 | Stmts []Stmt 52 | } 53 | 54 | type IfStmt struct { 55 | StmtBase 56 | 57 | Condition Expr 58 | Then []Stmt 59 | Else []Stmt 60 | } 61 | 62 | type NumberForStmt struct { 63 | StmtBase 64 | 65 | Name string 66 | Init Expr 67 | Limit Expr 68 | Step Expr 69 | Stmts []Stmt 70 | } 71 | 72 | type GenericForStmt struct { 73 | StmtBase 74 | 75 | Names []string 76 | Exprs []Expr 77 | Stmts []Stmt 78 | } 79 | 80 | type FuncDefStmt struct { 81 | StmtBase 82 | 83 | Name *FuncName 84 | Func *FunctionExpr 85 | } 86 | 87 | type ReturnStmt struct { 88 | StmtBase 89 | 90 | Exprs []Expr 91 | } 92 | 93 | type BreakStmt struct { 94 | StmtBase 95 | } 96 | 97 | type LabelStmt struct { 98 | StmtBase 99 | 100 | Name string 101 | } 102 | 103 | type GotoStmt struct { 104 | StmtBase 105 | 106 | Label string 107 | } 108 | -------------------------------------------------------------------------------- /ast/token.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Position struct { 8 | Source string 9 | Line int 10 | Column int 11 | } 12 | 13 | type Token struct { 14 | Type int 15 | Name string 16 | Str string 17 | Pos Position 18 | } 19 | 20 | func (self *Token) String() string { 21 | return fmt.Sprintf("", self.Name, self.Str) 22 | } 23 | -------------------------------------------------------------------------------- /auxlib_test.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func TestCheckInt(t *testing.T) { 9 | L := NewState() 10 | defer L.Close() 11 | errorIfGFuncNotFail(t, L, func(L *LState) int { 12 | L.Push(LNumber(10)) 13 | errorIfNotEqual(t, 10, L.CheckInt(2)) 14 | L.Push(LString("aaa")) 15 | L.CheckInt(3) 16 | return 0 17 | }, "number expected, got string") 18 | } 19 | 20 | func TestCheckInt64(t *testing.T) { 21 | L := NewState() 22 | defer L.Close() 23 | errorIfGFuncNotFail(t, L, func(L *LState) int { 24 | L.Push(LNumber(10)) 25 | errorIfNotEqual(t, int64(10), L.CheckInt64(2)) 26 | L.Push(LString("aaa")) 27 | L.CheckInt64(3) 28 | return 0 29 | }, "number expected, got string") 30 | } 31 | 32 | func TestCheckNumber(t *testing.T) { 33 | L := NewState() 34 | defer L.Close() 35 | errorIfGFuncNotFail(t, L, func(L *LState) int { 36 | L.Push(LNumber(10)) 37 | errorIfNotEqual(t, LNumber(10), L.CheckNumber(2)) 38 | L.Push(LString("11")) 39 | errorIfNotEqual(t, LNumber(11), L.CheckNumber(3)) 40 | L.Push(LString("aaa")) 41 | L.CheckNumber(4) 42 | return 0 43 | }, "number expected, got string") 44 | } 45 | 46 | func TestCheckString(t *testing.T) { 47 | L := NewState() 48 | defer L.Close() 49 | errorIfGFuncNotFail(t, L, func(L *LState) int { 50 | L.Push(LString("aaa")) 51 | errorIfNotEqual(t, "aaa", L.CheckString(2)) 52 | L.Push(LNumber(10)) 53 | errorIfNotEqual(t, "10", L.CheckString(3)) 54 | L.Push(L.NewTable()) 55 | L.CheckString(4) 56 | return 0 57 | }, "string expected, got table") 58 | } 59 | 60 | func TestCheckBool(t *testing.T) { 61 | L := NewState() 62 | defer L.Close() 63 | errorIfGFuncNotFail(t, L, func(L *LState) int { 64 | L.Push(LTrue) 65 | errorIfNotEqual(t, true, L.CheckBool(2)) 66 | L.Push(LNumber(10)) 67 | L.CheckBool(3) 68 | return 0 69 | }, "boolean expected, got number") 70 | } 71 | 72 | func TestCheckTable(t *testing.T) { 73 | L := NewState() 74 | defer L.Close() 75 | errorIfGFuncNotFail(t, L, func(L *LState) int { 76 | tbl := L.NewTable() 77 | L.Push(tbl) 78 | errorIfNotEqual(t, tbl, L.CheckTable(2)) 79 | L.Push(LNumber(10)) 80 | L.CheckTable(3) 81 | return 0 82 | }, "table expected, got number") 83 | } 84 | 85 | func TestCheckFunction(t *testing.T) { 86 | L := NewState() 87 | defer L.Close() 88 | errorIfGFuncNotFail(t, L, func(L *LState) int { 89 | fn := L.NewFunction(func(l *LState) int { return 0 }) 90 | L.Push(fn) 91 | errorIfNotEqual(t, fn, L.CheckFunction(2)) 92 | L.Push(LNumber(10)) 93 | L.CheckFunction(3) 94 | return 0 95 | }, "function expected, got number") 96 | } 97 | 98 | func TestCheckUserData(t *testing.T) { 99 | L := NewState() 100 | defer L.Close() 101 | errorIfGFuncNotFail(t, L, func(L *LState) int { 102 | ud := L.NewUserData() 103 | L.Push(ud) 104 | errorIfNotEqual(t, ud, L.CheckUserData(2)) 105 | L.Push(LNumber(10)) 106 | L.CheckUserData(3) 107 | return 0 108 | }, "userdata expected, got number") 109 | } 110 | 111 | func TestCheckThread(t *testing.T) { 112 | L := NewState() 113 | defer L.Close() 114 | errorIfGFuncNotFail(t, L, func(L *LState) int { 115 | th, _ := L.NewThread() 116 | L.Push(th) 117 | errorIfNotEqual(t, th, L.CheckThread(2)) 118 | L.Push(LNumber(10)) 119 | L.CheckThread(3) 120 | return 0 121 | }, "thread expected, got number") 122 | } 123 | 124 | func TestCheckChannel(t *testing.T) { 125 | L := NewState() 126 | defer L.Close() 127 | errorIfGFuncNotFail(t, L, func(L *LState) int { 128 | ch := make(chan LValue) 129 | L.Push(LChannel(ch)) 130 | errorIfNotEqual(t, ch, L.CheckChannel(2)) 131 | L.Push(LString("aaa")) 132 | L.CheckChannel(3) 133 | return 0 134 | }, "channel expected, got string") 135 | } 136 | 137 | func TestCheckType(t *testing.T) { 138 | L := NewState() 139 | defer L.Close() 140 | errorIfGFuncNotFail(t, L, func(L *LState) int { 141 | L.Push(LNumber(10)) 142 | L.CheckType(2, LTNumber) 143 | L.CheckType(2, LTString) 144 | return 0 145 | }, "string expected, got number") 146 | } 147 | 148 | func TestCheckTypes(t *testing.T) { 149 | L := NewState() 150 | defer L.Close() 151 | errorIfGFuncNotFail(t, L, func(L *LState) int { 152 | L.Push(LNumber(10)) 153 | L.CheckTypes(2, LTString, LTBool, LTNumber) 154 | L.CheckTypes(2, LTString, LTBool) 155 | return 0 156 | }, "string or boolean expected, got number") 157 | } 158 | 159 | func TestCheckOption(t *testing.T) { 160 | opts := []string{ 161 | "opt1", 162 | "opt2", 163 | "opt3", 164 | } 165 | L := NewState() 166 | defer L.Close() 167 | errorIfGFuncNotFail(t, L, func(L *LState) int { 168 | L.Push(LString("opt1")) 169 | errorIfNotEqual(t, 0, L.CheckOption(2, opts)) 170 | L.Push(LString("opt5")) 171 | L.CheckOption(3, opts) 172 | return 0 173 | }, "invalid option: opt5 \\(must be one of opt1,opt2,opt3\\)") 174 | } 175 | 176 | func TestOptInt(t *testing.T) { 177 | L := NewState() 178 | defer L.Close() 179 | errorIfGFuncNotFail(t, L, func(L *LState) int { 180 | errorIfNotEqual(t, 99, L.OptInt(1, 99)) 181 | L.Push(LNumber(10)) 182 | errorIfNotEqual(t, 10, L.OptInt(2, 99)) 183 | L.Push(LString("aaa")) 184 | L.OptInt(3, 99) 185 | return 0 186 | }, "number expected, got string") 187 | } 188 | 189 | func TestOptInt64(t *testing.T) { 190 | L := NewState() 191 | defer L.Close() 192 | errorIfGFuncNotFail(t, L, func(L *LState) int { 193 | errorIfNotEqual(t, int64(99), L.OptInt64(1, int64(99))) 194 | L.Push(LNumber(10)) 195 | errorIfNotEqual(t, int64(10), L.OptInt64(2, int64(99))) 196 | L.Push(LString("aaa")) 197 | L.OptInt64(3, int64(99)) 198 | return 0 199 | }, "number expected, got string") 200 | } 201 | 202 | func TestOptNumber(t *testing.T) { 203 | L := NewState() 204 | defer L.Close() 205 | errorIfGFuncNotFail(t, L, func(L *LState) int { 206 | errorIfNotEqual(t, LNumber(99), L.OptNumber(1, LNumber(99))) 207 | L.Push(LNumber(10)) 208 | errorIfNotEqual(t, LNumber(10), L.OptNumber(2, LNumber(99))) 209 | L.Push(LString("aaa")) 210 | L.OptNumber(3, LNumber(99)) 211 | return 0 212 | }, "number expected, got string") 213 | } 214 | 215 | func TestOptString(t *testing.T) { 216 | L := NewState() 217 | defer L.Close() 218 | errorIfGFuncNotFail(t, L, func(L *LState) int { 219 | errorIfNotEqual(t, "bbb", L.OptString(1, "bbb")) 220 | L.Push(LString("aaa")) 221 | errorIfNotEqual(t, "aaa", L.OptString(2, "bbb")) 222 | L.Push(LNumber(10)) 223 | L.OptString(3, "bbb") 224 | return 0 225 | }, "string expected, got number") 226 | } 227 | 228 | func TestOptBool(t *testing.T) { 229 | L := NewState() 230 | defer L.Close() 231 | errorIfGFuncNotFail(t, L, func(L *LState) int { 232 | errorIfNotEqual(t, true, L.OptBool(1, true)) 233 | L.Push(LTrue) 234 | errorIfNotEqual(t, true, L.OptBool(2, false)) 235 | L.Push(LNumber(10)) 236 | L.OptBool(3, false) 237 | return 0 238 | }, "boolean expected, got number") 239 | } 240 | 241 | func TestOptTable(t *testing.T) { 242 | L := NewState() 243 | defer L.Close() 244 | errorIfGFuncNotFail(t, L, func(L *LState) int { 245 | deftbl := L.NewTable() 246 | errorIfNotEqual(t, deftbl, L.OptTable(1, deftbl)) 247 | tbl := L.NewTable() 248 | L.Push(tbl) 249 | errorIfNotEqual(t, tbl, L.OptTable(2, deftbl)) 250 | L.Push(LNumber(10)) 251 | L.OptTable(3, deftbl) 252 | return 0 253 | }, "table expected, got number") 254 | } 255 | 256 | func TestOptFunction(t *testing.T) { 257 | L := NewState() 258 | defer L.Close() 259 | errorIfGFuncNotFail(t, L, func(L *LState) int { 260 | deffn := L.NewFunction(func(l *LState) int { return 0 }) 261 | errorIfNotEqual(t, deffn, L.OptFunction(1, deffn)) 262 | fn := L.NewFunction(func(l *LState) int { return 0 }) 263 | L.Push(fn) 264 | errorIfNotEqual(t, fn, L.OptFunction(2, deffn)) 265 | L.Push(LNumber(10)) 266 | L.OptFunction(3, deffn) 267 | return 0 268 | }, "function expected, got number") 269 | } 270 | 271 | func TestOptUserData(t *testing.T) { 272 | L := NewState() 273 | defer L.Close() 274 | errorIfGFuncNotFail(t, L, func(L *LState) int { 275 | defud := L.NewUserData() 276 | errorIfNotEqual(t, defud, L.OptUserData(1, defud)) 277 | ud := L.NewUserData() 278 | L.Push(ud) 279 | errorIfNotEqual(t, ud, L.OptUserData(2, defud)) 280 | L.Push(LNumber(10)) 281 | L.OptUserData(3, defud) 282 | return 0 283 | }, "userdata expected, got number") 284 | } 285 | 286 | func TestOptChannel(t *testing.T) { 287 | L := NewState() 288 | defer L.Close() 289 | errorIfGFuncNotFail(t, L, func(L *LState) int { 290 | defch := make(chan LValue) 291 | errorIfNotEqual(t, defch, L.OptChannel(1, defch)) 292 | ch := make(chan LValue) 293 | L.Push(LChannel(ch)) 294 | errorIfNotEqual(t, ch, L.OptChannel(2, defch)) 295 | L.Push(LString("aaa")) 296 | L.OptChannel(3, defch) 297 | return 0 298 | }, "channel expected, got string") 299 | } 300 | 301 | func TestLoadFileForShebang(t *testing.T) { 302 | tmpFile, err := os.CreateTemp("", "") 303 | errorIfNotNil(t, err) 304 | 305 | err = os.WriteFile(tmpFile.Name(), []byte(`#!/path/to/lua 306 | print("hello") 307 | `), 0644) 308 | errorIfNotNil(t, err) 309 | 310 | defer func() { 311 | tmpFile.Close() 312 | os.Remove(tmpFile.Name()) 313 | }() 314 | 315 | L := NewState() 316 | defer L.Close() 317 | 318 | _, err = L.LoadFile(tmpFile.Name()) 319 | errorIfNotNil(t, err) 320 | } 321 | 322 | func TestLoadFileForEmptyFile(t *testing.T) { 323 | tmpFile, err := os.CreateTemp("", "") 324 | errorIfNotNil(t, err) 325 | 326 | defer func() { 327 | tmpFile.Close() 328 | os.Remove(tmpFile.Name()) 329 | }() 330 | 331 | L := NewState() 332 | defer L.Close() 333 | 334 | _, err = L.LoadFile(tmpFile.Name()) 335 | errorIfNotNil(t, err) 336 | } 337 | -------------------------------------------------------------------------------- /baselib_test.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestOsDateFormatUTCWithTwoParam(t *testing.T) { 10 | t.Setenv("TZ", "Asia/Tokyo") 11 | ls := NewState() 12 | 13 | g := ls.GetGlobal("os") 14 | fn := ls.GetField(g, "date") 15 | 16 | int64ptr := func(i int64) *int64 { 17 | return &i 18 | } 19 | cases := []struct { 20 | Name string 21 | Local time.Time 22 | Now time.Time 23 | Format string 24 | Timestamp *int64 25 | }{ 26 | { 27 | "UTCWithTwoParam", 28 | time.Now(), 29 | time.Now().UTC(), 30 | "!*t", 31 | int64ptr(time.Now().UTC().Unix()), 32 | }, 33 | { 34 | "LocalWithTwoParam", 35 | time.Now(), 36 | time.Now(), 37 | "*t", 38 | int64ptr(time.Now().Unix()), 39 | }, 40 | { 41 | "UTCWithOnlyFormatParam", 42 | time.Now(), 43 | time.Now().UTC(), 44 | "!*t", 45 | nil, 46 | }, 47 | { 48 | "LocalWithOnlyFormatParam", 49 | time.Now(), 50 | time.Now(), 51 | "*t", 52 | nil, 53 | }, 54 | } 55 | 56 | for _, c := range cases { 57 | t.Run(c.Name, func(t *testing.T) { 58 | args := make([]LValue, 0) 59 | args = append(args, LString(c.Format)) 60 | if c.Timestamp != nil { 61 | args = append(args, LNumber(*c.Timestamp)) 62 | } 63 | err := ls.CallByParam(P{ 64 | Fn: fn, 65 | NRet: 1, 66 | Protect: true, 67 | }, args...) 68 | if err != nil { 69 | t.Fatal(err) 70 | } 71 | 72 | result := ls.ToTable(-1) 73 | 74 | resultMap := make(map[string]string) 75 | result.ForEach(func(key LValue, value LValue) { 76 | resultMap[key.String()] = value.String() 77 | assertOsDateFields(t, key, value, c.Now) 78 | }) 79 | t.Logf("%v resultMap=%+v\nnow=%+v\nLocal=%+v\nUTC=%v", c.Name, resultMap, c.Now, c.Local, c.Now.UTC()) 80 | }) 81 | } 82 | } 83 | 84 | func TestOsDateFormatLocalWithTwoParam(t *testing.T) { 85 | t.Setenv("TZ", "Asia/Tokyo") 86 | ls := NewState() 87 | 88 | g := ls.GetGlobal("os") 89 | fn := ls.GetField(g, "date") 90 | 91 | nowLocal := time.Now() 92 | nowUTC := nowLocal.UTC() 93 | 94 | err := ls.CallByParam(P{ 95 | Fn: fn, 96 | NRet: 1, 97 | Protect: true, 98 | }, LString("*t"), LNumber(nowLocal.Unix())) 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | 103 | result := ls.ToTable(-1) 104 | 105 | resultMap := make(map[string]string) 106 | result.ForEach(func(key LValue, value LValue) { 107 | t.Logf("key=%v, value=%v", key, value) 108 | resultMap[key.String()] = value.String() 109 | assertOsDateFields(t, key, value, nowLocal) 110 | }) 111 | t.Logf("resultMap=%+v, nowLocal=%+v, nowUTC=%v", resultMap, nowLocal, nowUTC) 112 | } 113 | 114 | func assertOsDateFields(t *testing.T, key LValue, value LValue, expect time.Time) { 115 | switch key.String() { 116 | case "year": 117 | if value.String() != strconv.Itoa(expect.Year()) { 118 | t.Errorf("year=%v, expect.Year=%v", value.String(), expect.Year()) 119 | } 120 | case "month": 121 | if value.String() != strconv.Itoa(int(expect.Month())) { 122 | t.Errorf("month=%v, expect.Month=%v", value.String(), expect.Month()) 123 | } 124 | case "day": 125 | if value.String() != strconv.Itoa(expect.Day()) { 126 | t.Errorf("day=%v, expect.Day=%v", value.String(), expect.Day()) 127 | } 128 | case "hour": 129 | if value.String() != strconv.Itoa(expect.Hour()) { 130 | t.Errorf("hour=%v, expect.Hour=%v", value.String(), expect.Hour()) 131 | } 132 | case "min": 133 | if value.String() != strconv.Itoa(expect.Minute()) { 134 | t.Errorf("min=%v, expect.Minute=%v", value.String(), expect.Minute()) 135 | } 136 | case "sec": 137 | if value.String() != strconv.Itoa(expect.Second()) { 138 | t.Errorf("sec=%v, expect.Second=%v", value.String(), expect.Second()) 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /channellib.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func checkChannel(L *LState, idx int) reflect.Value { 8 | ch := L.CheckChannel(idx) 9 | return reflect.ValueOf(ch) 10 | } 11 | 12 | func checkGoroutineSafe(L *LState, idx int) LValue { 13 | v := L.CheckAny(2) 14 | if !isGoroutineSafe(v) { 15 | L.ArgError(2, "can not send a function, userdata, thread or table that has a metatable") 16 | } 17 | return v 18 | } 19 | 20 | func OpenChannel(L *LState) int { 21 | var mod LValue 22 | //_, ok := L.G.builtinMts[int(LTChannel)] 23 | // if !ok { 24 | mod = L.RegisterModule(ChannelLibName, channelFuncs) 25 | mt := L.SetFuncs(L.NewTable(), channelMethods) 26 | mt.RawSetString("__index", mt) 27 | L.G.builtinMts[int(LTChannel)] = mt 28 | // } 29 | L.Push(mod) 30 | return 1 31 | } 32 | 33 | var channelFuncs = map[string]LGFunction{ 34 | "make": channelMake, 35 | "select": channelSelect, 36 | } 37 | 38 | func channelMake(L *LState) int { 39 | buffer := L.OptInt(1, 0) 40 | L.Push(LChannel(make(chan LValue, buffer))) 41 | return 1 42 | } 43 | 44 | func channelSelect(L *LState) int { 45 | //TODO check case table size 46 | cases := make([]reflect.SelectCase, L.GetTop()) 47 | top := L.GetTop() 48 | for i := 0; i < top; i++ { 49 | cas := reflect.SelectCase{ 50 | Dir: reflect.SelectSend, 51 | Chan: reflect.ValueOf(nil), 52 | Send: reflect.ValueOf(nil), 53 | } 54 | tbl := L.CheckTable(i + 1) 55 | dir, ok1 := tbl.RawGetInt(1).(LString) 56 | if !ok1 { 57 | L.ArgError(i+1, "invalid select case") 58 | } 59 | switch string(dir) { 60 | case "<-|": 61 | ch, ok := tbl.RawGetInt(2).(LChannel) 62 | if !ok { 63 | L.ArgError(i+1, "invalid select case") 64 | } 65 | cas.Chan = reflect.ValueOf((chan LValue)(ch)) 66 | v := tbl.RawGetInt(3) 67 | if !isGoroutineSafe(v) { 68 | L.ArgError(i+1, "can not send a function, userdata, thread or table that has a metatable") 69 | } 70 | cas.Send = reflect.ValueOf(v) 71 | case "|<-": 72 | ch, ok := tbl.RawGetInt(2).(LChannel) 73 | if !ok { 74 | L.ArgError(i+1, "invalid select case") 75 | } 76 | cas.Chan = reflect.ValueOf((chan LValue)(ch)) 77 | cas.Dir = reflect.SelectRecv 78 | case "default": 79 | cas.Dir = reflect.SelectDefault 80 | default: 81 | L.ArgError(i+1, "invalid channel direction:"+string(dir)) 82 | } 83 | cases[i] = cas 84 | } 85 | 86 | if L.ctx != nil { 87 | cases = append(cases, reflect.SelectCase{ 88 | Dir: reflect.SelectRecv, 89 | Chan: reflect.ValueOf(L.ctx.Done()), 90 | Send: reflect.ValueOf(nil), 91 | }) 92 | } 93 | 94 | pos, recv, rok := reflect.Select(cases) 95 | 96 | if L.ctx != nil && pos == L.GetTop() { 97 | return 0 98 | } 99 | 100 | lv := LNil 101 | if recv.Kind() != 0 { 102 | lv, _ = recv.Interface().(LValue) 103 | if lv == nil { 104 | lv = LNil 105 | } 106 | } 107 | tbl := L.Get(pos + 1).(*LTable) 108 | last := tbl.RawGetInt(tbl.Len()) 109 | if last.Type() == LTFunction { 110 | L.Push(last) 111 | switch cases[pos].Dir { 112 | case reflect.SelectRecv: 113 | if rok { 114 | L.Push(LTrue) 115 | } else { 116 | L.Push(LFalse) 117 | } 118 | L.Push(lv) 119 | L.Call(2, 0) 120 | case reflect.SelectSend: 121 | L.Push(tbl.RawGetInt(3)) 122 | L.Call(1, 0) 123 | case reflect.SelectDefault: 124 | L.Call(0, 0) 125 | } 126 | } 127 | L.Push(LNumber(pos + 1)) 128 | L.Push(lv) 129 | if rok { 130 | L.Push(LTrue) 131 | } else { 132 | L.Push(LFalse) 133 | } 134 | return 3 135 | } 136 | 137 | var channelMethods = map[string]LGFunction{ 138 | "receive": channelReceive, 139 | "send": channelSend, 140 | "close": channelClose, 141 | } 142 | 143 | func channelReceive(L *LState) int { 144 | rch := checkChannel(L, 1) 145 | var v reflect.Value 146 | var ok bool 147 | if L.ctx != nil { 148 | cases := []reflect.SelectCase{{ 149 | Dir: reflect.SelectRecv, 150 | Chan: reflect.ValueOf(L.ctx.Done()), 151 | Send: reflect.ValueOf(nil), 152 | }, { 153 | Dir: reflect.SelectRecv, 154 | Chan: rch, 155 | Send: reflect.ValueOf(nil), 156 | }} 157 | _, v, ok = reflect.Select(cases) 158 | } else { 159 | v, ok = rch.Recv() 160 | } 161 | if ok { 162 | L.Push(LTrue) 163 | L.Push(v.Interface().(LValue)) 164 | } else { 165 | L.Push(LFalse) 166 | L.Push(LNil) 167 | } 168 | return 2 169 | } 170 | 171 | func channelSend(L *LState) int { 172 | rch := checkChannel(L, 1) 173 | v := checkGoroutineSafe(L, 2) 174 | rch.Send(reflect.ValueOf(v)) 175 | return 0 176 | } 177 | 178 | func channelClose(L *LState) int { 179 | rch := checkChannel(L, 1) 180 | rch.Close() 181 | return 0 182 | } 183 | 184 | // 185 | -------------------------------------------------------------------------------- /channellib_test.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "context" 5 | "reflect" 6 | "sync" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestChannelMake(t *testing.T) { 12 | L := NewState() 13 | defer L.Close() 14 | errorIfScriptFail(t, L, ` 15 | ch = channel.make() 16 | `) 17 | obj := L.GetGlobal("ch") 18 | ch, ok := obj.(LChannel) 19 | errorIfFalse(t, ok, "channel expected") 20 | errorIfNotEqual(t, 0, reflect.ValueOf(ch).Cap()) 21 | close(ch) 22 | 23 | errorIfScriptFail(t, L, ` 24 | ch = channel.make(10) 25 | `) 26 | obj = L.GetGlobal("ch") 27 | ch, _ = obj.(LChannel) 28 | errorIfNotEqual(t, 10, reflect.ValueOf(ch).Cap()) 29 | close(ch) 30 | } 31 | 32 | func TestChannelSelectError(t *testing.T) { 33 | L := NewState() 34 | defer L.Close() 35 | errorIfScriptFail(t, L, `ch = channel.make()`) 36 | errorIfScriptNotFail(t, L, `channel.select({1,2,3})`, "invalid select case") 37 | errorIfScriptNotFail(t, L, `channel.select({"<-|", 1, 3})`, "invalid select case") 38 | errorIfScriptNotFail(t, L, `channel.select({"<-|", ch, function() end})`, "can not send a function") 39 | errorIfScriptNotFail(t, L, `channel.select({"|<-", 1, 3})`, "invalid select case") 40 | errorIfScriptNotFail(t, L, `channel.select({"<-->", 1, 3})`, "invalid channel direction") 41 | errorIfScriptFail(t, L, `ch:close()`) 42 | } 43 | 44 | func TestChannelSelect1(t *testing.T) { 45 | var result LValue 46 | var wg sync.WaitGroup 47 | receiver := func(ch, quit chan LValue) { 48 | defer wg.Done() 49 | L := NewState() 50 | defer L.Close() 51 | L.SetGlobal("ch", LChannel(ch)) 52 | L.SetGlobal("quit", LChannel(quit)) 53 | if err := L.DoString(` 54 | buf = "" 55 | local exit = false 56 | while not exit do 57 | channel.select( 58 | {"|<-", ch, function(ok, v) 59 | if not ok then 60 | buf = buf .. "channel closed" 61 | exit = true 62 | else 63 | buf = buf .. "received:" .. v 64 | end 65 | end}, 66 | {"|<-", quit, function(ok, v) 67 | buf = buf .. "quit" 68 | end} 69 | ) 70 | end 71 | `); err != nil { 72 | panic(err) 73 | } 74 | result = L.GetGlobal("buf") 75 | } 76 | 77 | sender := func(ch, quit chan LValue) { 78 | defer wg.Done() 79 | L := NewState() 80 | defer L.Close() 81 | L.SetGlobal("ch", LChannel(ch)) 82 | L.SetGlobal("quit", LChannel(quit)) 83 | if err := L.DoString(` 84 | ch:send("1") 85 | ch:send("2") 86 | `); err != nil { 87 | panic(err) 88 | } 89 | ch <- LString("3") 90 | quit <- LTrue 91 | time.Sleep(1 * time.Second) 92 | close(ch) 93 | } 94 | 95 | ch := make(chan LValue) 96 | quit := make(chan LValue) 97 | wg.Add(2) 98 | go receiver(ch, quit) 99 | go sender(ch, quit) 100 | wg.Wait() 101 | lstr, ok := result.(LString) 102 | errorIfFalse(t, ok, "must be string") 103 | str := string(lstr) 104 | errorIfNotEqual(t, "received:1received:2received:3quitchannel closed", str) 105 | 106 | } 107 | 108 | func TestChannelSelect2(t *testing.T) { 109 | var wg sync.WaitGroup 110 | receiver := func(ch, quit chan LValue) { 111 | defer wg.Done() 112 | L := NewState() 113 | defer L.Close() 114 | L.SetGlobal("ch", LChannel(ch)) 115 | L.SetGlobal("quit", LChannel(quit)) 116 | errorIfScriptFail(t, L, ` 117 | idx, rcv, ok = channel.select( 118 | {"|<-", ch}, 119 | {"|<-", quit} 120 | ) 121 | assert(idx == 1) 122 | assert(rcv == "1") 123 | assert(ok) 124 | idx, rcv, ok = channel.select( 125 | {"|<-", ch}, 126 | {"|<-", quit} 127 | ) 128 | assert(idx == 1) 129 | assert(rcv == nil) 130 | assert(not ok) 131 | `) 132 | } 133 | 134 | sender := func(ch, quit chan LValue) { 135 | defer wg.Done() 136 | L := NewState() 137 | defer L.Close() 138 | L.SetGlobal("ch", LChannel(ch)) 139 | L.SetGlobal("quit", LChannel(quit)) 140 | errorIfScriptFail(t, L, `ch:send("1")`) 141 | errorIfScriptFail(t, L, `ch:close()`) 142 | } 143 | 144 | ch := make(chan LValue) 145 | quit := make(chan LValue) 146 | wg.Add(2) 147 | go receiver(ch, quit) 148 | go sender(ch, quit) 149 | wg.Wait() 150 | } 151 | 152 | func TestChannelSelect3(t *testing.T) { 153 | var wg sync.WaitGroup 154 | receiver := func(ch chan LValue) { 155 | defer wg.Done() 156 | L := NewState() 157 | defer L.Close() 158 | L.SetGlobal("ch", LChannel(ch)) 159 | errorIfScriptFail(t, L, ` 160 | ok = true 161 | while ok do 162 | idx, rcv, ok = channel.select( 163 | {"|<-", ch} 164 | ) 165 | end 166 | `) 167 | } 168 | 169 | sender := func(ch chan LValue) { 170 | defer wg.Done() 171 | L := NewState() 172 | defer L.Close() 173 | L.SetGlobal("ch", LChannel(ch)) 174 | errorIfScriptFail(t, L, ` 175 | ok = false 176 | channel.select( 177 | {"<-|", ch, "1", function(v) 178 | ok = true 179 | end} 180 | ) 181 | assert(ok) 182 | idx, rcv, ok = channel.select( 183 | {"<-|", ch, "1"} 184 | ) 185 | assert(idx == 1) 186 | ch:close() 187 | `) 188 | } 189 | 190 | ch := make(chan LValue) 191 | wg.Add(2) 192 | go receiver(ch) 193 | time.Sleep(1) 194 | go sender(ch) 195 | wg.Wait() 196 | } 197 | 198 | func TestChannelSelect4(t *testing.T) { 199 | var wg sync.WaitGroup 200 | receiver := func(ch chan LValue) { 201 | defer wg.Done() 202 | L := NewState() 203 | defer L.Close() 204 | L.SetGlobal("ch", LChannel(ch)) 205 | errorIfScriptFail(t, L, ` 206 | idx, rcv, ok = channel.select( 207 | {"|<-", ch}, 208 | {"default"} 209 | ) 210 | assert(idx == 2) 211 | called = false 212 | idx, rcv, ok = channel.select( 213 | {"|<-", ch}, 214 | {"default", function() 215 | called = true 216 | end} 217 | ) 218 | assert(called) 219 | ch:close() 220 | `) 221 | } 222 | 223 | ch := make(chan LValue) 224 | wg.Add(1) 225 | go receiver(ch) 226 | wg.Wait() 227 | } 228 | 229 | func TestChannelSendReceive1(t *testing.T) { 230 | var wg sync.WaitGroup 231 | receiver := func(ch chan LValue) { 232 | defer wg.Done() 233 | L := NewState() 234 | defer L.Close() 235 | L.SetGlobal("ch", LChannel(ch)) 236 | errorIfScriptFail(t, L, ` 237 | local ok, v = ch:receive() 238 | assert(ok) 239 | assert(v == "1") 240 | `) 241 | time.Sleep(1 * time.Second) 242 | errorIfScriptFail(t, L, ` 243 | local ok, v = ch:receive() 244 | assert(not ok) 245 | assert(v == nil) 246 | `) 247 | } 248 | sender := func(ch chan LValue) { 249 | defer wg.Done() 250 | L := NewState() 251 | defer L.Close() 252 | L.SetGlobal("ch", LChannel(ch)) 253 | errorIfScriptFail(t, L, `ch:send("1")`) 254 | errorIfScriptNotFail(t, L, `ch:send(function() end)`, "can not send a function") 255 | errorIfScriptFail(t, L, `ch:close()`) 256 | } 257 | ch := make(chan LValue) 258 | wg.Add(2) 259 | go receiver(ch) 260 | go sender(ch) 261 | wg.Wait() 262 | } 263 | 264 | func TestCancelChannelReceive(t *testing.T) { 265 | done := make(chan struct{}) 266 | ctx, cancel := context.WithCancel(context.Background()) 267 | go func() { 268 | defer close(done) 269 | L := NewState() 270 | L.SetContext(ctx) 271 | defer L.Close() 272 | L.SetGlobal("ch", LChannel(make(chan LValue))) 273 | errorIfScriptNotFail(t, L, `ch:receive()`, context.Canceled.Error()) 274 | }() 275 | time.Sleep(time.Second) 276 | cancel() 277 | <-done 278 | } 279 | 280 | func TestCancelChannelReceive2(t *testing.T) { 281 | done := make(chan struct{}) 282 | ctx, cancel := context.WithCancel(context.Background()) 283 | go func() { 284 | defer close(done) 285 | L := NewState() 286 | L.SetContext(ctx) 287 | defer L.Close() 288 | L.SetGlobal("ch", LChannel(make(chan LValue))) 289 | errorIfScriptNotFail(t, L, `channel.select({"|<-", ch})`, context.Canceled.Error()) 290 | }() 291 | time.Sleep(time.Second) 292 | cancel() 293 | <-done 294 | } 295 | -------------------------------------------------------------------------------- /cmd/glua/glua.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/chzyer/readline" 7 | "github.com/yuin/gopher-lua" 8 | "github.com/yuin/gopher-lua/parse" 9 | "os" 10 | "runtime/pprof" 11 | ) 12 | 13 | func main() { 14 | os.Exit(mainAux()) 15 | } 16 | 17 | func mainAux() int { 18 | var opt_e, opt_l, opt_p string 19 | var opt_i, opt_v, opt_dt, opt_dc bool 20 | var opt_m int 21 | flag.StringVar(&opt_e, "e", "", "") 22 | flag.StringVar(&opt_l, "l", "", "") 23 | flag.StringVar(&opt_p, "p", "", "") 24 | flag.IntVar(&opt_m, "mx", 0, "") 25 | flag.BoolVar(&opt_i, "i", false, "") 26 | flag.BoolVar(&opt_v, "v", false, "") 27 | flag.BoolVar(&opt_dt, "dt", false, "") 28 | flag.BoolVar(&opt_dc, "dc", false, "") 29 | flag.Usage = func() { 30 | fmt.Println(`Usage: glua [options] [script [args]]. 31 | Available options are: 32 | -e stat execute string 'stat' 33 | -l name require library 'name' 34 | -mx MB memory limit(default: unlimited) 35 | -dt dump AST trees 36 | -dc dump VM codes 37 | -i enter interactive mode after executing 'script' 38 | -p file write cpu profiles to the file 39 | -v show version information`) 40 | } 41 | flag.Parse() 42 | if len(opt_p) != 0 { 43 | f, err := os.Create(opt_p) 44 | if err != nil { 45 | fmt.Println(err.Error()) 46 | os.Exit(1) 47 | } 48 | pprof.StartCPUProfile(f) 49 | defer pprof.StopCPUProfile() 50 | } 51 | if len(opt_e) == 0 && !opt_i && !opt_v && flag.NArg() == 0 { 52 | opt_i = true 53 | } 54 | 55 | status := 0 56 | 57 | L := lua.NewState() 58 | defer L.Close() 59 | if opt_m > 0 { 60 | L.SetMx(opt_m) 61 | } 62 | 63 | if opt_v || opt_i { 64 | fmt.Println(lua.PackageCopyRight) 65 | } 66 | 67 | if len(opt_l) > 0 { 68 | if err := L.DoFile(opt_l); err != nil { 69 | fmt.Println(err.Error()) 70 | } 71 | } 72 | 73 | if nargs := flag.NArg(); nargs > 0 { 74 | script := flag.Arg(0) 75 | argtb := L.NewTable() 76 | for i := 1; i < nargs; i++ { 77 | L.RawSet(argtb, lua.LNumber(i), lua.LString(flag.Arg(i))) 78 | } 79 | L.SetGlobal("arg", argtb) 80 | if opt_dt || opt_dc { 81 | file, err := os.Open(script) 82 | if err != nil { 83 | fmt.Println(err.Error()) 84 | return 1 85 | } 86 | chunk, err2 := parse.Parse(file, script) 87 | if err2 != nil { 88 | fmt.Println(err2.Error()) 89 | return 1 90 | } 91 | if opt_dt { 92 | fmt.Println(parse.Dump(chunk)) 93 | } 94 | if opt_dc { 95 | proto, err3 := lua.Compile(chunk, script) 96 | if err3 != nil { 97 | fmt.Println(err3.Error()) 98 | return 1 99 | } 100 | fmt.Println(proto.String()) 101 | } 102 | } 103 | if err := L.DoFile(script); err != nil { 104 | fmt.Println(err.Error()) 105 | status = 1 106 | } 107 | } 108 | 109 | if len(opt_e) > 0 { 110 | if err := L.DoString(opt_e); err != nil { 111 | fmt.Println(err.Error()) 112 | status = 1 113 | } 114 | } 115 | 116 | if opt_i { 117 | doREPL(L) 118 | } 119 | return status 120 | } 121 | 122 | // do read/eval/print/loop 123 | func doREPL(L *lua.LState) { 124 | rl, err := readline.New("> ") 125 | if err != nil { 126 | panic(err) 127 | } 128 | defer rl.Close() 129 | for { 130 | if str, err := loadline(rl, L); err == nil { 131 | if err := L.DoString(str); err != nil { 132 | fmt.Println(err) 133 | } 134 | } else { // error on loadline 135 | fmt.Println(err) 136 | return 137 | } 138 | } 139 | } 140 | 141 | func incomplete(err error) bool { 142 | if lerr, ok := err.(*lua.ApiError); ok { 143 | if perr, ok := lerr.Cause.(*parse.Error); ok { 144 | return perr.Pos.Line == parse.EOF 145 | } 146 | } 147 | return false 148 | } 149 | 150 | func loadline(rl *readline.Instance, L *lua.LState) (string, error) { 151 | rl.SetPrompt("> ") 152 | if line, err := rl.Readline(); err == nil { 153 | if _, err := L.LoadString("return " + line); err == nil { // try add return <...> then compile 154 | return line, nil 155 | } else { 156 | return multiline(line, rl, L) 157 | } 158 | } else { 159 | return "", err 160 | } 161 | } 162 | 163 | func multiline(ml string, rl *readline.Instance, L *lua.LState) (string, error) { 164 | for { 165 | if _, err := L.LoadString(ml); err == nil { // try compile 166 | return ml, nil 167 | } else if !incomplete(err) { // syntax error , but not EOF 168 | return ml, nil 169 | } else { 170 | rl.SetPrompt(">> ") 171 | if line, err := rl.Readline(); err == nil { 172 | ml = ml + "\n" + line 173 | } else { 174 | return "", err 175 | } 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | var CompatVarArg = true 8 | var FieldsPerFlush = 50 9 | var RegistrySize = 256 * 20 10 | var RegistryGrowStep = 32 11 | var CallStackSize = 256 12 | var MaxTableGetLoop = 100 13 | var MaxArrayIndex = 67108864 14 | 15 | type LNumber float64 16 | 17 | const LNumberBit = 64 18 | const LNumberScanFormat = "%f" 19 | const LuaVersion = "Lua 5.1" 20 | 21 | var LuaPath = "LUA_PATH" 22 | var LuaLDir string 23 | var LuaPathDefault string 24 | var LuaOS string 25 | var LuaDirSep string 26 | var LuaPathSep = ";" 27 | var LuaPathMark = "?" 28 | var LuaExecDir = "!" 29 | var LuaIgMark = "-" 30 | 31 | func init() { 32 | if os.PathSeparator == '/' { // unix-like 33 | LuaOS = "unix" 34 | LuaLDir = "/usr/local/share/lua/5.1" 35 | LuaDirSep = "/" 36 | LuaPathDefault = "./?.lua;" + LuaLDir + "/?.lua;" + LuaLDir + "/?/init.lua" 37 | } else { // windows 38 | LuaOS = "windows" 39 | LuaLDir = "!\\lua" 40 | LuaDirSep = "\\" 41 | LuaPathDefault = ".\\?.lua;" + LuaLDir + "\\?.lua;" + LuaLDir + "\\?\\init.lua" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /coroutinelib.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | func OpenCoroutine(L *LState) int { 4 | // TODO: Tie module name to contents of linit.go? 5 | mod := L.RegisterModule(CoroutineLibName, coFuncs) 6 | L.Push(mod) 7 | return 1 8 | } 9 | 10 | var coFuncs = map[string]LGFunction{ 11 | "create": coCreate, 12 | "yield": coYield, 13 | "resume": coResume, 14 | "running": coRunning, 15 | "status": coStatus, 16 | "wrap": coWrap, 17 | } 18 | 19 | func coCreate(L *LState) int { 20 | fn := L.CheckFunction(1) 21 | newthread, _ := L.NewThread() 22 | base := 0 23 | newthread.stack.Push(callFrame{ 24 | Fn: fn, 25 | Pc: 0, 26 | Base: base, 27 | LocalBase: base + 1, 28 | ReturnBase: base, 29 | NArgs: 0, 30 | NRet: MultRet, 31 | Parent: nil, 32 | TailCall: 0, 33 | }) 34 | L.Push(newthread) 35 | return 1 36 | } 37 | 38 | func coYield(L *LState) int { 39 | return -1 40 | } 41 | 42 | func coResume(L *LState) int { 43 | th := L.CheckThread(1) 44 | if L.G.CurrentThread == th { 45 | msg := "can not resume a running thread" 46 | if th.wrapped { 47 | L.RaiseError(msg) 48 | return 0 49 | } 50 | L.Push(LFalse) 51 | L.Push(LString(msg)) 52 | return 2 53 | } 54 | if th.Dead { 55 | msg := "can not resume a dead thread" 56 | if th.wrapped { 57 | L.RaiseError(msg) 58 | return 0 59 | } 60 | L.Push(LFalse) 61 | L.Push(LString(msg)) 62 | return 2 63 | } 64 | th.Parent = L 65 | L.G.CurrentThread = th 66 | if !th.isStarted() { 67 | cf := th.stack.Last() 68 | th.currentFrame = cf 69 | th.SetTop(0) 70 | nargs := L.GetTop() - 1 71 | L.XMoveTo(th, nargs) 72 | cf.NArgs = nargs 73 | th.initCallFrame(cf) 74 | th.Panic = panicWithoutTraceback 75 | } else { 76 | nargs := L.GetTop() - 1 77 | L.XMoveTo(th, nargs) 78 | } 79 | top := L.GetTop() 80 | threadRun(th) 81 | return L.GetTop() - top 82 | } 83 | 84 | func coRunning(L *LState) int { 85 | if L.G.MainThread == L { 86 | L.Push(LNil) 87 | return 1 88 | } 89 | L.Push(L.G.CurrentThread) 90 | return 1 91 | } 92 | 93 | func coStatus(L *LState) int { 94 | L.Push(LString(L.Status(L.CheckThread(1)))) 95 | return 1 96 | } 97 | 98 | func wrapaux(L *LState) int { 99 | L.Insert(L.ToThread(UpvalueIndex(1)), 1) 100 | return coResume(L) 101 | } 102 | 103 | func coWrap(L *LState) int { 104 | coCreate(L) 105 | L.CheckThread(L.GetTop()).wrapped = true 106 | v := L.Get(L.GetTop()) 107 | L.Pop(1) 108 | L.Push(L.NewClosure(wrapaux, v)) 109 | return 1 110 | } 111 | 112 | // 113 | -------------------------------------------------------------------------------- /debuglib.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func OpenDebug(L *LState) int { 9 | dbgmod := L.RegisterModule(DebugLibName, debugFuncs) 10 | L.Push(dbgmod) 11 | return 1 12 | } 13 | 14 | var debugFuncs = map[string]LGFunction{ 15 | "getfenv": debugGetFEnv, 16 | "getinfo": debugGetInfo, 17 | "getlocal": debugGetLocal, 18 | "getmetatable": debugGetMetatable, 19 | "getupvalue": debugGetUpvalue, 20 | "setfenv": debugSetFEnv, 21 | "setlocal": debugSetLocal, 22 | "setmetatable": debugSetMetatable, 23 | "setupvalue": debugSetUpvalue, 24 | "traceback": debugTraceback, 25 | } 26 | 27 | func debugGetFEnv(L *LState) int { 28 | L.Push(L.GetFEnv(L.CheckAny(1))) 29 | return 1 30 | } 31 | 32 | func debugGetInfo(L *LState) int { 33 | L.CheckTypes(1, LTFunction, LTNumber) 34 | arg1 := L.Get(1) 35 | what := L.OptString(2, "Slunf") 36 | var dbg *Debug 37 | var fn LValue 38 | var err error 39 | var ok bool 40 | switch lv := arg1.(type) { 41 | case *LFunction: 42 | dbg = &Debug{} 43 | fn, err = L.GetInfo(">"+what, dbg, lv) 44 | case LNumber: 45 | dbg, ok = L.GetStack(int(lv)) 46 | if !ok { 47 | L.Push(LNil) 48 | return 1 49 | } 50 | fn, err = L.GetInfo(what, dbg, LNil) 51 | } 52 | 53 | if err != nil { 54 | L.Push(LNil) 55 | return 1 56 | } 57 | tbl := L.NewTable() 58 | if len(dbg.Name) > 0 { 59 | tbl.RawSetString("name", LString(dbg.Name)) 60 | } else { 61 | tbl.RawSetString("name", LNil) 62 | } 63 | tbl.RawSetString("what", LString(dbg.What)) 64 | tbl.RawSetString("source", LString(dbg.Source)) 65 | tbl.RawSetString("currentline", LNumber(dbg.CurrentLine)) 66 | tbl.RawSetString("nups", LNumber(dbg.NUpvalues)) 67 | tbl.RawSetString("linedefined", LNumber(dbg.LineDefined)) 68 | tbl.RawSetString("lastlinedefined", LNumber(dbg.LastLineDefined)) 69 | tbl.RawSetString("func", fn) 70 | L.Push(tbl) 71 | return 1 72 | } 73 | 74 | func debugGetLocal(L *LState) int { 75 | level := L.CheckInt(1) 76 | idx := L.CheckInt(2) 77 | dbg, ok := L.GetStack(level) 78 | if !ok { 79 | L.ArgError(1, "level out of range") 80 | } 81 | name, value := L.GetLocal(dbg, idx) 82 | if len(name) > 0 { 83 | L.Push(LString(name)) 84 | L.Push(value) 85 | return 2 86 | } 87 | L.Push(LNil) 88 | return 1 89 | } 90 | 91 | func debugGetMetatable(L *LState) int { 92 | L.Push(L.GetMetatable(L.CheckAny(1))) 93 | return 1 94 | } 95 | 96 | func debugGetUpvalue(L *LState) int { 97 | fn := L.CheckFunction(1) 98 | idx := L.CheckInt(2) 99 | name, value := L.GetUpvalue(fn, idx) 100 | if len(name) > 0 { 101 | L.Push(LString(name)) 102 | L.Push(value) 103 | return 2 104 | } 105 | L.Push(LNil) 106 | return 1 107 | } 108 | 109 | func debugSetFEnv(L *LState) int { 110 | L.SetFEnv(L.CheckAny(1), L.CheckAny(2)) 111 | return 0 112 | } 113 | 114 | func debugSetLocal(L *LState) int { 115 | level := L.CheckInt(1) 116 | idx := L.CheckInt(2) 117 | value := L.CheckAny(3) 118 | dbg, ok := L.GetStack(level) 119 | if !ok { 120 | L.ArgError(1, "level out of range") 121 | } 122 | name := L.SetLocal(dbg, idx, value) 123 | if len(name) > 0 { 124 | L.Push(LString(name)) 125 | } else { 126 | L.Push(LNil) 127 | } 128 | return 1 129 | } 130 | 131 | func debugSetMetatable(L *LState) int { 132 | L.CheckTypes(2, LTNil, LTTable) 133 | obj := L.Get(1) 134 | mt := L.Get(2) 135 | L.SetMetatable(obj, mt) 136 | L.SetTop(1) 137 | return 1 138 | } 139 | 140 | func debugSetUpvalue(L *LState) int { 141 | fn := L.CheckFunction(1) 142 | idx := L.CheckInt(2) 143 | value := L.CheckAny(3) 144 | name := L.SetUpvalue(fn, idx, value) 145 | if len(name) > 0 { 146 | L.Push(LString(name)) 147 | } else { 148 | L.Push(LNil) 149 | } 150 | return 1 151 | } 152 | 153 | func debugTraceback(L *LState) int { 154 | msg := "" 155 | level := L.OptInt(2, 1) 156 | ls := L 157 | if L.GetTop() > 0 { 158 | if s, ok := L.Get(1).(LString); ok { 159 | msg = string(s) 160 | } 161 | if l, ok := L.Get(1).(*LState); ok { 162 | ls = l 163 | msg = "" 164 | } 165 | } 166 | 167 | traceback := strings.TrimSpace(ls.stackTrace(level)) 168 | if len(msg) > 0 { 169 | traceback = fmt.Sprintf("%s\n%s", msg, traceback) 170 | } 171 | L.Push(LString(traceback)) 172 | return 1 173 | } 174 | -------------------------------------------------------------------------------- /function.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | VarArgHasArg uint8 = 1 10 | VarArgIsVarArg uint8 = 2 11 | VarArgNeedsArg uint8 = 4 12 | ) 13 | 14 | type DbgLocalInfo struct { 15 | Name string 16 | StartPc int 17 | EndPc int 18 | } 19 | 20 | type DbgCall struct { 21 | Name string 22 | Pc int 23 | } 24 | 25 | type FunctionProto struct { 26 | SourceName string 27 | LineDefined int 28 | LastLineDefined int 29 | NumUpvalues uint8 30 | NumParameters uint8 31 | IsVarArg uint8 32 | NumUsedRegisters uint8 33 | Code []uint32 34 | Constants []LValue 35 | FunctionPrototypes []*FunctionProto 36 | 37 | DbgSourcePositions []int 38 | DbgLocals []*DbgLocalInfo 39 | DbgCalls []DbgCall 40 | DbgUpvalues []string 41 | 42 | stringConstants []string 43 | } 44 | 45 | /* Upvalue {{{ */ 46 | 47 | type Upvalue struct { 48 | next *Upvalue 49 | reg *registry 50 | index int 51 | value LValue 52 | closed bool 53 | } 54 | 55 | func (uv *Upvalue) Value() LValue { 56 | //if uv.IsClosed() { 57 | if uv.closed || uv.reg == nil { 58 | return uv.value 59 | } 60 | //return uv.reg.Get(uv.index) 61 | return uv.reg.array[uv.index] 62 | } 63 | 64 | func (uv *Upvalue) SetValue(value LValue) { 65 | if uv.IsClosed() { 66 | uv.value = value 67 | } else { 68 | uv.reg.Set(uv.index, value) 69 | } 70 | } 71 | 72 | func (uv *Upvalue) Close() { 73 | value := uv.Value() 74 | uv.closed = true 75 | uv.value = value 76 | } 77 | 78 | func (uv *Upvalue) IsClosed() bool { 79 | return uv.closed || uv.reg == nil 80 | } 81 | 82 | func UpvalueIndex(i int) int { 83 | return GlobalsIndex - i 84 | } 85 | 86 | /* }}} */ 87 | 88 | /* FunctionProto {{{ */ 89 | 90 | func newFunctionProto(name string) *FunctionProto { 91 | return &FunctionProto{ 92 | SourceName: name, 93 | LineDefined: 0, 94 | LastLineDefined: 0, 95 | NumUpvalues: 0, 96 | NumParameters: 0, 97 | IsVarArg: 0, 98 | NumUsedRegisters: 2, 99 | Code: make([]uint32, 0, 128), 100 | Constants: make([]LValue, 0, 32), 101 | FunctionPrototypes: make([]*FunctionProto, 0, 16), 102 | 103 | DbgSourcePositions: make([]int, 0, 128), 104 | DbgLocals: make([]*DbgLocalInfo, 0, 16), 105 | DbgCalls: make([]DbgCall, 0, 128), 106 | DbgUpvalues: make([]string, 0, 16), 107 | 108 | stringConstants: make([]string, 0, 32), 109 | } 110 | } 111 | 112 | func (fp *FunctionProto) String() string { 113 | return fp.str(1, 0) 114 | } 115 | 116 | func (fp *FunctionProto) str(level int, count int) string { 117 | indent := strings.Repeat(" ", level-1) 118 | buf := []string{} 119 | buf = append(buf, fmt.Sprintf("%v; function [%v] definition (level %v)\n", 120 | indent, count, level)) 121 | buf = append(buf, fmt.Sprintf("%v; %v upvalues, %v params, %v stacks\n", 122 | indent, fp.NumUpvalues, fp.NumParameters, fp.NumUsedRegisters)) 123 | for reg, linfo := range fp.DbgLocals { 124 | buf = append(buf, fmt.Sprintf("%v.local %v ; %v\n", indent, linfo.Name, reg)) 125 | } 126 | for reg, upvalue := range fp.DbgUpvalues { 127 | buf = append(buf, fmt.Sprintf("%v.upvalue %v ; %v\n", indent, upvalue, reg)) 128 | } 129 | for reg, conzt := range fp.Constants { 130 | buf = append(buf, fmt.Sprintf("%v.const %v ; %v\n", indent, conzt.String(), reg)) 131 | } 132 | buf = append(buf, "\n") 133 | 134 | protono := 0 135 | for no, code := range fp.Code { 136 | inst := opGetOpCode(code) 137 | if inst == OP_CLOSURE { 138 | buf = append(buf, "\n") 139 | buf = append(buf, fp.FunctionPrototypes[protono].str(level+1, protono)) 140 | buf = append(buf, "\n") 141 | protono++ 142 | } 143 | buf = append(buf, fmt.Sprintf("%v[%03d] %v (line:%v)\n", 144 | indent, no+1, opToString(code), fp.DbgSourcePositions[no])) 145 | 146 | } 147 | buf = append(buf, fmt.Sprintf("%v; end of function\n", indent)) 148 | return strings.Join(buf, "") 149 | } 150 | 151 | /* }}} */ 152 | 153 | /* LFunction {{{ */ 154 | 155 | func newLFunctionL(proto *FunctionProto, env *LTable, nupvalue int) *LFunction { 156 | return &LFunction{ 157 | IsG: false, 158 | Env: env, 159 | 160 | Proto: proto, 161 | GFunction: nil, 162 | Upvalues: make([]*Upvalue, nupvalue), 163 | } 164 | } 165 | 166 | func newLFunctionG(gfunc LGFunction, env *LTable, nupvalue int) *LFunction { 167 | return &LFunction{ 168 | IsG: true, 169 | Env: env, 170 | 171 | Proto: nil, 172 | GFunction: gfunc, 173 | Upvalues: make([]*Upvalue, nupvalue), 174 | } 175 | } 176 | 177 | func (fn *LFunction) LocalName(regno, pc int) (string, bool) { 178 | if fn.IsG { 179 | return "", false 180 | } 181 | p := fn.Proto 182 | for i := 0; i < len(p.DbgLocals) && p.DbgLocals[i].StartPc < pc; i++ { 183 | if pc < p.DbgLocals[i].EndPc { 184 | regno-- 185 | if regno == 0 { 186 | return p.DbgLocals[i].Name, true 187 | } 188 | } 189 | } 190 | return "", false 191 | } 192 | 193 | /* }}} */ 194 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/yuin/gopher-lua 2 | 3 | go 1.17 4 | 5 | require github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e 6 | 7 | require ( 8 | github.com/chzyer/logex v1.1.10 // indirect 9 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect 10 | golang.org/x/sys v0.0.0-20190204203706-41f3e6584952 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= 2 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 3 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= 4 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 5 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= 6 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 7 | golang.org/x/sys v0.0.0-20190204203706-41f3e6584952 h1:FDfvYgoVsA7TTZSbgiqjAbfPbK47CNHdWl3h/PJtii0= 8 | golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 9 | -------------------------------------------------------------------------------- /linit.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | const ( 4 | // BaseLibName is here for consistency; the base functions have no namespace/library. 5 | BaseLibName = "" 6 | // LoadLibName is here for consistency; the loading system has no namespace/library. 7 | LoadLibName = "package" 8 | // TabLibName is the name of the table Library. 9 | TabLibName = "table" 10 | // IoLibName is the name of the io Library. 11 | IoLibName = "io" 12 | // OsLibName is the name of the os Library. 13 | OsLibName = "os" 14 | // StringLibName is the name of the string Library. 15 | StringLibName = "string" 16 | // MathLibName is the name of the math Library. 17 | MathLibName = "math" 18 | // DebugLibName is the name of the debug Library. 19 | DebugLibName = "debug" 20 | // ChannelLibName is the name of the channel Library. 21 | ChannelLibName = "channel" 22 | // CoroutineLibName is the name of the coroutine Library. 23 | CoroutineLibName = "coroutine" 24 | ) 25 | 26 | type luaLib struct { 27 | libName string 28 | libFunc LGFunction 29 | } 30 | 31 | var luaLibs = []luaLib{ 32 | luaLib{LoadLibName, OpenPackage}, 33 | luaLib{BaseLibName, OpenBase}, 34 | luaLib{TabLibName, OpenTable}, 35 | luaLib{IoLibName, OpenIo}, 36 | luaLib{OsLibName, OpenOs}, 37 | luaLib{StringLibName, OpenString}, 38 | luaLib{MathLibName, OpenMath}, 39 | luaLib{DebugLibName, OpenDebug}, 40 | luaLib{ChannelLibName, OpenChannel}, 41 | luaLib{CoroutineLibName, OpenCoroutine}, 42 | } 43 | 44 | // OpenLibs loads the built-in libraries. It is equivalent to running OpenLoad, 45 | // then OpenBase, then iterating over the other OpenXXX functions in any order. 46 | func (ls *LState) OpenLibs() { 47 | // NB: Map iteration order in Go is deliberately randomised, so must open Load/Base 48 | // prior to iterating. 49 | for _, lib := range luaLibs { 50 | ls.Push(ls.NewFunction(lib.libFunc)) 51 | ls.Push(LString(lib.libName)) 52 | ls.Call(1, 0) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /loadlib.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | ) 9 | 10 | /* load lib {{{ */ 11 | 12 | var loLoaders = []LGFunction{loLoaderPreload, loLoaderLua} 13 | 14 | func loGetPath(env string, defpath string) string { 15 | path := os.Getenv(env) 16 | if len(path) == 0 { 17 | path = defpath 18 | } 19 | path = strings.Replace(path, ";;", ";"+defpath+";", -1) 20 | if os.PathSeparator != '/' { 21 | dir, err := filepath.Abs(filepath.Dir(os.Args[0])) 22 | if err != nil { 23 | panic(err) 24 | } 25 | path = strings.Replace(path, "!", dir, -1) 26 | } 27 | return path 28 | } 29 | 30 | func loFindFile(L *LState, name, pname string) (string, string) { 31 | name = strings.Replace(name, ".", string(os.PathSeparator), -1) 32 | lv := L.GetField(L.GetField(L.Get(EnvironIndex), "package"), pname) 33 | path, ok := lv.(LString) 34 | if !ok { 35 | L.RaiseError("package.%s must be a string", pname) 36 | } 37 | messages := []string{} 38 | for _, pattern := range strings.Split(string(path), ";") { 39 | luapath := strings.Replace(pattern, "?", name, -1) 40 | if _, err := os.Stat(luapath); err == nil { 41 | return luapath, "" 42 | } else { 43 | messages = append(messages, err.Error()) 44 | } 45 | } 46 | return "", strings.Join(messages, "\n\t") 47 | } 48 | 49 | func OpenPackage(L *LState) int { 50 | packagemod := L.RegisterModule(LoadLibName, loFuncs) 51 | 52 | L.SetField(packagemod, "preload", L.NewTable()) 53 | 54 | loaders := L.CreateTable(len(loLoaders), 0) 55 | for i, loader := range loLoaders { 56 | L.RawSetInt(loaders, i+1, L.NewFunction(loader)) 57 | } 58 | L.SetField(packagemod, "loaders", loaders) 59 | L.SetField(L.Get(RegistryIndex), "_LOADERS", loaders) 60 | 61 | loaded := L.NewTable() 62 | L.SetField(packagemod, "loaded", loaded) 63 | L.SetField(L.Get(RegistryIndex), "_LOADED", loaded) 64 | 65 | L.SetField(packagemod, "path", LString(loGetPath(LuaPath, LuaPathDefault))) 66 | L.SetField(packagemod, "cpath", emptyLString) 67 | 68 | L.SetField(packagemod, "config", LString(LuaDirSep+"\n"+LuaPathSep+ 69 | "\n"+LuaPathMark+"\n"+LuaExecDir+"\n"+LuaIgMark+"\n")) 70 | 71 | L.Push(packagemod) 72 | return 1 73 | } 74 | 75 | var loFuncs = map[string]LGFunction{ 76 | "loadlib": loLoadLib, 77 | "seeall": loSeeAll, 78 | } 79 | 80 | func loLoaderPreload(L *LState) int { 81 | name := L.CheckString(1) 82 | preload := L.GetField(L.GetField(L.Get(EnvironIndex), "package"), "preload") 83 | if _, ok := preload.(*LTable); !ok { 84 | L.RaiseError("package.preload must be a table") 85 | } 86 | lv := L.GetField(preload, name) 87 | if lv == LNil { 88 | L.Push(LString(fmt.Sprintf("no field package.preload['%s']", name))) 89 | return 1 90 | } 91 | L.Push(lv) 92 | return 1 93 | } 94 | 95 | func loLoaderLua(L *LState) int { 96 | name := L.CheckString(1) 97 | path, msg := loFindFile(L, name, "path") 98 | if len(path) == 0 { 99 | L.Push(LString(msg)) 100 | return 1 101 | } 102 | fn, err1 := L.LoadFile(path) 103 | if err1 != nil { 104 | L.RaiseError(err1.Error()) 105 | } 106 | L.Push(fn) 107 | return 1 108 | } 109 | 110 | func loLoadLib(L *LState) int { 111 | L.RaiseError("loadlib is not supported") 112 | return 0 113 | } 114 | 115 | func loSeeAll(L *LState) int { 116 | mod := L.CheckTable(1) 117 | mt := L.GetMetatable(mod) 118 | if mt == LNil { 119 | mt = L.CreateTable(0, 1) 120 | L.SetMetatable(mod, mt) 121 | } 122 | L.SetField(mt, "__index", L.Get(GlobalsIndex)) 123 | return 0 124 | } 125 | 126 | /* }}} */ 127 | 128 | // 129 | -------------------------------------------------------------------------------- /mathlib.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "math" 5 | "math/rand" 6 | ) 7 | 8 | func OpenMath(L *LState) int { 9 | mod := L.RegisterModule(MathLibName, mathFuncs).(*LTable) 10 | mod.RawSetString("pi", LNumber(math.Pi)) 11 | mod.RawSetString("huge", LNumber(math.MaxFloat64)) 12 | L.Push(mod) 13 | return 1 14 | } 15 | 16 | var mathFuncs = map[string]LGFunction{ 17 | "abs": mathAbs, 18 | "acos": mathAcos, 19 | "asin": mathAsin, 20 | "atan": mathAtan, 21 | "atan2": mathAtan2, 22 | "ceil": mathCeil, 23 | "cos": mathCos, 24 | "cosh": mathCosh, 25 | "deg": mathDeg, 26 | "exp": mathExp, 27 | "floor": mathFloor, 28 | "fmod": mathFmod, 29 | "frexp": mathFrexp, 30 | "ldexp": mathLdexp, 31 | "log": mathLog, 32 | "log10": mathLog10, 33 | "max": mathMax, 34 | "min": mathMin, 35 | "mod": mathMod, 36 | "modf": mathModf, 37 | "pow": mathPow, 38 | "rad": mathRad, 39 | "random": mathRandom, 40 | "randomseed": mathRandomseed, 41 | "sin": mathSin, 42 | "sinh": mathSinh, 43 | "sqrt": mathSqrt, 44 | "tan": mathTan, 45 | "tanh": mathTanh, 46 | } 47 | 48 | func mathAbs(L *LState) int { 49 | L.Push(LNumber(math.Abs(float64(L.CheckNumber(1))))) 50 | return 1 51 | } 52 | 53 | func mathAcos(L *LState) int { 54 | L.Push(LNumber(math.Acos(float64(L.CheckNumber(1))))) 55 | return 1 56 | } 57 | 58 | func mathAsin(L *LState) int { 59 | L.Push(LNumber(math.Asin(float64(L.CheckNumber(1))))) 60 | return 1 61 | } 62 | 63 | func mathAtan(L *LState) int { 64 | L.Push(LNumber(math.Atan(float64(L.CheckNumber(1))))) 65 | return 1 66 | } 67 | 68 | func mathAtan2(L *LState) int { 69 | L.Push(LNumber(math.Atan2(float64(L.CheckNumber(1)), float64(L.CheckNumber(2))))) 70 | return 1 71 | } 72 | 73 | func mathCeil(L *LState) int { 74 | L.Push(LNumber(math.Ceil(float64(L.CheckNumber(1))))) 75 | return 1 76 | } 77 | 78 | func mathCos(L *LState) int { 79 | L.Push(LNumber(math.Cos(float64(L.CheckNumber(1))))) 80 | return 1 81 | } 82 | 83 | func mathCosh(L *LState) int { 84 | L.Push(LNumber(math.Cosh(float64(L.CheckNumber(1))))) 85 | return 1 86 | } 87 | 88 | func mathDeg(L *LState) int { 89 | L.Push(LNumber(float64(L.CheckNumber(1)) * 180 / math.Pi)) 90 | return 1 91 | } 92 | 93 | func mathExp(L *LState) int { 94 | L.Push(LNumber(math.Exp(float64(L.CheckNumber(1))))) 95 | return 1 96 | } 97 | 98 | func mathFloor(L *LState) int { 99 | L.Push(LNumber(math.Floor(float64(L.CheckNumber(1))))) 100 | return 1 101 | } 102 | 103 | func mathFmod(L *LState) int { 104 | L.Push(LNumber(math.Mod(float64(L.CheckNumber(1)), float64(L.CheckNumber(2))))) 105 | return 1 106 | } 107 | 108 | func mathFrexp(L *LState) int { 109 | v1, v2 := math.Frexp(float64(L.CheckNumber(1))) 110 | L.Push(LNumber(v1)) 111 | L.Push(LNumber(v2)) 112 | return 2 113 | } 114 | 115 | func mathLdexp(L *LState) int { 116 | L.Push(LNumber(math.Ldexp(float64(L.CheckNumber(1)), L.CheckInt(2)))) 117 | return 1 118 | } 119 | 120 | func mathLog(L *LState) int { 121 | L.Push(LNumber(math.Log(float64(L.CheckNumber(1))))) 122 | return 1 123 | } 124 | 125 | func mathLog10(L *LState) int { 126 | L.Push(LNumber(math.Log10(float64(L.CheckNumber(1))))) 127 | return 1 128 | } 129 | 130 | func mathMax(L *LState) int { 131 | if L.GetTop() == 0 { 132 | L.RaiseError("wrong number of arguments") 133 | } 134 | max := L.CheckNumber(1) 135 | top := L.GetTop() 136 | for i := 2; i <= top; i++ { 137 | v := L.CheckNumber(i) 138 | if v > max { 139 | max = v 140 | } 141 | } 142 | L.Push(max) 143 | return 1 144 | } 145 | 146 | func mathMin(L *LState) int { 147 | if L.GetTop() == 0 { 148 | L.RaiseError("wrong number of arguments") 149 | } 150 | min := L.CheckNumber(1) 151 | top := L.GetTop() 152 | for i := 2; i <= top; i++ { 153 | v := L.CheckNumber(i) 154 | if v < min { 155 | min = v 156 | } 157 | } 158 | L.Push(min) 159 | return 1 160 | } 161 | 162 | func mathMod(L *LState) int { 163 | lhs := L.CheckNumber(1) 164 | rhs := L.CheckNumber(2) 165 | L.Push(luaModulo(lhs, rhs)) 166 | return 1 167 | } 168 | 169 | func mathModf(L *LState) int { 170 | v1, v2 := math.Modf(float64(L.CheckNumber(1))) 171 | L.Push(LNumber(v1)) 172 | L.Push(LNumber(v2)) 173 | return 2 174 | } 175 | 176 | func mathPow(L *LState) int { 177 | L.Push(LNumber(math.Pow(float64(L.CheckNumber(1)), float64(L.CheckNumber(2))))) 178 | return 1 179 | } 180 | 181 | func mathRad(L *LState) int { 182 | L.Push(LNumber(float64(L.CheckNumber(1)) * math.Pi / 180)) 183 | return 1 184 | } 185 | 186 | func mathRandom(L *LState) int { 187 | switch L.GetTop() { 188 | case 0: 189 | L.Push(LNumber(rand.Float64())) 190 | case 1: 191 | n := L.CheckInt(1) 192 | L.Push(LNumber(rand.Intn(n) + 1)) 193 | default: 194 | min := L.CheckInt(1) 195 | max := L.CheckInt(2) + 1 196 | L.Push(LNumber(rand.Intn(max-min) + min)) 197 | } 198 | return 1 199 | } 200 | 201 | func mathRandomseed(L *LState) int { 202 | rand.Seed(L.CheckInt64(1)) 203 | return 0 204 | } 205 | 206 | func mathSin(L *LState) int { 207 | L.Push(LNumber(math.Sin(float64(L.CheckNumber(1))))) 208 | return 1 209 | } 210 | 211 | func mathSinh(L *LState) int { 212 | L.Push(LNumber(math.Sinh(float64(L.CheckNumber(1))))) 213 | return 1 214 | } 215 | 216 | func mathSqrt(L *LState) int { 217 | L.Push(LNumber(math.Sqrt(float64(L.CheckNumber(1))))) 218 | return 1 219 | } 220 | 221 | func mathTan(L *LState) int { 222 | L.Push(LNumber(math.Tan(float64(L.CheckNumber(1))))) 223 | return 1 224 | } 225 | 226 | func mathTanh(L *LState) int { 227 | L.Push(LNumber(math.Tanh(float64(L.CheckNumber(1))))) 228 | return 1 229 | } 230 | 231 | // 232 | -------------------------------------------------------------------------------- /oslib.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | var startedAt time.Time 10 | 11 | func init() { 12 | startedAt = time.Now() 13 | } 14 | 15 | func getIntField(L *LState, tb *LTable, key string, v int) int { 16 | ret := tb.RawGetString(key) 17 | 18 | switch lv := ret.(type) { 19 | case LNumber: 20 | return int(lv) 21 | case LString: 22 | slv := string(lv) 23 | slv = strings.TrimLeft(slv, " ") 24 | if strings.HasPrefix(slv, "0") && !strings.HasPrefix(slv, "0x") && !strings.HasPrefix(slv, "0X") { 25 | // Standard lua interpreter only support decimal and hexadecimal 26 | slv = strings.TrimLeft(slv, "0") 27 | if slv == "" { 28 | return 0 29 | } 30 | } 31 | if num, err := parseNumber(slv); err == nil { 32 | return int(num) 33 | } 34 | default: 35 | return v 36 | } 37 | 38 | return v 39 | } 40 | 41 | func getBoolField(L *LState, tb *LTable, key string, v bool) bool { 42 | ret := tb.RawGetString(key) 43 | if lb, ok := ret.(LBool); ok { 44 | return bool(lb) 45 | } 46 | return v 47 | } 48 | 49 | func OpenOs(L *LState) int { 50 | osmod := L.RegisterModule(OsLibName, osFuncs) 51 | L.Push(osmod) 52 | return 1 53 | } 54 | 55 | var osFuncs = map[string]LGFunction{ 56 | "clock": osClock, 57 | "difftime": osDiffTime, 58 | "execute": osExecute, 59 | "exit": osExit, 60 | "date": osDate, 61 | "getenv": osGetEnv, 62 | "remove": osRemove, 63 | "rename": osRename, 64 | "setenv": osSetEnv, 65 | "setlocale": osSetLocale, 66 | "time": osTime, 67 | "tmpname": osTmpname, 68 | } 69 | 70 | func osClock(L *LState) int { 71 | L.Push(LNumber(float64(time.Now().Sub(startedAt)) / float64(time.Second))) 72 | return 1 73 | } 74 | 75 | func osDiffTime(L *LState) int { 76 | L.Push(LNumber(L.CheckInt64(1) - L.CheckInt64(2))) 77 | return 1 78 | } 79 | 80 | func osExecute(L *LState) int { 81 | var procAttr os.ProcAttr 82 | procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr} 83 | cmd, args := popenArgs(L.CheckString(1)) 84 | args = append([]string{cmd}, args...) 85 | process, err := os.StartProcess(cmd, args, &procAttr) 86 | if err != nil { 87 | L.Push(LNumber(1)) 88 | return 1 89 | } 90 | 91 | ps, err := process.Wait() 92 | if err != nil || !ps.Success() { 93 | L.Push(LNumber(1)) 94 | return 1 95 | } 96 | L.Push(LNumber(0)) 97 | return 1 98 | } 99 | 100 | func osExit(L *LState) int { 101 | L.Close() 102 | os.Exit(L.OptInt(1, 0)) 103 | return 1 104 | } 105 | 106 | func osDate(L *LState) int { 107 | t := time.Now() 108 | isUTC := false 109 | cfmt := "%c" 110 | if L.GetTop() >= 1 { 111 | cfmt = L.CheckString(1) 112 | if strings.HasPrefix(cfmt, "!") { 113 | cfmt = strings.TrimLeft(cfmt, "!") 114 | isUTC = true 115 | } 116 | if L.GetTop() >= 2 { 117 | t = time.Unix(L.CheckInt64(2), 0) 118 | } 119 | if isUTC { 120 | t = t.UTC() 121 | } 122 | if strings.HasPrefix(cfmt, "*t") { 123 | ret := L.NewTable() 124 | ret.RawSetString("year", LNumber(t.Year())) 125 | ret.RawSetString("month", LNumber(t.Month())) 126 | ret.RawSetString("day", LNumber(t.Day())) 127 | ret.RawSetString("hour", LNumber(t.Hour())) 128 | ret.RawSetString("min", LNumber(t.Minute())) 129 | ret.RawSetString("sec", LNumber(t.Second())) 130 | ret.RawSetString("wday", LNumber(t.Weekday()+1)) 131 | // TODO yday & dst 132 | ret.RawSetString("yday", LNumber(0)) 133 | ret.RawSetString("isdst", LFalse) 134 | L.Push(ret) 135 | return 1 136 | } 137 | } 138 | L.Push(LString(strftime(t, cfmt))) 139 | return 1 140 | } 141 | 142 | func osGetEnv(L *LState) int { 143 | v := os.Getenv(L.CheckString(1)) 144 | if len(v) == 0 { 145 | L.Push(LNil) 146 | } else { 147 | L.Push(LString(v)) 148 | } 149 | return 1 150 | } 151 | 152 | func osRemove(L *LState) int { 153 | err := os.Remove(L.CheckString(1)) 154 | if err != nil { 155 | L.Push(LNil) 156 | L.Push(LString(err.Error())) 157 | return 2 158 | } else { 159 | L.Push(LTrue) 160 | return 1 161 | } 162 | } 163 | 164 | func osRename(L *LState) int { 165 | err := os.Rename(L.CheckString(1), L.CheckString(2)) 166 | if err != nil { 167 | L.Push(LNil) 168 | L.Push(LString(err.Error())) 169 | return 2 170 | } else { 171 | L.Push(LTrue) 172 | return 1 173 | } 174 | } 175 | 176 | func osSetLocale(L *LState) int { 177 | // setlocale is not supported 178 | L.Push(LFalse) 179 | return 1 180 | } 181 | 182 | func osSetEnv(L *LState) int { 183 | err := os.Setenv(L.CheckString(1), L.CheckString(2)) 184 | if err != nil { 185 | L.Push(LNil) 186 | L.Push(LString(err.Error())) 187 | return 2 188 | } else { 189 | L.Push(LTrue) 190 | return 1 191 | } 192 | } 193 | 194 | func osTime(L *LState) int { 195 | if L.GetTop() == 0 { 196 | L.Push(LNumber(time.Now().Unix())) 197 | } else { 198 | lv := L.CheckAny(1) 199 | if lv == LNil { 200 | L.Push(LNumber(time.Now().Unix())) 201 | } else { 202 | tbl, ok := lv.(*LTable) 203 | if !ok { 204 | L.TypeError(1, LTTable) 205 | } 206 | sec := getIntField(L, tbl, "sec", 0) 207 | min := getIntField(L, tbl, "min", 0) 208 | hour := getIntField(L, tbl, "hour", 12) 209 | day := getIntField(L, tbl, "day", -1) 210 | month := getIntField(L, tbl, "month", -1) 211 | year := getIntField(L, tbl, "year", -1) 212 | isdst := getBoolField(L, tbl, "isdst", false) 213 | t := time.Date(year, time.Month(month), day, hour, min, sec, 0, time.Local) 214 | // TODO dst 215 | if false { 216 | print(isdst) 217 | } 218 | L.Push(LNumber(t.Unix())) 219 | } 220 | } 221 | return 1 222 | } 223 | 224 | func osTmpname(L *LState) int { 225 | file, err := os.CreateTemp("", "") 226 | if err != nil { 227 | L.RaiseError("unable to generate a unique filename") 228 | } 229 | file.Close() 230 | os.Remove(file.Name()) // ignore errors 231 | L.Push(LString(file.Name())) 232 | return 1 233 | } 234 | 235 | // 236 | -------------------------------------------------------------------------------- /oslib_test.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // correctly gc-ed. There was a bug in gopher lua where local vars were not being gc-ed in all circumstances. 8 | func TestOsWrite(t *testing.T) { 9 | s := ` 10 | local function write(filename, content) 11 | local f = assert(io.open(filename, "w")) 12 | f:write(content) 13 | assert(f:close()) 14 | end 15 | 16 | local filename = os.tmpname() 17 | write(filename, "abc") 18 | write(filename, "d") 19 | local f = assert(io.open(filename, "r")) 20 | local content = f:read("*all"):gsub("%s+", "") 21 | f:close() 22 | os.remove(filename) 23 | local expected = "d" 24 | if content ~= expected then 25 | error(string.format("Invalid content: Expecting \"%s\", got \"%s\"", expected, content)) 26 | end 27 | ` 28 | L := NewState() 29 | defer L.Close() 30 | if err := L.DoString(s); err != nil { 31 | t.Error(err) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /package.go: -------------------------------------------------------------------------------- 1 | // GopherLua: VM and compiler for Lua in Go 2 | package lua 3 | 4 | const PackageName = "GopherLua" 5 | const PackageVersion = "0.1" 6 | const PackageAuthors = "Yusuke Inuzuka" 7 | const PackageCopyRight = PackageName + " " + PackageVersion + " Copyright (C) 2015 -2017 " + PackageAuthors 8 | -------------------------------------------------------------------------------- /parse/Makefile: -------------------------------------------------------------------------------- 1 | all : parser.go 2 | 3 | parser.go : parser.go.y 4 | goyacc -o $@ parser.go.y; [ -f y.output ] && ( rm -f y.output ) 5 | 6 | clean: 7 | rm -f parser.go 8 | -------------------------------------------------------------------------------- /script_test.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime" 7 | "strings" 8 | "sync/atomic" 9 | "testing" 10 | "time" 11 | 12 | "github.com/yuin/gopher-lua/parse" 13 | ) 14 | 15 | const maxMemory = 40 16 | 17 | var gluaTests []string = []string{ 18 | "base.lua", 19 | "coroutine.lua", 20 | "db.lua", 21 | "issues.lua", 22 | "os.lua", 23 | "table.lua", 24 | "vm.lua", 25 | "math.lua", 26 | "strings.lua", 27 | "goto.lua", 28 | } 29 | 30 | var luaTests []string = []string{ 31 | "attrib.lua", 32 | "calls.lua", 33 | "closure.lua", 34 | "constructs.lua", 35 | "events.lua", 36 | "literals.lua", 37 | "locals.lua", 38 | "math.lua", 39 | "sort.lua", 40 | "strings.lua", 41 | "vararg.lua", 42 | "pm.lua", 43 | "files.lua", 44 | } 45 | 46 | func testScriptCompile(t *testing.T, script string) { 47 | file, err := os.Open(script) 48 | if err != nil { 49 | t.Fatal(err) 50 | return 51 | } 52 | chunk, err2 := parse.Parse(file, script) 53 | if err2 != nil { 54 | t.Fatal(err2) 55 | return 56 | } 57 | parse.Dump(chunk) 58 | proto, err3 := Compile(chunk, script) 59 | if err3 != nil { 60 | t.Fatal(err3) 61 | return 62 | } 63 | nop := func(s string) {} 64 | nop(proto.String()) 65 | } 66 | 67 | func testScriptDir(t *testing.T, tests []string, directory string) { 68 | if err := os.Chdir(directory); err != nil { 69 | t.Error(err) 70 | } 71 | defer os.Chdir("..") 72 | for _, script := range tests { 73 | fmt.Printf("testing %s/%s\n", directory, script) 74 | testScriptCompile(t, script) 75 | L := NewState(Options{ 76 | RegistrySize: 1024 * 20, 77 | CallStackSize: 1024, 78 | IncludeGoStackTrace: true, 79 | }) 80 | L.SetMx(maxMemory) 81 | if err := L.DoFile(script); err != nil { 82 | t.Error(err) 83 | } 84 | L.Close() 85 | } 86 | } 87 | 88 | var numActiveUserDatas int32 = 0 89 | 90 | type finalizerStub struct{ x byte } 91 | 92 | func allocFinalizerUserData(L *LState) int { 93 | ud := L.NewUserData() 94 | atomic.AddInt32(&numActiveUserDatas, 1) 95 | a := finalizerStub{} 96 | ud.Value = &a 97 | runtime.SetFinalizer(&a, func(aa *finalizerStub) { 98 | atomic.AddInt32(&numActiveUserDatas, -1) 99 | }) 100 | L.Push(ud) 101 | return 1 102 | } 103 | 104 | func sleep(L *LState) int { 105 | time.Sleep(time.Duration(L.CheckInt(1)) * time.Millisecond) 106 | return 0 107 | } 108 | 109 | func countFinalizers(L *LState) int { 110 | L.Push(LNumber(numActiveUserDatas)) 111 | return 1 112 | } 113 | 114 | // TestLocalVarFree verifies that tables and user user datas which are no longer referenced by the lua script are 115 | // correctly gc-ed. There was a bug in gopher lua where local vars were not being gc-ed in all circumstances. 116 | func TestLocalVarFree(t *testing.T) { 117 | s := ` 118 | function Test(a, b, c) 119 | local a = { v = allocFinalizer() } 120 | local b = { v = allocFinalizer() } 121 | return a 122 | end 123 | Test(1,2,3) 124 | for i = 1, 100 do 125 | collectgarbage() 126 | if countFinalizers() == 0 then 127 | return 128 | end 129 | sleep(100) 130 | end 131 | error("user datas not finalized after 100 gcs") 132 | ` 133 | L := NewState() 134 | L.SetGlobal("allocFinalizer", L.NewFunction(allocFinalizerUserData)) 135 | L.SetGlobal("sleep", L.NewFunction(sleep)) 136 | L.SetGlobal("countFinalizers", L.NewFunction(countFinalizers)) 137 | defer L.Close() 138 | if err := L.DoString(s); err != nil { 139 | t.Error(err) 140 | } 141 | } 142 | 143 | func TestGlua(t *testing.T) { 144 | testScriptDir(t, gluaTests, "_glua-tests") 145 | } 146 | 147 | func TestLua(t *testing.T) { 148 | testScriptDir(t, luaTests, "_lua5.1-tests") 149 | } 150 | 151 | func TestMergingLoadNilBug(t *testing.T) { 152 | // there was a bug where a multiple load nils were being incorrectly merged, and the following code exposed it 153 | s := ` 154 | function test() 155 | local a = 0 156 | local b = 1 157 | local c = 2 158 | local d = 3 159 | local e = 4 -- reg 4 160 | local f = 5 161 | local g = 6 162 | local h = 7 163 | 164 | if e == 4 then 165 | e = nil -- should clear reg 4, but clears regs 4-8 by mistake 166 | end 167 | if f == nil then 168 | error("bad f") 169 | end 170 | if g == nil then 171 | error("bad g") 172 | end 173 | if h == nil then 174 | error("bad h") 175 | end 176 | end 177 | 178 | test() 179 | ` 180 | 181 | L := NewState() 182 | defer L.Close() 183 | if err := L.DoString(s); err != nil { 184 | t.Error(err) 185 | } 186 | } 187 | 188 | func TestMergingLoadNil(t *testing.T) { 189 | // multiple nil assignments to consecutive registers should be merged 190 | s := ` 191 | function test() 192 | local a = 0 193 | local b = 1 194 | local c = 2 195 | 196 | -- this should generate just one LOADNIL byte code instruction 197 | a = nil 198 | b = nil 199 | c = nil 200 | 201 | print(a,b,c) 202 | end 203 | 204 | test()` 205 | 206 | chunk, err := parse.Parse(strings.NewReader(s), "test") 207 | if err != nil { 208 | t.Fatal(err) 209 | } 210 | 211 | compiled, err := Compile(chunk, "test") 212 | if err != nil { 213 | t.Fatal(err) 214 | } 215 | 216 | if len(compiled.FunctionPrototypes) != 1 { 217 | t.Fatal("expected 1 function prototype") 218 | } 219 | 220 | // there should be exactly 1 LOADNIL instruction in the byte code generated for the above 221 | // anymore, and the LOADNIL merging is not working correctly 222 | count := 0 223 | for _, instr := range compiled.FunctionPrototypes[0].Code { 224 | if opGetOpCode(instr) == OP_LOADNIL { 225 | count++ 226 | } 227 | } 228 | 229 | if count != 1 { 230 | t.Fatalf("expected 1 LOADNIL instruction, found %d", count) 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /table.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | const defaultArrayCap = 32 4 | const defaultHashCap = 32 5 | 6 | type lValueArraySorter struct { 7 | L *LState 8 | Fn *LFunction 9 | Values []LValue 10 | } 11 | 12 | func (lv lValueArraySorter) Len() int { 13 | return len(lv.Values) 14 | } 15 | 16 | func (lv lValueArraySorter) Swap(i, j int) { 17 | lv.Values[i], lv.Values[j] = lv.Values[j], lv.Values[i] 18 | } 19 | 20 | func (lv lValueArraySorter) Less(i, j int) bool { 21 | if lv.Fn != nil { 22 | lv.L.Push(lv.Fn) 23 | lv.L.Push(lv.Values[i]) 24 | lv.L.Push(lv.Values[j]) 25 | lv.L.Call(2, 1) 26 | return LVAsBool(lv.L.reg.Pop()) 27 | } 28 | return lessThan(lv.L, lv.Values[i], lv.Values[j]) 29 | } 30 | 31 | func newLTable(acap int, hcap int) *LTable { 32 | if acap < 0 { 33 | acap = 0 34 | } 35 | if hcap < 0 { 36 | hcap = 0 37 | } 38 | tb := <able{} 39 | tb.Metatable = LNil 40 | if acap != 0 { 41 | tb.array = make([]LValue, 0, acap) 42 | } 43 | if hcap != 0 { 44 | tb.strdict = make(map[string]LValue, hcap) 45 | } 46 | return tb 47 | } 48 | 49 | // Len returns length of this LTable without using __len. 50 | func (tb *LTable) Len() int { 51 | if tb.array == nil { 52 | return 0 53 | } 54 | var prev LValue = LNil 55 | for i := len(tb.array) - 1; i >= 0; i-- { 56 | v := tb.array[i] 57 | if prev == LNil && v != LNil { 58 | return i + 1 59 | } 60 | prev = v 61 | } 62 | return 0 63 | } 64 | 65 | // Append appends a given LValue to this LTable. 66 | func (tb *LTable) Append(value LValue) { 67 | if value == LNil { 68 | return 69 | } 70 | if tb.array == nil { 71 | tb.array = make([]LValue, 0, defaultArrayCap) 72 | } 73 | if len(tb.array) == 0 || tb.array[len(tb.array)-1] != LNil { 74 | tb.array = append(tb.array, value) 75 | } else { 76 | i := len(tb.array) - 2 77 | for ; i >= 0; i-- { 78 | if tb.array[i] != LNil { 79 | break 80 | } 81 | } 82 | tb.array[i+1] = value 83 | } 84 | } 85 | 86 | // Insert inserts a given LValue at position `i` in this table. 87 | func (tb *LTable) Insert(i int, value LValue) { 88 | if tb.array == nil { 89 | tb.array = make([]LValue, 0, defaultArrayCap) 90 | } 91 | if i > len(tb.array) { 92 | tb.RawSetInt(i, value) 93 | return 94 | } 95 | if i <= 0 { 96 | tb.RawSet(LNumber(i), value) 97 | return 98 | } 99 | i -= 1 100 | tb.array = append(tb.array, LNil) 101 | copy(tb.array[i+1:], tb.array[i:]) 102 | tb.array[i] = value 103 | } 104 | 105 | // MaxN returns a maximum number key that nil value does not exist before it. 106 | func (tb *LTable) MaxN() int { 107 | if tb.array == nil { 108 | return 0 109 | } 110 | for i := len(tb.array) - 1; i >= 0; i-- { 111 | if tb.array[i] != LNil { 112 | return i + 1 113 | } 114 | } 115 | return 0 116 | } 117 | 118 | // Remove removes from this table the element at a given position. 119 | func (tb *LTable) Remove(pos int) LValue { 120 | if tb.array == nil { 121 | return LNil 122 | } 123 | larray := len(tb.array) 124 | if larray == 0 { 125 | return LNil 126 | } 127 | i := pos - 1 128 | oldval := LNil 129 | switch { 130 | case i >= larray: 131 | // nothing to do 132 | case i == larray-1 || i < 0: 133 | oldval = tb.array[larray-1] 134 | tb.array = tb.array[:larray-1] 135 | default: 136 | oldval = tb.array[i] 137 | copy(tb.array[i:], tb.array[i+1:]) 138 | tb.array[larray-1] = nil 139 | tb.array = tb.array[:larray-1] 140 | } 141 | return oldval 142 | } 143 | 144 | // RawSet sets a given LValue to a given index without the __newindex metamethod. 145 | // It is recommended to use `RawSetString` or `RawSetInt` for performance 146 | // if you already know the given LValue is a string or number. 147 | func (tb *LTable) RawSet(key LValue, value LValue) { 148 | switch v := key.(type) { 149 | case LNumber: 150 | if isArrayKey(v) { 151 | if tb.array == nil { 152 | tb.array = make([]LValue, 0, defaultArrayCap) 153 | } 154 | index := int(v) - 1 155 | alen := len(tb.array) 156 | switch { 157 | case index == alen: 158 | tb.array = append(tb.array, value) 159 | case index > alen: 160 | for i := 0; i < (index - alen); i++ { 161 | tb.array = append(tb.array, LNil) 162 | } 163 | tb.array = append(tb.array, value) 164 | case index < alen: 165 | tb.array[index] = value 166 | } 167 | return 168 | } 169 | case LString: 170 | tb.RawSetString(string(v), value) 171 | return 172 | } 173 | 174 | tb.RawSetH(key, value) 175 | } 176 | 177 | // RawSetInt sets a given LValue at a position `key` without the __newindex metamethod. 178 | func (tb *LTable) RawSetInt(key int, value LValue) { 179 | if key < 1 || key >= MaxArrayIndex { 180 | tb.RawSetH(LNumber(key), value) 181 | return 182 | } 183 | if tb.array == nil { 184 | tb.array = make([]LValue, 0, 32) 185 | } 186 | index := key - 1 187 | alen := len(tb.array) 188 | switch { 189 | case index == alen: 190 | tb.array = append(tb.array, value) 191 | case index > alen: 192 | for i := 0; i < (index - alen); i++ { 193 | tb.array = append(tb.array, LNil) 194 | } 195 | tb.array = append(tb.array, value) 196 | case index < alen: 197 | tb.array[index] = value 198 | } 199 | } 200 | 201 | // RawSetString sets a given LValue to a given string index without the __newindex metamethod. 202 | func (tb *LTable) RawSetString(key string, value LValue) { 203 | if tb.strdict == nil { 204 | tb.strdict = make(map[string]LValue, defaultHashCap) 205 | } 206 | if tb.keys == nil { 207 | tb.keys = []LValue{} 208 | tb.k2i = map[LValue]int{} 209 | } 210 | 211 | if value == LNil { 212 | // TODO tb.keys and tb.k2i should also be removed 213 | delete(tb.strdict, key) 214 | } else { 215 | tb.strdict[key] = value 216 | lkey := LString(key) 217 | if _, ok := tb.k2i[lkey]; !ok { 218 | tb.k2i[lkey] = len(tb.keys) 219 | tb.keys = append(tb.keys, lkey) 220 | } 221 | } 222 | } 223 | 224 | // RawSetH sets a given LValue to a given index without the __newindex metamethod. 225 | func (tb *LTable) RawSetH(key LValue, value LValue) { 226 | if s, ok := key.(LString); ok { 227 | tb.RawSetString(string(s), value) 228 | return 229 | } 230 | if tb.dict == nil { 231 | tb.dict = make(map[LValue]LValue, len(tb.strdict)) 232 | } 233 | if tb.keys == nil { 234 | tb.keys = []LValue{} 235 | tb.k2i = map[LValue]int{} 236 | } 237 | 238 | if value == LNil { 239 | // TODO tb.keys and tb.k2i should also be removed 240 | delete(tb.dict, key) 241 | } else { 242 | tb.dict[key] = value 243 | if _, ok := tb.k2i[key]; !ok { 244 | tb.k2i[key] = len(tb.keys) 245 | tb.keys = append(tb.keys, key) 246 | } 247 | } 248 | } 249 | 250 | // RawGet returns an LValue associated with a given key without __index metamethod. 251 | func (tb *LTable) RawGet(key LValue) LValue { 252 | switch v := key.(type) { 253 | case LNumber: 254 | if isArrayKey(v) { 255 | if tb.array == nil { 256 | return LNil 257 | } 258 | index := int(v) - 1 259 | if index >= len(tb.array) { 260 | return LNil 261 | } 262 | return tb.array[index] 263 | } 264 | case LString: 265 | if tb.strdict == nil { 266 | return LNil 267 | } 268 | if ret, ok := tb.strdict[string(v)]; ok { 269 | return ret 270 | } 271 | return LNil 272 | } 273 | if tb.dict == nil { 274 | return LNil 275 | } 276 | if v, ok := tb.dict[key]; ok { 277 | return v 278 | } 279 | return LNil 280 | } 281 | 282 | // RawGetInt returns an LValue at position `key` without __index metamethod. 283 | func (tb *LTable) RawGetInt(key int) LValue { 284 | if tb.array == nil { 285 | return LNil 286 | } 287 | index := int(key) - 1 288 | if index >= len(tb.array) || index < 0 { 289 | return LNil 290 | } 291 | return tb.array[index] 292 | } 293 | 294 | // RawGet returns an LValue associated with a given key without __index metamethod. 295 | func (tb *LTable) RawGetH(key LValue) LValue { 296 | if s, sok := key.(LString); sok { 297 | if tb.strdict == nil { 298 | return LNil 299 | } 300 | if v, vok := tb.strdict[string(s)]; vok { 301 | return v 302 | } 303 | return LNil 304 | } 305 | if tb.dict == nil { 306 | return LNil 307 | } 308 | if v, ok := tb.dict[key]; ok { 309 | return v 310 | } 311 | return LNil 312 | } 313 | 314 | // RawGetString returns an LValue associated with a given key without __index metamethod. 315 | func (tb *LTable) RawGetString(key string) LValue { 316 | if tb.strdict == nil { 317 | return LNil 318 | } 319 | if v, vok := tb.strdict[string(key)]; vok { 320 | return v 321 | } 322 | return LNil 323 | } 324 | 325 | // ForEach iterates over this table of elements, yielding each in turn to a given function. 326 | func (tb *LTable) ForEach(cb func(LValue, LValue)) { 327 | if tb.array != nil { 328 | for i, v := range tb.array { 329 | if v != LNil { 330 | cb(LNumber(i+1), v) 331 | } 332 | } 333 | } 334 | if tb.strdict != nil { 335 | for k, v := range tb.strdict { 336 | if v != LNil { 337 | cb(LString(k), v) 338 | } 339 | } 340 | } 341 | if tb.dict != nil { 342 | for k, v := range tb.dict { 343 | if v != LNil { 344 | cb(k, v) 345 | } 346 | } 347 | } 348 | } 349 | 350 | // This function is equivalent to lua_next ( http://www.lua.org/manual/5.1/manual.html#lua_next ). 351 | func (tb *LTable) Next(key LValue) (LValue, LValue) { 352 | init := false 353 | if key == LNil { 354 | key = LNumber(0) 355 | init = true 356 | } 357 | 358 | if init || key != LNumber(0) { 359 | if kv, ok := key.(LNumber); ok && isInteger(kv) && int(kv) >= 0 && kv < LNumber(MaxArrayIndex) { 360 | index := int(kv) 361 | if tb.array != nil { 362 | for ; index < len(tb.array); index++ { 363 | if v := tb.array[index]; v != LNil { 364 | return LNumber(index + 1), v 365 | } 366 | } 367 | } 368 | if tb.array == nil || index == len(tb.array) { 369 | if (tb.dict == nil || len(tb.dict) == 0) && (tb.strdict == nil || len(tb.strdict) == 0) { 370 | return LNil, LNil 371 | } 372 | key = tb.keys[0] 373 | if v := tb.RawGetH(key); v != LNil { 374 | return key, v 375 | } 376 | } 377 | } 378 | } 379 | 380 | for i := tb.k2i[key] + 1; i < len(tb.keys); i++ { 381 | key := tb.keys[i] 382 | if v := tb.RawGetH(key); v != LNil { 383 | return key, v 384 | } 385 | } 386 | return LNil, LNil 387 | } 388 | -------------------------------------------------------------------------------- /table_test.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestTableNewLTable(t *testing.T) { 8 | tbl := newLTable(-1, -2) 9 | errorIfNotEqual(t, 0, cap(tbl.array)) 10 | 11 | tbl = newLTable(10, 9) 12 | errorIfNotEqual(t, 10, cap(tbl.array)) 13 | } 14 | 15 | func TestTableLen(t *testing.T) { 16 | tbl := newLTable(0, 0) 17 | tbl.RawSetInt(10, LNil) 18 | tbl.RawSetInt(9, LNumber(10)) 19 | tbl.RawSetInt(8, LNil) 20 | tbl.RawSetInt(7, LNumber(10)) 21 | errorIfNotEqual(t, 9, tbl.Len()) 22 | 23 | tbl = newLTable(0, 0) 24 | tbl.Append(LTrue) 25 | tbl.Append(LTrue) 26 | tbl.Append(LTrue) 27 | errorIfNotEqual(t, 3, tbl.Len()) 28 | } 29 | 30 | func TestTableLenType(t *testing.T) { 31 | L := NewState(Options{}) 32 | err := L.DoString(` 33 | mt = { 34 | __index = mt, 35 | __len = function (self) 36 | return {hello = "world"} 37 | end 38 | } 39 | 40 | v = {} 41 | v.__index = v 42 | 43 | setmetatable(v, mt) 44 | 45 | assert(#v ~= 0, "#v should return a table reference in this case") 46 | 47 | print(#v) 48 | `) 49 | if err != nil { 50 | t.Error(err) 51 | } 52 | } 53 | 54 | func TestTableAppend(t *testing.T) { 55 | tbl := newLTable(0, 0) 56 | tbl.RawSetInt(1, LNumber(1)) 57 | tbl.RawSetInt(2, LNumber(2)) 58 | tbl.RawSetInt(3, LNumber(3)) 59 | errorIfNotEqual(t, 3, tbl.Len()) 60 | 61 | tbl.RawSetInt(1, LNil) 62 | tbl.RawSetInt(2, LNil) 63 | errorIfNotEqual(t, 3, tbl.Len()) 64 | 65 | tbl.Append(LNumber(4)) 66 | errorIfNotEqual(t, 4, tbl.Len()) 67 | 68 | tbl.RawSetInt(3, LNil) 69 | tbl.RawSetInt(4, LNil) 70 | errorIfNotEqual(t, 0, tbl.Len()) 71 | 72 | tbl.Append(LNumber(5)) 73 | errorIfNotEqual(t, 1, tbl.Len()) 74 | } 75 | 76 | func TestTableInsert(t *testing.T) { 77 | tbl := newLTable(0, 0) 78 | tbl.Append(LTrue) 79 | tbl.Append(LTrue) 80 | tbl.Append(LTrue) 81 | 82 | tbl.Insert(5, LFalse) 83 | errorIfNotEqual(t, LFalse, tbl.RawGetInt(5)) 84 | errorIfNotEqual(t, 5, tbl.Len()) 85 | 86 | tbl.Insert(-10, LFalse) 87 | errorIfNotEqual(t, LFalse, tbl.RawGet(LNumber(-10))) 88 | errorIfNotEqual(t, 5, tbl.Len()) 89 | 90 | tbl = newLTable(0, 0) 91 | tbl.Append(LNumber(1)) 92 | tbl.Append(LNumber(2)) 93 | tbl.Append(LNumber(3)) 94 | tbl.Insert(1, LNumber(10)) 95 | errorIfNotEqual(t, LNumber(10), tbl.RawGetInt(1)) 96 | errorIfNotEqual(t, LNumber(1), tbl.RawGetInt(2)) 97 | errorIfNotEqual(t, LNumber(2), tbl.RawGetInt(3)) 98 | errorIfNotEqual(t, LNumber(3), tbl.RawGetInt(4)) 99 | errorIfNotEqual(t, 4, tbl.Len()) 100 | 101 | tbl = newLTable(0, 0) 102 | tbl.Insert(5, LNumber(10)) 103 | errorIfNotEqual(t, LNumber(10), tbl.RawGetInt(5)) 104 | 105 | } 106 | 107 | func TestTableMaxN(t *testing.T) { 108 | tbl := newLTable(0, 0) 109 | tbl.Append(LTrue) 110 | tbl.Append(LTrue) 111 | tbl.Append(LTrue) 112 | errorIfNotEqual(t, 3, tbl.MaxN()) 113 | 114 | tbl = newLTable(0, 0) 115 | errorIfNotEqual(t, 0, tbl.MaxN()) 116 | 117 | tbl = newLTable(10, 0) 118 | errorIfNotEqual(t, 0, tbl.MaxN()) 119 | } 120 | 121 | func TestTableRemove(t *testing.T) { 122 | tbl := newLTable(0, 0) 123 | errorIfNotEqual(t, LNil, tbl.Remove(10)) 124 | tbl.Append(LTrue) 125 | errorIfNotEqual(t, LNil, tbl.Remove(10)) 126 | 127 | tbl.Append(LFalse) 128 | tbl.Append(LTrue) 129 | errorIfNotEqual(t, LFalse, tbl.Remove(2)) 130 | errorIfNotEqual(t, 2, tbl.MaxN()) 131 | tbl.Append(LFalse) 132 | errorIfNotEqual(t, LFalse, tbl.Remove(-1)) 133 | errorIfNotEqual(t, 2, tbl.MaxN()) 134 | 135 | } 136 | 137 | func TestTableRawSetInt(t *testing.T) { 138 | tbl := newLTable(0, 0) 139 | tbl.RawSetInt(MaxArrayIndex+1, LTrue) 140 | errorIfNotEqual(t, 0, tbl.MaxN()) 141 | errorIfNotEqual(t, LTrue, tbl.RawGet(LNumber(MaxArrayIndex+1))) 142 | 143 | tbl.RawSetInt(1, LTrue) 144 | tbl.RawSetInt(3, LTrue) 145 | errorIfNotEqual(t, 3, tbl.MaxN()) 146 | errorIfNotEqual(t, LTrue, tbl.RawGetInt(1)) 147 | errorIfNotEqual(t, LNil, tbl.RawGetInt(2)) 148 | errorIfNotEqual(t, LTrue, tbl.RawGetInt(3)) 149 | tbl.RawSetInt(2, LTrue) 150 | errorIfNotEqual(t, LTrue, tbl.RawGetInt(1)) 151 | errorIfNotEqual(t, LTrue, tbl.RawGetInt(2)) 152 | errorIfNotEqual(t, LTrue, tbl.RawGetInt(3)) 153 | } 154 | 155 | func TestTableRawSetH(t *testing.T) { 156 | tbl := newLTable(0, 0) 157 | tbl.RawSetH(LString("key"), LTrue) 158 | tbl.RawSetH(LString("key"), LNil) 159 | _, found := tbl.dict[LString("key")] 160 | errorIfNotEqual(t, false, found) 161 | 162 | tbl.RawSetH(LTrue, LTrue) 163 | tbl.RawSetH(LTrue, LNil) 164 | _, foundb := tbl.dict[LTrue] 165 | errorIfNotEqual(t, false, foundb) 166 | } 167 | 168 | func TestTableRawGetH(t *testing.T) { 169 | tbl := newLTable(0, 0) 170 | errorIfNotEqual(t, LNil, tbl.RawGetH(LNumber(1))) 171 | errorIfNotEqual(t, LNil, tbl.RawGetH(LString("key0"))) 172 | tbl.RawSetH(LString("key0"), LTrue) 173 | tbl.RawSetH(LString("key1"), LFalse) 174 | tbl.RawSetH(LNumber(1), LTrue) 175 | errorIfNotEqual(t, LTrue, tbl.RawGetH(LString("key0"))) 176 | errorIfNotEqual(t, LTrue, tbl.RawGetH(LNumber(1))) 177 | errorIfNotEqual(t, LNil, tbl.RawGetH(LString("notexist"))) 178 | errorIfNotEqual(t, LNil, tbl.RawGetH(LTrue)) 179 | } 180 | 181 | func TestTableForEach(t *testing.T) { 182 | tbl := newLTable(0, 0) 183 | tbl.Append(LNumber(1)) 184 | tbl.Append(LNumber(2)) 185 | tbl.Append(LNumber(3)) 186 | tbl.Append(LNil) 187 | tbl.Append(LNumber(5)) 188 | 189 | tbl.RawSetH(LString("a"), LString("a")) 190 | tbl.RawSetH(LString("b"), LString("b")) 191 | tbl.RawSetH(LString("c"), LString("c")) 192 | 193 | tbl.RawSetH(LTrue, LString("true")) 194 | tbl.RawSetH(LFalse, LString("false")) 195 | 196 | tbl.ForEach(func(key, value LValue) { 197 | switch k := key.(type) { 198 | case LBool: 199 | switch bool(k) { 200 | case true: 201 | errorIfNotEqual(t, LString("true"), value) 202 | case false: 203 | errorIfNotEqual(t, LString("false"), value) 204 | default: 205 | t.Fail() 206 | } 207 | case LNumber: 208 | switch int(k) { 209 | case 1: 210 | errorIfNotEqual(t, LNumber(1), value) 211 | case 2: 212 | errorIfNotEqual(t, LNumber(2), value) 213 | case 3: 214 | errorIfNotEqual(t, LNumber(3), value) 215 | case 4: 216 | errorIfNotEqual(t, LNumber(5), value) 217 | default: 218 | t.Fail() 219 | } 220 | case LString: 221 | switch string(k) { 222 | case "a": 223 | errorIfNotEqual(t, LString("a"), value) 224 | case "b": 225 | errorIfNotEqual(t, LString("b"), value) 226 | case "c": 227 | errorIfNotEqual(t, LString("c"), value) 228 | default: 229 | t.Fail() 230 | } 231 | } 232 | }) 233 | } 234 | -------------------------------------------------------------------------------- /tablelib.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | func OpenTable(L *LState) int { 8 | tabmod := L.RegisterModule(TabLibName, tableFuncs) 9 | L.Push(tabmod) 10 | return 1 11 | } 12 | 13 | var tableFuncs = map[string]LGFunction{ 14 | "getn": tableGetN, 15 | "concat": tableConcat, 16 | "insert": tableInsert, 17 | "maxn": tableMaxN, 18 | "remove": tableRemove, 19 | "sort": tableSort, 20 | } 21 | 22 | func tableSort(L *LState) int { 23 | tbl := L.CheckTable(1) 24 | sorter := lValueArraySorter{L, nil, tbl.array} 25 | if L.GetTop() != 1 { 26 | sorter.Fn = L.CheckFunction(2) 27 | } 28 | sort.Sort(sorter) 29 | return 0 30 | } 31 | 32 | func tableGetN(L *LState) int { 33 | L.Push(LNumber(L.CheckTable(1).Len())) 34 | return 1 35 | } 36 | 37 | func tableMaxN(L *LState) int { 38 | L.Push(LNumber(L.CheckTable(1).MaxN())) 39 | return 1 40 | } 41 | 42 | func tableRemove(L *LState) int { 43 | tbl := L.CheckTable(1) 44 | if L.GetTop() == 1 { 45 | L.Push(tbl.Remove(-1)) 46 | } else { 47 | L.Push(tbl.Remove(L.CheckInt(2))) 48 | } 49 | return 1 50 | } 51 | 52 | func tableConcat(L *LState) int { 53 | tbl := L.CheckTable(1) 54 | sep := LString(L.OptString(2, "")) 55 | i := L.OptInt(3, 1) 56 | j := L.OptInt(4, tbl.Len()) 57 | if L.GetTop() == 3 { 58 | if i > tbl.Len() || i < 1 { 59 | L.Push(emptyLString) 60 | return 1 61 | } 62 | } 63 | i = intMax(intMin(i, tbl.Len()), 1) 64 | j = intMin(intMin(j, tbl.Len()), tbl.Len()) 65 | if i > j { 66 | L.Push(emptyLString) 67 | return 1 68 | } 69 | //TODO should flushing? 70 | retbottom := L.GetTop() 71 | for ; i <= j; i++ { 72 | v := tbl.RawGetInt(i) 73 | if !LVCanConvToString(v) { 74 | L.RaiseError("invalid value (%s) at index %d in table for concat", v.Type().String(), i) 75 | } 76 | L.Push(v) 77 | if i != j { 78 | L.Push(sep) 79 | } 80 | } 81 | L.Push(stringConcat(L, L.GetTop()-retbottom, L.reg.Top()-1)) 82 | return 1 83 | } 84 | 85 | func tableInsert(L *LState) int { 86 | tbl := L.CheckTable(1) 87 | nargs := L.GetTop() 88 | if nargs == 1 { 89 | L.RaiseError("wrong number of arguments") 90 | } 91 | 92 | if L.GetTop() == 2 { 93 | tbl.Append(L.Get(2)) 94 | return 0 95 | } 96 | tbl.Insert(int(L.CheckInt(2)), L.CheckAny(3)) 97 | return 0 98 | } 99 | 100 | // 101 | -------------------------------------------------------------------------------- /testutils_test.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "regexp" 7 | "runtime" 8 | "testing" 9 | ) 10 | 11 | func positionString(level int) string { 12 | _, file, line, _ := runtime.Caller(level + 1) 13 | return fmt.Sprintf("%v:%v:", filepath.Base(file), line) 14 | } 15 | 16 | func errorIfNotEqual(t *testing.T, v1, v2 interface{}) { 17 | if v1 != v2 { 18 | t.Errorf("%v '%v' expected, but got '%v'", positionString(1), v1, v2) 19 | } 20 | } 21 | 22 | func errorIfFalse(t *testing.T, cond bool, msg string, args ...interface{}) { 23 | if !cond { 24 | if len(args) > 0 { 25 | t.Errorf("%v %v", positionString(1), fmt.Sprintf(msg, args...)) 26 | } else { 27 | t.Errorf("%v %v", positionString(1), msg) 28 | } 29 | } 30 | } 31 | 32 | func errorIfNotNil(t *testing.T, v1 interface{}) { 33 | if fmt.Sprint(v1) != "" { 34 | t.Errorf("%v nil expected, but got '%v'", positionString(1), v1) 35 | } 36 | } 37 | 38 | func errorIfNil(t *testing.T, v1 interface{}) { 39 | if fmt.Sprint(v1) == "" { 40 | t.Errorf("%v non-nil value expected, but got nil", positionString(1)) 41 | } 42 | } 43 | 44 | func errorIfScriptFail(t *testing.T, L *LState, script string) { 45 | if err := L.DoString(script); err != nil { 46 | t.Errorf("%v %v", positionString(1), err.Error()) 47 | } 48 | } 49 | 50 | func errorIfGFuncFail(t *testing.T, L *LState, f LGFunction) { 51 | if err := L.GPCall(f, LNil); err != nil { 52 | t.Errorf("%v %v", positionString(1), err.Error()) 53 | } 54 | } 55 | 56 | func errorIfScriptNotFail(t *testing.T, L *LState, script string, pattern string) { 57 | if err := L.DoString(script); err != nil { 58 | reg := regexp.MustCompile(pattern) 59 | if len(reg.FindStringIndex(err.Error())) == 0 { 60 | t.Errorf("%v error message '%v' does not contains given pattern string '%v'.", positionString(1), err.Error(), pattern) 61 | return 62 | } 63 | return 64 | } 65 | t.Errorf("%v script should fail", positionString(1)) 66 | } 67 | 68 | func errorIfGFuncNotFail(t *testing.T, L *LState, f LGFunction, pattern string) { 69 | if err := L.GPCall(f, LNil); err != nil { 70 | reg := regexp.MustCompile(pattern) 71 | if len(reg.FindStringIndex(err.Error())) == 0 { 72 | t.Errorf("%v error message '%v' does not contains given pattern string '%v'.", positionString(1), err.Error(), pattern) 73 | return 74 | } 75 | return 76 | } 77 | t.Errorf("%v LGFunction should fail", positionString(1)) 78 | } 79 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "reflect" 8 | "strconv" 9 | "strings" 10 | "time" 11 | "unsafe" 12 | ) 13 | 14 | func intMin(a, b int) int { 15 | if a < b { 16 | return a 17 | } else { 18 | return b 19 | } 20 | } 21 | 22 | func intMax(a, b int) int { 23 | if a > b { 24 | return a 25 | } else { 26 | return b 27 | } 28 | } 29 | 30 | func defaultFormat(v interface{}, f fmt.State, c rune) { 31 | buf := make([]string, 0, 10) 32 | buf = append(buf, "%") 33 | for i := 0; i < 128; i++ { 34 | if f.Flag(i) { 35 | buf = append(buf, string(rune(i))) 36 | } 37 | } 38 | 39 | if w, ok := f.Width(); ok { 40 | buf = append(buf, strconv.Itoa(w)) 41 | } 42 | if p, ok := f.Precision(); ok { 43 | buf = append(buf, "."+strconv.Itoa(p)) 44 | } 45 | buf = append(buf, string(c)) 46 | format := strings.Join(buf, "") 47 | fmt.Fprintf(f, format, v) 48 | } 49 | 50 | type flagScanner struct { 51 | flag byte 52 | start string 53 | end string 54 | buf []byte 55 | str string 56 | Length int 57 | Pos int 58 | HasFlag bool 59 | ChangeFlag bool 60 | } 61 | 62 | func newFlagScanner(flag byte, start, end, str string) *flagScanner { 63 | return &flagScanner{flag, start, end, make([]byte, 0, len(str)), str, len(str), 0, false, false} 64 | } 65 | 66 | func (fs *flagScanner) AppendString(str string) { fs.buf = append(fs.buf, str...) } 67 | 68 | func (fs *flagScanner) AppendChar(ch byte) { fs.buf = append(fs.buf, ch) } 69 | 70 | func (fs *flagScanner) String() string { return string(fs.buf) } 71 | 72 | func (fs *flagScanner) Next() (byte, bool) { 73 | c := byte('\000') 74 | fs.ChangeFlag = false 75 | if fs.Pos == fs.Length { 76 | if fs.HasFlag { 77 | fs.AppendString(fs.end) 78 | } 79 | return c, true 80 | } else { 81 | c = fs.str[fs.Pos] 82 | if c == fs.flag { 83 | if fs.Pos < (fs.Length-1) && fs.str[fs.Pos+1] == fs.flag { 84 | fs.HasFlag = false 85 | fs.AppendChar(fs.flag) 86 | fs.Pos += 2 87 | return fs.Next() 88 | } else if fs.Pos != fs.Length-1 { 89 | if fs.HasFlag { 90 | fs.AppendString(fs.end) 91 | } 92 | fs.AppendString(fs.start) 93 | fs.ChangeFlag = true 94 | fs.HasFlag = true 95 | } 96 | } 97 | } 98 | fs.Pos++ 99 | return c, false 100 | } 101 | 102 | var cDateFlagToGo = map[byte]string{ 103 | 'a': "mon", 'A': "Monday", 'b': "Jan", 'B': "January", 'c': "02 Jan 06 15:04 MST", 'd': "02", 104 | 'F': "2006-01-02", 'H': "15", 'I': "03", 'm': "01", 'M': "04", 'p': "PM", 'P': "pm", 'S': "05", 105 | 'x': "15/04/05", 'X': "15:04:05", 'y': "06", 'Y': "2006", 'z': "-0700", 'Z': "MST"} 106 | 107 | func strftime(t time.Time, cfmt string) string { 108 | sc := newFlagScanner('%', "", "", cfmt) 109 | for c, eos := sc.Next(); !eos; c, eos = sc.Next() { 110 | if !sc.ChangeFlag { 111 | if sc.HasFlag { 112 | if v, ok := cDateFlagToGo[c]; ok { 113 | sc.AppendString(t.Format(v)) 114 | } else { 115 | switch c { 116 | case 'w': 117 | sc.AppendString(fmt.Sprint(int(t.Weekday()))) 118 | default: 119 | sc.AppendChar('%') 120 | sc.AppendChar(c) 121 | } 122 | } 123 | sc.HasFlag = false 124 | } else { 125 | sc.AppendChar(c) 126 | } 127 | } 128 | } 129 | 130 | return sc.String() 131 | } 132 | 133 | func isInteger(v LNumber) bool { 134 | return float64(v) == float64(int64(v)) 135 | //_, frac := math.Modf(float64(v)) 136 | //return frac == 0.0 137 | } 138 | 139 | func isArrayKey(v LNumber) bool { 140 | return isInteger(v) && v < LNumber(int((^uint(0))>>1)) && v > LNumber(0) && v < LNumber(MaxArrayIndex) 141 | } 142 | 143 | func parseNumber(number string) (LNumber, error) { 144 | var value LNumber 145 | number = strings.Trim(number, " \t\n") 146 | if v, err := strconv.ParseInt(number, 0, LNumberBit); err != nil { 147 | if v2, err2 := strconv.ParseFloat(number, LNumberBit); err2 != nil { 148 | return LNumber(0), err2 149 | } else { 150 | value = LNumber(v2) 151 | } 152 | } else { 153 | value = LNumber(v) 154 | } 155 | return value, nil 156 | } 157 | 158 | func popenArgs(arg string) (string, []string) { 159 | cmd := "/bin/sh" 160 | args := []string{"-c"} 161 | if LuaOS == "windows" { 162 | cmd = "C:\\Windows\\system32\\cmd.exe" 163 | args = []string{"/c"} 164 | } 165 | args = append(args, arg) 166 | return cmd, args 167 | } 168 | 169 | func isGoroutineSafe(lv LValue) bool { 170 | switch v := lv.(type) { 171 | case *LFunction, *LUserData, *LState: 172 | return false 173 | case *LTable: 174 | return v.Metatable == LNil 175 | default: 176 | return true 177 | } 178 | } 179 | 180 | func readBufioSize(reader *bufio.Reader, size int64) ([]byte, error, bool) { 181 | result := []byte{} 182 | read := int64(0) 183 | var err error 184 | var n int 185 | for read != size { 186 | buf := make([]byte, size-read) 187 | n, err = reader.Read(buf) 188 | if err != nil { 189 | break 190 | } 191 | read += int64(n) 192 | result = append(result, buf[:n]...) 193 | } 194 | e := err 195 | if e != nil && e == io.EOF { 196 | e = nil 197 | } 198 | 199 | return result, e, len(result) == 0 && err == io.EOF 200 | } 201 | 202 | func readBufioLine(reader *bufio.Reader) ([]byte, error, bool) { 203 | result := []byte{} 204 | var buf []byte 205 | var err error 206 | var isprefix bool = true 207 | for isprefix { 208 | buf, isprefix, err = reader.ReadLine() 209 | if err != nil { 210 | break 211 | } 212 | result = append(result, buf...) 213 | } 214 | e := err 215 | if e != nil && e == io.EOF { 216 | e = nil 217 | } 218 | 219 | return result, e, len(result) == 0 && err == io.EOF 220 | } 221 | 222 | func int2Fb(val int) int { 223 | e := 0 224 | x := val 225 | for x >= 16 { 226 | x = (x + 1) >> 1 227 | e++ 228 | } 229 | if x < 8 { 230 | return x 231 | } 232 | return ((e + 1) << 3) | (x - 8) 233 | } 234 | 235 | func strCmp(s1, s2 string) int { 236 | len1 := len(s1) 237 | len2 := len(s2) 238 | for i := 0; ; i++ { 239 | c1 := -1 240 | if i < len1 { 241 | c1 = int(s1[i]) 242 | } 243 | c2 := -1 244 | if i != len2 { 245 | c2 = int(s2[i]) 246 | } 247 | switch { 248 | case c1 < c2: 249 | return -1 250 | case c1 > c2: 251 | return +1 252 | case c1 < 0: 253 | return 0 254 | } 255 | } 256 | } 257 | 258 | func unsafeFastStringToReadOnlyBytes(s string) (bs []byte) { 259 | sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) 260 | bh := (*reflect.SliceHeader)(unsafe.Pointer(&bs)) 261 | bh.Data = sh.Data 262 | bh.Cap = sh.Len 263 | bh.Len = sh.Len 264 | return 265 | } 266 | -------------------------------------------------------------------------------- /value.go: -------------------------------------------------------------------------------- 1 | package lua 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type LValueType int 10 | 11 | const ( 12 | LTNil LValueType = iota 13 | LTBool 14 | LTNumber 15 | LTString 16 | LTFunction 17 | LTUserData 18 | LTThread 19 | LTTable 20 | LTChannel 21 | ) 22 | 23 | var lValueNames = [9]string{"nil", "boolean", "number", "string", "function", "userdata", "thread", "table", "channel"} 24 | 25 | func (vt LValueType) String() string { 26 | return lValueNames[int(vt)] 27 | } 28 | 29 | type LValue interface { 30 | String() string 31 | Type() LValueType 32 | } 33 | 34 | // LVIsFalse returns true if a given LValue is a nil or false otherwise false. 35 | func LVIsFalse(v LValue) bool { return v == LNil || v == LFalse } 36 | 37 | // LVIsFalse returns false if a given LValue is a nil or false otherwise true. 38 | func LVAsBool(v LValue) bool { return v != LNil && v != LFalse } 39 | 40 | // LVAsString returns string representation of a given LValue 41 | // if the LValue is a string or number, otherwise an empty string. 42 | func LVAsString(v LValue) string { 43 | switch sn := v.(type) { 44 | case LString, LNumber: 45 | return sn.String() 46 | default: 47 | return "" 48 | } 49 | } 50 | 51 | // LVCanConvToString returns true if a given LValue is a string or number 52 | // otherwise false. 53 | func LVCanConvToString(v LValue) bool { 54 | switch v.(type) { 55 | case LString, LNumber: 56 | return true 57 | default: 58 | return false 59 | } 60 | } 61 | 62 | // LVAsNumber tries to convert a given LValue to a number. 63 | func LVAsNumber(v LValue) LNumber { 64 | switch lv := v.(type) { 65 | case LNumber: 66 | return lv 67 | case LString: 68 | if num, err := parseNumber(string(lv)); err == nil { 69 | return num 70 | } 71 | } 72 | return LNumber(0) 73 | } 74 | 75 | type LNilType struct{} 76 | 77 | func (nl *LNilType) String() string { return "nil" } 78 | func (nl *LNilType) Type() LValueType { return LTNil } 79 | 80 | var LNil = LValue(&LNilType{}) 81 | 82 | type LBool bool 83 | 84 | func (bl LBool) String() string { 85 | if bool(bl) { 86 | return "true" 87 | } 88 | return "false" 89 | } 90 | func (bl LBool) Type() LValueType { return LTBool } 91 | 92 | var LTrue = LBool(true) 93 | var LFalse = LBool(false) 94 | 95 | type LString string 96 | 97 | func (st LString) String() string { return string(st) } 98 | func (st LString) Type() LValueType { return LTString } 99 | 100 | // fmt.Formatter interface 101 | func (st LString) Format(f fmt.State, c rune) { 102 | switch c { 103 | case 'd', 'i': 104 | if nm, err := parseNumber(string(st)); err != nil { 105 | defaultFormat(nm, f, 'd') 106 | } else { 107 | defaultFormat(string(st), f, 's') 108 | } 109 | default: 110 | defaultFormat(string(st), f, c) 111 | } 112 | } 113 | 114 | func (nm LNumber) String() string { 115 | if isInteger(nm) { 116 | return fmt.Sprint(int64(nm)) 117 | } 118 | return fmt.Sprint(float64(nm)) 119 | } 120 | 121 | func (nm LNumber) Type() LValueType { return LTNumber } 122 | 123 | // fmt.Formatter interface 124 | func (nm LNumber) Format(f fmt.State, c rune) { 125 | switch c { 126 | case 'q', 's': 127 | defaultFormat(nm.String(), f, c) 128 | case 'b', 'c', 'd', 'o', 'x', 'X', 'U': 129 | defaultFormat(int64(nm), f, c) 130 | case 'e', 'E', 'f', 'F', 'g', 'G': 131 | defaultFormat(float64(nm), f, c) 132 | case 'i': 133 | defaultFormat(int64(nm), f, 'd') 134 | default: 135 | if isInteger(nm) { 136 | defaultFormat(int64(nm), f, c) 137 | } else { 138 | defaultFormat(float64(nm), f, c) 139 | } 140 | } 141 | } 142 | 143 | type LTable struct { 144 | Metatable LValue 145 | 146 | array []LValue 147 | dict map[LValue]LValue 148 | strdict map[string]LValue 149 | keys []LValue 150 | k2i map[LValue]int 151 | } 152 | 153 | func (tb *LTable) String() string { return fmt.Sprintf("table: %p", tb) } 154 | func (tb *LTable) Type() LValueType { return LTTable } 155 | 156 | type LFunction struct { 157 | IsG bool 158 | Env *LTable 159 | Proto *FunctionProto 160 | GFunction LGFunction 161 | Upvalues []*Upvalue 162 | } 163 | type LGFunction func(*LState) int 164 | 165 | func (fn *LFunction) String() string { return fmt.Sprintf("function: %p", fn) } 166 | func (fn *LFunction) Type() LValueType { return LTFunction } 167 | 168 | type Global struct { 169 | MainThread *LState 170 | CurrentThread *LState 171 | Registry *LTable 172 | Global *LTable 173 | 174 | builtinMts map[int]LValue 175 | tempFiles []*os.File 176 | gccount int32 177 | } 178 | 179 | type LState struct { 180 | G *Global 181 | Parent *LState 182 | Env *LTable 183 | Panic func(*LState) 184 | Dead bool 185 | Options Options 186 | 187 | stop int32 188 | reg *registry 189 | stack callFrameStack 190 | alloc *allocator 191 | currentFrame *callFrame 192 | wrapped bool 193 | uvcache *Upvalue 194 | hasErrorFunc bool 195 | mainLoop func(*LState, *callFrame) 196 | ctx context.Context 197 | ctxCancelFn context.CancelFunc 198 | } 199 | 200 | func (ls *LState) String() string { return fmt.Sprintf("thread: %p", ls) } 201 | func (ls *LState) Type() LValueType { return LTThread } 202 | 203 | type LUserData struct { 204 | Value interface{} 205 | Env *LTable 206 | Metatable LValue 207 | } 208 | 209 | func (ud *LUserData) String() string { return fmt.Sprintf("userdata: %p", ud) } 210 | func (ud *LUserData) Type() LValueType { return LTUserData } 211 | 212 | type LChannel chan LValue 213 | 214 | func (ch LChannel) String() string { return fmt.Sprintf("channel: %p", ch) } 215 | func (ch LChannel) Type() LValueType { return LTChannel } 216 | --------------------------------------------------------------------------------