├── luna ├── __init__.py ├── tests │ ├── __init__.py │ ├── conftest.py │ ├── modules │ │ ├── __init__.py │ │ ├── test_math.py │ │ ├── test_string.py │ │ ├── test_builtin.py │ │ └── test_pattern.py │ ├── test_return.py │ ├── test_compatibility.py │ ├── helpers.py │ ├── test_main.py │ ├── test_while.py │ ├── test_repeat.py │ ├── test_div.py │ ├── test_sub.py │ ├── test_strings.py │ ├── test_conditions.py │ ├── test_add.py │ ├── test_bytecode.py │ ├── test_for.py │ ├── test_mul.py │ ├── test_call.py │ ├── test_compiled.py │ ├── scripts │ │ └── constructs.lua │ ├── test_table.py │ └── test_if.py ├── modules │ ├── __init__.py │ ├── lmath.py │ ├── string.py │ ├── table.py │ ├── builtin.py │ └── patterns.py ├── helpers.py ├── __main__.py ├── interpreter.py ├── objspace.py ├── module.py ├── main.py ├── opcodes.py ├── w_objects.py ├── bytecode.py └── luaframe.py ├── requirements.txt ├── bench ├── bench_loop.l ├── bench_fib.l ├── bench.py └── bench_mergesort.l ├── translate.py ├── .gitignore ├── .travis.yml ├── LICENSE └── README.md /luna/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /luna/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /luna/tests/conftest.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /luna/modules/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /luna/tests/modules/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # testing requirement 2 | pytest 3 | 4 | -------------------------------------------------------------------------------- /bench/bench_loop.l: -------------------------------------------------------------------------------- 1 | x = 1 2 | for i =0, 1000000 do 3 | x = x + i 4 | end 5 | -------------------------------------------------------------------------------- /luna/helpers.py: -------------------------------------------------------------------------------- 1 | DEBUG = False 2 | 3 | 4 | def debug_print(args): 5 | if DEBUG: 6 | print(args) 7 | -------------------------------------------------------------------------------- /luna/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from luna.main import create_entry_point 4 | 5 | entry_point = create_entry_point() 6 | sys.exit(entry_point(sys.argv)) 7 | -------------------------------------------------------------------------------- /bench/bench_fib.l: -------------------------------------------------------------------------------- 1 | function fib(n) 2 | if n == 0 then 3 | return 0 4 | end 5 | if n == 1 then 6 | return 1 7 | end 8 | 9 | return fib(n-1) + fib(n-2) 10 | 11 | end 12 | 13 | assert(fib(34) == 5702887) 14 | -------------------------------------------------------------------------------- /translate.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | from rpython.translator.goal.translate import main 5 | 6 | 7 | if __name__ == "__main__": 8 | root_dir = os.path.abspath(__file__) 9 | sys.argv = [arg for arg in 10 | sys.argv + ['--output', 'lunac', os.path.join('luna/main.py')]] 11 | main() 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lunac 2 | 3 | *.py[cod] 4 | 5 | # Packages 6 | *.egg 7 | *.egg-info 8 | dist 9 | build 10 | eggs 11 | parts 12 | bin 13 | var 14 | sdist 15 | develop-eggs 16 | .installed.cfg 17 | lib 18 | lib64 19 | 20 | # Installer logs 21 | pip-log.txt 22 | 23 | # Unit test / coverage reports 24 | .coverage 25 | .tox 26 | nosetests.xml 27 | -------------------------------------------------------------------------------- /luna/interpreter.py: -------------------------------------------------------------------------------- 1 | from luna.objspace import ObjectSpace 2 | from luna.helpers import debug_print 3 | 4 | 5 | class Interpreter(object): 6 | def __init__(self, flags, root_frame): 7 | self.flags = flags 8 | self.root_frame = root_frame 9 | 10 | def run(self): 11 | returnvalue = None 12 | space = ObjectSpace() 13 | 14 | returnvalue = self.root_frame.execute_frame(space) 15 | 16 | debug_print("Finished intepreting") 17 | return returnvalue[0] 18 | -------------------------------------------------------------------------------- /luna/tests/test_return.py: -------------------------------------------------------------------------------- 1 | from .helpers import codetest 2 | 3 | 4 | class TestReturn(object): 5 | """ 6 | """ 7 | def test_return_more_ints(self): 8 | ret = codetest(""" 9 | function foo(i) 10 | if i > 0 then 11 | return i, foo(i-1) 12 | else 13 | return i, 0 14 | end 15 | end 16 | x, y, z, a, b, c = foo(9) 17 | return x + y 18 | """) 19 | assert ret.getval() == 17 20 | -------------------------------------------------------------------------------- /luna/tests/test_compatibility.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | from luna.tests.helpers import test_file 5 | 6 | 7 | class TestCompatibility(object): 8 | """ 9 | Compatibility tests for Lua 5.1 from the 10 | official lua distribution 11 | """ 12 | 13 | PYLUA_BIN = os.path.join(os.path.dirname(os.path.abspath(__file__)), ('../../lunac')) 14 | 15 | def test_constructs(self, capsys): 16 | test_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), ('scripts/constructs.lua')) 17 | out = subprocess.check_output([TestCompatibility.PYLUA_BIN, test_file]) 18 | assert out == "testing syntax\n+\nOK\n" 19 | -------------------------------------------------------------------------------- /luna/objspace.py: -------------------------------------------------------------------------------- 1 | from luna.w_objects import W_Object 2 | 3 | from luna.modules.builtin import Builtin 4 | from luna.modules.table import TableModule 5 | from luna.modules.lmath import MathModule 6 | from luna.modules.string import StringModule 7 | 8 | 9 | class ObjectSpace(object): 10 | def __init__(self): 11 | self.globals = {} 12 | self.modules = {} 13 | self.registers = [W_Object()] * 10 14 | self.globals.update(Builtin.methods) 15 | self.add_module(TableModule) 16 | self.add_module(MathModule) 17 | self.add_module(StringModule) 18 | 19 | def add_module(self, moduledef): 20 | self.globals[moduledef.name] = moduledef.methods 21 | self.modules[moduledef.name] = moduledef 22 | -------------------------------------------------------------------------------- /luna/modules/lmath.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implementation of Lua's mathematical functions 3 | """ 4 | import sys 5 | from math import floor, sin 6 | 7 | from luna.w_objects import W_Num 8 | from luna.module import ModuleDef 9 | 10 | 11 | MathModule = ModuleDef('math') 12 | MathModule.add_constant('huge', W_Num(sys.maxint)) 13 | 14 | 15 | @MathModule.function('floor') 16 | def method_floor(args): 17 | return [W_Num(floor(args[0].n_val))] 18 | 19 | 20 | @MathModule.function('sin') 21 | def method_sin(args): 22 | return [W_Num(sin(args[0].n_val))] 23 | 24 | 25 | @MathModule.function('mod') 26 | def method_mod(args): 27 | # TODO rpython does not seem to support modulo of two floats, 28 | # but lua does, fix later 29 | return [W_Num(int(args[0].n_val) % int(args[1].n_val))] 30 | -------------------------------------------------------------------------------- /luna/module.py: -------------------------------------------------------------------------------- 1 | from luna.luaframe import LuaBuiltinFrame 2 | from luna.w_objects import W_Table, W_Str 3 | 4 | 5 | class ModuleDef(object): 6 | def __init__(self, name): 7 | self.name = name 8 | self.methods = W_Table() 9 | 10 | def function(self, name): 11 | def adder(func): 12 | self.methods.set(W_Str(name), LuaBuiltinFrame(func)) 13 | return adder 14 | 15 | def add_constant(self, name, w_const): 16 | self.methods.set(W_Str(name), w_const) 17 | 18 | 19 | class BuiltinDef(object): 20 | def __init__(self, name): 21 | self.name = name 22 | self.methods = {} 23 | 24 | def function(self, name): 25 | def adder(func): 26 | self.methods[name] = LuaBuiltinFrame(func) 27 | return adder 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | # command to install dependencies 5 | install: 6 | #- pip install . --use-mirrors 7 | - pip install pytest-cov python-coveralls 8 | - pip install -r requirements.txt --use-mirrors 9 | - wget https://bitbucket.org/pypy/pypy/get/default.tar.bz2 -O `pwd`/../pypy.tar.bz2 10 | - tar -xf `pwd`/../pypy.tar.bz2 -C `pwd`/../ 11 | - sudo apt-get install luajit 12 | - mkdir lua 13 | - ln -s /usr/bin/luajit-2.0.0-beta9 lua/luajit 14 | 15 | 16 | # command to run tests 17 | script: 18 | - export PYTHONPATH=$PYTONPATH:`python -c "import glob; import os; print os.path.abspath(glob.glob('../pypy-pypy*')[0])"` 19 | - export PATH=$PATH:`pwd`/lua/ 20 | - python translate.py 21 | - py.test --cov luna 22 | 23 | after_success: 24 | - coveralls 25 | -------------------------------------------------------------------------------- /luna/tests/helpers.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | 4 | from luna.bytecode import Parser 5 | from luna.interpreter import Interpreter 6 | 7 | 8 | def test_file(src='', suffix=''): 9 | f = tempfile.NamedTemporaryFile(suffix=suffix) 10 | f.write(src) 11 | f.flush() 12 | return f 13 | 14 | def compile_file(f): 15 | ret = os.system('luajit -b %s %s' %(f.name, f.name+'c')) 16 | if ret: 17 | raise RuntimeError("Compilation failed") 18 | 19 | def luabytecode_file(src): 20 | f = test_file(src, suffix='.l') 21 | compile_file(f) 22 | return open(f.name+'c') 23 | 24 | def codetest(src): 25 | f = luabytecode_file(src) 26 | flags, protos = Parser(f.name).parse() 27 | interpreter = Interpreter(flags, protos) 28 | ret = interpreter.run() 29 | return ret 30 | -------------------------------------------------------------------------------- /luna/tests/test_main.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from luna.main import create_entry_point 4 | from luna.tests.helpers import luabytecode_file, test_file 5 | 6 | 7 | ENTRY_POINT = create_entry_point() 8 | 9 | 10 | class TestMain(object): 11 | def test_with_bytecode_file(self): 12 | f = luabytecode_file("x = 1") 13 | assert ENTRY_POINT(['', f.name]) == 0 14 | 15 | def test_with_lua_file(self): 16 | f = test_file("x = 1", suffix=".l") 17 | assert ENTRY_POINT(['', f.name]) == 0 18 | assert os.path.exists(f.name+'c') 19 | 20 | def test_with_non_exisiting_file(self, tmpdir): 21 | assert ENTRY_POINT(['', tmpdir.dirname+'/'+'foo.l']) == 1 22 | 23 | def test_with_invalid_extension(self): 24 | f = test_file("int main(void{};", suffix=".c") 25 | assert ENTRY_POINT(['', f.name]) == 1 26 | 27 | def test_with_invalid_lua_file(self): 28 | f = test_file("this is no lua code", suffix=".l") 29 | assert ENTRY_POINT(['', f.name]) == 1 30 | -------------------------------------------------------------------------------- /luna/tests/test_while.py: -------------------------------------------------------------------------------- 1 | from .helpers import codetest 2 | 3 | 4 | class TestWhile(object): 5 | """ 6 | tests for the lua if then else and various comparisons 7 | """ 8 | 9 | def test_simple_while(self): 10 | ret = codetest(""" 11 | x = 0 12 | while x < 10 do 13 | x = x + 1 14 | end 15 | return x 16 | """) 17 | assert ret.getval() == 10 18 | 19 | def test_simple_while_false(self): 20 | ret = codetest(""" 21 | x = 99 22 | while x < 0 do 23 | x = x + 1 24 | end 25 | return x 26 | """) 27 | assert ret.getval() == 99 28 | 29 | def test_complex_while(self): 30 | ret = codetest(""" 31 | i = 0 32 | x = 0 33 | while i < 10 do 34 | i = i + 1 35 | x = x + 1 36 | j = 5 37 | while j > 0 do 38 | j = j - 1 39 | x = x + 1 40 | end 41 | end 42 | return x 43 | """) 44 | assert ret.getval() == 60 45 | -------------------------------------------------------------------------------- /luna/tests/test_repeat.py: -------------------------------------------------------------------------------- 1 | from .helpers import codetest 2 | 3 | 4 | class TestRepeat(object): 5 | """ 6 | tests for the lua if then else and various comparisons 7 | """ 8 | 9 | def test_simple_repeat(self): 10 | ret = codetest(""" 11 | x = 0 12 | repeat 13 | x = x + 1 14 | until x == 10 15 | return x 16 | """) 17 | assert ret.getval() == 10 18 | 19 | def test_simple_repeat_false(self): 20 | ret = codetest(""" 21 | x = 99 22 | repeat 23 | x = x + 1 24 | until x > 0 25 | return x 26 | """) 27 | assert ret.getval() == 100 28 | 29 | def test_nested_repeat(self): 30 | ret = codetest(""" 31 | i = 0 32 | x = 0 33 | repeat 34 | i = i + 1 35 | x = x + 1 36 | j = 5 37 | repeat 38 | j = j - 1 39 | x = x + 1 40 | until j == 0 41 | until i == 10 42 | return x 43 | """) 44 | assert ret.getval() == 60 45 | -------------------------------------------------------------------------------- /luna/tests/test_div.py: -------------------------------------------------------------------------------- 1 | from .helpers import codetest 2 | 3 | 4 | class TestDivision(object): 5 | def test_divvn(self): 6 | ret = codetest(""" 7 | x = 4 8 | return x / 2 9 | """) 10 | assert ret.getval() == 2 11 | 12 | def test_divnv(self): 13 | ret = codetest(""" 14 | x = 4 15 | return 16 / x 16 | """) 17 | assert ret.getval() == 4 18 | 19 | def test_divvv(self): 20 | ret = codetest(""" 21 | x = 10000 22 | y = 1000 23 | return x / y 24 | """) 25 | assert ret.getval() == 10 26 | 27 | def test_divvn_float(self): 28 | ret = codetest(""" 29 | x = 5.0 30 | return x / 2.0 31 | """) 32 | assert ret.getval() == 2.5 33 | 34 | def test_divnv_float(self): 35 | ret = codetest(""" 36 | x = 2 37 | return 35 / x 38 | """) 39 | assert ret.getval() == 17.5 40 | 41 | def test_divvv_float(self): 42 | ret = codetest(""" 43 | x = 2.5 44 | y = 2 45 | return x / y 46 | """) 47 | assert ret.getval() == 1.25 48 | -------------------------------------------------------------------------------- /bench/bench.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | 5 | bench_path = os.path.dirname(os.path.abspath(__file__)) 6 | 7 | def run_script(vm_path, args, script_path): 8 | if len(args) == 0: 9 | subprocess.call([vm_path, script_path]) 10 | else: 11 | subprocess.call([vm_path, args, script_path]) 12 | 13 | if __name__ == '__main__': 14 | import timeit 15 | 16 | vms = [ 17 | ('luajit', ""), 18 | ("luajit", "-joff"), ('lua', ""), 19 | (os.path.join(bench_path, '../lunac'),"") 20 | ] 21 | bench_scripts = ['bench_loop.l', 'bench_fib.l', 'bench_mergesort.l'] 22 | 23 | for script in bench_scripts: 24 | print("Running benchmark %s" % script) 25 | script_path = os.path.join(bench_path, script) 26 | results = {} 27 | for vm in vms: 28 | time = timeit.timeit('run_script("{0}", "{1}", "{2}")'.format(vm[0], vm[1], script_path), setup="from __main__ import run_script", number=5) 29 | 30 | print("\t %s took %lf" % (vm[0], time)) 31 | results[vm] = time 32 | 33 | print("") 34 | for k, v in results.iteritems(): 35 | if k != vms[-1]: 36 | # compare vm to luna 37 | factor = results[vms[-1]] / v 38 | print("\tVm %s is %lf faster than luna" % (k[0], factor)) 39 | -------------------------------------------------------------------------------- /luna/tests/test_sub.py: -------------------------------------------------------------------------------- 1 | from .helpers import codetest 2 | 3 | 4 | class TestSubtraction(object): 5 | def test_subvn(self): 6 | ret = codetest(""" 7 | x = 6500 8 | return x - 3000 9 | """) 10 | assert ret.getval() == 3500 11 | 12 | def test_subvn_long(self): 13 | ret = codetest(""" 14 | x = 113107200 15 | return x - 13107200 16 | """) 17 | assert ret.getval() == 100000000 18 | 19 | def test_subnv(self): 20 | ret = codetest(""" 21 | x = 6500 22 | return 3000 - x 23 | """) 24 | assert ret.getval() == -3500 25 | 26 | def test_subnv_long(self): 27 | ret = codetest(""" 28 | x = 13107200 29 | return 113107200 - x 30 | """) 31 | assert ret.getval() == 100000000 32 | 33 | def test_subvv(self): 34 | ret = codetest(""" 35 | x = 6500 36 | y = 10000 37 | return y - x 38 | """) 39 | assert ret.getval() == 3500 40 | 41 | def test_subvv_long(self): 42 | ret = codetest(""" 43 | x = 113107200 44 | y = 13107200 45 | return x - y 46 | """) 47 | assert ret.getval() == 100000000 48 | -------------------------------------------------------------------------------- /luna/tests/modules/test_math.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import pytest 4 | 5 | from ..helpers import codetest, test_file 6 | 7 | 8 | class TestMath(object): 9 | def test_huge(self, capsys): 10 | ret = codetest(""" 11 | return math.huge 12 | """) 13 | assert ret.n_val == sys.maxint 14 | 15 | def test_floor_1(self, capsys): 16 | ret = codetest(""" 17 | return math.floor(1.9) 18 | """) 19 | assert ret.n_val == 1 20 | 21 | def test_floor_2(self, capsys): 22 | ret = codetest(""" 23 | return math.floor(1.0) 24 | """) 25 | assert ret.n_val == 1 26 | 27 | def test_floor_3(self, capsys): 28 | ret = codetest(""" 29 | return math.floor(-1.1) 30 | """) 31 | assert ret.n_val == -2 32 | 33 | def test_sin_1(self, capsys): 34 | ret = codetest(""" 35 | return math.sin(1) 36 | """) 37 | assert abs(ret.n_val - 0.8414709848079) < 0.000000001 38 | 39 | def test_sin_0(self, capsys): 40 | ret = codetest(""" 41 | return math.sin(0) 42 | """) 43 | assert ret.n_val == 0 44 | 45 | def test_floor(self): 46 | ret = codetest(""" 47 | return math.mod(100, 10) 48 | """) 49 | assert ret.n_val == 100 % 10 50 | -------------------------------------------------------------------------------- /luna/tests/test_strings.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from .helpers import codetest 4 | 5 | 6 | class TestStrings(object): 7 | def test_string_constant(self): 8 | ret = codetest(""" 9 | x = "str" 10 | return x 11 | """) 12 | assert ret.s_val == "str" 13 | 14 | def test_empty_string_constant(self): 15 | ret = codetest(""" 16 | x = "" 17 | return x 18 | """) 19 | assert ret.s_val == "" 20 | 21 | def test_cat_strs(self): 22 | ret = codetest(""" 23 | return "hal".."lo" 24 | """) 25 | assert ret.s_val == "hallo" 26 | 27 | def test_cat_ints(self): 28 | ret = codetest(""" 29 | return 100 .. 99 30 | """) 31 | assert ret.s_val == "10099" 32 | 33 | def test_cat_str_int(self): 34 | ret = codetest(""" 35 | return "hallo" .. 99 36 | """) 37 | assert ret.s_val == "hallo99" 38 | 39 | def test_cat_int_str_int(self): 40 | ret = codetest(""" 41 | return 1 .. ", " .. 99 42 | """) 43 | assert ret.s_val == "1, 99" 44 | 45 | def test_compare_string_with_nil(self): 46 | ret = codetest(""" 47 | return nil ~= "foo" 48 | """) 49 | assert ret.is_true() 50 | 51 | 52 | -------------------------------------------------------------------------------- /luna/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from luna.bytecode import Parser 4 | from luna.interpreter import Interpreter 5 | 6 | 7 | def _needs_compilation(path1, path2): 8 | """Checks if path1 exists and is up to date or need to be compiled.""" 9 | try: 10 | f1_mtime = os.stat(path1).st_mtime 11 | if f1_mtime < os.stat(path2).st_mtime: 12 | return True 13 | except OSError: 14 | return True 15 | return False 16 | 17 | 18 | def create_entry_point(): 19 | def entry_point(argv): 20 | try: 21 | filename = argv[1] 22 | except IndexError: 23 | print "You must supply a filename" 24 | print (argv) 25 | return 1 26 | 27 | file_, ext = argv[1].rsplit('.', 1) 28 | """ 29 | if ext not in ('l', 'lc'): 30 | print("Unsupported extension %s" %ext) 31 | return 1 32 | """ 33 | 34 | if _needs_compilation(file_+'.lc', filename): 35 | ret = os.system('luajit -b %s %s' %(filename, filename+'c')) 36 | if ret: 37 | print("Error compiling %s using luajit" % filename) 38 | return 1 39 | 40 | if not ext.endswith('c'): 41 | filename += 'c' 42 | 43 | flags, protos = Parser(filename).parse() 44 | interpreter = Interpreter(flags, protos) 45 | interpreter.run() 46 | 47 | return 0 48 | return entry_point 49 | 50 | 51 | def target(driver, args): 52 | return create_entry_point(), None 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | New BSD License 2 | 3 | Copyright (c) 2013, Florian Hahn 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of luna nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /luna/tests/test_conditions.py: -------------------------------------------------------------------------------- 1 | from .helpers import codetest 2 | 3 | 4 | class TestConditionals(object): 5 | """ 6 | """ 7 | 8 | def test_not_num1(self): 9 | ret = codetest(""" 10 | return not 1 11 | """) 12 | assert ret.getval() == 1 13 | 14 | def test_not_num2(self): 15 | ret = codetest(""" 16 | return not 2 17 | """) 18 | assert ret.getval() == 1 19 | 20 | def test_not_nil(self): 21 | ret = codetest(""" 22 | return not nil 23 | """) 24 | assert ret.getval() == 2 25 | 26 | def test_not_false(self): 27 | ret = codetest(""" 28 | return not false 29 | """) 30 | assert ret.getval() == 2 31 | 32 | def test_not_true(self): 33 | ret = codetest(""" 34 | return not false 35 | """) 36 | assert ret.getval() == 2 37 | 38 | def test_not_string1(self): 39 | ret = codetest(""" 40 | return not "" 41 | """) 42 | assert ret.getval() == 1 43 | 44 | def test_not_string2(self): 45 | ret = codetest(""" 46 | return not "foo" 47 | """) 48 | assert ret.getval() == 1 49 | 50 | def test_knill(self): 51 | codetest(""" 52 | function f() 53 | return 1,2,3 54 | end 55 | a, b, c = (f()); 56 | assert(a==1 and b==nil and c==nil) 57 | """) 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /luna/tests/test_add.py: -------------------------------------------------------------------------------- 1 | from .helpers import codetest 2 | 3 | 4 | class TestAddition(object): 5 | def test_addvv(self): 6 | ret = codetest(""" 7 | x = 4 8 | y = 9 9 | z = x + y 10 | return x + y +z 11 | """) 12 | assert ret.getval() == 26 13 | 14 | def test_addvv_long(self): 15 | ret = codetest(""" 16 | x = 131072 17 | y = 131072 18 | z = x + y 19 | return z 20 | """) 21 | assert ret.getval() == 262144 22 | 23 | def test_addvv_float(self): 24 | ret = codetest(""" 25 | x = 6.5 26 | y = 1.2 27 | return x + y 28 | """) 29 | assert ret.getval() == 7.7 30 | 31 | def test_addvn(self): 32 | ret = codetest(""" 33 | x = 131072 34 | return x+10 35 | """) 36 | assert ret.getval() == 131082 37 | 38 | def test_addvn_long(self): 39 | ret = codetest(""" 40 | x = 1310720 41 | return x+10000000 42 | """) 43 | assert ret.getval() == 11310720 44 | 45 | def test_addnv(self): 46 | ret = codetest(""" 47 | x = 131 48 | return 10 + x 49 | """) 50 | assert ret.getval() == 141 51 | 52 | def test_addnv_long(self): 53 | ret = codetest(""" 54 | x = 1310720 55 | return 10000000 + x 56 | """) 57 | assert ret.getval() == 11310720 58 | -------------------------------------------------------------------------------- /bench/bench_mergesort.l: -------------------------------------------------------------------------------- 1 | --[[ 2 | Implementation of MergeSort 3 | taken from https://gist.github.com/biomood/1132659, slightly modified 4 | --]] 5 | 6 | 7 | 8 | -- main mergesort algorithm 9 | function mergeSort(A, p, r) 10 | -- return if only 1 element 11 | if p < r then 12 | local q = math.floor((p + r)/2) 13 | mergeSort(A, p, q) 14 | mergeSort(A, q+1, r) 15 | merge(A, p, q, r) 16 | end 17 | end 18 | 19 | -- merge an array split from p-q, q-r 20 | function merge(A, p, q, r) 21 | local n1 = q-p+1 22 | local n2 = r-q 23 | local left = {} 24 | local right = {} 25 | for i=1, n1 do 26 | left[i] = A[p+i-1] 27 | end 28 | for i=1, n2 do 29 | right[i] = A[q+i] 30 | end 31 | 32 | left[n1+1] = math.huge 33 | right[n2+1] = math.huge 34 | 35 | local i=1 36 | local j=1 37 | 38 | for k=p, r do 39 | if left[i]<=right[j] then 40 | A[k] = left[i] 41 | i=i+1 42 | else 43 | A[k] = right[j] 44 | j=j+1 45 | end 46 | end 47 | end 48 | 49 | function reverseArray(A) 50 | for i=1, 200000 do 51 | A[i] = 200000 - i 52 | end 53 | end 54 | 55 | -- print all the elements in the array A 56 | function printArray(A) 57 | --s = table.concat(A, ", ") 58 | s = "" 59 | for i=1, #A do 60 | s = s .. ", " .. A[i] 61 | end 62 | print(s) 63 | end 64 | 65 | -- create the original array 66 | A = {} 67 | reverseArray(A) 68 | 69 | --print("Original Array") 70 | --printArray(A) 71 | --print("\n") 72 | 73 | -- sort the array 74 | mergeSort(A, 1, #A) 75 | --print("Sorted Array") 76 | --printArray(A) 77 | assert(A[1] <= A[2] and A[2] <= A[3] and A[3] <= A[4] and A[5] <= A[6]) 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Luna 2 | ===== 3 | 4 | [![Build Status](https://travis-ci.org/fhahn/luna.png?branch=master)](https://travis-ci.org/fhahn/luna) 5 | [![Coverage Status](https://coveralls.io/repos/fhahn/luna/badge.png)](https://coveralls.io/r/fhahn/luna) 6 | 7 | 8 | Luna is a bytecode register interpreter for Lua. At the moment it uses Luajit to compile Lua files to bytecode 9 | and interprets it. 10 | 11 | 12 | You'll need to have a few dependencies installed. You can get them with ``pip 13 | install -r requirements.txt``. Then make sure you have a recent checkout of 14 | [PyPy][] and have it on your ``PYTHONPATH``. Finally you need a 15 | recent version of ``luajit`` (``luajit -b`` is used to generate 16 | the bytecode). 17 | 18 | To run the tests:: 19 | 20 | $ py.test 21 | 22 | To translate run:: 23 | 24 | $ python translate.py 25 | 26 | This will compile Luna, it'll take about 30 seconds. 27 | 28 | To run Luna directly on top of Python you can do:: 29 | 30 | $ python -m luna /path/to/file.lua 31 | 32 | 33 | [PyPy]: https://bitbucket.org/pypy/pypy 34 | 35 | 36 | TODO 37 | ----------- 38 | 39 | * Compiler in Rpython 40 | * Interators 41 | * JIT 42 | * more standard library functions 43 | 44 | 45 | Features 46 | --------- 47 | 48 | At the moment, Luna supports most of the Lua standard constructs, except iterators. 49 | 50 | Following parts of the standard library (the code can be found in [luna/modules][]) have been implemented: 51 | 52 | [luna/modules]: https://github.com/fhahn/luna/tree/master/luna/modules 53 | 54 | 55 | * Builtins 56 | * assert 57 | * print 58 | * loadfile 59 | * loadstring 60 | * tonumber 61 | * type 62 | * math 63 | * floor 64 | * sin 65 | * mod 66 | * table 67 | * concat 68 | * insert 69 | * remove 70 | * string 71 | * find 72 | * match 73 | * gsub 74 | -------------------------------------------------------------------------------- /luna/tests/test_bytecode.py: -------------------------------------------------------------------------------- 1 | from luna.bytecode import Parser 2 | from luna.tests.helpers import test_file, luabytecode_file 3 | 4 | 5 | class TestParser: 6 | def test_byte(self): 7 | data = bytes("\xA0\x00\xBC\xFF\x98\x66\x66\xA0\x00\xBC\xFF\x98\x66\x66\x99\x80") 8 | f = test_file(data) 9 | 10 | p = Parser(f.name) 11 | for b in bytearray(data): 12 | assert p.byte() == b 13 | 14 | def test_word(self, ): 15 | data = bytes("\xA0\x00\xBC\xFF\x98\x66\x66\xA0\x00\xBC\xFF\x98\x66\x66\x99\x80") 16 | f = test_file(data) 17 | # little endian words (4 byte blocks) 18 | words = [0xffbc00a0, 0xa0666698, 0x98ffbc00, 0x80996666 ] 19 | 20 | p = Parser(f.name) 21 | for w in words: 22 | x = p.word() 23 | print(hex(x)) 24 | assert x == w 25 | 26 | def test_h(self): 27 | data = bytes("\xA0\x00\xBC\xFF\x98\x66\x66\xA0\x00\xBC\xFF\x98\x66\x66\x99\x80") 28 | f = test_file(data) 29 | # little endian 2 byte blocks 30 | bb = [0x00a0, 0xffbc, 0x6698, 0xa066, 0xbc00, 0x98ff, 0x6666, 0x8099] 31 | p = Parser(f.name) 32 | for h in bb: 33 | assert p.h() == h 34 | 35 | def test_uleb(self): 36 | f = test_file("\xE5\x8E\x26") 37 | p = Parser(f.name) 38 | assert p.uleb() == 624485 39 | 40 | def test_parse_simple_assignment(self): 41 | """ 42 | checks if KSHORT, GSET and RET0 are decoded correctly 43 | """ 44 | f = luabytecode_file("x = 1") 45 | p = Parser(f.name) 46 | flags, proto = p.parse() 47 | 48 | assert proto.constants[0].s_val == 'x' 49 | assert proto.instructions == [ 50 | (39, (0, 1, 0)), (53, (0, 0, 0)), (71, (0, 1, 0)) 51 | ] 52 | -------------------------------------------------------------------------------- /luna/modules/string.py: -------------------------------------------------------------------------------- 1 | """ 2 | """ 3 | import pytest 4 | 5 | from luna.w_objects import W_Pri, W_Num, W_Str 6 | from luna.module import ModuleDef 7 | from luna.modules.patterns import find2, compile_re 8 | 9 | 10 | StringModule = ModuleDef('string') 11 | 12 | 13 | def handle_args(args): 14 | s = args[0].s_val 15 | start = 0 16 | plain = False 17 | expr = compile_re(args[1].s_val, plain) 18 | 19 | if len(args) > 2: 20 | start = args[2].n_val - 1 21 | if len(args) > 3: 22 | w_pri = args[3] 23 | assert isinstance(w_pri, W_Pri) 24 | plain = w_pri.is_true() 25 | return s, expr, start, plain 26 | 27 | 28 | @StringModule.function('find') 29 | def method_find(args): 30 | s, expr, start, plain = handle_args(args) 31 | matches = next(find2(expr, s, start)) 32 | if matches == (-1, -1): 33 | return [W_Pri(0)] 34 | else: 35 | return [W_Num(matches[0]), W_Num(matches[1])] 36 | 37 | 38 | @StringModule.function('match') 39 | def method_match(args): 40 | s, expr, start, plain = handle_args(args) 41 | start_i, stop_i = next(find2(expr, s, start)) 42 | if (start_i, stop_i) == (-1, -1): 43 | return [W_Pri(0)] 44 | else: 45 | return [W_Str(s[start_i-1:stop_i])] 46 | 47 | 48 | @StringModule.function('gsub') 49 | def method_gsub(args): 50 | s = args[0].s_val 51 | pattern = args[1].s_val 52 | replace = args[2].s_val 53 | expr = compile_re(pattern, False) 54 | sublist = [] 55 | last_stop = 0 56 | for m_start, m_stop in find2(expr, s, 0): 57 | if (m_start, m_stop) == (-1, -1): 58 | return [W_Str(s)] 59 | else: 60 | sublist.append(s[last_stop:m_start-1]) 61 | sublist.append(replace) 62 | last_stop = m_stop 63 | sublist.append(s[last_stop:]) 64 | res = "".join(sublist) 65 | return [W_Str(res)] 66 | -------------------------------------------------------------------------------- /luna/tests/test_for.py: -------------------------------------------------------------------------------- 1 | from .helpers import codetest 2 | 3 | 4 | class TestFor(object): 5 | """ 6 | """ 7 | 8 | def test_simple_fori_loop(self): 9 | ret = codetest(""" 10 | x = 0 11 | for i=1,10,1 do 12 | x = x + 1 13 | end 14 | return x 15 | """) 16 | assert ret.getval() == 10 17 | 18 | def test_simple_fori_loop_false(self): 19 | ret = codetest(""" 20 | x = 0 21 | for i=20,10,1 do 22 | x = x + 1 23 | end 24 | return x 25 | """) 26 | assert ret.getval() == 0 27 | 28 | def test_simple_fori_loop_step_2(self): 29 | ret = codetest(""" 30 | x = 0 31 | for i=1,10,2 do 32 | x = x + 1 33 | end 34 | return x 35 | """) 36 | assert ret.getval() == 5 37 | 38 | def test_nested_fori_loop(self): 39 | ret = codetest(""" 40 | x = 0 41 | for i=1,10,1 do 42 | x = x + 1 43 | for i=11,15,1 do 44 | x = x + 2 45 | end 46 | end 47 | return x 48 | """) 49 | assert ret.getval() == 110 50 | 51 | def test_backwards_fori_loop(self): 52 | ret = codetest(""" 53 | x = 0 54 | for i=10,1,-1 do 55 | x = x + 1 56 | end 57 | return x 58 | """) 59 | assert ret.getval() == 10 60 | 61 | def test_nested_backwards_fori_loop(self): 62 | ret = codetest(""" 63 | x = 0 64 | for i=10,1,-1 do 65 | x = x + 1 66 | for i=11,15,1 do 67 | x = x + 2 68 | end 69 | end 70 | return x 71 | """) 72 | assert ret.getval() == 110 73 | 74 | def test_access_loop_counter(self): 75 | ret = codetest(""" 76 | x = 0 77 | for i=1,10,1 do 78 | x = x + i 79 | end 80 | return x 81 | """) 82 | assert ret.getval() == 55 83 | 84 | 85 | -------------------------------------------------------------------------------- /luna/modules/table.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implementation of Lua's table functions 3 | http://www.lua.org/manual/5.1/manual.html#5.5 4 | """ 5 | from luna.w_objects import W_Table, W_Str, W_Pri, W_Num 6 | from luna.module import ModuleDef 7 | 8 | 9 | TableModule = ModuleDef('table') 10 | 11 | 12 | @TableModule.function('concat') 13 | def method_concat(args): 14 | assert isinstance(args[0], W_Table) 15 | w_table = args[0] 16 | 17 | num_args = len(args) 18 | if num_args > 1: 19 | assert isinstance(args[1], W_Str) 20 | sep = args[1].s_val 21 | else: 22 | sep = '' 23 | 24 | i = 0 25 | j = None 26 | if num_args > 2: 27 | i = args[2].n_val 28 | if num_args == 4: 29 | j = args[3].n_val + 1 30 | 31 | if j is None: 32 | values = w_table.values()[i:] 33 | else: 34 | values = w_table.values()[i:j] 35 | strs = [x.to_str() for x in values 36 | if not(isinstance(x, W_Pri) and x.n_val == 0)] 37 | s = sep.join(strs) 38 | return [W_Str(s)] 39 | 40 | 41 | @TableModule.function('insert') 42 | def method_insert(args): 43 | assert isinstance(args[0], W_Table) 44 | w_table = args[0] 45 | num_args = len(args) 46 | if num_args == 2: 47 | args[0].set(W_Num(w_table.size()), args[1]) 48 | elif num_args == 3: 49 | pos = args[1] 50 | items = list(w_table.items()) 51 | i = len(items) - 1 52 | while i >= pos.n_val: 53 | k, v = items[i] 54 | k += 1 55 | w_table.set(W_Num(k), v) 56 | i -= 1 57 | w_table.set(pos, args[2]) 58 | else: 59 | assert 0 60 | 61 | 62 | @TableModule.function('maxn') 63 | def method_maxn(args): 64 | raise NotImplementedError("table.maxn not implemented") 65 | 66 | 67 | @TableModule.function("remove") 68 | def method_remove(args): 69 | assert isinstance(args[0], W_Table) 70 | w_table = args[0] 71 | assert isinstance(args[1], W_Num) 72 | pos = args[1] 73 | 74 | elem = None 75 | if pos.n_val < w_table.size() and pos.n_val > 0: 76 | elem = w_table.get(pos) 77 | else: 78 | return [W_Pri(0)] 79 | 80 | items = list(w_table.items()) 81 | 82 | i = pos.n_val 83 | assert isinstance(i, int) 84 | while (i+1) < len(items): 85 | k, v = items[i+1] 86 | k -= 1 87 | w_table.set(W_Num(k), v) 88 | i += 1 89 | del w_table.content[items[-1][0]] 90 | return [elem] 91 | -------------------------------------------------------------------------------- /luna/modules/builtin.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implementation of Lua's basic functions 3 | http://www.lua.org/manual/5.1/manual.html#5.1 4 | """ 5 | import os 6 | 7 | from luna.bytecode import Parser 8 | from luna.w_objects import W_Num, W_Pri, W_Str, W_Table 9 | from luna.luaframe import LuaFrame 10 | from luna.module import BuiltinDef 11 | 12 | 13 | Builtin = BuiltinDef('Builtin') 14 | 15 | 16 | @Builtin.function('assert') 17 | def method_assert(args): 18 | # TODO return all arguments 19 | if len(args) == 2: 20 | msg = args[1].to_str() 21 | else: 22 | msg = 'assertion failed' 23 | 24 | if ((isinstance(args[0], W_Pri) and args[0].n_val != 2) or 25 | (isinstance(args[0], W_Num) and args[0].n_val == 0)): 26 | raise AssertionError(msg) 27 | 28 | 29 | @Builtin.function('print') 30 | def method_print(args): 31 | args = ' '.join([x.to_str() for x in args]) 32 | print args 33 | 34 | 35 | @Builtin.function('loadfile') 36 | def method_loadfile(args): 37 | filename = args[0].s_val 38 | os.system('luajit -b %s %s' %(filename, filename+'c')) 39 | flags, protos = Parser(filename+'c').parse() 40 | return [protos] 41 | 42 | 43 | @Builtin.function('loadstring') 44 | def method_loadstring(args): 45 | s = args[0].s_val 46 | i = 0 47 | filename = '' 48 | name_tpl= '/tmp/luna' 49 | while True: 50 | filename = "".join([name_tpl, str(i), ".lua"]) 51 | if not os.path.exists(filename): 52 | break 53 | i += 1 54 | fd = os.open(filename, os.O_WRONLY|os.O_CREAT, 0777) 55 | os.write(fd, s) 56 | os.close(fd) 57 | os.system('luajit -b %s %s' %(filename, filename+'c')) 58 | os.remove(filename) 59 | flags, protos = Parser(filename+'c').parse() 60 | return [protos] 61 | 62 | 63 | @Builtin.function('tonumber') 64 | def method_tonumber(args): 65 | if len(args) > 1: 66 | raise RuntimeError("tonumber with base not supported at the moment") 67 | try: 68 | return [W_Num(float(args[0].s_val))] 69 | except ValueError: 70 | return [W_Pri(0)] 71 | 72 | 73 | @Builtin.function('type') 74 | def method_type(args): 75 | w_obj = args[0] 76 | t = "" 77 | if isinstance(w_obj, W_Pri): 78 | t = 'boolean' 79 | elif isinstance(w_obj, W_Num): 80 | t = "number" 81 | elif isinstance(w_obj, W_Str): 82 | t = 'string' 83 | elif isinstance(w_obj, W_Table): 84 | t = 'table' 85 | elif isinstance(w_obj, LuaFrame): 86 | t = 'function' 87 | else: 88 | raise RuntimeError('Unsupported type') 89 | return [W_Str(t)] 90 | -------------------------------------------------------------------------------- /luna/tests/test_mul.py: -------------------------------------------------------------------------------- 1 | from .helpers import codetest 2 | 3 | 4 | class TestMultiplication(object): 5 | def test_mulvn(self): 6 | ret = codetest(""" 7 | x = 9 8 | return x * 4 9 | """) 10 | assert ret.getval() == 36 11 | 12 | def test_mulvn_long(self): 13 | ret = codetest(""" 14 | x = 90000 15 | return x * 100000 16 | """) 17 | assert ret.getval() == 90000 * 100000 18 | 19 | def test_mulvn_float_int(self): 20 | ret = codetest(""" 21 | x = 90000 22 | return x * 0.5 23 | """) 24 | assert ret.getval() == 45000 25 | 26 | def test_mulvn_int_float(self): 27 | ret = codetest(""" 28 | x = 0.5 29 | return x * 90000 30 | """) 31 | assert ret.getval() == 45000 32 | 33 | def test_mulnv(self): 34 | ret = codetest(""" 35 | x = 9 36 | return 4 * x 37 | """) 38 | assert ret.getval() == 36 39 | 40 | def test_mulnv_long(self): 41 | ret = codetest(""" 42 | x = 90000 43 | return 100000 * x 44 | """) 45 | assert ret.getval() == 90000 * 100000 46 | 47 | def test_mulnv_float_int(self): 48 | ret = codetest(""" 49 | x = 90000 50 | return 0.5 * x 51 | """) 52 | assert ret.getval() == 45000 53 | 54 | def test_mulnv_int_float(self): 55 | ret = codetest(""" 56 | x = 0.5 57 | return 90000 * x 58 | """) 59 | assert ret.getval() == 45000 60 | 61 | def test_mulvv(self): 62 | ret = codetest(""" 63 | x = 9 64 | y = 1000 65 | return x * y 66 | """) 67 | assert ret.getval() == 9000 68 | 69 | def test_mulvv_float_int(self): 70 | ret = codetest(""" 71 | x = 0.5 72 | y = 1000 73 | return x * y 74 | """) 75 | assert ret.getval() == 500 76 | 77 | def test_mulvv_float_float(self): 78 | ret = codetest(""" 79 | x = 0.5 80 | y = 0.25 81 | return x * y 82 | """) 83 | assert ret.getval() == 0.125 84 | 85 | def test_mulvv_long(self): 86 | ret = codetest(""" 87 | x = 90000 88 | y = 100000 89 | return x * y 90 | """) 91 | assert ret.getval() == 90000 * 100000 92 | -------------------------------------------------------------------------------- /luna/tests/test_call.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from .helpers import codetest 4 | 5 | 6 | class TestRepeat(object): 7 | """ 8 | """ 9 | def test_constant_return_0_args_1_ret(self): 10 | ret = codetest(""" 11 | function foo() 12 | return 2; 13 | end 14 | x = foo() 15 | return x 16 | """) 17 | assert ret.getval() == 2 18 | 19 | def test_call_1_arg_1_ret(self): 20 | ret = codetest(""" 21 | function foo(x) 22 | return x+1; 23 | end 24 | x = foo(10) 25 | return x 26 | """) 27 | assert ret.getval() == 11 28 | 29 | def test_call_2_args_1_ret(self): 30 | ret = codetest(""" 31 | function foo(x, y) 32 | return x+y; 33 | end 34 | x = foo(30, 10) 35 | return x 36 | """) 37 | assert ret.getval() == 40 38 | 39 | def test_call_2_args_ret_without_var(self): 40 | ret = codetest(""" 41 | function foo(x, y) 42 | return x+y; 43 | end 44 | return foo(30, 10) 45 | """) 46 | assert ret.getval() == 40 47 | 48 | def test_call_recursive(self): 49 | ret = codetest(""" 50 | function fac(n) 51 | if n == 1 then 52 | return 1 53 | end 54 | x = n * fac(n-1); 55 | print(n) 56 | return x 57 | end 58 | x = fac(10) 59 | return x 60 | """) 61 | assert ret.getval() == 3628800 62 | 63 | def test_nested_function_tailcall(self): 64 | """ 65 | Tests CALLMT with user defined function 66 | """ 67 | ret = codetest(""" 68 | function f(x) 69 | y = x+1 70 | return y 71 | end 72 | return f(f(5)) 73 | """) 74 | assert ret.getval() == 7 75 | 76 | def test_nested_function_tailcall_builtin(self): 77 | """ 78 | Tests CALLMT with builtin function 79 | """ 80 | ret = codetest(""" 81 | return math.sin(math.sin(1)) 82 | """) 83 | assert ret.getval() == 0.7456241416655579 84 | 85 | def test_nested_function_call(self): 86 | """ 87 | Tests CALLM with user function 88 | """ 89 | ret = codetest(""" 90 | function f(x) 91 | y = x+1 92 | return y 93 | end 94 | x = f(f(5)) 95 | return x 96 | """) 97 | assert ret.getval() == 7 98 | 99 | def test_nested_function_call_builtin(self): 100 | """ 101 | Tests CALLM with builtin function 102 | """ 103 | ret = codetest(""" 104 | x = math.sin(math.sin(1)) 105 | return x 106 | """) 107 | assert ret.getval() == 0.7456241416655579 108 | 109 | def test_call_unknown_builtin(self): 110 | with pytest.raises(RuntimeError): 111 | codetest(""" 112 | x = math.find(1) 113 | """) 114 | -------------------------------------------------------------------------------- /luna/opcodes.py: -------------------------------------------------------------------------------- 1 | from rpython.rlib.unroll import unrolling_iterable 2 | 3 | OP_DESC = [None] * 93 4 | 5 | class OpCodeDescritption(object): 6 | def __init__(self, name, index, args_type): 7 | self.name = name 8 | self.index = index 9 | self.args_type = args_type 10 | 11 | def _freeze_(self): 12 | return True 13 | 14 | def def_op(name, index, args_type): 15 | OP_DESC[index] = OpCodeDescritption(name, index, args_type) 16 | 17 | ARGS_AD = 0 18 | ARGS_ABC = 1 19 | 20 | def_op('ISLT', 0, ARGS_AD) 21 | 22 | def_op('ISGE', 1, ARGS_AD) 23 | 24 | def_op('ISLE', 2, ARGS_AD) 25 | 26 | def_op('ISGT', 3, ARGS_AD) 27 | 28 | def_op('ISEQV', 4, ARGS_AD) 29 | 30 | def_op('ISNEV', 5, ARGS_AD) 31 | 32 | def_op('ISEQS', 6, ARGS_AD) 33 | 34 | def_op('ISNES', 7, ARGS_AD) 35 | 36 | def_op('ISEQN', 8, ARGS_AD) 37 | 38 | def_op('ISNEN', 9, ARGS_AD) 39 | 40 | def_op('ISEQP', 10, ARGS_AD) 41 | 42 | def_op('ISNEP', 11, ARGS_AD) 43 | 44 | def_op('ISTC', 12, ARGS_AD) 45 | 46 | def_op('ISFC', 13, ARGS_AD) 47 | 48 | def_op('IST', 14, ARGS_AD) 49 | 50 | def_op('ISF', 15, ARGS_AD) 51 | 52 | def_op('MOV', 16, ARGS_AD) 53 | 54 | def_op('NOT', 17, ARGS_AD) 55 | 56 | def_op('UNM', 18, ARGS_AD) 57 | 58 | def_op('LEN', 19, ARGS_AD) 59 | 60 | def_op('ADDVN', 20, ARGS_ABC) 61 | 62 | def_op('SUBVN', 21, ARGS_ABC) 63 | 64 | def_op('MULVN', 22, ARGS_ABC) 65 | 66 | def_op('DIVVN', 23, ARGS_ABC) 67 | 68 | def_op('MODVN', 24, ARGS_ABC) 69 | 70 | def_op('ADDNV', 25, ARGS_ABC) 71 | 72 | def_op('SUBNV', 26, ARGS_ABC) 73 | 74 | def_op('MULNV', 27, ARGS_ABC) 75 | 76 | def_op('DIVNV', 28, ARGS_ABC) 77 | 78 | def_op('MODNV', 29, ARGS_ABC) 79 | 80 | def_op('ADDVV', 30, ARGS_ABC) 81 | 82 | def_op('SUBVV', 31, ARGS_ABC) 83 | 84 | def_op('MULVV', 32, ARGS_ABC) 85 | 86 | def_op('DIVVV', 33, ARGS_ABC) 87 | 88 | def_op('MODVV', 34, ARGS_ABC) 89 | 90 | def_op('POW', 35, ARGS_ABC) 91 | 92 | def_op('CAT', 36, ARGS_ABC) 93 | 94 | def_op('KSTR', 37, ARGS_AD) 95 | 96 | def_op('KCDATA', 38, ARGS_AD) 97 | 98 | def_op('KSHORT', 39, ARGS_AD) 99 | 100 | def_op('KNUM', 40, ARGS_AD) 101 | 102 | def_op('KPRI', 41, ARGS_AD) 103 | 104 | def_op('KNIL', 42, ARGS_AD) 105 | 106 | def_op('UGET', 43, ARGS_AD) 107 | 108 | def_op('USETV', 44, ARGS_AD) 109 | 110 | def_op('USETS', 45, ARGS_AD) 111 | 112 | def_op('USETN', 46, ARGS_AD) 113 | 114 | def_op('USETP', 47, ARGS_AD) 115 | 116 | def_op('UCLO', 48, ARGS_AD) 117 | 118 | def_op('FNEW', 49, ARGS_AD) 119 | 120 | def_op('TNEW', 50, ARGS_AD) 121 | 122 | def_op('TDUP', 51, ARGS_AD) 123 | 124 | def_op('GGET', 52, ARGS_AD) 125 | 126 | def_op('GSET', 53, ARGS_AD) 127 | 128 | def_op('TGETV', 54, ARGS_ABC) 129 | 130 | def_op('TGETS', 55, ARGS_ABC) 131 | 132 | def_op('TGETB', 56, ARGS_ABC) 133 | 134 | def_op('TSETV', 57, ARGS_ABC) 135 | 136 | def_op('TSETS', 58, ARGS_ABC) 137 | 138 | def_op('TSETB', 59, ARGS_ABC) 139 | 140 | def_op('TSETM', 60, ARGS_AD) 141 | 142 | def_op('CALLM', 61, ARGS_ABC) 143 | 144 | def_op('CALL', 62, ARGS_ABC) 145 | 146 | def_op('CALLMT', 63, ARGS_AD) 147 | 148 | def_op('CALLT', 64, ARGS_AD) 149 | 150 | def_op('ITERC', 65, ARGS_ABC) 151 | 152 | def_op('ITERN', 66, ARGS_ABC) 153 | 154 | def_op('VARG', 67, ARGS_ABC) 155 | 156 | def_op('ISNEXT', 68, ARGS_AD) 157 | 158 | def_op('RETM', 69, ARGS_AD) 159 | 160 | def_op('RET', 70, ARGS_AD) 161 | 162 | def_op('RET0', 71, ARGS_AD) 163 | 164 | def_op('RET1', 72, ARGS_AD) 165 | 166 | def_op('FORI', 73, ARGS_AD) 167 | 168 | def_op('JFORI', 74, ARGS_AD) 169 | 170 | def_op('FORL', 75, ARGS_AD) 171 | 172 | def_op('IFORL', 76, ARGS_AD) 173 | 174 | def_op('JFORL', 77, ARGS_AD) 175 | 176 | def_op('ITERL', 78, ARGS_AD) 177 | 178 | def_op('IITERL', 79, ARGS_AD) 179 | 180 | def_op('JITERL', 80, ARGS_AD) 181 | 182 | def_op('LOOP', 81, ARGS_AD) 183 | 184 | def_op('ILOOP', 82, ARGS_AD) 185 | 186 | def_op('JLOOP', 83, ARGS_AD) 187 | 188 | def_op('JMP', 84, ARGS_AD) 189 | 190 | def_op('FUNCF', 85, ARGS_AD) 191 | 192 | def_op('IFUNCF', 86, ARGS_AD) 193 | 194 | def_op('JFUNCF', 87, ARGS_AD) 195 | 196 | def_op('FUNCV', 88, ARGS_AD) 197 | 198 | def_op('IFUNCV', 89, ARGS_AD) 199 | 200 | def_op('JFUNCV', 90, ARGS_AD) 201 | 202 | def_op('FUNCC', 91, ARGS_AD) 203 | 204 | def_op('FUNCCW', 92, ARGS_AD) 205 | 206 | 207 | unrolled_op_desc = unrolling_iterable(OP_DESC) 208 | -------------------------------------------------------------------------------- /luna/tests/test_compiled.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | from luna.tests.helpers import test_file 5 | 6 | 7 | class TestCompiled(object): 8 | """ 9 | Tests compiled binary 10 | """ 11 | 12 | PYLUA_BIN = os.path.join(os.path.dirname(os.path.abspath(__file__)), ('../../lunac')) 13 | 14 | def test_addition(self): 15 | f = test_file(src=""" 16 | -- short add 17 | x = 10 18 | y = 5 19 | z = y + y + x 20 | print(z) 21 | print(z+y) 22 | --a = 100+y 23 | 24 | lx = 1234567890.55 25 | ly = 99999999 26 | print(lx+ly) 27 | --print(lx+1234567890) 28 | """, suffix=".l" 29 | ) 30 | out = subprocess.check_output([TestCompiled.PYLUA_BIN, f.name]) 31 | assert out == "20\n25\n1334567889.550000\n" 32 | 33 | def test_if_with_num(self): 34 | f = test_file(src=""" 35 | x = 10 36 | y = 5 37 | if x == 10 then 38 | print ("OK1") 39 | end 40 | 41 | if x ~= 10 then 42 | print ("F1") 43 | end 44 | 45 | if x < y then 46 | print ("F2") 47 | end 48 | 49 | if x <= y then 50 | print ("F3") 51 | end 52 | 53 | if x > y then 54 | print ("OK2") 55 | end 56 | 57 | if x >= y then 58 | print ("OK3") 59 | end 60 | 61 | if x == y then 62 | print ("F4") 63 | end 64 | 65 | if x ~= y then 66 | print ("OK4") 67 | end 68 | 69 | xx = 10 70 | 71 | if x == xx then 72 | print ("OK5") 73 | end 74 | 75 | if x >= xx then 76 | print ("OK6") 77 | end 78 | 79 | if x <= xx then 80 | print ("OK7") 81 | end 82 | """, suffix=".l" 83 | ) 84 | out = subprocess.check_output([TestCompiled.PYLUA_BIN, f.name]) 85 | assert out == "OK1\nOK2\nOK3\nOK4\nOK5\nOK6\nOK7\n" 86 | 87 | def test_if_with_str(self): 88 | f = test_file(src=""" 89 | x = "foo" 90 | y = "bar" 91 | if x == "foo" then 92 | print ("OK1") 93 | end 94 | 95 | if x ~= "foo" then 96 | print ("F1") 97 | end 98 | 99 | if x < y then 100 | print ("F2") 101 | end 102 | 103 | if x <= y then 104 | print ("F3") 105 | end 106 | 107 | if x > y then 108 | print ("OK2") 109 | end 110 | 111 | if x >= y then 112 | print ("OK3") 113 | end 114 | 115 | if x == y then 116 | print ("F4") 117 | end 118 | 119 | if x ~= y then 120 | print ("OK4") 121 | end 122 | 123 | xx = "foo" 124 | 125 | if x == xx then 126 | print ("OK5") 127 | end 128 | 129 | if x >= xx then 130 | print ("OK6") 131 | end 132 | 133 | if x <= xx then 134 | print ("OK7") 135 | end 136 | """, suffix=".l" 137 | ) 138 | out = subprocess.check_output([TestCompiled.PYLUA_BIN, f.name]) 139 | assert out == "OK1\nOK2\nOK3\nOK4\nOK5\nOK6\nOK7\n" 140 | 141 | def test_if_with_bool(self): 142 | f = test_file(src=""" 143 | x = true 144 | y = false 145 | if x == true then 146 | print ("OK1") 147 | end 148 | 149 | if x ~= true then 150 | print ("F1") 151 | end 152 | 153 | if x == y then 154 | print("F2") 155 | end 156 | 157 | if x ~= y then 158 | print("OK2") 159 | end 160 | 161 | if true == x then 162 | print ("OK3") 163 | end 164 | 165 | if y == true then 166 | print("F3") 167 | end 168 | """, suffix=".l" 169 | ) 170 | out = subprocess.check_output([TestCompiled.PYLUA_BIN, f.name]) 171 | assert out == "OK1\nOK2\nOK3\n" 172 | 173 | def test_recursive_call(self): 174 | 175 | f = test_file(src=""" 176 | function fac(n) 177 | if n == 1 then 178 | return 1 179 | end 180 | return fac(n-1) * n 181 | end 182 | x = fac(10) 183 | print(x) 184 | """, suffix=".l" 185 | ) 186 | out = subprocess.check_output([TestCompiled.PYLUA_BIN, f.name]) 187 | assert out == "3628800\n" 188 | -------------------------------------------------------------------------------- /luna/tests/scripts/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 | x = {f(3), f(5), f(10);}; 43 | --for key,value in pairs(x) do print(key,value) end 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 | local f = function (i) 54 | if i < 10 then return 'a'; 55 | elseif i < 20 then return 'b'; 56 | elseif i < 30 then return 'c'; 57 | end; 58 | end 59 | 60 | assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == nil) 61 | 62 | for i=1,1000 do break; end; 63 | n=100; 64 | i=3; 65 | t = {}; 66 | a=nil 67 | while not a do 68 | a=0; for i=1,n do for i=i,1,-1 do a=a+1; t[i]=1; end; end; 69 | end 70 | assert(a == n*(n+1)/2 and i==3); 71 | assert(t[1] and t[n] and not t[0] and not t[n+1]) 72 | 73 | function f(b) 74 | local x = 1; 75 | repeat 76 | local a; 77 | if b==1 then local b=1; x=10; break 78 | elseif b==2 then x=20; break; 79 | elseif b==3 then x=30; 80 | else local a,b,c,d=math.sin(1); x=x+1; 81 | end 82 | until x>=12; 83 | return x; 84 | end; 85 | 86 | assert(f(1) == 10 and f(2) == 20 and f(3) == 30 and f(4)==12) 87 | 88 | 89 | local f = function (i) 90 | if i < 10 then return 'a' 91 | elseif i < 20 then return 'b' 92 | elseif i < 30 then return 'c' 93 | else return 8 94 | end 95 | end 96 | 97 | assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == 8) 98 | 99 | local a, b = nil, 23 100 | x = {f(100)*2+3 or a, a or b+2} 101 | assert(x[1] == 19 and x[2] == 25) 102 | x = {f=2+3 or a, a = b+2} 103 | assert(x.f == 5 and x.a == 25) 104 | 105 | a={y=1} 106 | x = {a.y} 107 | assert(x[1] == 1) 108 | 109 | function f(i) 110 | while 1 do 111 | if i>0 then i=i-1; 112 | else return; end; 113 | end; 114 | end; 115 | 116 | function g(i) 117 | while 1 do 118 | if i>0 then i=i-1 119 | else return end 120 | end 121 | end 122 | 123 | f(10); g(10); 124 | 125 | do 126 | function f () return 1,2,3; end 127 | local a, b, c = f(); 128 | assert(a==1 and b==2 and c==3) 129 | a, b, c = (f()); 130 | assert(a==1 and b==nil and c==nil) 131 | end 132 | 133 | local a,b = 3 and f(); 134 | assert(a==1 and b==nil) 135 | 136 | function g() f(); return; end; 137 | assert(g() == nil) 138 | function g() return nil or f() end 139 | a,b = g() 140 | assert(a==1 and b==nil) 141 | 142 | print'+'; 143 | 144 | f = [[ 145 | return function ( a , b , c , d , e ) 146 | local x = a >= b or c or ( d and e ) or nil 147 | return x 148 | end , { a = 1 , b = 2 >= 1 , } or { 1 }; 149 | ]] 150 | f = string.gsub(f, "%s+", "\n"); -- force a SETLINE between opcodes 151 | f,a = loadstring(f)(); 152 | assert(a.a == 1 and a.b) 153 | 154 | function g (a,b,c,d,e) 155 | if not (a>=b or c or d and e or nil) then return 0; else return 1; end; 156 | end 157 | 158 | function h (a,b,c,d,e) 159 | while (a>=b or c or (d and e) or nil) do return 1; end; 160 | return 0; 161 | end; 162 | 163 | assert(f(2,1) == true and g(2,1) == 1 and h(2,1) == 1) 164 | assert(f(1,2,'a') == 'a' and g(1,2,'a') == 1 and h(1,2,'a') == 1) 165 | assert(f(1,2,'a') 166 | ~= -- force SETLINE before nil 167 | nil, "") 168 | assert(f(1,2,'a') == 'a' and g(1,2,'a') == 1 and h(1,2,'a') == 1) 169 | assert(f(1,2,nil,1,'x') == 'x' and g(1,2,nil,1,'x') == 1 and 170 | h(1,2,nil,1,'x') == 1) 171 | assert(f(1,2,nil,nil,'x') == nil and g(1,2,nil,nil,'x') == 0 and 172 | h(1,2,nil,nil,'x') == 0) 173 | assert(f(1,2,nil,1,nil) == nil and g(1,2,nil,1,nil) == 0 and 174 | h(1,2,nil,1,nil) == 0) 175 | 176 | assert(1 and 2<3 == true and 2<3 and 'a'<'b' == true) 177 | x = 2<3 and not 3; assert(x==false) 178 | x = 2<1 or (2>1 and 'a'); assert(x=='a') 179 | 180 | 181 | print("OK") 182 | -------------------------------------------------------------------------------- /luna/w_objects.py: -------------------------------------------------------------------------------- 1 | from rpython.rlib.objectmodel import compute_hash, compute_identity_hash 2 | 3 | 4 | class W_Object(object): 5 | def __init__(self): 6 | self.n_val = 0 7 | self.s_val = '' 8 | self.content = {} 9 | 10 | def eq(self, w_other): 11 | raise NotImplementedError('eq not supported by this class') 12 | 13 | def neq(self, w_other): 14 | raise NotImplementedError('neq not supported by this class') 15 | 16 | def gt(self, other): 17 | raise NotImplementedError('gt not supported by this class') 18 | 19 | def lt(self, other): 20 | raise NotImplementedError('lt not supported by this class') 21 | 22 | def ge(self, other): 23 | raise NotImplementedError('ge not supported by this class') 24 | 25 | def le(self, other): 26 | raise NotImplementedError('le not supported by this class') 27 | 28 | def is_true(self): 29 | return True 30 | 31 | def to_str(self): 32 | raise NotImplementedError('to_str not supported by this class') 33 | 34 | def clone(self): 35 | raise NotImplementedError('clone not supported by this class') 36 | 37 | def get_val(self, key): 38 | raise NotImplementedError('to_str not supported by this class') 39 | 40 | def hash(self): 41 | raise NotImplementedError('hash not supported by this class') 42 | 43 | 44 | class W_Num(W_Object): 45 | def __init__(self, val): 46 | self.n_val = val 47 | 48 | def getval(self): 49 | return self.n_val 50 | 51 | def eq(self, w_other): 52 | assert isinstance(w_other, W_Num) 53 | return self.n_val == w_other.getval() 54 | 55 | def neq(self, w_other): 56 | return not self.eq(w_other) 57 | 58 | def gt(self, w_other): 59 | assert isinstance(w_other, W_Num) 60 | return self.n_val > w_other.n_val 61 | 62 | def lt(self, w_other): 63 | assert isinstance(w_other, W_Num) 64 | return self.n_val < w_other.n_val 65 | 66 | def le(self, w_other): 67 | assert isinstance(w_other, W_Num) 68 | return self.n_val <= w_other.n_val 69 | 70 | def ge(self, w_other): 71 | assert isinstance(w_other, W_Num) 72 | return self.n_val >= w_other.n_val 73 | 74 | def to_str(self): 75 | i_val = int(self.n_val) 76 | if self.n_val - i_val == 0: 77 | return str(i_val) 78 | else: 79 | return str(self.n_val) 80 | 81 | def clone(self): 82 | return W_Num(self.n_val) 83 | 84 | def hash(self): 85 | return self.n_val 86 | 87 | 88 | class W_Str(W_Object): 89 | def __init__(self, val): 90 | self.s_val = val 91 | 92 | def getval(self): 93 | return self.s_val 94 | 95 | def eq(self, w_other): 96 | if isinstance(w_other, W_Str): 97 | return self.s_val == w_other.getval() 98 | else: 99 | assert isinstance(w_other, W_Pri) 100 | if w_other.n_val == 0: 101 | return self.s_val is None 102 | else: 103 | assert 0 104 | 105 | def neq(self, w_other): 106 | return not self.eq(w_other) 107 | 108 | def gt(self, w_other): 109 | assert isinstance(w_other, W_Str) 110 | return self.s_val > w_other.s_val 111 | 112 | def lt(self, w_other): 113 | assert isinstance(w_other, W_Str) 114 | return self.s_val < w_other.s_val 115 | 116 | def le(self, w_other): 117 | assert isinstance(w_other, W_Str) 118 | return self.s_val <= w_other.s_val 119 | 120 | def ge(self, w_other): 121 | assert isinstance(w_other, W_Str) 122 | return self.s_val >= w_other.s_val 123 | 124 | def to_str(self): 125 | return str(self.s_val) 126 | 127 | def clone(self): 128 | return W_Str(self.s_val) 129 | 130 | def hash(self): 131 | return compute_hash(self.s_val) 132 | 133 | 134 | class W_Pri(W_Num): 135 | def __init__(self, val): 136 | assert val in (0, 1, 2) 137 | self.n_val = val 138 | 139 | def to_str(self): 140 | if self.n_val == 0: 141 | return 'nil' 142 | elif self.n_val == 1: 143 | return 'false' 144 | else: 145 | return 'true' 146 | 147 | def is_true(self): 148 | return self.n_val not in (0, 1) 149 | 150 | def clone(self): 151 | return W_Pri(self.n_val) 152 | 153 | 154 | class W_Table(W_Object): 155 | def __init__(self): 156 | self.content = {} 157 | 158 | def size(self): 159 | return len(self.content) 160 | 161 | def get(self, key): 162 | try: 163 | w_v = self.content[key.hash()] 164 | assert isinstance(w_v, W_Object) 165 | return w_v 166 | except KeyError: 167 | return W_Pri(0) 168 | 169 | def set(self, key, val): 170 | self.content[key.hash()] = val 171 | 172 | def clone(self): 173 | # TODO: deep copy expceted here? 174 | cpy = W_Table() 175 | cpy.content = self.content.copy() 176 | return cpy 177 | 178 | def hash(self): 179 | return compute_identity_hash(self.content) 180 | 181 | def values(self): 182 | return self.content.values() 183 | 184 | def items(self): 185 | return self.content.items() 186 | -------------------------------------------------------------------------------- /luna/tests/modules/test_string.py: -------------------------------------------------------------------------------- 1 | from ..helpers import codetest 2 | 3 | 4 | class TestStringModule(object): 5 | def test_find_simple_pattern_match(self, capsys): 6 | codetest(""" 7 | x, y = string.find('aabaajjbabaajaaaaa', 'aaa') 8 | print(x, y) 9 | """) 10 | out, _ = capsys.readouterr() 11 | assert out == "14 16\n" 12 | 13 | def test_find_simple_pattern_no_match(self, capsys): 14 | codetest(""" 15 | x, y = string.find('aabaajjbabaajaaaaa', 'abc') 16 | print(x, y) 17 | """) 18 | out, _ = capsys.readouterr() 19 | assert out == "nil nil\n" 20 | 21 | def test_find_pattern_with_dot_match(self, capsys): 22 | codetest(""" 23 | x, y = string.find('ajajajaccaabaa', '.b.') 24 | print(x, y) 25 | """) 26 | out, _ = capsys.readouterr() 27 | assert out == "11 13\n" 28 | 29 | def test_find_pattern_with_dot_no_match(self, capsys): 30 | codetest(""" 31 | x, y = string.find('ajajajaccajyasda', '.b.') 32 | print(x, y) 33 | """) 34 | out, _ = capsys.readouterr() 35 | assert out == "nil nil\n" 36 | 37 | def test_find_simple_pattern_offset_match(self, capsys): 38 | codetest(""" 39 | x, y = string.find("Hello Lua user", "Lua", 1) 40 | print(x, y) 41 | """) 42 | out, _ = capsys.readouterr() 43 | assert out == "7 9\n" 44 | 45 | def test_find_simple_pattern_offset_no_match(self, capsys): 46 | codetest(""" 47 | x, y = string.find("Hello Lua user", "Lua", 8) 48 | print(x, y) 49 | """) 50 | out, _ = capsys.readouterr() 51 | assert out == "nil nil\n" 52 | 53 | def test_find_simple_pattern_negative_offset_match(self, capsys): 54 | codetest(""" 55 | x, y = string.find("Hello Lua user", "e", -5) 56 | print(x, y) 57 | """) 58 | out, _ = capsys.readouterr() 59 | assert out == "13 13\n" 60 | 61 | def test_find_simple_pattern_big_negative_offset_match(self, capsys): 62 | codetest(""" 63 | x, y = string.find("Hello Lua user", "e", -100) 64 | print(x, y) 65 | """) 66 | out, _ = capsys.readouterr() 67 | assert out == "2 2\n" 68 | 69 | def test_find_simple_pattern_big_offset_match(self, capsys): 70 | codetest(""" 71 | x, y = string.find("Hello Lua user", "e", 100) 72 | print(x, y) 73 | """) 74 | out, _ = capsys.readouterr() 75 | assert out == "nil nil\n" 76 | 77 | def test_find_pattern_with_special_a_match(self, capsys): 78 | codetest(""" 79 | x, y = string.find('123a23aB9Z', '%a%a') 80 | print(x, y) 81 | """) 82 | out, _ = capsys.readouterr() 83 | assert out == "7 8\n" 84 | 85 | def test_find_pattern_with_special_no_match(self, capsys): 86 | codetest(""" 87 | x, y = string.find('123a23a 9Z', '%a%a') 88 | print(x, y) 89 | """) 90 | out, _ = capsys.readouterr() 91 | assert out == "nil nil\n" 92 | 93 | def test_match_simple_pattern_no_match(self, capsys): 94 | codetest(""" 95 | x = string.match('aabaajjbabaajaaaaa', 'abc') 96 | print(x) 97 | """) 98 | out, _ = capsys.readouterr() 99 | assert out == "nil\n" 100 | 101 | def test_match_simple_pattern_one_match(self, capsys): 102 | codetest(""" 103 | x = string.match('aacbaajjbabaajaacabaabc', 'abc') 104 | print(x) 105 | """) 106 | out, _ = capsys.readouterr() 107 | assert out == "abc\n" 108 | 109 | def test_match_pattern_with_dot_no_match(self, capsys): 110 | codetest(""" 111 | x = string.match('aaaajjbabaajaaaaa', '.b.c') 112 | print(x) 113 | """) 114 | out, _ = capsys.readouterr() 115 | assert out == "nil\n" 116 | 117 | def test_match_pattern_with_dot_match(self, capsys): 118 | codetest(""" 119 | x = string.match('aaaajjbabaajaaxbycaa', '.b.c') 120 | print(x) 121 | """) 122 | out, _ = capsys.readouterr() 123 | assert out == "xbyc\n" 124 | 125 | def test_match_pattern_with_special_a_match(self, capsys): 126 | codetest(""" 127 | x = string.match('123a23aB9Z', '%a%a') 128 | print(x) 129 | """) 130 | out, _ = capsys.readouterr() 131 | assert out == "aB\n" 132 | 133 | def test_match_pattern_with_special_no_match(self, capsys): 134 | codetest(""" 135 | x = string.match('123a23a 9Z', '%a%a') 136 | print(x) 137 | """) 138 | out, _ = capsys.readouterr() 139 | assert out == "nil\n" 140 | 141 | def test_gsub_simple_pattern(self, capsys): 142 | codetest(""" 143 | print(string.gsub('aaaxdvbaalaaa', 'aaa', 'A')) 144 | """) 145 | out, _ = capsys.readouterr() 146 | assert out == "AxdvbaalA\n" 147 | 148 | def test_gsub_simple_pattern_no_match(self, capsys): 149 | codetest(""" 150 | print(string.gsub('aaaxdvbaalaaa', 'BBB', 'A')) 151 | """) 152 | out, _ = capsys.readouterr() 153 | assert out == "aaaxdvbaalaaa\n" 154 | -------------------------------------------------------------------------------- /luna/tests/test_table.py: -------------------------------------------------------------------------------- 1 | from .helpers import codetest 2 | 3 | 4 | class TestTable(object): 5 | def test_set_get_str_key_num(self): 6 | ret = codetest(""" 7 | x = {} 8 | x["test"] = 99 9 | return x["test"] 10 | """) 11 | assert ret.getval() == 99 12 | 13 | def test_set_get_str_key_str(self): 14 | ret = codetest(""" 15 | x = {} 16 | x["test"] = "str" 17 | return x["test"] 18 | """) 19 | assert ret.getval() == "str" 20 | 21 | def test_set_get_num_key_num(self): 22 | ret = codetest(""" 23 | x = {} 24 | x[1] = 99 25 | return x[1] 26 | """) 27 | assert ret.getval() == 99 28 | 29 | def test_set_get_num_key_str(self): 30 | ret = codetest(""" 31 | x = {} 32 | x[1] = "str" 33 | return x[1] 34 | """) 35 | assert ret.getval() == "str" 36 | 37 | def test_set_get_var_str_key_num(self): 38 | ret = codetest(""" 39 | x = {} 40 | key = "key" 41 | x[key] = 99 42 | return x[key] 43 | """) 44 | assert ret.getval() == 99 45 | 46 | def test_set_get_var_str_key_str(self): 47 | ret = codetest(""" 48 | x = {} 49 | key = "key" 50 | x[key] = "str" 51 | return x[key] 52 | """) 53 | assert ret.getval() == "str" 54 | 55 | def test_table_as_array_num(self): 56 | ret = codetest(""" 57 | x = { 5, 2, 3} 58 | return x[1] + x[3] 59 | """) 60 | assert ret.getval() == 8 61 | 62 | def test_table_as_array_str(self): 63 | ret = codetest(""" 64 | x = { "a", "b", "c"} 65 | return x[2] 66 | """) 67 | assert ret.getval() == "b" 68 | 69 | def test_table_constructor_str_vals(self): 70 | ret = codetest(""" 71 | x = {["foo"] = "bar", ["tmp"] = "dir"} 72 | return x["tmp"] 73 | """) 74 | assert ret.getval() == "dir" 75 | 76 | def test_table_constructor_num_vals(self): 77 | ret = codetest(""" 78 | x = {["foo"] = 100, ["tmp"] = 55} 79 | return x["tmp"] + x["foo"] 80 | """) 81 | assert ret.getval() == 100+55 82 | 83 | def test_table_constructor_num_key(self): 84 | ret = codetest(""" 85 | x = {["Foo"] = "bar", [111] = 1} 86 | return x[111] 87 | """) 88 | assert ret.getval() == 1 89 | 90 | def test_table_constructor_num_and_float_vals(self): 91 | ret = codetest(""" 92 | x = {["foo"] = 100.0001, ["tmp"] = 555.5} 93 | return x["tmp"] + x["foo"] 94 | """) 95 | assert ret.getval() == 100.0001+555.5 96 | 97 | def test_table_invalid_num_key(self): 98 | ret = codetest(""" 99 | x = {["foo"] = 100.0001, ["tmp"] = 555.5} 100 | return x[999] 101 | """) 102 | assert ret.getval() == 0 103 | 104 | def test_table_invalid_str_key(self): 105 | ret = codetest(""" 106 | x = {["foo"] = 100.0001, ["tmp"] = 555.5} 107 | return x["invlaid"] 108 | """) 109 | assert ret.getval() == 0 110 | 111 | def test_table_dot_key(self): 112 | ret = codetest(""" 113 | x = {["foo"] = 999} 114 | return x.foo 115 | """) 116 | assert ret.getval() == 999 117 | 118 | def test_table_same_int_and_str_as_key(self): 119 | ret = codetest(""" 120 | x = {} 121 | x[111] = 99 122 | x["111"] = 10 123 | return x[111] + x["111"] 124 | """) 125 | assert ret.getval() == 99+10 126 | 127 | def test_table_concat(self): 128 | ret = codetest(""" 129 | t = {"a", "b", "c"} 130 | return table.concat(t, ";") 131 | """) 132 | assert ret.getval() == "a;b;c" 133 | 134 | def test_table_concat_with_i_and_j(self): 135 | ret = codetest(""" 136 | return table.concat({ 1, 2, "three", 4, "five" }, ", ", 2, 4) 137 | """) 138 | assert ret.getval() == "2, three, 4" 139 | 140 | def test_table_concat_with_i(self): 141 | ret = codetest(""" 142 | return table.concat({ 1, 2, "three", 4, "five" }, ", ", 2) 143 | """) 144 | assert ret.getval() == "2, three, 4, five" 145 | 146 | def test_table_concat_no_sep(self): 147 | ret = codetest(""" 148 | return table.concat({ 1, 2, "three", 4, "five" }) 149 | """) 150 | assert ret.getval() == "12three4five" 151 | 152 | def test_table_insert_at_end(self): 153 | ret = codetest(""" 154 | t = {} 155 | table.insert(t, "c") 156 | table.insert(t, "b") 157 | table.insert(t, "a") 158 | return table.concat(t) 159 | """) 160 | assert ret.getval() == "cba" 161 | 162 | def test_table_insert_in_the_middle(self): 163 | ret = codetest(""" 164 | t = {"d"} 165 | table.insert(t, 1, "c") 166 | table.insert(t, 1, "b") 167 | table.insert(t, 1, "a") 168 | return table.concat(t) 169 | """) 170 | assert ret.getval() == "abcd" 171 | 172 | def test_table_remove_in_the_middle(self): 173 | ret = codetest(""" 174 | t = {"a", "b", "c"} 175 | table.remove(t, 2) 176 | return t[2] 177 | """) 178 | assert ret.getval() == "c" 179 | 180 | def test_table_remove_beginning(self): 181 | ret = codetest(""" 182 | t = {"a", "b", "c"} 183 | table.remove(t, 1) 184 | return t[2] 185 | """) 186 | assert ret.getval() == "c" 187 | 188 | def test_table_remove_end(self): 189 | ret = codetest(""" 190 | t = {"a", "b", "c"} 191 | table.remove(t, 3) 192 | return t[2] 193 | """) 194 | assert ret.getval() == "b" 195 | 196 | def test_table_remove_invalid(self): 197 | ret = codetest(""" 198 | t = {"a", "b", "c"} 199 | return table.remove(t, 99) 200 | """) 201 | assert ret.getval() == 0 202 | 203 | def test_mixed_table(self, capsys): 204 | ret = codetest(""" 205 | t = {"a", foo="bar", "b", "c"} 206 | print(t[1]) 207 | print(t[2]) 208 | print(t["foo"]) 209 | print(t[3]) 210 | """) 211 | out, _ = capsys.readouterr() 212 | assert out == "a\nb\nbar\nc\n" 213 | -------------------------------------------------------------------------------- /luna/tests/modules/test_builtin.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from ..helpers import codetest, test_file 4 | 5 | 6 | class TestBuiltin(object): 7 | def test_print_str(self, capsys): 8 | codetest(""" 9 | print("hallo") 10 | """) 11 | out, _ = capsys.readouterr() 12 | assert out == "hallo\n" 13 | 14 | def test_print_more_strings(self, capsys): 15 | codetest(""" 16 | print("hallo", "how", "are", "you") 17 | """) 18 | out, _ = capsys.readouterr() 19 | assert out == "hallo how are you\n" 20 | 21 | def test_print_int(self, capsys): 22 | codetest(""" 23 | print(1) 24 | """) 25 | out, _ = capsys.readouterr() 26 | assert out == "1\n" 27 | 28 | def test_print_int_var(self, capsys): 29 | codetest(""" 30 | x = 2 31 | print(x) 32 | """) 33 | out, _ = capsys.readouterr() 34 | assert out == "2\n" 35 | 36 | def test_print_more_ints(self, capsys): 37 | codetest(""" 38 | print(1, 2, 3) 39 | """) 40 | out, _ = capsys.readouterr() 41 | assert out == "1 2 3\n" 42 | 43 | def test_print_int_strings(self, capsys): 44 | codetest(""" 45 | print(2, "plus", 3, "=", 5) 46 | """) 47 | out, _ = capsys.readouterr() 48 | assert out == "2 plus 3 = 5\n" 49 | 50 | def test_print_int_strings_as_vars(self, capsys): 51 | codetest(""" 52 | x1 = 2 53 | x2 = "plus" 54 | x3 = 3 55 | x4 = "=" 56 | x5 = 5 57 | print(x1, x2, x3, x4, x5) 58 | """) 59 | out, _ = capsys.readouterr() 60 | assert out == "2 plus 3 = 5\n" 61 | 62 | def test_assert_with_stuff(self): 63 | codetest(""" 64 | assert(-2^- -2 == - - -4) 65 | """) 66 | 67 | def test_assert_false_0(self): 68 | with pytest.raises(AssertionError) as ex: 69 | codetest(""" 70 | assert(0) 71 | """) 72 | assert ex.exconly() == "AssertionError: assertion failed" 73 | 74 | def test_assert_false_nil(self): 75 | with pytest.raises(AssertionError) as ex: 76 | codetest(""" 77 | assert(nil) 78 | """) 79 | assert ex.exconly() == "AssertionError: assertion failed" 80 | 81 | def test_assert_false_gt(self): 82 | with pytest.raises(AssertionError) as ex: 83 | codetest(""" 84 | assert(1 > 2) 85 | """) 86 | assert ex.exconly() == "AssertionError: assertion failed" 87 | 88 | def test_assert_false_with_vars_lt(self): 89 | with pytest.raises(AssertionError) as ex: 90 | codetest(""" 91 | x = 100 92 | y = 10 93 | assert(y > x) 94 | """) 95 | assert ex.exconly() == "AssertionError: assertion failed" 96 | 97 | def test_assert_false_with_msg(self): 98 | with pytest.raises(AssertionError) as ex: 99 | codetest(""" 100 | x = 100 101 | y = 10 102 | assert(y == x, "error") 103 | """) 104 | assert ex.exconly() == "AssertionError: error" 105 | 106 | def test_assert_false_with_and(self): 107 | with pytest.raises(AssertionError): 108 | codetest(""" 109 | assert(true and false) 110 | """) 111 | 112 | def test_assert_false_with_or(self): 113 | with pytest.raises(AssertionError): 114 | codetest(""" 115 | assert(false or false) 116 | """) 117 | 118 | def test_assert_true_1(self): 119 | codetest(""" 120 | assert(1) 121 | """) 122 | 123 | def test_assert_true_with_vars_lt(self): 124 | codetest(""" 125 | x = 100 126 | y = 10 127 | assert(y < x) 128 | """) 129 | 130 | def test_assert_true_with_and(self): 131 | codetest(""" 132 | assert(true and true) 133 | """) 134 | 135 | def test_assert_true_with_or(self): 136 | codetest(""" 137 | assert(false or true) 138 | """) 139 | 140 | def test_loadfile_simple(self): 141 | f = test_file(src='return "test"') 142 | ret = codetest(""" 143 | x = loadfile("%s") 144 | return x() 145 | """ % f.name) 146 | assert ret.s_val == "test" 147 | 148 | def test_loadfile_function_with_params(self): 149 | f = test_file(src=''' 150 | function foo(x, y) 151 | return x + y 152 | end 153 | ''') 154 | ret = codetest(""" 155 | x = loadfile("%s") 156 | x() 157 | return foo(10, 20) 158 | """ % f.name) 159 | assert ret.n_val == 30 160 | 161 | def test_loadstring_simple(self): 162 | ret = codetest(""" 163 | s = [[return "test"]] 164 | x = loadstring(s) 165 | return x() 166 | """) 167 | assert ret.s_val == "test" 168 | 169 | def test_loadstring_function_with_params(self): 170 | ret = codetest(""" 171 | s = [[ 172 | function foo(x, y) 173 | return x + y 174 | end 175 | ]] 176 | x = loadstring(s) 177 | x() 178 | return foo(10, 20) 179 | """) 180 | assert ret.n_val == 30 181 | 182 | def test_tonumber_int(self): 183 | ret = codetest(""" 184 | x = tonumber("100") 185 | return x + 19 186 | """) 187 | assert ret.n_val == 119 188 | 189 | def test_tonumber_float(self): 190 | ret = codetest(""" 191 | x = tonumber("1.55") 192 | return x + 19 193 | """) 194 | assert ret.n_val == 20.55 195 | 196 | def test_tonumber_invalid(self): 197 | ret = codetest(""" 198 | x = tonumber("str") 199 | if x == nil then 200 | return 10 201 | else 202 | return 99 203 | end 204 | """) 205 | assert ret.n_val == 10 206 | 207 | def test_type_int(self): 208 | ret = codetest(""" 209 | x = 10 210 | return type(x) 211 | """) 212 | assert ret.s_val == "number" 213 | 214 | def test_type_float(self): 215 | ret = codetest(""" 216 | x = 10.10 217 | return type(x) 218 | """) 219 | assert ret.s_val == "number" 220 | 221 | def test_type_str(self): 222 | ret = codetest(""" 223 | x = "test" 224 | return type(x) 225 | """) 226 | assert ret.s_val == "string" 227 | 228 | def test_type_table(self): 229 | ret = codetest(""" 230 | x = {1,2} 231 | return type(x) 232 | """) 233 | assert ret.s_val == "table" 234 | 235 | def test_type_boolean(self): 236 | ret = codetest(""" 237 | x = false 238 | return type(x) 239 | """) 240 | assert ret.s_val == "boolean" 241 | 242 | def test_type_mixed(self): 243 | ret = codetest(""" 244 | x = false 245 | x = "string" 246 | x = 10 247 | x = "hallo" 248 | return type(x) 249 | """) 250 | assert ret.s_val == "string" 251 | -------------------------------------------------------------------------------- /luna/modules/patterns.py: -------------------------------------------------------------------------------- 1 | """ 2 | Lua pattern matcher based on a NFA 3 | 4 | inspired by 5 | http://swtch.com/~rsc/regexp/regexp1.html 6 | """ 7 | 8 | 9 | class State(object): 10 | pass 11 | 12 | 13 | class StateMatch(State): 14 | def __init__(self): 15 | pass 16 | 17 | def clone(self, seen): 18 | return StateMatch() 19 | 20 | 21 | class StateCharRange(State): 22 | def __init__(self, c1, c2, out): 23 | self.start = ord(c1) 24 | self.stop = ord(c2) 25 | self.out = out 26 | 27 | def match(self, c): 28 | return ord(c) >= self.start and ord(c) <= self.stop 29 | 30 | 31 | class StateChar(StateCharRange): 32 | def __init__(self, c, out): 33 | StateCharRange.__init__(self, c, c, out) 34 | 35 | 36 | class StateDot(StateCharRange): 37 | def __init__(self, out): 38 | StateCharRange.__init__(self, ' ', ' ', out) 39 | 40 | def match(self, c): 41 | return True 42 | 43 | 44 | class StateSplit(State): 45 | def __init__(self, out, out2): 46 | self.out = out 47 | self.out2 = out2 48 | 49 | 50 | def find2(expr, string, start): 51 | assert isinstance(start, int) 52 | if start < 0: 53 | start = len(string) + start 54 | # if negative offset is bigger than length of string 55 | # start at the beginning 56 | if start < 0: 57 | start = 0 58 | start = int(start) 59 | 60 | found = False 61 | i = start 62 | while i < len(string): 63 | match = False 64 | valid = True 65 | j = i 66 | state = expr 67 | backtrack = [] 68 | while valid and not match and (j < len(string) or len(backtrack) > 0): 69 | if j >= len(string) and len(backtrack) > 0: 70 | state, j = backtrack.pop() 71 | if isinstance(state, StateCharRange): 72 | if not state.match(string[j]): 73 | if len(backtrack) == 0: 74 | valid = False 75 | else: 76 | state, j = backtrack.pop() 77 | else: 78 | state = state.out 79 | j += 1 80 | elif isinstance(state, StateMatch): 81 | match = True 82 | elif isinstance(state, StateSplit): 83 | backtrack.append((state.out2, j)) 84 | state = state.out 85 | else: 86 | valid = False 87 | if j == len(string): 88 | if (isinstance(state, StateMatch) or 89 | (isinstance(state, StateSplit) and 90 | isinstance(state.out2, StateMatch))): 91 | match = True 92 | if match: 93 | found = True 94 | yield (i+1, j) 95 | if j > i: 96 | i = j 97 | else: 98 | i += 1 99 | else: 100 | i += 1 101 | if not found: 102 | yield (-1, -1) 103 | 104 | 105 | SPECIAL_CHARS = { 106 | 'a': ('A', 'z'), 107 | 's': (' ', ' ') 108 | } 109 | 110 | 111 | def set_next(state, next_state, seen, propagate=False): 112 | seen.append(state) 113 | if next_state in seen: 114 | return 115 | if isinstance(state, StateSplit): 116 | if state.out is None: 117 | state.out = next_state 118 | elif state.out2 is None: 119 | state.out2 = next_state 120 | elif propagate: 121 | if state.out not in seen: 122 | set_next(state.out, next_state, seen, propagate=propagate) 123 | if state.out2 not in seen: 124 | set_next(state.out2, next_state, seen, propagate=propagate) 125 | else: 126 | if state.out is None: 127 | state.out = next_state 128 | elif propagate: 129 | if state.out not in seen: 130 | set_next(state.out, next_state, seen, propagate=propagate) 131 | 132 | 133 | T_CHAR = 0 134 | T_DOT = 1 135 | T_CHAR_RANGE = 2 136 | T_STAR = 3 137 | T_OR = 4 138 | T_GROUP = 5 139 | T_PLUS = 6 140 | 141 | 142 | class Token(object): 143 | def __init__(self, t_type, value, sub_tokens=[], tokens_right=[], 144 | prop=False): 145 | self.type = t_type 146 | self.value = value 147 | self.sub_tokens = sub_tokens 148 | self.tokens_right = tokens_right 149 | self.prop = prop 150 | 151 | def clone(self): 152 | cloned_sub = [t.clone() for t in self.sub_tokens] 153 | cloned_right = [t.clone() for t in self.tokens_right] 154 | return Token( 155 | self.type, self.value, sub_tokens=cloned_sub, 156 | tokens_right=cloned_right, prop=self.prop 157 | ) 158 | 159 | 160 | def tokenize(pattern): 161 | tokens = [] 162 | 163 | i = 0 164 | prop = False 165 | while i < len(pattern): 166 | c = pattern[i] 167 | if ord(c) >= ord('0') and ord(c) <= ord('z'): 168 | tokens.append(Token(T_CHAR, [c])) 169 | elif c == '.': 170 | tokens.append(Token(T_DOT, [c])) 171 | elif c == '%': 172 | if i+1 < len(pattern): 173 | if pattern[i+1] == '%': 174 | tokens.append(Token(T_CHAR, ['%'])) 175 | elif pattern[i+1] in SPECIAL_CHARS: 176 | tokens.append( 177 | Token(T_CHAR_RANGE, list(SPECIAL_CHARS[pattern[i+1]])) 178 | ) 179 | else: 180 | raise RuntimeError('Invalid pattern') 181 | i += 1 182 | else: 183 | raise RuntimeError('Invalid pattern') 184 | elif c == '*': 185 | if len(tokens) > 0: 186 | prev = tokens.pop() 187 | tokens.append(Token(T_STAR, [], sub_tokens=[prev])) 188 | else: 189 | raise RuntimeError('Invalid pattern') 190 | elif c == '+': 191 | if len(tokens) > 0: 192 | prev = tokens.pop() 193 | tokens.append(Token(T_STAR, [], sub_tokens=[prev])) 194 | tokens.append(prev.clone()) 195 | else: 196 | raise RuntimeError('Invalid pattern') 197 | 198 | elif c == '|': 199 | tokens_right = tokenize(pattern[i+1:]) 200 | return [ 201 | Token(T_OR, [], sub_tokens=tokens, tokens_right=tokens_right) 202 | ] 203 | elif c == '(': 204 | i_g = i + 1 205 | open_count = 1 206 | close_count = 0 207 | while open_count > close_count and i_g < len(pattern): 208 | if pattern[i_g] == '(': 209 | open_count += 1 210 | elif pattern[i_g] == ')': 211 | close_count += 1 212 | i_g += 1 213 | end = i_g - 1 214 | assert end >= 0 215 | group_str = pattern[(i+1):end] 216 | tokens.append(Token(T_GROUP, [], sub_tokens=tokenize(group_str))) 217 | i += len(group_str) + 2 218 | # Force propagation of next state after group 219 | prop = True 220 | continue 221 | elif c == '{': 222 | count_str = pattern[i+1:].split('}', 1)[0] 223 | count = int(count_str) 224 | prev = tokens.pop() 225 | for j in range(0, count): 226 | tokens.append(prev.clone()) 227 | i += len(count_str) + 1 228 | else: 229 | raise RuntimeError('Invalid pattern') 230 | i += 1 231 | tokens[-1].prop = prop 232 | return tokens 233 | 234 | 235 | def tokens_to_expression(tokens, top=True): 236 | expr = StateChar('c', None) 237 | start = expr 238 | for t in tokens: 239 | new_expr = None 240 | if t.type == T_CHAR: 241 | new_expr = StateChar(t.value[0], None) 242 | elif t.type == T_DOT: 243 | new_expr = StateDot(None) 244 | elif t.type == T_CHAR_RANGE: 245 | new_expr = StateCharRange(t.value[0], t.value[1], None) 246 | elif t.type == T_STAR: 247 | match_expr = tokens_to_expression(t.sub_tokens, top=False) 248 | new_expr = StateSplit(match_expr, None) 249 | set_next(match_expr, new_expr, [], propagate=True) 250 | elif t.type == T_OR: 251 | expr_left = tokens_to_expression(t.sub_tokens, top=False) 252 | expr_right = tokens_to_expression(t.tokens_right, top=False) 253 | new_expr = StateSplit(expr_left, expr_right) 254 | elif t.type == T_GROUP: 255 | new_expr = tokens_to_expression(t.sub_tokens, top=False) 256 | set_next(expr, new_expr, [], propagate=True) 257 | else: 258 | raise RuntimeError('Invalid pattern') 259 | set_next(expr, new_expr, [], propagate=t.prop) 260 | expr = new_expr 261 | if top: 262 | set_next(expr, StateMatch(), [], propagate=True) 263 | return start.out 264 | 265 | 266 | def compile_re(pattern, plain=False): 267 | tokens = tokenize(pattern) 268 | return tokens_to_expression(tokens) 269 | -------------------------------------------------------------------------------- /luna/bytecode.py: -------------------------------------------------------------------------------- 1 | """ 2 | useful links: 3 | - http://wiki.luajit.org/Bytecode/c728f657001eaeee7f64eb99f47dc413c8e29a56 4 | - http://wiki.luajit.org/Bytecode-2.0 5 | - https://github.com/creationix/brozula/blob/master/parser.js 6 | - http://lua-users.org/lists/lua-l/2012-12/msg00229.html 7 | - http://luajit.org/running.html#opt_b 8 | 9 | """ 10 | import os 11 | 12 | from rpython.annotator.model import SomeByteArray 13 | from rpython.rlib.rstruct.runpack import runpack 14 | from rpython.rlib.rstruct.ieee import float_unpack 15 | from rpython.rlib.unroll import unrolling_iterable 16 | 17 | from luna.opcodes import OP_DESC, ARGS_AD, ARGS_ABC 18 | from luna.luaframe import LuaBytecodeFrame 19 | from luna.helpers import debug_print 20 | from luna.w_objects import W_Str, W_Num, W_Table, W_Pri 21 | 22 | 23 | KGC_TYPES = ["CHILD", "TAB", "I64", "U64", "COMPLEX", "STR", "const_str"] 24 | UNROLLED_KGC_TYPES = unrolling_iterable(KGC_TYPES) 25 | 26 | """ 27 | def decode_arg(self, type, val): 28 | if type == 'lit': # literal 29 | return Arg(val >> 0)#? 30 | elif type == 'lits': # signed literal 31 | return Arg(0x10000 - val if (val & 0x8000) > 0 else val) 32 | elif type == 'pri': 33 | if val == 0: return Arg(None) 34 | elif val == 1: return Arg(False) 35 | elif val == 2: return Arg(True) 36 | else: assert 0 37 | elif type == 'num': return Arg(val) #return self.constants[val] 38 | elif type in ('str', 'tab', 'func', 'cdata'): 39 | return Arg(self.constants[self.num_consts-val-1]) 40 | elif type == 'jump': 41 | return Arg(val - 0x8000) 42 | else: 43 | return Arg(val) 44 | """ 45 | 46 | 47 | class Parser(object): 48 | def __init__(self, filename): 49 | if isinstance(filename, SomeByteArray): 50 | self.bytes = filename 51 | else: 52 | f = os.open(filename, os.O_RDONLY, 0777) 53 | self.bytes = bytes(os.read(f, 99999)) 54 | self.pos = 0 55 | 56 | def next_bytes(self, l): 57 | if self.pos >= 0: 58 | v = self.bytes[self.pos:self.pos+l] 59 | self.pos += l 60 | return v 61 | 62 | def byte(self): 63 | return runpack('=B', self.next_bytes(1)) 64 | 65 | def peek(self): 66 | return runpack('=B', self.bytes[self.pos]) 67 | 68 | def h(self): 69 | return runpack('=H', self.next_bytes(2)) 70 | 71 | def word(self): 72 | return runpack('=I', self.next_bytes(4)) 73 | 74 | def uleb(self): 75 | #https://en.wikipedia.org/wiki/LEB128#Decode_unsigned_integer 76 | result = 0 77 | shift = 0 78 | while True: 79 | b = self.byte() 80 | result |= (b & 0x7f) << shift 81 | if b & 0x80 == 0: 82 | break 83 | shift += 7 84 | return result 85 | 86 | def parse_tab_entry(self): 87 | t_type = self.uleb() 88 | if t_type == 0: 89 | # NIL 90 | return W_Pri(0) 91 | elif t_type == 1: 92 | # FALSE 93 | return W_Pri(1) 94 | elif t_type == 2: 95 | # TRUE 96 | return W_Pri(2) 97 | elif t_type == 3: 98 | # INT 99 | return W_Num(self.uleb()) 100 | elif t_type == 4: 101 | # for some reason luajit does save 102 | # table values as floats and makes no 103 | # distinction between float and int numbers 104 | # so we cant user Parser.read_knum 105 | lo = self.uleb() 106 | hi = self.uleb() 107 | hi = hi << 32 108 | res = float_unpack(lo | hi, 8) 109 | i_res = int(res) 110 | # TODO improve 111 | # numbers are stored as floats, so res is a float, 112 | # but if its whole number the string of the number has 113 | # a .0 at the end, but when accessing the key does not 114 | # have .0 115 | if (res - i_res) == 0: 116 | return W_Num(i_res) 117 | else: 118 | return W_Num(res) 119 | else: 120 | return self.const_str(t_type) 121 | 122 | def parse(self): 123 | # parses a luajit bytecode file 124 | # see http://wiki.luajit.org/Bytecode-2.0 for format information 125 | 126 | # header = ESC 'L' 'J' versionB flagsU [namelenU nameB*] 127 | if self.byte() != 0x1b: 128 | raise ValueError("Expected ESC in first byte") 129 | if self.byte() != 0x4c: 130 | raise ValueError("Expected L in second byte") 131 | if self.byte() != 0x4a: 132 | raise ValueError("Expected J in third byte") 133 | if self.byte() != 1: 134 | raise ValueError("Only version 1 supported") 135 | 136 | # flags 137 | flags = self.uleb() 138 | 139 | # proto+ 140 | self.frames = [] 141 | while True: 142 | l = self.uleb() 143 | self.frames.append(self.parse_frame()) 144 | # peek at next byte only, do not consume 145 | if self.peek() == 0: 146 | break 147 | # 0U and EOF 148 | if self.uleb() != 0: 149 | raise ValueError("Missing 0U at end of file") 150 | if self.pos < len(self.bytes): 151 | raise ValueError(" bytes leftover") 152 | return (flags, self.frames[-1]) 153 | 154 | def parse_frame(self): 155 | flags = self.byte() 156 | num_params = self.byte() 157 | frame_size = self.byte() 158 | num_uv = self.byte() 159 | num_kgc = self.uleb() 160 | num_kn = self.uleb() 161 | num_bc = self.uleb() 162 | 163 | instructions = [] 164 | debug_print("found "+str(num_bc)+" bc instructions") 165 | for i in xrange(0, num_bc): 166 | instructions.append(self.decode_opcode(self.word())) 167 | 168 | debug_print("num uv "+str(num_uv)) 169 | uv_data = [] 170 | for i in xrange(0, num_uv): 171 | uv = self.h() 172 | uv_data.append((uv & 0x8000, uv & 0x4000, uv & 0x3fff)) 173 | 174 | constants = [None] * (num_kgc+num_kn) 175 | 176 | childc = len(self.frames) 177 | #TODO imlement constant parsing 178 | for i in xrange(0, num_kgc): 179 | u = self.uleb() 180 | # CHILD 181 | if u == 0: 182 | childc -= 1 183 | constants[num_kn+i] = self.frames[childc] 184 | elif u == 1: 185 | len_array = self.uleb() 186 | len_hash = self.uleb() 187 | w_table = W_Table() 188 | self.parse_array(w_table, len_array) 189 | self.parse_hash(w_table, len_hash) 190 | constants[num_kn+i] = w_table 191 | else: # string and all other things 192 | constants[num_kn+i] = self.const_str(u) 193 | """ 194 | for t in UNROLLED_KGC_TYPES: 195 | if t == kgc_type: 196 | meth = getattr(self, t) 197 | """ 198 | 199 | for i in xrange(0, num_kn): 200 | debug_print("read knum") 201 | constants[i] = self.read_knum() 202 | 203 | debug_print(str(constants)) 204 | for (ind, args) in instructions: 205 | debug_print(str(OP_DESC[ind].name)+" "+str(args)) 206 | 207 | return LuaBytecodeFrame(flags, constants, uv_data, instructions) 208 | 209 | def parse_array(self, w_table, len_array): 210 | for j in xrange(0, len_array): 211 | w_table.set(W_Num(j), self.parse_tab_entry()) 212 | 213 | def parse_hash(self, w_table, len_hash): 214 | for j in xrange(0, len_hash): 215 | w_key = self.parse_tab_entry() 216 | w_val = self.parse_tab_entry() 217 | w_table.set(w_key, w_val) 218 | 219 | 220 | def const_str(self, l): 221 | l -= 5 # Offset for STR enum 222 | assert l >= 0 223 | v = self.bytes[self.pos:self.pos+l] 224 | self.pos += l 225 | return W_Str(v) 226 | 227 | def read_knum(self): 228 | isnum = self.peek() & 1 229 | lo = self.uleb() >> 1 230 | if isnum == 1: 231 | """ 232 | IEEE 64 bit floating point constant 233 | """ 234 | hi = self.uleb() 235 | hi = hi << 32 236 | res = float_unpack(lo | hi, 8) 237 | # TODO n_val can be a float or a int, can this lead 238 | # to problems when translating? 239 | return W_Num(res) 240 | 241 | # check if int contant is negative 242 | # TODO: not sure if check is correct 243 | if lo & (1 << 31) != 0: 244 | lo = lo - (1 << 32) 245 | return W_Num(lo) 246 | 247 | def decode_opcode(self, word): 248 | ind = word & 0xff 249 | op_desc = OP_DESC[ind] 250 | args_type = op_desc.args_type 251 | args = (0, 0, 0) 252 | a = (word >> 8) & 0xff 253 | if args_type == ARGS_AD: 254 | args = (a, word >> 16, 0) 255 | elif args_type == ARGS_ABC: 256 | args = (a, word >> 24, (word >> 16) & 0xff) 257 | else: 258 | raise ValueError('Invalid argument type') 259 | return (ind, args) 260 | -------------------------------------------------------------------------------- /luna/tests/test_if.py: -------------------------------------------------------------------------------- 1 | from .helpers import codetest 2 | 3 | 4 | class TestIf(object): 5 | """ 6 | tests for the lua if then else and various comparisons 7 | """ 8 | 9 | def test_isnen_false_with_eq(self): 10 | ret = codetest(""" 11 | x = 99 12 | if x == 99 then 13 | return 2 14 | end 15 | return 9 16 | """) 17 | assert ret.getval() == 2 18 | 19 | def test_isnen_true_with_eq(self): 20 | ret = codetest(""" 21 | x = 99; 22 | if x == 88 then 23 | return 2; 24 | end 25 | return 9; 26 | """) 27 | assert ret.getval() == 9 28 | 29 | def test_iseqn_false_with_neq(self): 30 | ret = codetest(""" 31 | x = 99 32 | if x ~= 99 then 33 | return 2 34 | end 35 | return 9 36 | """) 37 | assert ret.getval() == 9 38 | 39 | def test_iseqn_true_with_neq(self): 40 | ret = codetest(""" 41 | x = 99 42 | if x ~= 88 then 43 | return 2 44 | end 45 | return 9 46 | """) 47 | assert ret.getval() == 2 48 | 49 | def test_iseqp_true_with_neq(self): 50 | ret = codetest(""" 51 | x = true 52 | if x ~= false then 53 | return 2 54 | end 55 | return 9 56 | """) 57 | assert ret.getval() == 2 58 | 59 | def test_iseqp_false_with_neq(self): 60 | ret = codetest(""" 61 | x = false 62 | if x ~= false then 63 | return 2 64 | end 65 | return 9 66 | """) 67 | assert ret.getval() == 9 68 | 69 | def test_isneqp_true_with_eq(self): 70 | ret = codetest(""" 71 | x = true 72 | if x == true then 73 | return 2 74 | end 75 | return 9 76 | """) 77 | assert ret.getval() == 2 78 | 79 | def test_isneqp_false_with_eq(self): 80 | ret = codetest(""" 81 | x = false 82 | if x == true then 83 | return 2 84 | end 85 | return 9 86 | """) 87 | assert ret.getval() == 9 88 | 89 | def test_iseqs_true_with_neq(self): 90 | ret = codetest(""" 91 | x = "foo" 92 | if x ~= "bar" then 93 | return 2 94 | end 95 | return 9 96 | """) 97 | assert ret.getval() == 2 98 | 99 | def test_iseqs_false_with_neq(self): 100 | ret = codetest(""" 101 | x = "foo" 102 | if x ~= "foo" then 103 | return 2 104 | end 105 | return 9 106 | """) 107 | assert ret.getval() == 9 108 | 109 | def test_isneqs_true_with_eq(self): 110 | ret = codetest(""" 111 | x = "foo" 112 | if x == "foo" then 113 | return 2 114 | end 115 | return 9 116 | """) 117 | assert ret.getval() == 2 118 | 119 | def test_isneqs_false_with_eq(self): 120 | ret = codetest(""" 121 | x = "foo" 122 | if x == "bar" then 123 | return 2 124 | end 125 | return 9 126 | """) 127 | assert ret.getval() == 9 128 | 129 | def test_iseqv_true_with_neq(self): 130 | ret = codetest(""" 131 | x = "foo" 132 | y = "bar" 133 | if x ~= y then 134 | return 2 135 | end 136 | return 9 137 | """) 138 | assert ret.getval() == 2 139 | 140 | def test_iseqv_false_with_neq(self): 141 | ret = codetest(""" 142 | x = 101 143 | y = 101 144 | if x ~= y then 145 | return 2 146 | end 147 | return 9 148 | """) 149 | assert ret.getval() == 9 150 | 151 | def test_isneqv_true_with_eq(self): 152 | ret = codetest(""" 153 | x = "foo" 154 | y = "foo" 155 | if x == y then 156 | return 2 157 | end 158 | return 9 159 | """) 160 | assert ret.getval() == 2 161 | 162 | def test_isneqv_false_with_eq(self): 163 | ret = codetest(""" 164 | x = true 165 | y = false 166 | if x == y then 167 | return 2 168 | end 169 | return 9 170 | """) 171 | assert ret.getval() == 9 172 | 173 | def test_isgt_true_with_str(self): 174 | ret = codetest(""" 175 | x = "foo" 176 | y = "bar" 177 | if x >= y then 178 | return 2 179 | end 180 | return 9 181 | """) 182 | assert ret.getval() == 2 183 | 184 | def test_isgt_false_with_str(self): 185 | ret = codetest(""" 186 | x = "bar" 187 | y = "foo" 188 | if x >= y then 189 | return 2 190 | end 191 | return 9 192 | """) 193 | assert ret.getval() == 9 194 | 195 | def test_isgt_true_with_num(self): 196 | ret = codetest(""" 197 | x = 100 198 | y = 50 199 | if x >= y then 200 | return 2 201 | end 202 | return 9 203 | """) 204 | assert ret.getval() == 2 205 | 206 | def test_isgt_false_with_num(self): 207 | ret = codetest(""" 208 | x = 50 209 | y = 100 210 | if x >= y then 211 | return 2 212 | end 213 | return 9 214 | """) 215 | assert ret.getval() == 9 216 | 217 | def test_isle_true_with_str(self): 218 | ret = codetest(""" 219 | x = "foo" 220 | y = "bar" 221 | if not (x <= y) then 222 | return 2 223 | end 224 | return 9 225 | """) 226 | assert ret.getval() == 2 227 | 228 | def test_isle_false_with_str(self): 229 | ret = codetest(""" 230 | x = "bar" 231 | y = "foo" 232 | if not (x <= y) then 233 | return 2 234 | end 235 | return 9 236 | """) 237 | assert ret.getval() == 9 238 | 239 | def test_isle_true_with_num(self): 240 | ret = codetest(""" 241 | x = 100 242 | y = 50 243 | if not (x <= y) then 244 | return 2 245 | end 246 | return 9 247 | """) 248 | assert ret.getval() == 2 249 | 250 | def test_isle_false_with_num(self): 251 | ret = codetest(""" 252 | x = 50 253 | y = 100 254 | if not (x <= y) then 255 | return 2 256 | end 257 | return 9 258 | """) 259 | assert ret.getval() == 9 260 | 261 | def test_isge_true_with_str(self): 262 | ret = codetest(""" 263 | x = "bar" 264 | y = "foo" 265 | if x < y then 266 | return 2 267 | end 268 | return 9 269 | """) 270 | assert ret.getval() == 2 271 | 272 | def test_isge_false_with_str(self): 273 | ret = codetest(""" 274 | x = "foo" 275 | y = "bar" 276 | if x < y then 277 | return 2 278 | end 279 | return 9 280 | """) 281 | assert ret.getval() == 9 282 | 283 | def test_isge_true_with_num(self): 284 | ret = codetest(""" 285 | x = 50 286 | y = 100 287 | if x < y then 288 | return 2 289 | end 290 | return 9 291 | """) 292 | assert ret.getval() == 2 293 | 294 | def test_isge_false_with_num(self): 295 | ret = codetest(""" 296 | x = 100 297 | y = 50 298 | if x < y then 299 | return 2 300 | end 301 | return 9 302 | """) 303 | assert ret.getval() == 9 304 | 305 | def test_islt_true_with_str(self): 306 | ret = codetest(""" 307 | y = "foo" 308 | x = "bar" 309 | if not (x > y) then 310 | return 2 311 | end 312 | return 9 313 | """) 314 | assert ret.getval() == 2 315 | 316 | def test_islt_false_with_str(self): 317 | ret = codetest(""" 318 | x = "foo" 319 | y = "bar" 320 | if not (x > y) then 321 | return 2 322 | end 323 | return 9 324 | """) 325 | assert ret.getval() == 9 326 | 327 | def test_islt_true_with_num(self): 328 | ret = codetest(""" 329 | x = 50 330 | y = 100 331 | if not (x > y) then 332 | return 2 333 | end 334 | return 9 335 | """) 336 | assert ret.getval() == 2 337 | 338 | def test_islt_false_with_num(self): 339 | ret = codetest(""" 340 | x = 100 341 | y = 50 342 | if not (x > y) then 343 | return 2 344 | end 345 | return 9 346 | """) 347 | assert ret.getval() == 9 348 | 349 | def test_if_else1(self): 350 | ret = codetest(""" 351 | x = 100 352 | y = 50 353 | if x == y then 354 | return 9 355 | else 356 | return 2 357 | end 358 | return -1 359 | """) 360 | assert ret.getval() == 2 361 | 362 | def test_if_else1(self): 363 | ret = codetest(""" 364 | x = 100 365 | y = 50 366 | if x ~= y then 367 | return 9 368 | else 369 | return 2 370 | end 371 | return -1 372 | """) 373 | assert ret.getval() == 9 374 | -------------------------------------------------------------------------------- /luna/tests/modules/test_pattern.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from luna.modules.patterns import ( 4 | StateMatch, StateChar, find2, StateSplit, StateDot, StateCharRange, 5 | compile_re 6 | ) 7 | 8 | 9 | class TestPattern2(object): 10 | def test_single_char_no_match(self): 11 | expr = StateChar('c', StateMatch()) 12 | result = find2(expr, 'xyz', 0) 13 | assert list(result) == [(-1, -1)] 14 | 15 | def test_single_char_one_match(self): 16 | expr = StateChar('c', StateMatch()) 17 | result = find2(expr, 'asdasdxcz', 0) 18 | assert list(result) == [(8, 8)] 19 | 20 | def test_single_char_more_matches(self): 21 | expr = StateChar('c', StateMatch()) 22 | result = find2(expr, 'xyzaaaccaa', 0) 23 | assert list(result) == [(7, 7), (8, 8)] 24 | 25 | def test_two_chars_no_matches(self): 26 | expr = StateChar('a', StateChar('b', StateMatch())) 27 | result = find2(expr, 'acbaaubbbbb', 0) 28 | assert list(result) == [(-1, -1)] 29 | 30 | def test_two_chars_one_match(self): 31 | expr = StateChar('a', StateChar('b', StateMatch())) 32 | result = find2(expr, 'ccvvvbbajbajbabb', 0) 33 | assert list(result) == [(14, 15)] 34 | 35 | def tests_find_two_chars_matches(self): 36 | expr = StateChar('a', StateChar('b', StateMatch())) 37 | result = find2(expr, 'baaaabbacaabbcc', 0) 38 | assert list(result) == [(5, 6), (11, 12)] 39 | 40 | def test_three_chars_no_matches(self): 41 | expr = StateChar('a', StateChar('b', StateChar('c', StateMatch()))) 42 | result = find2(expr, 'ccababababababacccbaccabbbc', 0) 43 | assert list(result) == [(-1, -1)] 44 | 45 | def test_three_chars_one_match(self): 46 | expr = StateChar('a', StateChar('b', StateChar('c', StateMatch()))) 47 | result = find2(expr, 'ccabababccbababacccbaccabbbc', 0) 48 | assert list(result) == [(7, 9)] 49 | 50 | def test_three_chars_two_matches(self): 51 | expr = StateChar('a', StateChar('b', StateChar('c', StateMatch()))) 52 | result = find2(expr, 'babcccabababccbababacccbaccabbbc', 0) 53 | assert list(result) == [(2, 4), (11, 13)] 54 | 55 | def test_three_chars_one_matches_offset(self): 56 | expr = StateChar('a', StateChar('b', StateChar('c', StateMatch()))) 57 | result = find2(expr, 'abcjjjabc', 4) 58 | assert list(result) == [(7, 9)] 59 | 60 | def test_three_chars_negative_offset_no_match(self): 61 | expr = StateChar('a', StateChar('b', StateChar('c', StateMatch()))) 62 | result = find2(expr, 'abcjjjabc', -2) 63 | assert list(result) == [(-1, -1)] 64 | 65 | def test_three_chars_negative_offset_match(self): 66 | expr = StateChar('a', StateChar('b', StateChar('c', StateMatch()))) 67 | result = find2(expr, 'abcjjjabc', -3) 68 | assert list(result) == [(7, 9)] 69 | 70 | def test_three_chars_big_negative_offset_match(self): 71 | expr = StateChar('a', StateChar('b', StateChar('c', StateMatch()))) 72 | result = find2(expr, 'abcjjjabc', -100) 73 | assert list(result) == [(1, 3), (7, 9)] 74 | 75 | def test_three_chars_big_offset(self): 76 | expr = StateChar('a', StateChar('b', StateChar('c', StateMatch()))) 77 | result = find2(expr, 'abcjjjabc', 100) 78 | assert list(result) == [(-1, -1)] 79 | 80 | def test_star_1(self): 81 | expr = StateSplit(None, StateMatch()) 82 | expr.out = StateChar('c', expr) 83 | result = find2(expr, 'aaaabacccca', 0) 84 | assert list(result) == [ 85 | (1, 0), (2, 1), (3, 2), (4, 3), (5, 4), (6, 5), (7, 10), 86 | (11, 10) 87 | ] 88 | 89 | def test_star_2(self): 90 | expr = StateSplit(None, StateMatch()) 91 | expr.out = StateChar('a', expr) 92 | result = find2(expr, 'aaaaaaaabacbca', 0) 93 | assert list(result) == [ 94 | (1, 8), (9, 8), (10, 10), (11, 10), (12, 11), (13, 12), (14, 14) 95 | ] 96 | 97 | def test_star_between_chars_star_match_end(self): 98 | star = StateSplit(None, StateMatch()) 99 | star.out = StateChar('b', star) 100 | expr = StateChar('a', star) 101 | result = find2(expr, 'acjjjabcabbbbb', 0) 102 | assert list(result) == [(1, 1), (6, 7), (9, 14)] 103 | 104 | def test_star_and_char_star_not_match_end(self): 105 | star = StateSplit(None, StateMatch()) 106 | star.out = StateChar('b', star) 107 | expr = StateChar('a', star) 108 | result = find2(expr, 'acjjjabcabbbbbfoobr', 0) 109 | assert list(result) == [(1, 1), (6, 7), (9, 14)] 110 | 111 | def test_star_between_chars_match_star(self): 112 | star = StateSplit(None, StateChar('c', StateMatch())) 113 | star.out = StateChar('b', star) 114 | expr = StateChar('a', star) 115 | result = find2(expr, 'xaabbbbbcjjjabcxalcac', 0) 116 | assert list(result) == [(3, 9), (13, 15), (20, 21)] 117 | 118 | def test_simple_or(self): 119 | expr = compile_re('(aa|bb)') 120 | result = find2(expr, 'xyzabbaab', 0) 121 | assert list(result) == [(5, 6), (7, 8)] 122 | 123 | def test_grouped_or_between_chars(self): 124 | expr = compile_re('x(aa|bb)x') 125 | result = find2(expr, 'axaaxaxbxbbxa', 0) 126 | assert list(result) == [(2, 5), (9, 12)] 127 | 128 | def test_chained_grouped_or_match(self): 129 | expr = compile_re('x(aa|bb)(cc|dd)x') 130 | result = find2(expr, 'axaaddaxbbccxxaacx', 0) 131 | assert list(result) == [(8, 13)] 132 | 133 | def test_chained_grouped_or_no_match(self): 134 | expr = compile_re('x(aa|bb)(cc|dd)x') 135 | result = find2(expr, 'xaaccddxxaaddddxxaacc', 0) 136 | assert list(result) == [(-1, -1)] 137 | 138 | def test_grouped_star(self): 139 | expr = compile_re('(ab)*') 140 | result = find2(expr, 'ababababab', 0) 141 | assert list(result) == [(1, 10)] 142 | 143 | def test_grouped_star_between_chars_match(self): 144 | expr = compile_re('x(ab)*x') 145 | result = find2(expr, 'ababxababxabab', 0) 146 | assert list(result) == [(5, 10)] 147 | 148 | def test_grouped_star_between_chars_no_match(self): 149 | expr = compile_re('x(ab)*x') 150 | result = find2(expr, 'ababxabababab', 0) 151 | assert list(result) == [(-1, -1)] 152 | 153 | def test_grouped_star_and_or_match(self): 154 | expr = compile_re('x((aa)*|(bb)*)x') 155 | result = find2(expr, 'xaaaaaaxxx', 0) 156 | assert list(result) == [(1, 8), (9, 10)] 157 | 158 | def test_grouped_star_and_or_no_match(self): 159 | expr = compile_re('x((aa)*|(bb)*)x') 160 | result = find2(expr, 'xaaaaaxbxabxbbbb', 0) 161 | assert list(result) == [(-1, -1)] 162 | 163 | def test_simple_plus(self): 164 | expr = compile_re('a+') 165 | result = find2(expr, 'bxaaaabak', 0) 166 | assert list(result) == [(3, 6), (8, 8)] 167 | 168 | def test_grouped_plus(self): 169 | expr = compile_re('(a|b)+c') 170 | result = find2(expr, 'xxcaababcvbc', 0) 171 | assert list(result) == [(4, 9), (11, 12)] 172 | 173 | def test_or_repetition(self): 174 | expr = compile_re('(aa|bb){2}') 175 | result = find2(expr, 'xabbxaaaaxjkbbajbbaal', 0) 176 | assert list(result) == [(6, 9), (17, 20)] 177 | 178 | def test_match_evil(self): 179 | expr = compile_re('(a|b)*a(a|b){5}a(a|b)*') 180 | result = find2(expr, 'aaaababababba', 0) 181 | assert list(result) == [(1, 13)] 182 | 183 | def test_match_evil_no_match(self): 184 | expr = compile_re('(a|b)*a(a|b){5}a(a|b)*') 185 | result = find2(expr, 'aaaaaaxbbbbaaaaabbbbbbb', 0) 186 | assert list(result) == [(-1, -1)] 187 | 188 | def test_single_char_build_expr(self): 189 | expr = compile_re('a') 190 | assert isinstance(expr, StateChar) 191 | assert expr.start == ord('a') 192 | assert expr.stop == ord('a') 193 | 194 | def test_two_chars_build_expr(self): 195 | expr = compile_re('ab') 196 | assert isinstance(expr, StateChar) 197 | assert expr.start == ord('a') 198 | assert isinstance(expr.out, StateChar) 199 | assert expr.out.start == ord('b') 200 | 201 | def test_three_chars_build_expr(self): 202 | expr = compile_re('abc') 203 | assert isinstance(expr, StateChar) 204 | assert expr.start == ord('a') 205 | assert isinstance(expr.out, StateChar) 206 | assert expr.out.start == ord('b') 207 | assert isinstance(expr.out.out, StateChar) 208 | assert expr.out.out.start == ord('c') 209 | 210 | def test_chars_and_dots_build_expr(self): 211 | expr = compile_re('a.c.', False) 212 | assert isinstance(expr, StateChar) 213 | assert expr.start == ord('a') 214 | assert isinstance(expr.out, StateDot) 215 | assert isinstance(expr.out.out, StateChar) 216 | assert expr.out.out.start == ord('c') 217 | assert isinstance(expr.out.out.out, StateDot) 218 | assert isinstance(expr.out.out.out.out, StateMatch) 219 | 220 | def test_chars_and_special_a_build_expr(self): 221 | expr = compile_re('%aa%a', False) 222 | assert isinstance(expr, StateCharRange) 223 | assert expr.start == ord('A') 224 | assert expr.stop == ord('z') 225 | assert isinstance(expr.out, StateChar) 226 | assert expr.out.stop == ord('a') 227 | assert isinstance(expr.out.out, StateCharRange) 228 | assert expr.out.out.start == ord('A') 229 | assert expr.out.out.stop == ord('z') 230 | assert isinstance(expr.out.out.out, StateMatch) 231 | 232 | def test_escape_percent_build_expr(self): 233 | expr = compile_re('%%', False) 234 | assert isinstance(expr, StateChar) 235 | assert expr.start == ord('%') 236 | assert isinstance(expr.out, StateMatch) 237 | 238 | def test_build_expr_pattern_with_star(self): 239 | expr = compile_re('a*', False) 240 | assert isinstance(expr, StateSplit) 241 | assert isinstance(expr.out, StateChar) 242 | assert expr.out.out == expr 243 | assert expr.out.start == ord('a') 244 | assert isinstance(expr.out2, StateMatch) 245 | 246 | def test_build_expr_pattern_with_star_2(self): 247 | expr = compile_re('a*b*', False) 248 | assert isinstance(expr, StateSplit) 249 | assert isinstance(expr.out, StateChar) 250 | assert expr.out.out == expr 251 | assert expr.out.start == ord('a') 252 | assert isinstance(expr.out2, StateSplit) 253 | assert isinstance(expr.out2.out, StateChar) 254 | assert expr.out2.out.out == expr.out2 255 | assert expr.out2.out.start == ord('b') 256 | assert isinstance(expr.out2.out2, StateMatch) 257 | 258 | def test_build_expr_pattern_with_star_3(self): 259 | expr = compile_re('a*cb*', False) 260 | assert isinstance(expr, StateSplit) 261 | assert isinstance(expr.out, StateChar) 262 | assert expr.out.out == expr 263 | assert expr.out.start == ord('a') 264 | 265 | assert isinstance(expr.out2, StateChar) 266 | assert expr.out2.start == ord('c') 267 | 268 | assert isinstance(expr.out2.out, StateSplit) 269 | assert isinstance(expr.out2.out.out, StateChar) 270 | assert expr.out2.out.out.out == expr.out2.out 271 | assert expr.out2.out.out.start == ord('b') 272 | assert isinstance(expr.out2.out.out2, StateMatch) 273 | 274 | def test_build_expr_pattern_with_star_4(self): 275 | expr = compile_re('a.*c%a*', False) 276 | 277 | # a 278 | assert isinstance(expr, StateChar) 279 | assert expr.start == ord('a') 280 | 281 | # .* 282 | node = expr.out 283 | assert isinstance(node, StateSplit) 284 | assert isinstance(node.out, StateDot) 285 | assert node.out.out == node 286 | 287 | # c 288 | node = node.out2 289 | assert isinstance(node, StateChar) 290 | assert node.start == ord('c') 291 | 292 | # %a* 293 | node = node.out 294 | assert isinstance(node, StateSplit) 295 | assert isinstance(node.out, StateCharRange) 296 | assert node.out.start == ord('A') 297 | assert node.out.stop == ord('z') 298 | assert node.out.out == node 299 | 300 | # match 301 | node = node.out2 302 | assert isinstance(node, StateMatch) 303 | 304 | def test_build_expr_simple_or(self): 305 | expr = compile_re('a|b', False) 306 | 307 | # | 308 | assert isinstance(expr, StateSplit) 309 | 310 | # a 311 | node = expr.out 312 | assert isinstance(node, StateChar) 313 | assert node.stop == ord('a') 314 | assert isinstance(node.out, StateMatch) 315 | 316 | # b 317 | node = expr.out2 318 | assert isinstance(node, StateChar) 319 | assert node.stop == ord('b') 320 | assert isinstance(node.out, StateMatch) 321 | 322 | def test_build_group_star(self): 323 | expr = compile_re('(ab)*', False) 324 | 325 | # * 326 | assert isinstance(expr, StateSplit) 327 | 328 | # a 329 | node = expr.out 330 | assert isinstance(node, StateChar) 331 | assert node.stop == ord('a') 332 | 333 | # b 334 | node = node.out 335 | assert isinstance(node, StateChar) 336 | assert node.stop == ord('b') 337 | assert node.out == expr 338 | 339 | # match 340 | assert isinstance(expr.out2, StateMatch) 341 | 342 | def test_build_group_star_chained(self): 343 | expr = compile_re('(ab)*ab', False) 344 | 345 | # * 346 | assert isinstance(expr, StateSplit) 347 | 348 | # a 349 | node = expr.out 350 | assert isinstance(node, StateChar) 351 | assert node.stop == ord('a') 352 | 353 | # b 354 | node = node.out 355 | assert isinstance(node, StateChar) 356 | assert node.stop == ord('b') 357 | assert node.out == expr 358 | 359 | # ab 360 | node = expr.out2 361 | assert isinstance(node, StateChar) 362 | assert node.stop == ord('a') 363 | 364 | # b 365 | node = node.out 366 | assert isinstance(node, StateChar) 367 | assert node.stop == ord('b') 368 | 369 | # match 370 | assert isinstance(node.out, StateMatch) 371 | 372 | def test_build_group_or(self): 373 | expr = compile_re('(aa|bb)', False) 374 | 375 | # | 376 | assert isinstance(expr, StateSplit) 377 | 378 | # aa 379 | node = expr.out 380 | assert isinstance(node, StateChar) 381 | assert node.stop == ord('a') 382 | node = node.out 383 | assert isinstance(node, StateChar) 384 | assert node.stop == ord('a') 385 | assert isinstance(node.out, StateMatch) 386 | 387 | # bb 388 | node = expr.out2 389 | assert isinstance(node, StateChar) 390 | assert node.stop == ord('b') 391 | node = node.out 392 | assert isinstance(node, StateChar) 393 | assert node.stop == ord('b') 394 | assert isinstance(node.out, StateMatch) 395 | 396 | def test_build_group_or_between_chars(self): 397 | expr = compile_re('x(aa|bb)x') 398 | # xaax 399 | assert isinstance(expr, StateChar) 400 | assert expr.start == ord('x') 401 | 402 | # | 403 | node = expr.out 404 | assert isinstance(node, StateSplit) 405 | 406 | # aax 407 | node = node.out 408 | assert isinstance(node, StateChar) 409 | assert node.stop == ord('a') 410 | node = node.out 411 | assert isinstance(node, StateChar) 412 | assert node.stop == ord('a') 413 | node = node.out 414 | assert isinstance(node, StateChar) 415 | assert node.stop == ord('x') 416 | assert isinstance(node.out, StateMatch) 417 | 418 | # bbx 419 | node = expr.out.out2 420 | assert isinstance(node, StateChar) 421 | assert node.stop == ord('b') 422 | node = node.out 423 | assert isinstance(node, StateChar) 424 | assert node.stop == ord('b') 425 | node = node.out 426 | assert isinstance(node, StateChar) 427 | assert node.stop == ord('x') 428 | assert isinstance(node.out, StateMatch) 429 | 430 | def test_build_expr_with_repitition(self): 431 | expr = compile_re('a{3}') 432 | 433 | assert isinstance(expr, StateChar) 434 | assert isinstance(expr.out, StateChar) 435 | assert isinstance(expr.out.out, StateChar) 436 | assert isinstance(expr.out.out.out, StateMatch) 437 | 438 | def test_build_expr_misplaced_star(self): 439 | with pytest.raises(RuntimeError): 440 | compile_re('*') 441 | 442 | def test_build_expr_invalid_special_char(self): 443 | with pytest.raises(RuntimeError): 444 | compile_re('%,') 445 | 446 | def test_build_expr_misplaced_percent_1(self): 447 | with pytest.raises(RuntimeError): 448 | compile_re('%') 449 | 450 | def test_build_expr_misplaced_percent_2(self): 451 | with pytest.raises(RuntimeError): 452 | compile_re('a%%%') 453 | 454 | def test_build_expr_misplaced_percent_3(self): 455 | with pytest.raises(RuntimeError): 456 | compile_re('a%') 457 | -------------------------------------------------------------------------------- /luna/luaframe.py: -------------------------------------------------------------------------------- 1 | from rpython.rlib.rstruct.ieee import pack_float 2 | from rpython.rlib.rstruct.runpack import runpack 3 | from luna.opcodes import unrolled_op_desc 4 | from luna.helpers import debug_print 5 | from luna.w_objects import W_Str, W_Num, W_Object, W_Pri, W_Table 6 | 7 | 8 | class LuaFrame(W_Object): 9 | def __init__(self, flags, constants, uv_data, instructions): 10 | self.flags = flags 11 | self.constants = constants 12 | self.num_constants = len(constants) 13 | self.instructions = instructions 14 | self.num_instructions = len(instructions) 15 | self.cmp_result = False 16 | self.registers = [W_Pri(0)] * 50 17 | self.multires = [] 18 | self.uv_data = uv_data 19 | self.parent = None 20 | 21 | def getval(self): 22 | return self 23 | 24 | 25 | class LuaBuiltinFrame(LuaFrame): 26 | def __init__(self, function): 27 | self.function = function 28 | self.registers = [] 29 | 30 | def call(self, args, space): 31 | return self.function(args) 32 | 33 | def clone(self): 34 | # no need to cleon, LuaBuilinFrame has no state 35 | return self 36 | 37 | 38 | class LuaBytecodeFrame(LuaFrame): 39 | def execute_frame(self, space): 40 | next_instr = 0 41 | self.space = space 42 | while True: 43 | i_opcode, i_args = self.instructions[next_instr] 44 | 45 | for op_desc in unrolled_op_desc: 46 | if i_opcode == op_desc.index: 47 | meth = getattr(self, op_desc.name) 48 | res = meth(i_args, space) 49 | if op_desc.name in ('RET0', 'RET1', 'RET', 'RETM', 'CALLT', 'CALLMT'): 50 | return res 51 | # TODO: return -1 everywhere 52 | if res is None or res == -1: 53 | next_instr += 1 54 | else: 55 | next_instr += res 56 | 57 | if next_instr >= self.num_instructions: 58 | break 59 | 60 | def decode_lits(self, val): 61 | return val - 0x10000 if (val & 0x8000) > 0 else val 62 | 63 | def get_str_constant(self, val): 64 | w_v = self.constants[self.num_constants-val-1] 65 | return w_v 66 | 67 | def get_func_constant(self, val): 68 | w_v = self.constants[self.num_constants-val-1] 69 | assert isinstance(w_v, LuaFrame) 70 | return w_v 71 | 72 | def get_num_constant(self, val): 73 | return self.constants[val].n_val 74 | 75 | def get_num_register(self, pos): 76 | return self.registers[pos].n_val 77 | 78 | def get_tab_constant(self, val): 79 | return self.constants[self.num_constants-1-val] 80 | 81 | def ISLT(self, args, space): 82 | """ 83 | A: var, D: var 84 | A < D 85 | """ 86 | w_x = self.registers[args[0]] 87 | w_y = self.registers[args[1]] 88 | self.cmp_result = w_x.lt(w_y) 89 | 90 | def ISGE(self, args, space): 91 | """ 92 | A: var, D: var 93 | A >= D 94 | """ 95 | w_x = self.registers[args[0]] 96 | w_y = self.registers[args[1]] 97 | self.cmp_result = w_x.ge(w_y) 98 | 99 | def ISLE(self, args, space): 100 | """ 101 | A: var, D: var 102 | A <= D 103 | """ 104 | w_x = self.registers[args[0]] 105 | w_y = self.registers[args[1]] 106 | self.cmp_result = w_x.le(w_y) 107 | 108 | def ISGT(self, args, space): 109 | """ 110 | A: var, D: var 111 | A > D 112 | """ 113 | w_x = self.registers[args[0]] 114 | w_y = self.registers[args[1]] 115 | self.cmp_result = w_x.gt(w_y) 116 | 117 | def ISEQV(self, args, space): 118 | """ 119 | A: var, D: var 120 | A == D 121 | """ 122 | w_x = self.registers[args[0]] 123 | w_y = self.registers[args[1]] 124 | self.cmp_result = w_x.eq(w_y) 125 | 126 | def ISNEV(self, args, space): 127 | """ 128 | A: var, D: var 129 | A != D 130 | """ 131 | w_x = self.registers[args[0]] 132 | w_y = self.registers[args[1]] 133 | self.cmp_result = w_x.neq(w_y) 134 | 135 | def ISEQS(self, args, space): 136 | """ 137 | A: var, D: str 138 | A == D 139 | """ 140 | w_var = self.registers[args[0]] 141 | w_str = self.get_str_constant(args[1]) 142 | if w_var is not None: 143 | self.cmp_result = w_var.eq(w_str) 144 | else: 145 | self.cmp_result = False 146 | 147 | def ISNES(self, args, space): 148 | """ 149 | A: var, D: str 150 | A != D 151 | """ 152 | w_var = self.registers[args[0]] 153 | w_str = self.get_str_constant(args[1]) 154 | self.cmp_result = w_var.neq(w_str) 155 | 156 | def ISEQN(self, args, space): 157 | """ 158 | A: var, D: num 159 | """ 160 | w_var = self.registers[args[0]] 161 | w_num = self.constants[args[1]] 162 | self.cmp_result = w_var.eq(w_num) 163 | 164 | def ISNEN(self, args, space): 165 | """ 166 | A: var, D: num 167 | """ 168 | w_var = self.registers[args[0]] 169 | w_num = self.constants[args[1]] 170 | self.cmp_result = w_var.neq(w_num) 171 | 172 | def ISEQP(self, args, space): 173 | """ 174 | A: var, D: pri 175 | A == D 176 | """ 177 | w_var = self.registers[args[0]] 178 | w_pri = W_Pri(args[1]) 179 | self.cmp_result = w_var.eq(w_pri) 180 | 181 | def ISNEP(self, args, space): 182 | """ 183 | A: var, D: pri 184 | A != D 185 | """ 186 | w_var = self.registers[args[0]] 187 | w_pri = W_Pri(args[1]) 188 | self.cmp_result = w_var.neq(w_pri) 189 | 190 | def ISTC(self, args, space): 191 | """ 192 | A: dst, D: var 193 | Copy D to A and jump, if D is true 194 | """ 195 | w_var = self.registers[args[1]] 196 | self.registers[args[0]] = w_var.clone() 197 | self.cmp_result = w_var.is_true() 198 | 199 | def ISFC(self, args, space): raise NotImplementedError('ISFC not implemented') 200 | 201 | def IST(self, args, space): 202 | """ 203 | A: , D:var 204 | Jump if D is true 205 | """ 206 | w_var = self.registers[args[1]] 207 | self.cmp_result = w_var.is_true() 208 | 209 | def ISF(self, args, space): 210 | """ 211 | A: , D:var 212 | Jump if D is false 213 | """ 214 | w_var = self.registers[args[1]] 215 | self.cmp_result = not w_var.is_true() 216 | 217 | def MOV(self, args, space): 218 | w_var = self.registers[args[1]] 219 | if not isinstance(w_var, W_Table) and not isinstance(w_var, LuaBytecodeFrame): 220 | w_var = w_var.clone() 221 | self.registers[args[0]] = w_var 222 | 223 | def NOT(self, args, space): 224 | """ 225 | A:dst, D: var 226 | Set A to boolean not of D 227 | """ 228 | w_var = self.registers[args[1]] 229 | if w_var.is_true(): 230 | self.registers[args[0]] = W_Pri(1) 231 | else: 232 | self.registers[args[0]] = W_Pri(2) 233 | 234 | def UNM(self, args, space): 235 | """ 236 | A: dst, D: var 237 | Set A to -D (unary minus) 238 | """ 239 | w_var = self.registers[args[1]] 240 | assert isinstance(w_var, W_Num) 241 | self.registers[args[0]] = W_Num(-w_var.n_val) 242 | 243 | def LEN(self, args, space): 244 | w_var = self.registers[args[1]] 245 | if isinstance(w_var, W_Table): 246 | l = len(w_var.content) 247 | w_first = w_var.get(W_Num(0)) 248 | try: 249 | w_v = w_var.content[W_Num(0).hash()] 250 | if isinstance(w_v, W_Pri) and w_v.n_val == 0: 251 | l -= 1 252 | except KeyError: 253 | pass 254 | self.registers[args[0]] = W_Num(l) 255 | else: 256 | raise NotImplementedError('Len for types other than table not supported atm') 257 | 258 | def ADDVN(self, args, space): 259 | """ 260 | A: dst, B: var, C: num 261 | """ 262 | v1 = self.get_num_register(args[1]) 263 | v2 = self.get_num_constant(args[2]) 264 | debug_print("ADDVN: Reg[%s] = %s + %s" % (args[0], v1, v2)) 265 | self.registers[args[0]] = W_Num(v1+v2) 266 | 267 | def SUBVN(self, args, space): 268 | """ 269 | A: dst, B: var, C: num 270 | A = B + C 271 | """ 272 | v1 = self.get_num_register(args[1]) 273 | v2 = self.get_num_constant(args[2]) 274 | self.registers[args[0]] = W_Num(v1 - v2) 275 | 276 | def MULVN(self, args, space): 277 | v1 = self.get_num_register(args[1]) 278 | v2 = self.get_num_constant(args[2]) 279 | self.registers[args[0]] = W_Num(v1*v2) 280 | 281 | def DIVVN(self, args, space): 282 | v1 = self.get_num_register(args[1]) 283 | v2 = self.get_num_constant(args[2]) 284 | self.registers[args[0]] = W_Num(v1/float(v2)) 285 | 286 | def MODVN(self, args, space): raise NotImplementedError('MODVN not implemented') 287 | 288 | def ADDNV(self, args, space): 289 | v1 = self.get_num_constant(args[2]) 290 | v2 = self.get_num_register(args[1]) 291 | self.registers[args[0]] = W_Num(v1+v2) 292 | 293 | def SUBNV(self, args, space): 294 | v1 = self.get_num_constant(args[2]) 295 | v2 = self.get_num_register(args[1]) 296 | self.registers[args[0]] = W_Num(v1-v2) 297 | 298 | def MULNV(self, args, space): 299 | v1 = self.get_num_constant(args[2]) 300 | v2 = self.get_num_register(args[1]) 301 | self.registers[args[0]] = W_Num(v1*v2) 302 | 303 | def DIVNV(self, args, space): 304 | v1 = self.get_num_constant(args[2]) 305 | v2 = self.get_num_register(args[1]) 306 | self.registers[args[0]] = W_Num(v1/float(v2)) 307 | 308 | def MODNV(self, args, space): raise NotImplementedError('MODNV not implemented') 309 | 310 | def ADDVV(self, args, space): 311 | """ 312 | A: dst, B: var, C: var 313 | Sets A to B + C 314 | """ 315 | v1 = self.get_num_register(args[1]) 316 | v2 = self.get_num_register(args[2]) 317 | debug_print("ADDVV: Reg %d = %s + %s" % (args[0], v1, v2)) 318 | self.registers[args[0]] = W_Num(v1 + v2) 319 | 320 | def SUBVV(self, args, space): 321 | v1 = self.get_num_register(args[1]) 322 | v2 = self.get_num_register(args[2]) 323 | debug_print("ADDVV: Reg %d = %s + %s" % (args[0], v1, v2)) 324 | self.registers[args[0]] = W_Num(v1-v2) 325 | 326 | def MULVV(self, args, space): 327 | v1 = self.get_num_register(args[1]) 328 | v2 = self.get_num_register(args[2]) 329 | self.registers[args[0]] = W_Num(v1*v2) 330 | 331 | def DIVVV(self, args, space): 332 | v1 = self.get_num_register(args[1]) 333 | v2 = self.get_num_register(args[2]) 334 | self.registers[args[0]] = W_Num(v1/float(v2)) 335 | 336 | def MODVV(self, args, space): raise NotImplementedError('MODVV not implemented') 337 | 338 | def POW(self, args, space): raise NotImplementedError('POW not implemented') 339 | 340 | def CAT(self, args, space): 341 | """ 342 | A: dst, B: rbase, C: rbase 343 | concat strings from b to c 344 | """ 345 | 346 | strs = [] 347 | for i in xrange(args[1], args[2]+1): 348 | strs.append(self.registers[i].to_str()) 349 | self.registers[args[0]] = W_Str(''.join(strs)) 350 | 351 | def KSTR(self, args, space): 352 | """ 353 | A: dst, D: str 354 | Set register A to str 355 | """ 356 | w_str = self.get_str_constant(args[1]) 357 | self.registers[args[0]] = w_str 358 | 359 | def KCDATA(self, args, space): raise NotImplementedError('KCDATA not implemented') 360 | 361 | def KSHORT(self, args, space): 362 | """ 363 | A: dst, D: lits 364 | Set A to 16 bit signed integer D 365 | """ 366 | val = self.decode_lits(args[1]) 367 | debug_print("KSHORT: set R %d to %d" %(args[0], val)) 368 | self.registers[args[0]] = W_Num(val) 369 | 370 | def KNUM(self, args, space): 371 | """ 372 | A: dst, D: num 373 | Set A to number constant D 374 | """ 375 | val = self.get_num_constant(args[1]) 376 | self.registers[args[0]] = W_Num(val) 377 | 378 | def KPRI(self, args, space): 379 | """ 380 | A: dst, D pri 381 | sets dst register to pri 382 | """ 383 | self.registers[args[0]] = W_Pri(args[1]) 384 | 385 | def KNIL(self, args, space): 386 | """ 387 | A: base, D: base 388 | Set slots A to D to nil 389 | """ 390 | for i in xrange(0, args[1]-args[0]+1): 391 | self.registers[args[0]+i] = W_Pri(0) 392 | 393 | def get_uv(self, uv_ind): 394 | local, immutable, ind = self.uv_data[uv_ind] 395 | 396 | if local == 0: 397 | return self.parent.get_uv(uv_ind) 398 | else: 399 | return self.parent.registers[ind] 400 | 401 | def UGET(self, args, space): 402 | """ 403 | A: dst, D: uv 404 | Set A to upvalue D 405 | """ 406 | self.registers[args[0]] = self.get_uv(args[1]) 407 | 408 | def USETV(self, args, space): raise NotImplementedError('USETV not implemented') 409 | 410 | def USETS(self, args, space): raise NotImplementedError('USETS not implemented') 411 | 412 | def USETN(self, args, space): raise NotImplementedError('USETN not implemented') 413 | 414 | def USETP(self, args, space): raise NotImplementedError('USETP not implemented') 415 | 416 | def UCLO(self, args, space): 417 | if args[0] != 0: 418 | raise NotImplementedError('Nonzero rbase not implemented') 419 | else: 420 | return args[1] - 32768 + 1 421 | 422 | def FNEW(self, args, space): 423 | self.registers[args[0]]= self.get_func_constant(args[1]) 424 | 425 | def TNEW(self, args, space): 426 | # ignore table size at the moment 427 | self.registers[args[0]] = W_Table() 428 | 429 | def TDUP(self, args, space): 430 | w_table = self.get_tab_constant(args[1]) 431 | assert isinstance(w_table, W_Table) 432 | self.registers[args[0]] = w_table.clone() 433 | 434 | def GGET(self, args, space): 435 | """ 436 | A: dst, D: str 437 | get global 438 | """ 439 | key = self.get_str_constant(args[1]).s_val 440 | debug_print("GGET: get %s in R %s" % (key, args[0])) 441 | self.registers[args[0]] = space.globals[key] 442 | 443 | def GSET(self, args, space): 444 | """ 445 | A: dst, D: str 446 | Set Global 447 | """ 448 | key = self.get_str_constant(args[1]).s_val 449 | val = self.registers[args[0]] 450 | debug_print('GSET: set global %s to %s' %(key, val)) 451 | self.space.globals[key] = val 452 | 453 | def TGETV(self, args, space): 454 | w_t = self.registers[args[1]] 455 | assert isinstance(w_t, W_Table) 456 | w_key = self.registers[args[2]] 457 | self.registers[args[0]] = w_t.get(w_key) 458 | 459 | def TGETS(self, args, space): 460 | w_t = self.registers[args[1]] 461 | assert isinstance(w_t, W_Table) 462 | w_key = self.get_str_constant(args[2]) 463 | self.registers[args[0]] = w_t.get(w_key) 464 | 465 | def TGETB(self, args, space): 466 | w_t = self.registers[args[1]] 467 | assert isinstance(w_t, W_Table) 468 | # TODO: we must wrap ints into W_Num because set 469 | # expects a W_Object as key 470 | self.registers[args[0]] = w_t.get(W_Num(args[2])) 471 | 472 | def TSETV(self, args, space): 473 | w_t = self.registers[args[1]] 474 | assert isinstance(w_t, W_Table) 475 | w_key = self.registers[args[2]] 476 | w_t.set(w_key, self.registers[args[0]]) 477 | 478 | def TSETS(self, args, space): 479 | w_t = self.registers[args[1]] 480 | assert isinstance(w_t, W_Table) 481 | w_key = self.get_str_constant(args[2]) 482 | w_t.set(w_key, self.registers[args[0]]) 483 | 484 | def TSETB(self, args, space): 485 | w_t = self.registers[args[1]] 486 | assert isinstance(w_t, W_Table) 487 | # TODO: we must wrap ints into W_Num because set 488 | # expects a W_Object as key 489 | w_t.set(W_Num(args[2]), self.registers[args[0]]) 490 | 491 | def TSETM(self, args, space): 492 | """ 493 | A: base, D: *num 494 | (A-1)[D], (A-1)[D+1], ... = A, A+1, ... 495 | 496 | *num is the index to a num constant that's a float 497 | only use first 32 bit of mantissa 498 | """ 499 | w_table = self.registers[args[0]-1] 500 | index = self.get_num_constant(args[1]) 501 | packed = [] 502 | pack_float(packed, index, 8, True) 503 | index = runpack('>i', packed[0][4:]) 504 | for i in xrange(0, len(self.multires)): 505 | w_table.set(W_Num(index+i), self.multires[i]) 506 | 507 | def CALLM(self, args, space): 508 | w_res = self.call_function( 509 | args[0], args[2], space, multires=len(self.multires) 510 | ) 511 | self.store_return_values(w_res, args[0], args[1]) 512 | 513 | def call_function(self, f_index, num_args, space, multires=0): 514 | w_func = self.registers[f_index] 515 | assert num_args >= 0 516 | # clone the frame, so every frame has it's own registers 517 | # because a frame can be called multiple times (recursion) 518 | if isinstance(w_func, LuaBytecodeFrame): 519 | w_func.parent = self 520 | old_regs = w_func.registers 521 | w_func.registers = [x for x in self.registers] 522 | r_stop = f_index + num_args 523 | w_func.registers[0:num_args] = self.registers[f_index+1:r_stop] 524 | w_func.registers[num_args:(num_args+multires)] = self.multires[:multires] 525 | w_res = w_func.execute_frame(space) 526 | w_func.registers = old_regs 527 | elif isinstance(w_func, LuaBuiltinFrame): 528 | params = [] 529 | for w_param in (self.registers[f_index+1:f_index+num_args] + 530 | self.multires[:multires]): 531 | if isinstance(w_param, W_Table): 532 | # pass tables as reference 533 | params.append(w_param) 534 | else: 535 | params.append(w_param.clone()) 536 | w_res = w_func.function(params) 537 | else: 538 | raise RuntimeError('Invalid function') 539 | return w_res 540 | 541 | def store_return_values(self, values, slots_start, slots_end): 542 | if slots_end == 0: # multires return 543 | self.multires = values 544 | else: 545 | # TODO: handle overflow case better 546 | overflow = max(slots_end-1 - len(values or []), 0) 547 | for i in xrange(0, slots_end-1 - overflow): 548 | self.registers[slots_start+i] = values[i] 549 | for i in xrange(len(values or []), slots_end): 550 | self.registers[slots_start+i] = W_Pri(0) 551 | 552 | def CALL(self, args, space): 553 | """ 554 | A: base, B: lit, C: lit 555 | Call: A, ..., A+B-2 = A(A+1, ..., A+C-1) 556 | """ 557 | w_res = self.call_function(args[0], args[2], space) 558 | self.store_return_values(w_res, args[0], args[1]) 559 | 560 | def CALLMT(self, args, space): 561 | w_res = self.call_function( 562 | args[0], args[1], space, multires=len(self.multires) 563 | ) 564 | return w_res 565 | 566 | def CALLT(self, args, space): 567 | w_res = self.call_function(args[0], args[1], space) 568 | return w_res 569 | 570 | def ITERC(self, args, space): raise NotImplementedError('ITERC not implemented') 571 | 572 | def ITERN(self, args, space): raise NotImplementedError('ITERN not implemented') 573 | 574 | def VARG(self, args, space): raise NotImplementedError('VARG not implemented') 575 | 576 | def ISNEXT(self, args, space): raise NotImplementedError('ISNEXT not implemented') 577 | 578 | def RETM(self, args, space): 579 | """ 580 | A: base, D: lit 581 | return A, ..., A+D+MULTRES-1 582 | """ 583 | w_return_values = [] 584 | for i in xrange(0, args[1]): 585 | w_return_values.append(self.registers[args[0]+i]) 586 | 587 | w_return_values += self.multires 588 | return w_return_values 589 | 590 | def RET(self, args, space): 591 | """ 592 | A: rbase, B: lit 593 | return A, ..., A+D-2 594 | """ 595 | w_return_values = [] 596 | for i in xrange(0, args[1]-1): 597 | w_return_values.append(self.registers[args[0]+i]) 598 | return w_return_values 599 | 600 | def RET0(self, args, space): 601 | """ 602 | A: rbase, D: lit 603 | Return without value 604 | """ 605 | debug_print("RET0 called") 606 | return [W_Num(0)] 607 | 608 | def RET1(self, args, space): 609 | """ 610 | A: rbase, D: lit 611 | Return with exactly one value, R(A) holds the value 612 | """ 613 | # TODO only numbers at the moment 614 | w_v = self.registers[args[0]] 615 | debug_print('RET1: return %s' % w_v.to_str()) 616 | # TODO results are wrapped in a list, because it makes returning multiple arguments 617 | # easier, improve if possible 618 | return [w_v] 619 | 620 | def continue_for_loop(self, idx, stop, step): 621 | if step >= 0: 622 | return idx <= stop 623 | else: 624 | return idx >= stop 625 | 626 | def FORI(self, args, space): 627 | #TODO combine FORI and FORL? 628 | base = args[0] 629 | w_idx = self.registers[base] 630 | w_stop = self.registers[base+1] 631 | w_step = self.registers[base+2] 632 | self.registers[base+3] = w_idx.clone() 633 | if self.continue_for_loop(w_idx.n_val, w_stop.n_val, w_step.n_val): 634 | return 1 635 | else: 636 | return args[1] - 32768 + 1 637 | 638 | def JFORI(self, args, space): raise NotImplementedError('JFORI not implemented') 639 | 640 | def FORL(self, args, space): 641 | base = args[0] 642 | w_idx = self.registers[base] 643 | w_stop = self.registers[base+1] 644 | w_step = self.registers[base+2] 645 | w_idx.n_val += w_step.n_val 646 | self.registers[base+3] = w_idx.clone() 647 | if self.continue_for_loop(w_idx.n_val, w_stop.n_val, w_step.n_val): 648 | return args[1] - 32768 + 1 649 | else: 650 | return 1 651 | 652 | def IFORL(self, args, space): raise NotImplementedError('IFORL not implemented') 653 | 654 | def JFORL(self, args, space): raise NotImplementedError('JFORL not implemented') 655 | 656 | def ITERL(self, args, space): raise NotImplementedError('ITERL not implemented') 657 | 658 | def IITERL(self, args, space): raise NotImplementedError('IITERL not implemented') 659 | 660 | def JITERL(self, args, space): raise NotImplementedError('JITERL not implemented') 661 | 662 | def LOOP(self, args, space): 663 | """ 664 | No Op, but can be used as loop hint for the jit in future 665 | """ 666 | pass 667 | 668 | def ILOOP(self, args, space): raise NotImplementedError('ILOOP not implemented') 669 | 670 | def JLOOP(self, args, space): raise NotImplementedError('JLOOP not implemented') 671 | 672 | def JMP(self, args, space): 673 | """ 674 | A: Rbase, D: jmp (next instr) 675 | """ 676 | if self.cmp_result: 677 | return args[1] - 32768 + 1 678 | else: 679 | # rpython does not like returning None here 680 | self.cmp_result = True 681 | return -1 682 | 683 | def FUNCF(self, args, space): raise NotImplementedError('FUNCF not implemented') 684 | 685 | def IFUNCF(self, args, space): raise NotImplementedError('IFUNCF not implemented') 686 | 687 | def JFUNCF(self, args, space): raise NotImplementedError('JFUNCF not implemented') 688 | 689 | def FUNCV(self, args, space): raise NotImplementedError('FUNCV not implemented') 690 | 691 | def IFUNCV(self, args, space): raise NotImplementedError('IFUNCV not implemented') 692 | 693 | def JFUNCV(self, args, space): raise NotImplementedError('JFUNCV not implemented') 694 | 695 | def FUNCC(self, args, space): raise NotImplementedError('FUNCC not implemented') 696 | 697 | def FUNCCW(self, args, space): raise NotImplementedError('FUNCCW not implemented') 698 | --------------------------------------------------------------------------------