Full Code of zzwlpx/ljd for AI

master 16d485d0f822 cached
46 files
217.5 KB
64.5k tokens
584 symbols
1 requests
Download .txt
Showing preview only (231K chars total). Download the full file or copy to clipboard to get everything.
Repository: zzwlpx/ljd
Branch: master
Commit: 16d485d0f822
Files: 46
Total size: 217.5 KB

Directory structure:
gitextract_2i5rc5bq/

├── .gitignore
├── LICENSE
├── README.md
├── README.md.txt
├── gconfig.py
├── ljd/
│   ├── __init__.py
│   ├── ast/
│   │   ├── __init__.py
│   │   ├── builder.py
│   │   ├── helpers.py
│   │   ├── locals.py
│   │   ├── mutator.py
│   │   ├── nodes.py
│   │   ├── slotworks.py
│   │   ├── traverse.py
│   │   ├── unwarper.py
│   │   └── validator.py
│   ├── bytecode/
│   │   ├── __init__.py
│   │   ├── constants.py
│   │   ├── debuginfo.py
│   │   ├── helpers.py
│   │   ├── instructions.py
│   │   └── prototype.py
│   ├── lua/
│   │   ├── __init__.py
│   │   └── writer.py
│   ├── pseudoasm/
│   │   ├── __init__.py
│   │   ├── constants.py
│   │   ├── instructions.py
│   │   ├── prototype.py
│   │   └── writer.py
│   ├── rawdump/
│   │   ├── __init__.py
│   │   ├── code.py
│   │   ├── constants.py
│   │   ├── debuginfo.py
│   │   ├── header.py
│   │   ├── parser.py
│   │   └── prototype.py
│   └── util/
│       ├── __init__.py
│       ├── binstream.py
│       ├── indentedstream.py
│       └── log.py
├── main.py
└── test/
    ├── breaks.lua
    ├── expression.lua
    ├── ifs.lua
    ├── loop.lua
    └── primitive.lua

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.lua[co]
*.py[cod]

# C extensions
*.so

# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
lib
lib64
__pycache__

# Installer logs
pip-log.txt

# Unit test / coverage reports
.coverage
.tox
nosetests.xml

# Translations
*.mo

# Mr Developer
.mr.developer.cfg
.project
.pydevproject
.*.swp


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2013 Andrian Nord

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
代码在fork https://github.com/NightNord/ljd 的基础上修改的

主要修正了原版本没有将_read_header函数解析过的header中的flag值带进_read_protoypes。

解决了原代码只能解析带调试信息的luajit字节码,而解析不带调试信息字节码时报错的问题。

此版本可解析luajit2.1.0-beta2的字节码文件。如果要解析其它版本的luajit字节码文件,需要将

luajit源码中的opcode,写进instruction.py 和 code.py


使用说明:python main.py  "path of luajit-bytecode"

解释文档:http://www.freebuf.com/column/177810.html

参考:https://github.com/NightNord/ljd
      https://github.com/feicong/lua_re/blob/master/lua/lua_re3.md
	  https://bbs.pediy.com/thread-216800.htm

================================================
FILE: README.md.txt
================================================
LuaJIT raw-bytecode decompiler (LJD)
===

The original name was _ljwthgnd_ as in _LuaJIT 'What The Hell is Going On'
Decompiler_ named under the LuaJIT C sources variable naming convention.


__WARNING!__ This code is nor finished, nor tested yet! There is no even
slightest warranty that resulting code is even near to the original. Use it at
your own risk of the wasted time.

__SECOND WARNING!__ And, BTW, this all is a one huge prototype. Because the
"release" version should be written into lua itself. Because it's cool to
decompile the decompiler - a great test too!

Requirements
---

Python __3.0+__ from python.org


How to use it
---

There is no argument parsing right now, so comment out things in the ```main.py```
script and launch it as in ```main.py path/to/file.luac```

TODO
---

There is a lot of work to do, in the order of priority

0. Logical subexpressions in while statements:
	```lua
		while x < (xi and 2 or 3) do
			print ("Hello crazy world!")
		end
	```

	Logical subexpressions (the subexpressions used as operands in
	ariphmetic or comparison operations inside other exrpressions) are
	currently supported only for ifs. To support them for whiles and
	repeat untils an expression unwarping logic should be moved at the
	very beginning. But it won't work without all fixes being done in
	a loop unwarping logic. So we need to split that and move the fixes 
	before expressions before loops before ifs. That's not that easy...

1. AST Mutations:
	1. Use the line information (or common sense if there is no line
	   information) to squash similar expressions into a single expression.

2. Formatting improvements
	1. Use the line information (or common sense) to preserve empty lines
	   and break long statements like in the original code.
	   
	   This is mostly done, but only in the "common sense" part.

	2. Use method-style calls and definitions for tables.

3. Features not supported:
	1. GOTO statement (from lua 5.2). All the required functionality is
		now in place, but that's rather a low-priority task right now.

	2. Local sub-blocks:
	```lua
	do
		...
	end
	```
	   These subblocks are not reflected anyhow directly in the bytecode.
	   The only way to guess them is to watch local variable scopes, which
	   is simple enough in case of non-stripped bytecode and a bit
	   harder otherwise.


================================================
FILE: gconfig.py
================================================

#zzw 20180714 support str encode
gFlagDic = {'strEncode' : 'ascii'}

================================================
FILE: ljd/__init__.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#



================================================
FILE: ljd/ast/__init__.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#



================================================
FILE: ljd/ast/builder.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import ljd.bytecode.instructions as ins

from ljd.bytecode.helpers import get_jump_destination
from ljd.bytecode.constants import T_NIL, T_FALSE, T_TRUE

import ljd.ast.nodes as nodes


class _State():
	def __init__(self):
		self.constants = None
		self.debuginfo = None
		self.block = None
		self.blocks = []
		self.block_starts = {}

	def _warp_in_block(self, addr):
		#print (self.block_starts)
		#print (addr)
		block = self.block_starts[addr]
		block.warpins_count += 1
		return block


def build(prototype):
	return _build_function_definition(prototype)


def _build_function_definition(prototype):
	node = nodes.FunctionDefinition()

	state = _State()

	state.constants = prototype.constants
	state.debuginfo = prototype.debuginfo

	node._upvalues = prototype.constants.upvalue_references
	node._debuginfo = prototype.debuginfo
	node._instructions_count = len(prototype.instructions)

	node.arguments.contents = _build_function_arguments(state, prototype)

	if prototype.flags.is_variadic:
		node.arguments.contents.append(nodes.Vararg())

	instructions = prototype.instructions
	#print (len(instructions))
	node.statements.contents = _build_function_blocks(state, instructions)

	return node


def _build_function_arguments(state, prototype):
	arguments = []

	count = prototype.arguments_count

	slot = 0
	while slot < count:
		variable = _build_slot(state, 0, slot)

		arguments.append(variable)
		slot += 1

	return arguments


def _build_function_blocks(state, instructions):
	_blockenize(state, instructions)
	_establish_warps(state, instructions)

	state.blocks[0].warpins_count = 1

	for block in state.blocks:
		addr = block.first_address
		state.block = block

		while addr <= block._last_body_addr:
			instruction = instructions[addr]

			statement = _build_statement(state, addr, instruction)

			if statement is not None:
				line = state.debuginfo.lookup_line_number(addr)

				setattr(statement, "_addr", addr)
				setattr(statement, "_line", line)

				block.contents.append(statement)

			addr += 1

	return state.blocks


_JUMP_WARP_INSTRUCTIONS = set((
	ins.UCLO.opcode,
	ins.ISNEXT.opcode,
	ins.JMP.opcode,
	ins.FORI.opcode,
	ins.JFORI.opcode
))


_WARP_INSTRUCTIONS = _JUMP_WARP_INSTRUCTIONS | set((
	ins.FORL.opcode, ins.IFORL.opcode, ins.JFORL.opcode,
	ins.ITERL.opcode, ins.IITERL.opcode, ins.JITERL.opcode,
	ins.LOOP.opcode
))


def _blockenize(state, instructions):
	addr = 1

	# Duplicates are possible and ok, but we need to sort them out
	last_addresses = set()

	while addr < len(instructions):
		instruction = instructions[addr]
		opcode = instruction.opcode

		if opcode not in _WARP_INSTRUCTIONS:
			addr += 1
			continue

		if opcode in _JUMP_WARP_INSTRUCTIONS:
			destination = get_jump_destination(addr, instruction)

			if opcode != ins.UCLO.opcode or destination != addr + 1:
				last_addresses.add(destination - 1)
				last_addresses.add(addr)
		else:
			last_addresses.add(addr)

		addr += 1

	last_addresses = sorted(list(last_addresses))
	last_addresses.append(len(instructions) - 1)

	# This could happen if something jumps to the first instruction
	# We don't need "zero block" with function header, so simply ignore
	# this
	if last_addresses[0] == 0:
		last_addresses.pop(0)

	previous_last_address = 0

	index = 0
	for last_address in last_addresses:
		block = nodes.Block()
		block.index = index
		block.first_address = previous_last_address + 1
		block.last_address = last_address

		state.blocks.append(block)
		state.block_starts[block.first_address] = block

		previous_last_address = last_address

		index += 1


def _establish_warps(state, instructions):
	state.blocks[0].warpins_count = 1

	for block in state.blocks[:-1]:
		state.block = block

		end_addr = block.last_address + 1
		start_addr = max(block.last_address - 1, block.first_address)

		warp = instructions[start_addr:end_addr]
		#print (block.first_address)
		block.warp, shift = _build_warp(state, block.last_address, warp)

		setattr(block, "_last_body_addr", block.last_address - shift)
		setattr(block.warp, "_addr", block.last_address - shift + 1)

	last_block = state.blocks[-1]
	last_block.warp = nodes.EndWarp()

	setattr(last_block, "_last_body_addr", last_block.last_address)
	setattr(last_block.warp, "_addr", last_block.last_address)


def _build_warp(state, last_addr, instructions):
	last = instructions[-1]
	#print ("%x"%(last.opcode))
	if last.opcode in (ins.JMP.opcode, ins.UCLO.opcode, ins.ISNEXT.opcode):
		return _build_jump_warp(state, last_addr, instructions)

	elif last.opcode >= ins.ITERL.opcode and last.opcode <= ins.JITERL.opcode:
		assert len(instructions) == 2
		return _build_iterator_warp(state, last_addr, instructions)

	elif last.opcode >= ins.FORL.opcode and last.opcode <= ins.JFORL.opcode:
		return _build_numeric_loop_warp(state, last_addr, last)

	else:
		return _build_flow_warp(state, last_addr, last)


def _build_jump_warp(state, last_addr, instructions):
	last = instructions[-1]
	opcode = 256 if len(instructions) == 1 else instructions[-2].opcode

	if opcode <= ins.ISF.opcode:
		assert last.opcode != ins.ISNEXT.opcode
		return _build_conditional_warp(state, last_addr, instructions)
	else:
		return _build_unconditional_warp(state, last_addr, last)


def _build_conditional_warp(state, last_addr, instructions):
	condition = instructions[-2]
	condition_addr = last_addr - 1

	warp = nodes.ConditionalWarp()

	if condition.opcode in (ins.ISTC.opcode, ins.ISFC.opcode):
		expression = _build_unary_expression(state,
							condition_addr,
							condition)

		setattr(warp, "_slot", condition.A)
	elif condition.opcode >= ins.IST.opcode:
		expression = _build_unary_expression(state,
							condition_addr,
							condition)

		setattr(warp, "_slot", condition.CD)
	else:
		expression = _build_comparison_expression(state,
							condition_addr,
							condition)

	warp.condition = expression

	jump = instructions[-1]
	jump_addr = last_addr

	destination = get_jump_destination(jump_addr, jump)

	# A condition is inverted during the preparation phase above
	warp.false_target = state._warp_in_block(destination)
	warp.true_target = state._warp_in_block(jump_addr + 1)

	return warp, 2


def _build_unconditional_warp(state, addr, instruction):
	warp = nodes.UnconditionalWarp()
	warp.type = nodes.UnconditionalWarp.T_JUMP

	opcode = instruction.opcode

	warp.is_uclo = opcode == ins.UCLO.opcode

	if warp.is_uclo and instruction.CD == 0:
		# Not a jump
		return _build_flow_warp(state, addr, instruction)
	else:
		destination = get_jump_destination(addr, instruction)
		warp.target = state._warp_in_block(destination)

	return warp, 1


def _build_iterator_warp(state, last_addr, instructions):
	iterator = instructions[-2]
	iterator_addr = last_addr - 1

	assert iterator.opcode in (ins.ITERC.opcode, ins.ITERN.opcode)

	warp = nodes.IteratorWarp()

	base = iterator.A

	warp.controls.contents = [
		_build_slot(state, iterator_addr, base - 3),  # generator
		_build_slot(state, iterator_addr, base - 2),  # state
		_build_slot(state, iterator_addr, base - 1)  # control
	]

	last_slot = base + iterator.B - 2

	slot = base

	while slot <= last_slot:
		variable = _build_slot(state, iterator_addr - 1, slot)
		warp.variables.contents.append(variable)
		slot += 1

	jump = instructions[-1]
	jump_addr = last_addr

	destination = get_jump_destination(jump_addr, jump)
	warp.way_out = state._warp_in_block(jump_addr + 1)
	warp.body = state._warp_in_block(destination)

	return warp, 2


def _build_numeric_loop_warp(state, addr, instruction):
	warp = nodes.NumericLoopWarp()

	base = instruction.A

	warp.index = _build_slot(state, addr, base + 3)
	warp.controls.contents = [
		_build_slot(state, addr, base + 0),  # start
		_build_slot(state, addr, base + 1),  # limit
		_build_slot(state, addr, base + 2)  # step
	]
	#print ("%x" % instruction.A)
	#print ("%x" %instruction.CD)
	destination = get_jump_destination(addr, instruction)
	warp.body = state._warp_in_block(destination)
	warp.way_out = state._warp_in_block(addr + 1)

	return warp, 1


def _build_flow_warp(state, addr, instruction):
	warp = nodes.UnconditionalWarp()
	warp.type = nodes.UnconditionalWarp.T_FLOW
	warp.target = state._warp_in_block(addr + 1)

	opcode = instruction.opcode
	shift = 1 if opcode in (ins.FORI.opcode, ins.UCLO.opcode) else 0

	return warp, shift


def _build_statement(state, addr, instruction):
	opcode = instruction.opcode
	A_type = instruction.A_type

	# Generic assignments - handle the ASSIGNMENT stuff below
	if A_type == ins.T_DST or A_type == ins.T_UV:
		return _build_var_assignment(state, addr, instruction)

	# ASSIGNMENT starting from MOV and ending at KPRI

	elif opcode == ins.KNIL.opcode:
		return _build_knil(state, addr, instruction)

	# ASSIGNMENT starting from UGET and ending at USETP

	# SKIP UCL0 is handled below

	# ASSIGNMENT starting from FNEW and ending at GGET

	elif opcode == ins.GSET.opcode:
		return _build_global_assignment(state, addr, instruction)

	# ASSIGNMENT starting from TGETV and ending at TGETB

	elif opcode >= ins.TSETV.opcode and opcode <= ins.TSETB.opcode:
		return _build_table_assignment(state, addr, instruction)

	elif opcode == ins.TSETM.opcode:
		return _build_table_mass_assignment(state, addr, instruction)

	elif opcode >= ins.CALLM.opcode and opcode <= ins.CALLT.opcode:
		return _build_call(state, addr, instruction)

	elif opcode == ins.VARG.opcode:
		return _build_vararg(state, addr, instruction)

	elif opcode >= ins.RETM.opcode and opcode <= ins.RET1.opcode:
		return _build_return(state, addr, instruction)

	else:
		assert opcode == ins.UCLO.opcode or (
				opcode >= ins.LOOP.opcode
						and opcode <= ins.JLOOP.opcode)
		# Noop
		return None


def _build_var_assignment(state, addr, instruction):
	opcode = instruction.opcode

	assignment = nodes.Assignment()

	# Unary assignment operators (A = op D)
	if opcode == ins.MOV.opcode			\
			or opcode == ins.NOT.opcode	\
			or opcode == ins.UNM.opcode	\
			or opcode == ins.LEN.opcode:
		expression = _build_unary_expression(state, addr, instruction)

	# Binary assignment operators (A = B op C)
	elif opcode <= ins.POW.opcode:
		expression = _build_binary_expression(state, addr, instruction)

	# Concat assignment type (A = B .. B + 1 .. ... .. C - 1 .. C)
	elif opcode == ins.CAT.opcode:
		expression = _build_concat_expression(state, addr, instruction)

	# Constant assignment operators except KNIL, which is weird anyway
	elif opcode <= ins.KPRI.opcode:
		expression = _build_const_expression(state, addr, instruction)

	elif opcode == ins.UGET.opcode:
		expression = _build_upvalue(state, addr, instruction.CD)

	elif opcode == ins.USETV.opcode:
		expression = _build_slot(state, addr, instruction.CD)

	elif opcode <= ins.USETP.opcode:
		expression = _build_const_expression(state, addr, instruction)

	elif opcode == ins.FNEW.opcode:
		expression = _build_function(state, instruction.CD)

	elif opcode == ins.TNEW.opcode:
		expression = nodes.TableConstructor()

	elif opcode == ins.TDUP.opcode:
		expression = _build_table_copy(state, instruction.CD)

	elif opcode == ins.GGET.opcode:
		expression = _build_global_variable(state, addr, instruction.CD)

	else:
		assert opcode <= ins.TGETB.opcode
		expression = _build_table_element(state, addr, instruction)

	assignment.expressions.contents.append(expression)

	if instruction.A_type == ins.T_DST:
		destination = _build_slot(state, addr, instruction.A)
	else:
		assert instruction.A_type == ins.T_UV

		destination = _build_upvalue(state, addr, instruction.A)

	assignment.destinations.contents.append(destination)

	return assignment


def _build_knil(state, addr, instruction):
	node = _build_range_assignment(state, addr, instruction.A, instruction.CD)

	node.expressions.contents = [_build_primitive(state, None)]

	return node


def _build_global_assignment(state, addr, instruction):
	assignment = nodes.Assignment()

	variable = _build_global_variable(state, addr, instruction.CD)
	expression = _build_slot(state, addr, instruction.A)

	assignment.destinations.contents.append(variable)
	assignment.expressions.contents.append(expression)

	return assignment


def _build_table_assignment(state, addr, instruction):
	assignment = nodes.Assignment()

	destination = _build_table_element(state, addr, instruction)
	expression = _build_slot(state, addr, instruction.A)

	assignment.destinations.contents.append(destination)
	assignment.expressions.contents.append(expression)

	return assignment


def _build_table_mass_assignment(state, addr, instruction):
	assignment = nodes.Assignment()

	base = instruction.A

	destination = nodes.TableElement()
	destination.key = nodes.MULTRES()
	destination.table = _build_slot(state, addr, base - 1)

	assignment.destinations.contents = [destination]
	assignment.expressions.contents = [nodes.MULTRES()]

	return assignment


def _build_call(state, addr, instruction):
	call = nodes.FunctionCall()
	call.function = _build_slot(state, addr, instruction.A)
	call.arguments.contents = _build_call_arguments(state, addr, instruction)

	if instruction.opcode <= ins.CALL.opcode:
		if instruction.B == 0:
			node = nodes.Assignment()
			node.destinations.contents.append(nodes.MULTRES())
			node.expressions.contents.append(call)
		elif instruction.B == 1:
			node = call
		else:
			from_slot = instruction.A
			to_slot = instruction.A + instruction.B - 2
			node = _build_range_assignment(state, addr, from_slot,
									to_slot)
			node.expressions.contents.append(call)
	else:
		assert instruction.opcode <= ins.CALLT.opcode
		node = nodes.Return()
		node.returns.contents.append(call)

	return node


def _build_vararg(state, addr, instruction):
	base = instruction.A
	last_slot = base + instruction.B - 2

	if last_slot < base:
		node = nodes.Assignment()
		node.destinations.contents.append(nodes.MULTRES())
		node.expressions.contents.append(nodes.Vararg())
	else:
		node = _build_range_assignment(state, addr, base, last_slot)
		node.expressions.contents.append(nodes.Vararg())

	return node


def _build_return(state, addr, instruction):
	node = nodes.Return()

	base = instruction.A
	last_slot = base + instruction.CD - 1

	if instruction.opcode != ins.RETM.opcode:
		last_slot -= 1

	slot = base

	# Negative count for the RETM is OK
	while slot <= last_slot:
		variable = _build_slot(state, addr, slot)
		node.returns.contents.append(variable)
		slot += 1

	if instruction.opcode == ins.RETM.opcode:
		node.returns.contents.append(nodes.MULTRES())

	return node


def _build_call_arguments(state, addr, instruction):
	base = instruction.A
	last_argument_slot = base + instruction.CD

	is_variadic = (instruction.opcode == ins.CALLM.opcode	\
			or instruction.opcode == ins.CALLMT.opcode)

	if not is_variadic:
		last_argument_slot -= 1

	arguments = []

	slot = base + 1

	while slot <= last_argument_slot:
		argument = _build_slot(state, addr, slot)
		arguments.append(argument)
		slot += 1

	if is_variadic:
		arguments.append(nodes.MULTRES())

	return arguments


def _build_range_assignment(state, addr, from_slot, to_slot):
	assignment = nodes.Assignment()

	slot = from_slot

	assert from_slot <= to_slot

	while slot <= to_slot:
		destination = _build_slot(state, addr, slot)

		assignment.destinations.contents.append(destination)

		slot += 1

	return assignment


_BINARY_OPERATOR_MAP = [None] * 255

_BINARY_OPERATOR_MAP[ins.ADDVN.opcode] = nodes.BinaryOperator.T_ADD
_BINARY_OPERATOR_MAP[ins.SUBVN.opcode] = nodes.BinaryOperator.T_SUBTRACT
_BINARY_OPERATOR_MAP[ins.MULVN.opcode] = nodes.BinaryOperator.T_MULTIPLY
_BINARY_OPERATOR_MAP[ins.DIVVN.opcode] = nodes.BinaryOperator.T_DIVISION
_BINARY_OPERATOR_MAP[ins.MODVN.opcode] = nodes.BinaryOperator.T_MOD


def _build_binary_expression(state, addr, instruction):
	operator = nodes.BinaryOperator()
	opcode = instruction.opcode

	if opcode == ins.POW.opcode:
		operator.type = nodes.BinaryOperator.T_POW
	else:
		map_index = opcode - ins.ADDVN.opcode
		map_index %= 5
		map_index += ins.ADDVN.opcode

		operator.type = _BINARY_OPERATOR_MAP[map_index]

	if instruction.B_type == ins.T_VAR and instruction.CD_type == ins.T_VAR:
		operator.left = _build_slot(state, addr, instruction.B)
		operator.right = _build_slot(state, addr, instruction.CD)
	elif instruction.B_type == ins.T_VAR:
		operator.left = _build_slot(state, addr, instruction.B)
		operator.right = _build_numeric_constant(state, instruction.CD)
	else:
		assert instruction.CD_type == ins.T_VAR

		operator.right = _build_slot(state, addr, instruction.B)
		operator.left = _build_numeric_constant(state, instruction.CD)

	return operator


def _build_concat_expression(state, addr, instruction):
	operator = nodes.BinaryOperator()
	operator.type = nodes.BinaryOperator.T_CONCAT

	slot = instruction.B

	operator.left = _build_slot(state, addr, slot)
	operator.right = _build_slot(state, addr, slot + 1)

	slot += 2

	while slot <= instruction.CD:
		upper_operator = nodes.BinaryOperator()
		upper_operator.left = operator
		upper_operator.right = _build_slot(state, addr, slot)
		upper_operator.type = nodes.BinaryOperator.T_CONCAT

		operator = upper_operator

		slot += 1

	return operator


def _build_const_expression(state, addr, instruction):
	CD_type = instruction.CD_type

	if CD_type == ins.T_STR:
		return _build_string_constant(state, instruction.CD)
	elif CD_type == ins.T_CDT:
		return _build_cdata_constant(state, instruction.CD)
	elif CD_type == ins.T_SLIT:
		value = instruction.CD

		if value & 0x8000:
			value = -0x10000 + value

		return _build_literal(state, value)
	elif CD_type == ins.T_LIT:
		return _build_literal(state, instruction.CD)
	elif CD_type == ins.T_NUM:
		return _build_numeric_constant(state, instruction.CD)
	else:
		assert CD_type == ins.T_PRI
		return _build_primitive(state, instruction.CD)


def _build_table_element(state, addr, instruction):
	node = nodes.TableElement()
	node.table = _build_slot(state, addr, instruction.B)

	if instruction.CD_type == ins.T_VAR:
		node.key = _build_slot(state, addr, instruction.CD)
	else:
		node.key = _build_const_expression(state, addr, instruction)

	return node


def _build_function(state, slot):
	prototype = state.constants.complex_constants[slot]

	return _build_function_definition(prototype)


def _build_table_copy(state, slot):
	node = nodes.TableConstructor()

	table = state.constants.complex_constants[slot]

	i = 0

	for value in table.array:
		record = nodes.ArrayRecord()
		record.value = _build_table_record_item(value)

		node.array.contents.append(record)

		i += 1

	for key, value in table.dictionary:
		record = nodes.TableRecord()
		record.key = _build_table_record_item(key)
		record.value = _build_table_record_item(value)

		node.records.contents.append(record)

	return node


def _build_table_record_item(value):
	if value is None:
		item = nodes.Primitive()
		item.type = nodes.Primitive.T_NIL
	elif value is True:
		item = nodes.Primitive()
		item.type = nodes.Primitive.T_TRUE
	elif value is False:
		item = nodes.Primitive()
		item.type = nodes.Primitive.T_FALSE
	elif isinstance(value, int):
		item = nodes.Constant()
		item.value = value
		item.type = nodes.Constant.T_INTEGER
	elif isinstance(value, float):
		item = nodes.Constant()
		item.value = value
		item.type = nodes.Constant.T_FLOAT
	elif isinstance(value, str):
		item = nodes.Constant()
		item.value = value
		item.type = nodes.Constant.T_STRING

	return item


_COMPARISON_MAP = [None] * 255

# Mind the inversion - comparison operators are affecting JMP to the next block
# So in the normal code a comparison will be inverted
_COMPARISON_MAP[ins.ISLT.opcode] = nodes.BinaryOperator.T_GREATER_OR_EQUAL
_COMPARISON_MAP[ins.ISGE.opcode] = nodes.BinaryOperator.T_LESS_THEN
_COMPARISON_MAP[ins.ISLE.opcode] = nodes.BinaryOperator.T_GREATER_THEN
_COMPARISON_MAP[ins.ISGT.opcode] = nodes.BinaryOperator.T_LESS_OR_EQUAL

_COMPARISON_MAP[ins.ISEQV.opcode] = nodes.BinaryOperator.T_NOT_EQUAL
_COMPARISON_MAP[ins.ISNEV.opcode] = nodes.BinaryOperator.T_EQUAL

_COMPARISON_MAP[ins.ISEQS.opcode] = nodes.BinaryOperator.T_NOT_EQUAL
_COMPARISON_MAP[ins.ISNES.opcode] = nodes.BinaryOperator.T_EQUAL

_COMPARISON_MAP[ins.ISEQN.opcode] = nodes.BinaryOperator.T_NOT_EQUAL
_COMPARISON_MAP[ins.ISNEN.opcode] = nodes.BinaryOperator.T_EQUAL

_COMPARISON_MAP[ins.ISEQP.opcode] = nodes.BinaryOperator.T_NOT_EQUAL
_COMPARISON_MAP[ins.ISNEP.opcode] = nodes.BinaryOperator.T_EQUAL


def _build_comparison_expression(state, addr, instruction):
	operator = nodes.BinaryOperator()

	operator.left = _build_slot(state, addr, instruction.A)

	opcode = instruction.opcode

	if opcode == ins.ISEQS.opcode or opcode == ins.ISNES.opcode:
		operator.right = _build_string_constant(state, instruction.CD)
	elif opcode == ins.ISEQN.opcode	or opcode == ins.ISNEN.opcode:
		operator.right = _build_numeric_constant(state, instruction.CD)
	elif opcode == ins.ISEQP.opcode or opcode == ins.ISNEP.opcode:
		operator.right = _build_primitive(state, instruction.CD)
	else:
		operator.right = _build_slot(state, addr, instruction.CD)

	operator.type = _COMPARISON_MAP[instruction.opcode]
	assert operator.type is not None

	return operator


def _build_unary_expression(state, addr, instruction):
	opcode = instruction.opcode

	variable = _build_slot(state, addr, instruction.CD)

	# Mind the inversion
	if opcode == ins.ISFC.opcode			\
			or opcode == ins.ISF.opcode	\
			or opcode == ins.MOV.opcode:
		return variable

	operator = nodes.UnaryOperator()
	operator.operand = variable

	if opcode == ins.ISTC.opcode			\
			or opcode == ins.IST.opcode	\
			or opcode == ins.NOT.opcode:
		operator.type = nodes.UnaryOperator.T_NOT
	elif opcode == ins.UNM.opcode:
		operator.type = nodes.UnaryOperator.T_MINUS
	else:
		assert opcode == ins.LEN.opcode
		operator.type = nodes.UnaryOperator.T_LENGTH_OPERATOR

	return operator


def _build_slot(state, addr, slot):
	return _build_identifier(state, addr, slot, nodes.Identifier.T_LOCAL)


def _build_upvalue(state, addr, slot):
	return _build_identifier(state, addr, slot, nodes.Identifier.T_UPVALUE)


def _build_identifier(state, addr, slot, want_type):
	node = nodes.Identifier()
	setattr(node, "_addr", addr)

	node.slot = slot
	node.type = nodes.Identifier.T_SLOT

	if want_type == nodes.Identifier.T_UPVALUE:
		name = state.debuginfo.lookup_upvalue_name(slot)

		if name is not None:
			node.name = name
			node.type = want_type

	return node


def _build_global_variable(state, addr, slot):
	node = nodes.TableElement()
	node.table = nodes.Identifier()
	node.table.type = nodes.Identifier.T_BUILTIN
	node.table.name = "_env"

	node.key = _build_string_constant(state, slot)

	return node


def _build_string_constant(state, index):
	node = nodes.Constant()
	node.type = nodes.Constant.T_STRING
	node.value = state.constants.complex_constants[index]

	return node


def _build_cdata_constant(state, index):
	node = nodes.Constant()
	node.type = nodes.Constant.T_CDATA
	node.value = state.constants.complex_constants[index]

	return node


def _build_numeric_constant(state, index):
	number = state.constants.numeric_constants[index]

	node = nodes.Constant()
	node.value = number

	if isinstance(number, int):
		node.type = nodes.Constant.T_INTEGER
	else:
		node.type = nodes.Constant.T_FLOAT

	return node


def _build_primitive(state, value):
	node = nodes.Primitive()

	if value is True or value == T_TRUE:
		node.type = nodes.Primitive.T_TRUE
	elif value is False or value == T_FALSE:
		node.type = nodes.Primitive.T_FALSE
	else:
		assert value is None or value == T_NIL

		node.type = nodes.Primitive.T_NIL

	return node


def _build_literal(state, value):
	node = nodes.Constant()
	node.value = value
	node.type = nodes.Constant.T_INTEGER

	return node


================================================
FILE: ljd/ast/helpers.py
================================================
import ljd.ast.nodes as nodes
import ljd.ast.traverse as traverse


def insert_table_record(constructor, key, value):
	array = constructor.array.contents
	records = constructor.records.contents

	if isinstance(key, nodes.MULTRES):
		assert len(records) == 0 \
			or isinstance(records[-1], nodes.TableRecord)

		records.append(value)
		return

	while isinstance(key, nodes.Constant)			\
				and key.type == key.T_INTEGER	\
				and key.value >= 0:
		index = key.value

		if index == 1 and len(array) == 0:
			record = nodes.ArrayRecord()
			record.value = nodes.Primitive()
			record.value.type = nodes.Primitive.T_NIL

			array.append(record)

		if (index > len(array)):
			break

		record = nodes.ArrayRecord()
		record.value = value

		if len(array) == 0 or index == len(array):
			array.append(record)
		else:
			array[index] = record

		return

	record = nodes.TableRecord()
	record.key = key
	record.value = value

	if len(records) == 0:
		records.append(record)
		return

	last = records[-1]

	if isinstance(last, (nodes.FunctionCall, nodes.Vararg)):
		records.insert(-1, record)
	else:
		records.append(record)


def has_same_table(node, table):
	class Checker(traverse.Visitor):
		def __init__(self, table):
			self.found = False
			self.table = table

		def visit_table_element(self, node):
			if is_equal(self.table, node):
				self.found = True

		def _visit(self, node):
			if not self.found:
				traverse.Visitor._visit(self, node)

		def _visit_list(self, nodes_list):
			if not self.found:
				traverse.Visitor._visit_list(self, nodes_list)

	checker = Checker(table)
	traverse.traverse(checker, node)

	return checker.found


def is_equal(a, b):
	if type(a) != type(b):
		return False

	if isinstance(a, nodes.Identifier):
		return a.type == b.type and a.slot == b.slot
	elif isinstance(a, nodes.TableElement):
		return is_equal(a.table, b.table)		\
			and is_equal(a.key, b.key)
	else:
		assert isinstance(a, nodes.Constant)
		return a.type == b.type and a.value == b.value


================================================
FILE: ljd/ast/locals.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#


import ljd.ast.nodes as nodes
import ljd.ast.traverse as traverse


def mark_locals(ast):
	traverse.traverse(_LocalsMarker(), ast)


def mark_local_definitions(ast):
	traverse.traverse(_LocalDefinitionsMarker(), ast)


class _LocalsMarker(traverse.Visitor):
	class _State():
		def __init__(self):
			self.pending_slots = {}
			self.debuginfo = None
			self.addr = -1

	def __init__(self):
		self._states = []

	# ##

	def _push_state(self):
		self._states.append(_LocalsMarker._State())

	def _pop_state(self):
		self._states.pop()

	def _state(self):
		return self._states[-1]

	def _process_slots(self, addr):
		debuginfo = self._state().debuginfo

		cleanup = []

		for slot, nodes in self._state().pending_slots.items():
			varinfo = debuginfo.lookup_local_name(addr, slot)

			if varinfo is None:
				continue

			cleanup.append(slot)

			if varinfo.type == varinfo.T_INTERNAL:
				continue

			for node in nodes:
				node.name = varinfo.name
				node.type = node.T_LOCAL

                #zzw.20180712
				setattr(node, "_varinfo", varinfo)

		for slot in cleanup:
			del self._state().pending_slots[slot]

	def _reset_slot(self, slot):
		self._state().pending_slots.pop(slot, None)

	def _reset_all(self, slots):
		for slot in slots:
			if isinstance(slot, nodes.Identifier):
				self._reset_slot(slot.slot)

	# ##

	def visit_function_definition(self, node):
		self._push_state()
		self._state().debuginfo = node._debuginfo

	def leave_function_definition(self, node):
		addr = node._instructions_count
		self._process_slots(addr)

		self._pop_state()

	# ##

	def visit_variables_list(self, node):
		# Last chance for a local = local + 1 type assignments
		self._process_slots(self._state().addr)
		self._reset_all(node.contents)

	def visit_identifiers_list(self, node):
		self._reset_all(node.contents)

	def visit_numeric_loop_warp(self, node):
		self._reset_slot(node.index.slot)

	def visit_identifier(self, node):
		if node.type == nodes.Identifier.T_SLOT:
			queue = self._state().pending_slots
			slots = queue.setdefault(node.slot, [])

			slots.append(node)

	# ##

	def _process_worthy_node(self, node):
		addr = getattr(node, "_addr", None)

		if not isinstance(node, nodes.Identifier) and addr is not None:
			assert self._state().addr <= addr
			self._state().addr = addr
			self._process_slots(addr)

	# We need to process slots twice as it could be the last
	# statement in the function/block and it could be anassignment
	# as well so we need to process slots before the reset

	def _leave_node(self, handler, node):
		traverse.Visitor._leave_node(self, handler, node)

		self._process_worthy_node(node)

	def _visit_node(self, handler, node):
		self._process_worthy_node(node)

		traverse.Visitor._visit_node(self, handler, node)


class _LocalDefinitionsMarker(traverse.Visitor):
	class _State():
		def __init__(self):
			self.known_locals = [None] * 255
			self.addr = 0

	def __init__(self):
		self._states = []

	def _push_state(self):
		self._states.append(_LocalDefinitionsMarker._State())

	def _pop_state(self):
		self._states.pop()

	def _state(self):
		return self._states[-1]

	def _update_known_locals(self, local, addr):
		varinfo = self._state().known_locals[local.slot]
		#zzw 20180712
		self._state().known_locals[local.slot] = getattr(local, "_varinfo", None)#local._varinfo

		if varinfo is None:
			return False

		if varinfo.end_addr <= addr:
			return False

		return True

	# ##

	def visit_function_definition(self, node):
		self._push_state()

		for local in node.arguments.contents:
			if not isinstance(local, nodes.Vararg):
				self._update_known_locals(local, 1)

	def leave_function_definition(self, node):
		self._pop_state()

	def visit_iterator_for(self, node):
		addr = node._addr

		for local in node.identifiers.contents:
			if local.type == nodes.Identifier.T_LOCAL:
				self._update_known_locals(local, addr)

	def visit_numeric_for(self, node):
		addr = node._addr

		if node.variable.type == nodes.Identifier.T_LOCAL:
			self._update_known_locals(node.variable, addr)

	# ##

	def visit_assignment(self, node):
		dst = node.destinations.contents[0]
		addr = self._state().addr

		if not isinstance(dst, nodes.Identifier):
			return

		if dst.type != nodes.Identifier.T_LOCAL:
			return

		known_slot = self._update_known_locals(dst, addr)

		for slot in node.destinations.contents[1:]:
			if not isinstance(slot, nodes.Identifier):
				return

			if slot.type != nodes.Identifier.T_LOCAL:
				return

			also_known = self._update_known_locals(slot, addr)

			assert known_slot == also_known

		if not known_slot:
			node.type = nodes.Assignment.T_LOCAL_DEFINITION

	def _visit(self, node):
		node_addr = getattr(node, "_addr", -1)

		if node_addr >= 0:
			self._state().addr = node_addr

		traverse.Visitor._visit(self, node)


================================================
FILE: ljd/ast/mutator.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import copy

from ljd.ast.helpers import *


class SimpleLoopWarpSwapper(traverse.Visitor):
	def visit_statements_list(self, node):
		blocks = node.contents

		fixed = []
		index_shift = 0

		for i, block in enumerate(node.contents):
			warp = block.warp
			fixed.append(block)

			block.index += index_shift

			if isinstance(warp, nodes.IteratorWarp):
				self._swap_iterator_warps(blocks, block)
				continue

			if isinstance(warp, nodes.NumericLoopWarp):
				self._swap_numeric_loop_warps(blocks, block)
				continue

			if isinstance(warp, nodes.UnconditionalWarp)	\
							and warp.is_uclo:
				assert block != node.contents[-1]
				next_block = node.contents[i + 1]
				self._fix_uclo_return(block, next_block)

			if not isinstance(warp, nodes.ConditionalWarp):
				continue

			if warp.true_target != warp.false_target:
				continue

			slot = getattr(warp, "_slot", -1)

			if slot < 0:
				continue

			next_index = block.index - index_shift + 1
			assert block.warp.false_target.index == next_index

			new_block = self._create_dummy_block(block, slot)

			fixed.append(new_block)

			index_shift += 1

		node.contents = fixed

	def _create_dummy_block(self, block, slot):
		new_block = nodes.Block()
		new_block.first_address = block.last_address
		new_block.last_address = new_block.first_address
		new_block.index = block.index + 1
		new_block.warpins_count = 1

		new_block.warp = nodes.UnconditionalWarp()
		new_block.warp.type = nodes.UnconditionalWarp.T_FLOW
		new_block.warp.target = block.warp.false_target

		statement = nodes.Assignment()

		identifier = nodes.Identifier()
		identifier.type = nodes.Identifier.T_SLOT
		identifier.slot = slot

		statement.destinations.contents.append(identifier)
		statement.expressions.contents.append(copy.copy(identifier))

		new_block.contents.append(statement)

		block.warp.true_target = new_block

		return new_block

	def _fix_uclo_return(self, block, next_block):
		warp = block.warp
		target = warp.target

		if len(target.contents) != 1:
			return

		statement = target.contents[0]

		if not isinstance(statement, nodes.Return):
			return

		block.contents.append(statement)
		statement._addr = block.last_address
		target.contents = []

		warp.type = nodes.UnconditionalWarp.T_FLOW
		warp.target = next_block

	def _swap_iterator_warps(self, blocks, end):
		warp = end.warp
		index = blocks.index(warp.body)

		assert index > 0

		start = blocks[index - 1]

		assert isinstance(start.warp, nodes.UnconditionalWarp)
		assert start.warp.type == nodes.UnconditionalWarp.T_JUMP
		assert start.warp.target == end

		end_addr = end.warp._addr
		start_addr = start.warp._addr

		new_end_warp = start.warp
		new_end_warp._addr = end_addr

		new_start_warp = end.warp
		new_start_warp._addr = start_addr

		end.warp = new_end_warp
		start.warp = new_start_warp

		new_end_warp.target = start

	def _swap_numeric_loop_warps(self, blocks, end):
		warp = end.warp
		index = blocks.index(warp.body)

		assert index > 0

		start = blocks[index - 1]

		assert isinstance(start.warp, nodes.UnconditionalWarp)
		assert start.warp.type == nodes.UnconditionalWarp.T_FLOW
		assert start.warp.target == warp.body

		end_addr = end.warp._addr
		start_addr = start.warp._addr

		new_end_warp = start.warp
		new_end_warp._addr = end_addr

		new_start_warp = end.warp
		new_start_warp._addr = start_addr

		end.warp = new_end_warp
		start.warp = new_start_warp

		new_end_warp.type = nodes.UnconditionalWarp.T_JUMP
		new_end_warp.target = start


class MutatorVisitor(traverse.Visitor):
	# ##

	def leave_if(self, node):
		if len(node.else_block.contents) != 1:
			return

		subif = node.else_block.contents[0]

		if not isinstance(subif, nodes.If):
			return

		elseif = nodes.ElseIf()
		elseif.expression = subif.expression
		elseif.then_block = subif.then_block

		node.elseifs.append(elseif)
		node.elseifs += subif.elseifs
		node.else_block = subif.else_block

	def visit_statements_list(self, node):
		patched = []

		i = -1

		while i < len(node.contents) - 1:
			i += 1
			statement = node.contents[i]

			patched.append(statement)

			if not isinstance(statement, nodes.Assignment):
				continue

			src = statement.expressions.contents[0]

			if not isinstance(src, nodes.TableConstructor):
				continue

			assert len(statement.destinations.contents) == 1

			dst = statement.destinations.contents[0]

			i += self._fill_constructor(dst, src, node.contents[i + 1:])

		node.contents = patched

	def _fill_constructor(self, table, constructor, statements):
		consumed = 0

		for statement in statements:
			if not isinstance(statement, nodes.Assignment):
				break

			if len(statement.destinations.contents) > 1:
				break

			dst = statement.destinations.contents[0]

			if not isinstance(dst, nodes.TableElement):
				break

			if not is_equal(dst.table, table):
				break

			assert len(statement.expressions.contents) == 1

			src = statement.expressions.contents[0]

			if has_same_table(src, table):
				break

			insert_table_record(constructor, dst.key, src)
			consumed += 1

		return consumed


def pre_pass(ast):
	traverse.traverse(SimpleLoopWarpSwapper(), ast)

	return ast


def primary_pass(ast):
	traverse.traverse(MutatorVisitor(), ast)

	return ast


================================================
FILE: ljd/ast/nodes.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#


# We should visit stuff in it's execution order. That's important


class FunctionDefinition():
	def __init__(self):
		self.arguments = IdentifiersList()
		self.statements = StatementsList()

		self._upvalues = None
		self._debuginfo = None
		self._instructions_count = 0

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_function_definition, self)

		visitor._visit(self.arguments)
		visitor._visit(self.statements)

		visitor._leave_node(visitor.leave_function_definition, self)


class TableConstructor():
	def __init__(self):
		self.array = RecordsList()
		self.records = RecordsList()

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_table_constructor, self)

		visitor._visit(self.array)
		visitor._visit(self.records)

		visitor._leave_node(visitor.leave_table_constructor, self)


class ArrayRecord():
	def __init__(self):
		self.value = None

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_array_record, self)

		visitor._visit(self.value)

		visitor._leave_node(visitor.leave_array_record, self)


class TableRecord():
	def __init__(self):
		self.key = None
		self.value = None

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_table_record, self)

		visitor._visit(self.key)
		visitor._visit(self.value)

		visitor._leave_node(visitor.leave_table_record, self)


class Assignment():
	T_LOCAL_DEFINITION = 0
	T_NORMAL = 1

	def __init__(self):
		self.expressions = ExpressionsList()
		self.destinations = VariablesList()
		self.type = -1

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_assignment, self)

		visitor._visit(self.expressions)
		visitor._visit(self.destinations)

		visitor._leave_node(visitor.leave_assignment, self)


class BinaryOperator():
	T_LOGICAL_OR = 0  # left or right
	T_LOGICAL_AND = 10  # left and right

	T_LESS_THEN = 20  # left < right
	T_GREATER_THEN = 21  # left > right
	T_LESS_OR_EQUAL = 22  # left <= right
	T_GREATER_OR_EQUAL = 23  # left >= right

	T_NOT_EQUAL = 24  # left ~= right
	T_EQUAL = 25  # left == right

	T_CONCAT = 30  # left .. right

	T_ADD = 40  # left + right
	T_SUBTRACT = 41  # left - right

	T_MULTIPLY = 50  # left * right
	T_DIVISION = 51  # left / right
	T_MOD = 52  # left % right

	T_POW = 70  # left ^ right

	def __init__(self):
		self.type = -1
		self.left = None
		self.right = None

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_binary_operator, self)

		visitor._visit(self.left)
		visitor._visit(self.right)

		visitor._leave_node(visitor.leave_binary_operator, self)


class UnaryOperator():
	T_NOT = 60  # not operand
	T_LENGTH_OPERATOR = 61  # #operand
	T_MINUS = 62  # -operand

	def __init__(self):
		self.type = -1
		self.operand = None

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_unary_operator, self)

		visitor._visit(self.operand)

		visitor._leave_node(visitor.leave_unary_operator, self)


class StatementsList():
	def __init__(self):
		self.contents = []

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_statements_list, self)

		visitor._visit_list(self.contents)

		visitor._leave_node(visitor.leave_statements_list, self)


class IdentifiersList():
	def __init__(self):
		self.contents = []

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_identifiers_list, self)

		visitor._visit_list(self.contents)

		visitor._leave_node(visitor.leave_identifiers_list, self)


class RecordsList():
	def __init__(self):
		self.contents = []

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_records_list, self)

		visitor._visit_list(self.contents)

		visitor._leave_node(visitor.leave_records_list, self)


class VariablesList():
	def __init__(self):
		self.contents = []

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_variables_list, self)

		visitor._visit_list(self.contents)

		visitor._leave_node(visitor.leave_variables_list, self)


class ExpressionsList():
	def __init__(self):
		self.contents = []

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_expressions_list, self)

		visitor._visit_list(self.contents)

		visitor._leave_node(visitor.leave_expressions_list, self)


# Called Name in the Lua 5.1 reference
class Identifier():
	T_SLOT = 0
	T_LOCAL = 1
	T_UPVALUE = 2
	T_BUILTIN = 3

	def __init__(self):
		self.name = None
		self.type = -1
		self.slot = -1

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_identifier, self)
		visitor._leave_node(visitor.leave_identifier, self)


# helper vararg/varreturn

class MULTRES():
	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_multres, self)
		visitor._leave_node(visitor.leave_multres, self)


class TableElement():
	def __init__(self):
		self.table = None
		self.key = None

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_table_element, self)

		visitor._visit(self.key)
		visitor._visit(self.table)

		visitor._leave_node(visitor.leave_table_element, self)


class Vararg():
	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_vararg, self)
		visitor._leave_node(visitor.leave_vararg, self)


class FunctionCall():
	def __init__(self):
		self.function = None
		self.arguments = ExpressionsList()

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_function_call, self)

		visitor._visit(self.arguments)
		visitor._visit(self.function)

		visitor._leave_node(visitor.leave_function_call, self)


class If():
	def __init__(self):
		self.expression = None
		self.then_block = StatementsList()
		self.elseifs = []
		self.else_block = StatementsList()

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_if, self)

		visitor._visit(self.expression)
		visitor._visit(self.then_block)

		visitor._visit_list(self.elseifs)

		visitor._visit(self.else_block)

		visitor._leave_node(visitor.leave_if, self)


class ElseIf():
	def __init__(self):
		self.expression = None
		self.then_block = StatementsList()

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_elseif, self)

		visitor._visit(self.expression)
		visitor._visit(self.then_block)

		visitor._leave_node(visitor.leave_elseif, self)

# ##


class Block():
	def __init__(self):
		self.index = -1
		self.warp = None
		self.contents = []
		self.first_address = 0
		self.last_address = 0
		self.warpins_count = 0

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_block, self)

		visitor._visit_list(self.contents)
		visitor._visit(self.warp)

		visitor._leave_node(visitor.leave_block, self)


class UnconditionalWarp():
	T_JUMP = 0
	T_FLOW = 1

	def __init__(self):
		self.type = -1
		self.target = None
		self.is_uclo = False

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_unconditional_warp, self)

		# DO NOT VISIT self.target - warps are not part of the tree

		visitor._leave_node(visitor.leave_unconditional_warp, self)


class ConditionalWarp():
	def __init__(self):
		self.condition = None
		self.true_target = None
		self.false_target = None

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_conditional_warp, self)

		visitor._visit(self.condition)
		# DO NOT VISIT self.true_target - warps are not part of the tree
		# DO NOT VISIT self.false_target - warps are not part of the tree

		visitor._leave_node(visitor.leave_conditional_warp, self)


class IteratorWarp():
	def __init__(self):
		self.variables = VariablesList()
		self.controls = ExpressionsList()
		self.body = None
		self.way_out = None

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_iterator_warp, self)

		visitor._visit(self.variables)
		visitor._visit(self.controls)
		# DO NOT VISIT self.body - warps are not part of the tree
		# DO NOT VISIT self.way_out - warps are not part of the tree

		visitor._leave_node(visitor.leave_iterator_warp, self)


class NumericLoopWarp():
	def __init__(self):
		self.index = Identifier()
		self.controls = ExpressionsList()
		self.body = None
		self.way_out = None

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_numeric_loop_warp, self)

		visitor._visit(self.index)
		visitor._visit(self.controls)
		# DO NOT VISIT self.body - warps are not part of the tree
		# DO NOT VISIT self.way_out - warps are not part of the tree

		visitor._leave_node(visitor.leave_numeric_loop_warp, self)


class EndWarp():
	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_end_warp, self)
		visitor._leave_node(visitor.leave_end_warp, self)


# ##


class Return():
	def __init__(self):
		self.returns = ExpressionsList()

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_return, self)

		visitor._visit(self.returns)

		visitor._leave_node(visitor.leave_return, self)


class Break():
	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_break, self)
		visitor._leave_node(visitor.leave_break, self)


class While():
	def __init__(self):
		self.expression = None
		self.statements = StatementsList()

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_while, self)

		visitor._visit(self.expression)
		visitor._visit(self.statements)

		visitor._leave_node(visitor.leave_while, self)


class RepeatUntil():
	def __init__(self):
		self.expression = None
		self.statements = StatementsList()

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_repeat_until, self)

		visitor._visit(self.statements)
		visitor._visit(self.expression)

		visitor._leave_node(visitor.leave_repeat_until, self)


class NumericFor():
	def __init__(self):
		self.variable = None
		self.expressions = ExpressionsList()
		self.statements = StatementsList()

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_numeric_for, self)

		visitor._visit(self.variable)
		visitor._visit(self.expressions)
		visitor._visit(self.statements)

		visitor._leave_node(visitor.leave_numeric_for, self)


class IteratorFor():
	def __init__(self):
		self.expressions = ExpressionsList()
		self.identifiers = VariablesList()
		self.statements = StatementsList()

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_iterator_for, self)

		visitor._visit(self.expressions)
		visitor._visit(self.identifiers)
		visitor._visit(self.statements)

		visitor._leave_node(visitor.leave_iterator_for, self)


class Constant():
	T_INTEGER = 0
	T_FLOAT = 1
	T_STRING = 2
	T_CDATA = 3

	def __init__(self):
		self.type = -1
		self.value = None

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_constant, self)
		visitor._leave_node(visitor.leave_constant, self)


class Primitive():
	T_NIL = 0
	T_TRUE = 1
	T_FALSE = 2

	def __init__(self):
		self.type = -1

	def _accept(self, visitor):
		visitor._visit_node(visitor.visit_primitive, self)
		visitor._leave_node(visitor.leave_primitive, self)


================================================
FILE: ljd/ast/slotworks.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import ljd.ast.nodes as nodes
import ljd.ast.traverse as traverse
from ljd.ast.helpers import insert_table_record


def eliminate_temporary(ast):
	_eliminate_multres(ast)

	slots, unused = _collect_slots(ast)
	_eliminate_temporary(slots)

	# _remove_unused(unused)

	_cleanup_invalid_nodes(ast)

	return ast


def _eliminate_temporary(slots):
	simple = []
	massive = []
	tables = []
	iterators = []

	for info in slots:
		assignment = info.assignment

		if not isinstance(assignment, nodes.Assignment):
			assert isinstance(assignment, (nodes.IteratorWarp,
							nodes.NumericLoopWarp,
							nodes.FunctionDefinition))

			src = info.references[1].identifier
			simple.append((info.references, src))
			continue

		assert len(assignment.expressions.contents) == 1

		is_massive = len(assignment.destinations.contents) > 1

		if is_massive:
			_fill_massive_refs(info, simple, massive, iterators)
		else:
			_fill_simple_refs(info, simple, tables)

	_eliminate_simple_cases(simple)
	_eliminate_into_table_constructors(tables)
	_eliminate_mass_assignments(massive)
	_eliminate_iterators(iterators)


def _fill_massive_refs(info, simple, massive, iterators):
	ref = info.references[1]
	holder = _get_holder(ref.path)

	src = info.assignment.expressions.contents[0]

	assert isinstance(src, (nodes.FunctionCall,
				nodes.Vararg,
				nodes.Primitive))
	if isinstance(holder, nodes.Assignment):
		dst = holder.destinations.contents[0]

		assert len(info.references) == 2
		orig = info.references[0].identifier

		assignment = ref.path[-3]

		assert isinstance(assignment, nodes.Assignment)

		massive.append((orig, info.assignment, assignment, dst))
	elif isinstance(holder, nodes.IteratorWarp):
		assert len(info.references) == 2
		iterators.append((info.assignment, src, holder))
	elif isinstance(src, nodes.Primitive) and src.type == src.T_NIL:
		assert len(info.references) == 2

		# Create a new primitive, so it won't mess with the
		# writer's ignore list
		src = nodes.Primitive()
		src.type = nodes.Primitive.T_NIL

		simple.append((info, ref, src))


def _fill_simple_refs(info, simple, tables):
	src = info.assignment.expressions.contents[0]

	if isinstance(src, nodes.FunctionCall) and len(info.references) > 3:
		return

	src_is_table = isinstance(src, nodes.TableConstructor)

	for ref in info.references[1:]:
		holder = _get_holder(ref.path)

		is_element = isinstance(holder, nodes.TableElement)

		path_index = ref.path.index(holder)

		statement = _get_holder(ref.path[:path_index])

		statement_is_assignment = isinstance(statement, nodes.Assignment)

		if statement_is_assignment:
			is_dst = statement.destinations.contents[0] == holder
		else:
			is_dst = False

		# Could be more then one reference here
		if src_is_table and is_element and is_dst:
			assert holder.table == ref.identifier
			tables.append((info, ref))
		else:
			simple.append((info, ref, None))


LIST_TYPES = (nodes.VariablesList,
		nodes.IdentifiersList,
		nodes.ExpressionsList,
		nodes.StatementsList)


def _get_holder(path):
	for node in reversed(path[:-1]):
		if not isinstance(node, LIST_TYPES):
			return node

	return None


def _eliminate_simple_cases(simple):
	for info, ref, src in simple:
		holder = ref.path[-2]
		dst = ref.identifier

		if src is None:
			src = info.assignment.expressions.contents[0]

		_mark_invalidated(info.assignment)

		if isinstance(holder, LIST_TYPES):
			nodes = holder.contents
			found = _replace_node_in_list(nodes, dst, src)
		else:
			found = _replace_node(holder, dst, src)

		if found == None:
			print("err: slotworks.py assert found")


def _eliminate_into_table_constructors(tables):
	for info, ref in tables:
		constructor = info.assignment.expressions.contents[0]
		table_element = ref.path[-2]
		assignment = ref.path[-4]

		assert isinstance(assignment, nodes.Assignment)

		assert len(assignment.expressions.contents) == 1

		_mark_invalidated(assignment)

		key = table_element.key
		value = assignment.expressions.contents[0]

		insert_table_record(constructor, key, value)


def _eliminate_mass_assignments(massive):
	for identifier, assignment, base_assignment, globalvar in massive:
		destinations = assignment.destinations.contents
		found = _replace_node_in_list(destinations, identifier, globalvar)

		_mark_invalidated(base_assignment)

		assert found


def _replace_node(holder, original, replacement):
	for key, value in holder.__dict__.items():
		if value == original:
			setattr(holder, key, replacement)
			return True

	return False


def _replace_node_in_list(nodes, original, replacement):
	try:
		index = nodes.index(original)
	except ValueError:
		return False

	nodes[index] = replacement
	return True


def _eliminate_iterators(iterators):
	processed_warps = set()

	for assignment, src, warp in iterators:
		if warp in processed_warps:
			continue

		for i, slot in enumerate(assignment.destinations.contents):
			if hasattr(warp.controls.contents[i], 'slot') and hasattr(slot, 'slot'):
				#assert warp.controls.contents[i].slot == slot.slot
				if warp.controls.contents[i].slot != slot.slot:
					print ("err: slotworks.py assert warp.controls.contents[i].slot == slot.slot")

		warp.controls.contents = [src]
		processed_warps.add(warp)

		_mark_invalidated(assignment)


def _mark_invalidated(node):
	setattr(node, "_invalidated", True)


def _is_invalidated(node):
	return getattr(node, "_invalidated", False)


def _remove_unused(unused):
	pass


def _collect_slots(ast):
	collector = _SlotsCollector()
	traverse.traverse(collector, ast)

	return collector.slots, collector.unused


def _eliminate_multres(ast):
	traverse.traverse(_MultresEliminator(), ast)
	_cleanup_invalid_nodes(ast)


class _MultresEliminator(traverse.Visitor):
	def __init__(self):
		self._last_multres_value = None

	def leave_assignment(self, node):
		src = node.expressions.contents[0]
		dst = node.destinations.contents[0]

		if isinstance(dst, nodes.MULTRES):
			assert len(node.destinations.contents) == 1
			assert len(node.expressions.contents) == 1

			assert isinstance(src, (nodes.FunctionCall, nodes.Vararg))

			assert self._last_multres_value is None

			self._last_multres_value = src

			_mark_invalidated(node)
		else:
			for i, src in enumerate(node.expressions.contents):
				if isinstance(src, nodes.MULTRES):
					break
			else:
				return

			assert self._last_multres_value is not None

			node.expressions.contents[i] = self._last_multres_value
			self._last_multres_value = None

	def _process_multres_in_list(self, nodes_list):
		for i, node in enumerate(nodes_list):
			if isinstance(node, nodes.MULTRES):
				break
		else:
			return

		assert self._last_multres_value is not None

		nodes_list[i] = self._last_multres_value
		self._last_multres_value = None

	def visit_function_call(self, node):
		self._process_multres_in_list(node.arguments.contents)

	def visit_return(self, node):
		self._process_multres_in_list(node.returns.contents)


class _SlotReference():
	def __init__(self):
		self.path = []
		self.identifier = None


class _SlotInfo():
	def __init__(self):
		self.slot = 0

		self.assignment = None
		self.references = []
		self.termination = None

		self.function = None


class _SlotsCollector(traverse.Visitor):
	class _State():
		def __init__(self):
			self.known_slots = {}
			self.function = None

	# ##

	def __init__(self):
		self._states = []
		self._path = []
		self._skip = None

		self.slots = []
		self.unused = []

		self._push_state()

	# ##

	def _state(self):
		return self._states[-1]

	def _push_state(self):
		self._states.append(_SlotsCollector._State())

	def _pop_state(self):
		self._states.pop()

	def _commit_info(self, info):
		assert len(info.references) > 0

		if len(info.references) == 1:
			self.unused.append(info)
		else:
			self.slots.append(info)

	def _commit_slot(self, slot, node):
		info = self._state().known_slots.get(slot)

		if info is None:
			return

		info.termination = node

		del self._state().known_slots[slot]

		self._commit_info(info)

	def _register_slot(self, slot, node):
		self._commit_slot(slot, node)

		info = _SlotInfo()
		info.slot = slot
		info.assignment = node
		info.function = self._state().function

		self._state().known_slots[slot] = info

	def _register_all_slots(self, node, slots):
		for slot in slots:
			if not isinstance(slot, nodes.Identifier):
				continue

			if slot.type != nodes.Identifier.T_SLOT:
				continue

			self._register_slot(slot.slot, node)

	def _commit_all_slots(self, slots, node):
		for slot in slots:
			if not isinstance(slot, nodes.Identifier):
				continue

			self._commit_slot(slot.slot, node)

	def _register_slot_reference(self, slot, node):
		info = self._state().known_slots.get(slot)

		if info is None:
			return

		reference = _SlotReference()
		reference.identifier = node

		# Copy the list, but not contents
		reference.path = self._path[:]

		info.references.append(reference)

	# ##

	def visit_assignment(self, node):
		self._visit(node.expressions)
		self._skip = node.expressions

		self._register_all_slots(node, node.destinations.contents)

	def leave_assignment(self, node):
		self._skip = None

	def visit_identifier(self, node):
		if node.type == nodes.Identifier.T_SLOT:
			self._register_slot_reference(node.slot, node)

	# ##

	def visit_function_definition(self, node):
		self._push_state()
		self._state().function = node

	def leave_function_definition(self, node):
		self._pop_state()

	def leave_block(self, node):
		for info in self._state().known_slots.values():
			self._commit_info(info)

		self._state().known_slots = {}

	def visit_iterator_warp(self, node):
		self._commit_all_slots(node.variables.contents, node)

	def visit_numeric_loop_warp(self, node):
		self._commit_slot(node.index.slot, node)

	# ##

	def _visit_node(self, handler, node):
		self._path.append(node)

		traverse.Visitor._visit_node(self, handler, node)

	def _leave_node(self, handler, node):
		self._path.pop()

		traverse.Visitor._leave_node(self, handler, node)

	def _visit(self, node):
		if self._skip == node:
			return

		traverse.Visitor._visit(self, node)


def _cleanup_invalid_nodes(ast):
	traverse.traverse(_TreeCleanup(), ast)


class _TreeCleanup(traverse.Visitor):
	def visit_block(self, node):
		patched = []

		for subnode in node.contents:
			if not _is_invalidated(subnode):
				patched.append(subnode)

		node.contents = patched


================================================
FILE: ljd/ast/traverse.py
================================================
class Visitor():
	def __init__(self):
		pass

	# ##

	def visit_function_definition(self, node):
		pass

	def leave_function_definition(self, node):
		pass

	# ##

	def visit_table_constructor(self, node):
		pass

	def leave_table_constructor(self, node):
		pass

	def visit_table_record(self, node):
		pass

	def leave_table_record(self, node):
		pass

	def visit_array_record(self, node):
		pass

	def leave_array_record(self, node):
		pass

	# ##

	def visit_assignment(self, node):
		pass

	def leave_assignment(self, node):
		pass

	# ##

	def visit_binary_operator(self, node):
		pass

	def leave_binary_operator(self, node):
		pass

	def visit_unary_operator(self, node):
		pass

	def leave_unary_operator(self, node):
		pass

	# ##

	def visit_statements_list(self, node):
		pass

	def leave_statements_list(self, node):
		pass

	def visit_identifiers_list(self, node):
		pass

	def leave_identifiers_list(self, node):
		pass

	def visit_records_list(self, node):
		pass

	def leave_records_list(self, node):
		pass

	def visit_variables_list(self, node):
		pass

	def leave_variables_list(self, node):
		pass

	def visit_expressions_list(self, node):
		pass

	def leave_expressions_list(self, node):
		pass

	# ##

	def visit_identifier(self, node):
		pass

	def leave_identifier(self, node):
		pass

	def visit_multres(self, node):
		pass

	def leave_multres(self, node):
		pass

	def visit_table_element(self, node):
		pass

	def leave_table_element(self, node):
		pass

	def visit_vararg(self, node):
		pass

	def leave_vararg(self, node):
		pass

	def visit_function_call(self, node):
		pass

	def leave_function_call(self, node):
		pass

	# ##

	def visit_if(self, node):
		pass

	def leave_if(self, node):
		pass

	def visit_elseif(self, node):
		pass

	def leave_elseif(self, node):
		pass

	# ##

	def visit_block(self, node):
		pass

	def leave_block(self, node):
		pass

	def visit_unconditional_warp(self, node):
		pass

	def leave_unconditional_warp(self, node):
		pass

	def visit_conditional_warp(self, node):
		pass

	def leave_conditional_warp(self, node):
		pass

	def visit_iterator_warp(self, node):
		pass

	def leave_iterator_warp(self, node):
		pass

	def visit_numeric_loop_warp(self, node):
		pass

	def leave_numeric_loop_warp(self, node):
		pass

	def visit_end_warp(self, node):
		pass

	def leave_end_warp(self, node):
		pass

	# ##

	def visit_return(self, node):
		pass

	def leave_return(self, node):
		pass

	def visit_break(self, node):
		pass

	def leave_break(self, node):
		pass

	# ##

	def visit_while(self, node):
		pass

	def leave_while(self, node):
		pass

	def visit_repeat_until(self, node):
		pass

	def leave_repeat_until(self, node):
		pass

	def visit_numeric_for(self, node):
		pass

	def leave_numeric_for(self, node):
		pass

	def visit_iterator_for(self, node):
		pass

	def leave_iterator_for(self, node):
		pass

	# ##

	def visit_constant(self, node):
		pass

	def leave_constant(self, node):
		pass

	def visit_primitive(self, node):
		pass

	def leave_primitive(self, node):
		pass

	# ##

	def _visit_node(self, handler, node):
		handler(node)

	def _leave_node(self, handler, node):
		handler(node)

	# ##

	def _visit(self, node):
		assert node is not None

		node._accept(self)

	def _visit_list(self, nodes_list):
		assert isinstance(nodes_list, list)

		for node in nodes_list:
			self._visit(node)


def traverse(visitor, node):
	if isinstance(node, list):
		visitor._visit_list(node)
	else:
		visitor._visit(node)


================================================
FILE: ljd/ast/unwarper.py
================================================
import copy

import ljd.ast.nodes as nodes
import ljd.ast.traverse as traverse
import ljd.ast.slotworks as slotworks

binop = nodes.BinaryOperator


# ##
# ## REMEMBER
# ##
# ## Block indices are unreliable while you are mangling them!
# ##
# ## P.S. Probably they should not be named indices... But they ARE used as indices during other phases. Sometimes.
# ## 
# ##


class _StatementsCollector(traverse.Visitor):
	def __init__(self):
		self.result = []

	def visit_statements_list(self, node):
		if len(node.contents) > 0:
			self.result.append(node)


def unwarp(node):
	# There could be many negative jumps within while conditions, so
	# filter them first
	_run_step(_unwarp_loops, node, repeat_until=False)

	_run_step(_unwarp_loops, node, repeat_until=True)
	_run_step(_unwarp_expressions, node)
	_run_step(_unwarp_ifs, node)

	_glue_flows(node)


def _run_step(step, node, **kargs):
	for statements in _gather_statements_lists(node):
		statements.contents = step(statements.contents, **kargs)

	# Fix block indices in case anything was moved
	for statements in _gather_statements_lists(node):
		for i, block in enumerate(statements.contents):
			block.index = i


def _gather_statements_lists(node):
	collector = _StatementsCollector()
	traverse.traverse(collector, node)
	return collector.result


def _glue_flows(node):
	for statements in _gather_statements_lists(node):
		blocks = statements.contents

		if hasattr(blocks[-1], 'warp'):
			assert isinstance(blocks[-1].warp, nodes.EndWarp)

			for i, block in enumerate(blocks[:-1]):
				warp = block.warp

				assert _is_flow(warp)

				target = warp.target

				assert target == blocks[i + 1]

				target.contents = block.contents + target.contents
				block.contents = []

			statements.contents = blocks[-1].contents


# ##
# ## IFs AND EXPRESSIONs PROCESSING
# ##

def _unwarp_expressions(blocks):
	pack = []
	pack_set = set()

	start_index = 0
	while start_index < len(blocks) - 1:
		start = blocks[start_index]
		warp = start.warp

		if isinstance(warp, nodes.UnconditionalWarp):
			if warp.type == nodes.UnconditionalWarp.T_FLOW:
				start_index += 1
				continue

		body, end, end_index = _extract_if_body(start_index,
							blocks, None)

		if body is None:
			raise NotImplementedError("GOTO statements are not"
								" supported")

		expressions = _find_expressions(start, body, end)

		assert pack_set.isdisjoint(expressions)

		expressions_set = set(expressions)

		assert len(expressions_set) == len(expressions)

		if len(expressions) == 0:
			start_index += 1
			continue

		pack += list(reversed(expressions))
		pack_set |= expressions_set

		endest_end = _find_endest_end(expressions)

		if endest_end != end:
			end_index = blocks.index(endest_end)

		start_index = end_index

	return _unwarp_expressions_pack(blocks, pack)


def _find_endest_end(expressions):
	endest_end = expressions[0][1]

	for _start, exp_end, _slot, _slot_type in expressions[1:]:
		if exp_end.index > endest_end.index:
			endest_end = exp_end

	return endest_end


def _unwarp_ifs(blocks, top_end=None, topmost_end=None):
	boundaries = []

	start_index = 0

	while start_index < len(blocks) - 1:
		start = blocks[start_index]
		warp = start.warp

		if isinstance(warp, nodes.UnconditionalWarp):
			if warp.type == nodes.UnconditionalWarp.T_FLOW:
				start_index += 1
				continue

		body, end, end_index = _extract_if_body(start_index,
							blocks, topmost_end)

		if body is None:
			raise NotImplementedError("GOTO statements are not"
								" supported")

		is_end = isinstance(body[-1].warp, nodes.EndWarp)

		_unwarp_if_statement(start, body, end, end)

		if is_end:
			_set_end(start)
		else:
			_set_flow_to(start, end)

		boundaries.append((start_index, end_index - 1))

		start_index = end_index

	return _remove_processed_blocks(blocks, boundaries)


def _extract_if_body(start_index, blocks, topmost_end):
	end = _find_branching_end(blocks[start_index:], topmost_end)

	try:
		end_index = blocks.index(end)
	except ValueError:
		if end == topmost_end:
			end_index = len(blocks)
		else:
			return None, None, None

	body = blocks[start_index + 1:end_index]

	return body, end, end_index


def _unwarp_expressions_pack(blocks, pack):
	replacements = {}

	for start, end, slot, slot_type in reversed(pack):
		end = replacements.get(end, end)

		start_index = blocks.index(start)
		end_index = blocks.index(end)

		body = blocks[start_index + 1:end_index]

		_unwarp_logical_expression(start, end, body)

		statements = start.contents + end.contents

		if slot_type == nodes.Identifier.T_SLOT:
			min_i = len(start.contents)
			split_i = _split_by_slot_use(statements, min_i,
							end.warp, slot)
		else:
			split_i = len(start.contents)

		max_i = len(statements)

		if split_i > max_i:
			end.contents = start.contents + end.contents
			start.contents = []

			blocks = blocks[:start_index] + blocks[end_index:]

			_replace_targets(blocks, start, end)

			replacements[start] = end

			slotworks.eliminate_temporary(end)

			_set_flow_to(start, end)
		else:
			blocks = blocks[:start_index + 1] + blocks[end_index:]

			start.contents = statements[:split_i]
			end.contents = statements[split_i:]

			# We need to kill the start's warp before slot
			# elimination or it could result in a cycled AST.
			_set_flow_to(start, end)

			slotworks.eliminate_temporary(start)

	return blocks


def _split_by_slot_use(statements, min_i, warp, slot):
	known_slots = set([slot])

	split_i = min_i

	for i, statement in enumerate(statements):
		if isinstance(statement, nodes.Assignment):
			sets = _extract_destination_slots(statement)

			if i < min_i:
				known_slots |= sets
			else:
				known_slots -= sets

			if len(known_slots) == 0:
				break

		split_i = i + 1

	if split_i < len(statements):
		return split_i

	if isinstance(warp, nodes.ConditionalWarp):
		known_slots -= _gather_slots(warp)

		if len(known_slots) == 0:
			split_i += 1

	return split_i


def _extract_destination_slots(statement):
	sets = set()

	for node in statement.destinations.contents:
		if not isinstance(node, nodes.Identifier):
			continue

		if node.type == nodes.Identifier.T_SLOT:
			sets.add(node.slot)

	return sets


def _gather_slots(node):
	class Collector(traverse.Visitor):
		def __init__(self):
			self.slots = set()

		def visit_identifier(self, node):
			if node.type == nodes.Identifier.T_SLOT:
				self.slots.add(node.slot)

	collector = Collector()

	traverse.traverse(collector, node)

	return collector.slots


def _find_expressions(start, body, end):
	# Explicitly allow the local a = x ~= "b" case
	slot, slot_type = _get_simple_local_assignment_slot(start, body, end)

	if slot >= 0:
		return [(start, end, slot, slot_type)]

	expressions = []

	# We have something at the end, but not the true/false?

	i = 0
	extbody = [start] + body

	is_local = False
	sure_expression = False

	while i < len(extbody):
		block = extbody[i]

		subs = _find_subexpressions(block, body[i:])

		if len(subs) != 0:
			endest_end = _find_endest_end(subs)
			new_i = extbody.index(endest_end)

			# Loop? No way!
			if new_i <= i:
				return expressions

			# It should end with a conditional warp if that's
			# really a subexpression-as-operand
			end_warp = endest_end.warp

			if not isinstance(end_warp, nodes.ConditionalWarp):
				return expressions

			expressions = subs + expressions
			i = new_i
			continue

		if isinstance(block.warp, nodes.ConditionalWarp):
			condition = block.warp.condition

			is_end = block.warp.false_target == end
			is_binop = isinstance(condition, nodes.BinaryOperator)
			block_slot = getattr(block.warp, "_slot", slot)

			if is_end:
				if is_binop:
					return expressions
				elif slot < 0 and block_slot >= 0:
					slot = block_slot
					slot_type = nodes.Identifier.T_SLOT
					sure_expression = True
				elif slot != block_slot:
					return expressions
				else:
					sure_expression = True
		elif isinstance(block.warp, nodes.UnconditionalWarp):
			if block == start and len(block.contents) == 0:
				return []

		if len(block.contents) == 0:
			i += 1
			continue

		if block != start and len(block.contents) > 1:
			return expressions

		assignment = block.contents[-1]

		if not isinstance(assignment, nodes.Assignment):
			if block == start:
				i += 1
				continue

			return expressions

		destinations = assignment.destinations.contents

		if len(destinations) != 1:
			if block == start:
				i += 1
				continue

			return expressions

		dst = destinations[0]

		if not isinstance(dst, nodes.Identifier):
			if block == start:
				i += 1
				continue

			return expressions

		if isinstance(block.warp, nodes.ConditionalWarp):
			if block == start:
				i += 1
				continue

			return expressions

		if slot < 0:
			# If all encounters are locals, which means
			# that the first encounter is a local
			if dst.type == nodes.Identifier.T_LOCAL:
				is_local = True
			elif dst.type == nodes.Identifier.T_UPVALUE:
				return []

			slot = dst.slot
			slot_type = dst.type
		elif slot == dst.slot:
			slot_type = dst.type

			if dst.type == nodes.Identifier.T_UPVALUE:
				return []
		else:
			assert block != start

			return []

		i += 1

	if slot < 0:
		return []

	true, _false, body = _get_terminators(body)

	if true is not None:
		sure_expression = True

	if len(expressions) > 0:
		sure_expression = True

	if not sure_expression and is_local:
		return expressions

	return expressions + [(start, end, slot, slot_type)]


def _find_subexpressions(start, body):
	try:
		body, end, _end_index = _extract_if_body(0, [start] + body, None)
	except ValueError:
		# a warp target is not in a list
		return []

	if body is None:
		return []

	return _find_expressions(start, body, end)


def _get_simple_local_assignment_slot(start, body, end):
	if len(body) != 2:
		return -1, None

	true, _false, body = _get_terminators(body)

	if true is None:
		return -1, None
	else:
		slot = true.contents[0].destinations.contents[0]
		return slot.slot, slot.type


def _find_expression_slot(body):
	slot = None

	for block in reversed(body):
		if len(block.contents) != 1:
			continue

		slot = block.contents[0].destinations.contents[0]
		break

	return slot


def _unwarp_logical_expression(start, end, body):
	slot = _find_expression_slot(body)

	assert slot is not None

	true, false, body = _get_terminators(body)

	expression = _compile_expression([start] + body, end, true, false)

	dst = copy.deepcopy(slot)

	assignment = nodes.Assignment()
	assignment.destinations.contents.append(dst)
	assignment.expressions.contents.append(expression)

	start.contents.append(assignment)


def _compile_expression(body, end, true, false):
	parts = _unwarp_expression(body, end, true, false)

	if len(parts) < 3:
		assert len(parts) == 1
		return parts[0]

	explicit_parts = _make_explicit_subexpressions(parts)
	return _assemble_expression(explicit_parts)


#
# The logical expressions:
#
# There are terminators: a true, a false and an end
#
# For an if case the true will be a "then" clause and the false - an "else" or
# "after-the-if" clause. The end is required for a single-variable (unary)
# components and is only used at during topmost phase.
#
# The last block in expression is always "double terminal" - both ends are
# pointing at terminators. It's rather useless so we just append it's condition
# (inverted if needed - it's easy to see if true end targets the false
# terminator) at the end of processing.
#
# Then we need to pack all other blocks into subexpressions. Subexpressions
# always end with a _terminal block_, i.e. the block which warp points to a
# terminator. Idea is that we can guess the operator only right of a
# terminal block, because we can check if the block's warp condition is
# inverted or not.
#
# If that's an "OR" clause then it will jump out of the current expression if
# the condition is true, so the condition is inverted and the false branch is
# pointing at the way out (at the TRUE terminator - because the result of the
# expression level will be true). (because in the bytecode there
# is only one JMP, so a ConditionalWarp's true branch is actually a fake and
# always points to the next block - in the bytecode a "positive" jump
# will be represented by a normal negative jump with inverted condition).
#
# Otherwise, if that's an "AND" clause then it will jump out of the current
# expression level if a condition is false, so the condition is not inverted
# and a false branch points to the false.
#
# This way we can understand which operators go just right of terminal blocks.
# Everything in-between these block is considered a subexpression. And just
# because we don't know where exactly the subexpression ends we are using
# greedy approach and trying to pack into a subexpression as much blocks as
# possible, including any terminal blocks pointing at the same terminator
# with the same inversion status (that is - we are always using the
# rightmost block if there are consequitive similar terminal blocks, ignoring
# all the blocks to the left).
#
# Then comes the trick: the subexpression is a component of this expression and
# we know the operator to the right of it. We can guess now what will
# be evaluated if the subexpression evaluates to "false" and what - if it's
# "true". If the operator is "AND" then the subexpression failure will cause
# the expression failure too, i.e. the "FALSE" target remains the same and the
# true terminator is set to the next block (after the "AND").
#
# If the operator is "OR" the the subexpression success will cause the success
# of the expression, so the "TRUE" target remains the same, but the false
# target is set to the next block (after the "OR").
#
# Now we have a subexpression and both TRUE and FALSE terminators for it.
# Recurse and repeat.
#
def _unwarp_expression(body, end, true, false):
	parts = []

	if true is not None:
		terminator_index = min(true.index, false.index)

		if end is not None:
			terminator_index = min(end.index, terminator_index)
	else:
		assert end is not None

		terminator_index = end.index

	terminators = set((true, false, end))

	subexpression_start = 0

	i = 0
	while i < len(body) - 1:
		block = body[i]
		warp = block.warp

		target = _get_target(warp)

		#
		# A chance for
		# (foo and (bar and y or z)) or x
		# type expressions, because the first "foo and ... )) or" part
		# will be broken by the "or z))" part in the code below.
		#
		# So we are going to intercept subexpressions by it's start
		# instead of an end, but only if we are already at the
		# subexpression start (so nothing formally changes, but creates
		# a bit more correct execution order)
		#
		if target.index < terminator_index:
			if i != subexpression_start:
				i += 1
				continue

			target_index = body.index(target)
			last_block = body[target_index - 1]

			last_block_target = _get_target(last_block.warp)

			if last_block_target.index < terminator_index:
				i += 1
				continue

			assert last_block_target in terminators

			subexpression = body[i:target_index]
		else:
			assert target in terminators

			while i < len(body) - 2:
				next_block = body[i + 1]
				next_target = _get_target(next_block.warp)

				if next_target != target:
					break

				next_inv = _is_inverted(next_block.warp, true, end)

				this_inv = _is_inverted(warp, true, end)

				# Special hack for unary expressions (x, not x)...
				if next_inv != this_inv:
					break

				block = next_block
				warp = next_block.warp
				i += 1

			subexpression = body[subexpression_start:i + 1]

		last_block = subexpression[-1]
		last_block_index = body.index(last_block)

		next_block = body[last_block_index + 1]

		operator = _get_operator(last_block, true, end)

		subexpression = _compile_subexpression(subexpression, operator,
							last_block, next_block,
							true, end)

		parts.append(subexpression)
		parts.append(operator)

		i = last_block_index + 1
		subexpression_start = i

	last = body[-1]

	if isinstance(last.warp, nodes.ConditionalWarp):
		if _is_inverted(last.warp, true, end):
			last = _invert(last.warp.condition)
		else:
			last = last.warp.condition

		parts.append(last)
	else:
		assert isinstance(last.warp, (nodes.EndWarp,
						nodes.UnconditionalWarp))

		src = _get_last_assignment_source(last)

		if src is None:
			src = nodes.Primitive()

			if last.warp.target == true:
				src.type = nodes.Primitive.T_TRUE
			else:
				src.type = nodes.Primitive.T_FALSE

		parts.append(src)

	return parts


def _get_target(warp, allow_end=False):
	if isinstance(warp, nodes.ConditionalWarp):
		return warp.false_target
	else:
		if allow_end and isinstance(warp, nodes.EndWarp):
			return getattr(warp, "_target", None)

		assert isinstance(warp, nodes.UnconditionalWarp)
		return warp.target


def _set_target(warp, target):
	if isinstance(warp, nodes.ConditionalWarp):
		warp.false_target = target
	else:
		assert isinstance(warp, nodes.UnconditionalWarp)
		warp.target = target


def _get_operator(block, true, end):
	if isinstance(block.warp, nodes.UnconditionalWarp):
		src = _get_last_assignment_source(block)

		if isinstance(src, nodes.Constant):
			is_true = src.value != 0
		elif isinstance(src, nodes.BinaryOperator):
			is_true = True
		elif isinstance(src, nodes.Primitive):
			is_true = src.type == nodes.Primitive.T_TRUE
		elif isinstance(src, nodes.Identifier):
			is_true = True
		else:
			#assert src is None

			is_true = block.warp.target == true

		if is_true:
			return binop.T_LOGICAL_OR
		else:
			return binop.T_LOGICAL_AND
	else:
		is_inverted = _is_inverted(block.warp, true, end)

		if is_inverted:
			return binop.T_LOGICAL_OR
		else:
			return binop.T_LOGICAL_AND


def _get_last_assignment_source(block):
	if len(block.contents) == 0:
		return None

	assignment = block.contents[-1]
	assert isinstance(assignment, nodes.Assignment)
	return assignment.expressions.contents[0]


def _get_and_remove_last_assignment_source(block):
	assignment = block.contents.pop()
	assert isinstance(assignment, nodes.Assignment)
	return assignment.expressions.contents[0]


def _compile_subexpression(subexpression, operator,
						block, next_block, true, end):
	warp = block.warp

	if len(subexpression) == 1:
		if isinstance(warp, nodes.UnconditionalWarp):
			return _get_and_remove_last_assignment_source(block)
		elif _is_inverted(warp, true, end):
			return _invert(warp.condition)
		else:
			return warp.condition
	else:
		if isinstance(warp, nodes.UnconditionalWarp):
			if operator == binop.T_LOGICAL_OR:
				subtrue = warp.target
				subfalse = next_block
			else:
				subtrue = next_block
				subfalse = warp.target
		else:
			if operator == binop.T_LOGICAL_OR:
				subtrue = warp.false_target
				subfalse = warp.true_target
			else:
				subtrue = warp.true_target
				subfalse = warp.false_target

		return _unwarp_expression(subexpression, None, subtrue, subfalse)


def _is_inverted(warp, true, end):
	if isinstance(warp, nodes.UnconditionalWarp):
		return warp.target == end

	if warp.false_target == true:
		return True
	elif warp.false_target == end:
		assert not isinstance(warp.condition, nodes.BinaryOperator)

		if not isinstance(warp.condition, nodes.UnaryOperator):
			return False

		return warp.condition.type == nodes.UnaryOperator.T_NOT

	return False


_NEGATION_MAP = [None] * 100

_NEGATION_MAP[binop.T_LESS_THEN] = binop.T_GREATER_OR_EQUAL
_NEGATION_MAP[binop.T_GREATER_THEN] = binop.T_LESS_OR_EQUAL
_NEGATION_MAP[binop.T_LESS_OR_EQUAL] = binop.T_GREATER_THEN
_NEGATION_MAP[binop.T_GREATER_OR_EQUAL] = binop.T_LESS_THEN

_NEGATION_MAP[binop.T_NOT_EQUAL] = binop.T_EQUAL
_NEGATION_MAP[binop.T_EQUAL] = binop.T_NOT_EQUAL


def _invert(expression):
	if isinstance(expression, nodes.UnaryOperator):
		return expression.operand

	if not isinstance(expression, nodes.BinaryOperator):
		node = nodes.UnaryOperator()
		node.type = nodes.UnaryOperator.T_NOT
		node.operand = expression

		return node

	# Just in case
	expression = copy.deepcopy(expression)

	new_type = _NEGATION_MAP[expression.type]

	assert new_type is not None

	expression.type = new_type

	return expression


def _get_terminators(body):
	if len(body) < 2:
		return None, None, body

	last = body[-1]

	if len(last.contents) != 1:
		return None, None, body

	assignment = last.contents[0]

	if not isinstance(assignment, nodes.Assignment):
		return None, None, body

	src = assignment.expressions.contents[0]

	if not isinstance(src, nodes.Primitive) or src.type != src.T_TRUE:
		return None, None, body

	prev = body[-2]

	if len(prev.contents) != 1:
		return None, None, body

	src = prev.contents[0].expressions.contents[0]

	if not isinstance(src, nodes.Primitive) or src.type != src.T_FALSE:
		return None, None, body

	return last, prev, body[:-2]


def _assemble_expression(parts):
	if not isinstance(parts, list):
		return parts

	node = nodes.BinaryOperator()
	node.left = _assemble_expression(parts[-3])

	node.type = parts[-2]
	assert isinstance(node.type, int)

	node.right = _assemble_expression(parts[-1])

	i = len(parts) - 4

	while i > 0:
		operator = parts[i]
		component = parts[i - 1]

		upper_node = nodes.BinaryOperator()
		upper_node.right = node
		upper_node.left = _assemble_expression(component)

		upper_node.type = operator

		node = upper_node

		i -= 2

	return node


# Split the topmost expression into smaller subexpressions at each
# operator change to simplify the assembly phase
def _make_explicit_subexpressions(parts):
	patched = []

	i = 0

	last_operator = parts[1]
	subexpression_start = -1

	while i < len(parts) - 1:
		component = parts[i]
		operator = parts[i + 1]

		if operator < last_operator:
			subexpression_start = i
			last_operator = operator
		elif subexpression_start > 0:
			if operator > last_operator				\
					and (i - subexpression_start) % 2 != 0:
				subexpression = parts[subexpression_start:i]

				patched.append(subexpression)
				subexpression_start = -1
		else:
			patched += [component, operator]

		i += 2

	if subexpression_start >= 0:
		patched.append(parts[subexpression_start:])
	else:
		patched.append(parts[-1])

	return patched


def _unwarp_if_statement(start, body, end, topmost_end):
	expression, body, false = _extract_if_expression(start, body, end,
								topmost_end)

	node = nodes.If()
	node.expression = expression

	# has an else branch
	if false != end and false != topmost_end:
		else_start = false

		else_start_index = body.index(else_start)

		then_body = body[:else_start_index]

		then_warp_out = then_body[-1].warp

		assert _is_jump(then_warp_out)
		assert then_warp_out.target in (end, topmost_end)

		else_body = body[else_start_index:]

		else_warp_out = else_body[-1].warp

		if isinstance(else_warp_out, nodes.UnconditionalWarp):
			if else_warp_out.type == nodes.UnconditionalWarp.T_JUMP:
				assert else_warp_out.target in (end, topmost_end)
			else:
				assert else_warp_out.target == end
		else:
			print ("err: unwarper.py assert isinstance(else_warp_out, nodes.EndWarp), Block indices are unreliable while you are mangling them! P.S. Probably they should not be named indices... But they ARE used as indices during other phases. Sometimes.")

		_set_end(then_body[-1])
		then_blocks = _unwarp_ifs(then_body, then_body[-1], topmost_end)
		node.then_block.contents = then_blocks

		_set_end(else_body[-1])
		else_blocks = _unwarp_ifs(else_body, else_body[-1], topmost_end)
		node.else_block.contents = else_blocks
	else:
		warp_out = body[-1].warp

		if not isinstance(warp_out, nodes.EndWarp):
			assert isinstance(warp_out, nodes.UnconditionalWarp)
			assert warp_out.target in (end, topmost_end)

		_set_end(body[-1])
		then_blocks = _unwarp_ifs(body, body[-1], topmost_end)
		node.then_block.contents = then_blocks

	start.contents.append(node)


def _extract_if_expression(start, body, end, topmost_end):
	for i, block in enumerate(body):
		if len(block.contents) != 0:
			break

	assert i < len(body)

	expression = [start] + body[:i]
	body = body[i:]

	falses = set()

	for i, block in enumerate(body[:-1]):
		if not isinstance(block.warp, nodes.UnconditionalWarp):
			continue

		if block.warp.type != nodes.UnconditionalWarp.T_JUMP:
			continue

		if block.warp.target != end and block.warp.target != topmost_end:
			continue

		falses.add(body[i + 1])

	falses.add(end)

	if topmost_end is not None:
		falses.add(topmost_end)

	false, end_i = _search_expression_end(expression, falses)

	assert end_i >= 0

	body = expression[end_i:] + body
	expression = expression[:end_i]

	assert len(expression) > 0

	true = body[0]

	expression = _compile_expression(expression, None, true, false)

	return expression, body, false


def _search_expression_end(expression, falses):
	expression_end = -1
	false = None

	for i, block in enumerate(expression):
		target = _get_target(block.warp)

		if target not in falses:
			continue

		if false is None or target == false:
			false = target
			expression_end = i + 1
		else:
			break

	assert false is not None

	return false, expression_end


def _find_branching_end(blocks, topmost_end):
	end = blocks[0]

	for block in blocks:
		warp = block.warp

		target = _get_target(warp, allow_end=True)

		if isinstance(warp, nodes.EndWarp) and target is None:
			assert block == end
			return block

		if isinstance(warp, nodes.UnconditionalWarp) and target == end:
			return end

		if target.index > end.index:
			end = target

	return end


def _remove_processed_blocks(blocks, boundaries):
	remains = []
	last_end_index = -1

	for start, end in boundaries:
		if start == end:
			up_to_index = start
		else:
			up_to_index = start + 1

		remains += blocks[last_end_index + 1:up_to_index]
		last_end_index = end

	remains += blocks[last_end_index + 1:]

	return remains


# ##
# ## LOOPS PROCESSING
# ##


def _unwarp_loops(blocks, repeat_until):
	loops = _find_all_loops(blocks, repeat_until)

	if len(loops) == 0:
		return blocks

	fixed = _cleanup_breaks_and_if_ends(loops, blocks)

	for start, end in fixed:
		start_index = blocks.index(start)
		end_index = blocks.index(end)

		if repeat_until:
			body = blocks[start_index:end_index]
		else:
			body = blocks[start_index + 1:end_index]

		loop = _unwarp_loop(start, end, body)
		body = loop.statements.contents

		block = nodes.Block()
		block.first_address = body[0].first_address
		block.last_address = body[-1].last_address
		block.index = start.index + 1
		block.contents.append(loop)

		block.warp = nodes.UnconditionalWarp()
		block.warp.type = nodes.UnconditionalWarp.T_FLOW
		block.warp.target = end

		_replace_targets(blocks, body[0], block)

		_set_end(body[-1])
		_unwarp_breaks(start, body, end)

		blocks = blocks[:start_index + 1] + [block] + blocks[end_index:]

	return blocks


def _cleanup_breaks_and_if_ends(loops, blocks):
	outer_start_index = -1
	outer_end = None

	current_start_index = -1
	current_end = None

	fixed = []

	for start, end in loops:
		if start.index in (outer_start_index, current_start_index):
			end_i = blocks.index(end)
			last_in_body = blocks[end_i - 1]
			warp = last_in_body.warp

			assert isinstance(warp, nodes.UnconditionalWarp)
			assert warp.target == start

			# Break
			if start.index == outer_start_index:
				assert outer_end is not None

				outer_end_i = blocks.index(outer_end)
				warp.target = blocks[outer_end_i - 1]

				assert blocks[outer_end_i - 2] != end
			else:
				assert current_end is not None
				assert start.index == current_start_index

				current_end_i = blocks.index(current_end)

				last = blocks[current_end_i - 1]

				if last == end:
					last = _create_next_block(end)
					last.warp = end.warp

					_set_flow_to(end, last)

					blocks.insert(current_end_i, last)

				warp.target = last
		else:
			fixed.append((start, end))

			if current_end is not None				\
					and current_start_index < start.index	\
					and current_end.index >= end.index:
				outer_start_index = current_start_index
				outer_end = current_end
			else:
				outer_start_index = -1
				outer_end = None

			current_start_index = start.index
			current_end = end

	return fixed


def _replace_targets(blocks, original, replacement):
	for block in blocks:
		warp = block.warp

		if isinstance(warp, nodes.UnconditionalWarp):
			if warp.target == original:
				warp.target = replacement
		elif isinstance(warp, nodes.ConditionalWarp):
			if warp.true_target == original:
				warp.true_target = replacement

			if warp.false_target == original:
				warp.false_target = replacement
		elif isinstance(warp, nodes.EndWarp):
			pass
		else:
			if warp.way_out == original:
				warp.way_out = replacement

			if warp.body == original:
				warp.body = replacement


def _unwarp_loop(start, end, body):
	if len(body) > 0:
		last = body[-1]
	else:
		last = start

	if isinstance(start.warp, nodes.IteratorWarp):
		assert isinstance(last.warp, nodes.UnconditionalWarp)
		assert last.warp.target == start

		loop = nodes.IteratorFor()
		loop.statements.contents = body
		loop.identifiers = start.warp.variables
		loop.expressions = start.warp.controls
		loop._addr = body[0].first_address

		_set_flow_to(start, body[0])

	elif isinstance(start.warp, nodes.NumericLoopWarp):
		assert isinstance(last.warp, nodes.UnconditionalWarp)
		assert last.warp.target == start

		loop = nodes.NumericFor()
		loop.statements.contents = body
		loop.variable = start.warp.index
		loop.expressions = start.warp.controls
		loop._addr = body[0].first_address
		_set_flow_to(start, body[0])

	# While (including "while true" and "repeat until false" which will be
	# while true)
	elif isinstance(last.warp, nodes.UnconditionalWarp):
		assert last.warp.target == start

		# while true
		if _is_flow(start.warp):
			loop = nodes.While()
			loop.expression = nodes.Primitive()
			loop.expression.type = nodes.Primitive.T_TRUE

			loop.statements.contents = body
		else:
			# There shouldn't be many problems similar to ifs, as
			# we are processing loops in the order from innermost
			# to outermost
			for i, block in enumerate(body):
				assert len(block.contents) == 0

				if _is_flow(block.warp):
					break

			assert i < len(body)

			expression = [start] + body[:i]
			body = body[i:]

			# Sometimes expression may decide to jump to the
			# outer loop start instead
			_fix_expression(expression, start, end)

			true = body[0]
			false = end

			expression = _compile_expression(expression, None,
								true, false)

			# If something jumps to the start (instead of the end)
			# - that's a nested if
			loop = nodes.While()
			loop.expression = expression
			loop.statements.contents = body

		_fix_nested_ifs(body, start)

		_set_flow_to(start, body[0])

	# Repeat until
	else:
		assert isinstance(last.warp, nodes.ConditionalWarp)
		assert last.warp.false_target == start

		i = len(body) - 1

		while i >= 0:
			block = body[i]
			warp = block.warp

			if _is_flow(warp):
				i += 1
				break

			if len(block.contents) != 0:
				break

			i -= 1

		expression = body[i:]
		body = body[:i + 1]

		assert len(expression) > 0

		first = expression[0]
		if _is_jump(first.warp):
			# Don't append to the body - it already has it
			expression.pop(0)
			body[-1].contents.append(nodes.Break())

		false = body[0]
		# Don't use end as it could be broken by a previous
		# repeat until pass
		true = expression[-1].warp.true_target

		loop = nodes.RepeatUntil()
		loop.expression = _compile_expression(expression, None,
								true, false)

		start_copy = copy.copy(start)
		start.contents = []

		if len(body) > 1:
			_set_flow_to(start_copy, body[1])
		else:
			_set_end(start_copy)

		_set_flow_to(start, start_copy)

		body[0] = start_copy

		loop.statements.contents = body

	return loop


def _create_next_block(original):
	block = nodes.Block()
	block.first_address = original.last_address + 1
	block.last_address = block.first_address
	block.index = original.index + 1
	block.warpins_count = original.warpins_count

	return block


def _set_flow_to(block, target):
	block.warp = nodes.UnconditionalWarp()
	block.warp.type = nodes.UnconditionalWarp.T_FLOW
	block.warp.target = target


def _set_end(block):
	target = None

	if block.warp is not None:
		target = _get_target(block.warp, allow_end=True)

	block.warp = nodes.EndWarp()

	setattr(block.warp, "_target", target)


def _is_flow(warp):
	return isinstance(warp, nodes.UnconditionalWarp)	\
		and warp.type == nodes.UnconditionalWarp.T_FLOW


def _is_jump(warp):
	return isinstance(warp, nodes.UnconditionalWarp)	\
		and warp.type == nodes.UnconditionalWarp.T_JUMP


def _fix_nested_ifs(blocks, start):
	# We can't point both targets of a conditional warp to the
	# same block. We will have to create a new block
	last = _create_next_block(blocks[-1])

	if isinstance(blocks[-1].warp, nodes.ConditionalWarp):
		blocks[-1].warp.false_target = last
	else:
		_set_flow_to(blocks[-1], last)

	blocks.append(last)
	_set_end(last)

	for block in blocks[:-1]:
		target = _get_target(block.warp)

		if target == start:
			_set_target(block.warp, last)


def _fix_expression(blocks, start, end):
	for block in blocks:
		if len(block.contents) != 0:
			break

		target = _get_target(block.warp)

		if target.index < start.index:
			_set_target(block.warp, end)


def _gather_possible_ends(block):
	warp = block.warp

	ends = set((block,))

	while _is_jump(warp):
		block = warp.target
		warp = block.warp

		ends.add(block)

	return ends


BREAK_INFINITE = 0
BREAK_ONE_USE = 1


def _unwarp_breaks(start, blocks, next_block):
	blocks_set = set([start] + blocks)

	ends = _gather_possible_ends(next_block)

	breaks = set()

	patched = []

	for i, block in enumerate(blocks):
		warp = block.warp

		if not isinstance(warp, nodes.UnconditionalWarp):
			patched.append(block)
			continue

		target = _get_target(warp)

		if target in blocks_set:
			patched.append(block)
			continue

		assert target in ends, "GOTO statements are not supported"

		if block.warpins_count != 0:
			new_block = _create_next_block(block)
			new_block.warpins_count = block.warpins_count
			_set_flow_to(block, new_block)

			patched.append(block)
			patched.append(new_block)

			block = new_block
		else:
			patched.append(block)

		block.contents.append(nodes.Break())

		if i + 1 == len(blocks):
			_set_end(block)
		else:
			_set_flow_to(block, blocks[i + 1])

		breaks.add(block)

	blocks[:] = patched

	if len(breaks) == 0:
		return

	breaks_stack = []
	warpsout = []
	pending_break = None

	for i, block in enumerate(reversed(blocks)):
		if block in breaks:
			pending_break = None

			if block.warpins_count == 0:
				breaks_stack.append((BREAK_ONE_USE, block))
			else:
				breaks_stack.append((BREAK_INFINITE, block))

			continue

		warp = block.warp

		if not isinstance(warp, nodes.ConditionalWarp):
			if _is_flow(warp):
				pending_break = None

			continue

		target = _get_target(warp)

		if target in blocks_set:
			continue

		assert target in ends, "GOTO statements are not supported"

		if pending_break is None:
			assert len(breaks_stack) > 0

			top_break = breaks_stack[-1]

			_set_target(warp, top_break[1])

			if top_break[0] == BREAK_ONE_USE:
				pending_break = breaks_stack.pop()

				warpsout = []
			else:
				warpsout.append(block)
		else:
			_set_target(warp, pending_break[1])
			warpsout.append(block)

		if len(block.contents) > 0:
			pending_break = None

	while len(breaks_stack) > 0 and breaks_stack[-1][0] == BREAK_INFINITE:
		breaks_stack.pop()

	# And pray for the best...
	while len(warpsout) > 0 and len(breaks_stack) > 0:
		_set_target(warpsout.pop().warp, breaks_stack.pop()[1])


#
# We don't need any complex checks here.
#
# Just search for any negative jump - that's a loop and what it's jumping to is
# a loop start.
#
def _find_all_loops(blocks, repeat_until):
	# Duplicates are NOT possible
	loops = []

	i = 0

	while i < len(blocks):
		block = blocks[i]
		warp = block.warp

		if isinstance(warp, nodes.UnconditionalWarp):
			if warp.type == nodes.UnconditionalWarp.T_FLOW:
				i += 1
				continue

			if warp.target.index <= block.index:
				assert not repeat_until
				assert i < len(blocks) - 1
				loops.append((warp.target, blocks[i + 1]))

		elif repeat_until and isinstance(warp, nodes.ConditionalWarp):
			if warp.false_target.index > block.index:
				i += 1
				continue

			start = warp.false_target
			first = block
			end = block
			last_i = i

			# Find the end of the expression
			while i < len(blocks):
				block = blocks[i]
				warp = block.warp

				if block != first and len(block.contents) != 0:
					break

				if isinstance(warp, nodes.EndWarp):
					break

				# Go up to a first negative jump of an
				# another loop

				target = _get_target(warp)
				if target.index < block.index:
					if target == start:
						start = target
						end = block
						last_i = i
					else:
						break

				i += 1

			# And then rollback to the last known negative jump
			# of our loop
			i = last_i

			# There always should be at least one return block
			end_index = blocks.index(end)
			end = blocks[end_index + 1]

			loops.append((start, end))

		i += 1

	# Reverse the order so inner "while" loops are processed before
	# outer loops
	return list(reversed(sorted(loops, key=lambda x: x[0].index)))


def _get_previous_block(block, blocks):
	block_index = blocks.index(block)

	assert block_index > 0

	return blocks[block_index - 1]


================================================
FILE: ljd/ast/validator.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import ljd.ast.nodes as nodes
import ljd.ast.traverse as traverse


class TypeRestriction():
	def __init__(self, default, specific):
		if isinstance(default, dict) and specific == {}:
			specific = default
			default = None

		self.default = default
		self.specific = specific

	def check(self, node):
		try:
			typespec = self.specific[node]
		except KeyError:
			typespec = self.default

		#assert typespec, "Unknown node: {0}".format(node)

		#assert isinstance(node, typespec),"Invalid node type: {0} should be: {1}".format(type(node), typespec)


STATEMENT_TYPES = (
	nodes.Assignment,
	nodes.If,
	nodes.IteratorFor,
	nodes.NumericFor,
	nodes.RepeatUntil,
	nodes.Return,
	nodes.Break,
	nodes.FunctionCall,
	nodes.While
)

EXPRESSION_TYPES = (
	nodes.FunctionCall,
	nodes.Primitive,
	nodes.Constant,
	nodes.Identifier,
	nodes.FunctionDefinition,
	nodes.TableConstructor,
	nodes.Vararg,
	nodes.BinaryOperator,
	nodes.UnaryOperator,
	nodes.MULTRES,
	nodes.TableElement,
)

VARIABLE_TYPES = (
	nodes.Identifier,
	nodes.TableElement,
	nodes.MULTRES  # It's not valid here, but it is a hack anyway...
)

WARP_TYPES = (
	nodes.UnconditionalWarp,
	nodes.ConditionalWarp,
	nodes.IteratorWarp,
	nodes.NumericLoopWarp,
	nodes.EndWarp
)


class Visitor(traverse.Visitor):
	def __init__(self, warped=True):
		# Restrictions for the upmost level
		self.restrictions = [None]
		self.warped = warped

	def _set_restrictions(self, default, specific={}):
		self.restrictions[-1] = TypeRestriction(default, specific)

	# ##

	def visit_function_definition(self, node):
		self._set_restrictions(nodes.Block, {
			node.arguments: nodes.IdentifiersList,
			node.statements: nodes.StatementsList
		})

	# ##

	def visit_table_constructor(self, node):
		self._set_restrictions(nodes.RecordsList)

	def visit_array_record(self, node):
		self._set_restrictions(EXPRESSION_TYPES)

	def visit_table_record(self, node):
		self._set_restrictions(EXPRESSION_TYPES)

	# ##

	def visit_assignment(self, node):
		self._set_restrictions({
			node.destinations: nodes.VariablesList,
			node.expressions: nodes.ExpressionsList
		})

		if not isinstance(node.destinations.contents[0], nodes.Identifier):
			return

		if node.destinations.contents[0].type != nodes.Identifier.T_LOCAL:
			return

		# Don't test type flag here
	# ##

	def visit_binary_operator(self, node):
		self._set_restrictions(EXPRESSION_TYPES)

		assert	node.type == nodes.BinaryOperator.T_LOGICAL_OR		\
			or node.type == nodes.BinaryOperator.T_LOGICAL_AND	\
										\
			or node.type == nodes.BinaryOperator.T_LESS_THEN	\
			or node.type == nodes.BinaryOperator.T_GREATER_THEN	\
			or node.type == nodes.BinaryOperator.T_LESS_OR_EQUAL	\
			or node.type == nodes.BinaryOperator.T_GREATER_OR_EQUAL	\
										\
			or node.type == nodes.BinaryOperator.T_NOT_EQUAL	\
			or node.type == nodes.BinaryOperator.T_EQUAL		\
										\
			or node.type == nodes.BinaryOperator.T_CONCAT		\
										\
			or node.type == nodes.BinaryOperator.T_ADD		\
			or node.type == nodes.BinaryOperator.T_SUBTRACT		\
										\
			or node.type == nodes.BinaryOperator.T_MULTIPLY		\
			or node.type == nodes.BinaryOperator.T_DIVISION		\
			or node.type == nodes.BinaryOperator.T_MOD		\
										\
			or node.type == nodes.BinaryOperator.T_POW

	def visit_unary_operator(self, node):
		self._set_restrictions(EXPRESSION_TYPES)

		assert node.type == nodes.UnaryOperator.T_NOT			\
			or node.type == nodes.UnaryOperator.T_LENGTH_OPERATOR	\
			or node.type == nodes.UnaryOperator.T_MINUS

	# ##

	def visit_statements_list(self, node):
		if self.warped:
			types = nodes.Block
		else:
			types = STATEMENT_TYPES

		self._set_restrictions(types)

	def visit_identifiers_list(self, node):
		# HACK
		self._set_restrictions((nodes.Identifier, nodes.Vararg))

	def visit_records_list(self, node):
		self._set_restrictions((nodes.TableRecord,
					nodes.ArrayRecord,
					nodes.FunctionCall,
					nodes.Vararg))

		if len(node.contents) == 0:
			return

		is_array = isinstance(node.contents[0], nodes.ArrayRecord)

		for i, x in enumerate(node.contents):
			if is_array:
				assert isinstance(x, nodes.ArrayRecord)
			elif not isinstance(x, nodes.TableRecord):
				assert i == (len(node.contents) - 1)

	def visit_variables_list(self, node):
		self._set_restrictions(VARIABLE_TYPES)

	def visit_expressions_list(self, node):
		self._set_restrictions(EXPRESSION_TYPES)

	# ##

	def visit_identifier(self, node):
		assert node.type == nodes.Identifier.T_SLOT		\
			or node.type == nodes.Identifier.T_BUILTIN	\
			or node.type == nodes.Identifier.T_UPVALUE	\
			or (node.name is not None			\
				and node.type == nodes.Identifier.T_LOCAL)

		assert node.type == nodes.Identifier.T_BUILTIN or node.slot >= 0

	def visit_table_element(self, node):
		self._set_restrictions(EXPRESSION_TYPES)

	def visit_function_call(self, node):
		self._set_restrictions({
			node.function: VARIABLE_TYPES,
			node.arguments: nodes.ExpressionsList
		})

	# ##

	def visit_if(self, node):
		self._set_restrictions(nodes.ElseIf, {
			node.expression: EXPRESSION_TYPES,
			node.then_block: nodes.StatementsList,
			node.else_block: nodes.StatementsList
		})

	def visit_elseif(self, node):
		self._set_restrictions({
			node.expression: EXPRESSION_TYPES,
			node.then_block: nodes.StatementsList
		})

	# ##

	def visit_block(self, node):
		self._set_restrictions(STATEMENT_TYPES, {
			node.warp: WARP_TYPES
		})

		assert node.index >= 0

		assert node.first_address >= 0			\
			and node.first_address <= node.last_address

		# if false produce a statements without warps in
		# assert node.warpins_count > 0

	def visit_unconditional_warp(self, node):
		assert node.target is not None

		assert node.type == nodes.UnconditionalWarp.T_JUMP	\
			or node.type == nodes.UnconditionalWarp.T_FLOW

	def visit_conditional_warp(self, node):
		self._set_restrictions({
			node.condition: EXPRESSION_TYPES
		})

		assert node.true_target is not None
		assert node.false_target is not None

		# It might happen in case of if blabla or true stuff
		# or in case of a = a and foo(a) or a type expression
		# assert node.true_target != node.false_target
		# assert node.true_target.index != node.false_target.index

	def visit_iterator_warp(self, node):
		assert node.body is not None
		assert node.way_out is not None

		assert node.way_out.index > node.body.index

		self._set_restrictions(nodes.Block, {
			node.variables: nodes.VariablesList,
			node.controls: nodes.ExpressionsList
		})

	def visit_numeric_loop_warp(self, node):
		assert node.body is not None
		assert node.way_out is not None

		self._set_restrictions(nodes.Block, {
			node.index: nodes.Identifier,
			node.controls: nodes.ExpressionsList
		})

	# ##

	def visit_return(self, node):
		self._set_restrictions(nodes.ExpressionsList)

	# ##

	def visit_while(self, node):
		self._set_restrictions({
			node.expression: EXPRESSION_TYPES,
			node.statements: nodes.StatementsList
		})

	def visit_repeat_until(self, node):
		self._set_restrictions({
			node.expression: EXPRESSION_TYPES,
			node.statements: nodes.StatementsList
		})

	def visit_numeric_for(self, node):
		self._set_restrictions({
			node.expressions: nodes.ExpressionsList,
			node.statements: nodes.StatementsList,
			node.variable: VARIABLE_TYPES
		})

	def visit_iterator_for(self, node):
		self._set_restrictions({
			node.expressions: nodes.ExpressionsList,
			node.identifiers: nodes.VariablesList,
			node.statements: nodes.StatementsList
		})

	# ##

	def visit_constant(self, node):
		assert	node.type == nodes.Constant.T_CDATA		\
			or node.type == nodes.Constant.T_FLOAT		\
			or node.type == nodes.Constant.T_INTEGER	\
			or node.type == nodes.Constant.T_STRING

	def visit_primitive(self, node):
		assert	node.type == nodes.Primitive.T_NIL		\
			or node.type == nodes.Primitive.T_TRUE		\
			or node.type == nodes.Primitive.T_FALSE

	# ##

	def _visit(self, node):
		restrictions = self.restrictions[-1]

		if restrictions is not None:
			restrictions.check(node)

		# Add layer for the child node
		self.restrictions.append(None)

		traverse.Visitor._visit(self, node)

		# And pop it back
		self.restrictions.pop()


def validate(ast, warped=True):
	visitor = Visitor(warped)
	traverse.traverse(visitor, ast)


================================================
FILE: ljd/bytecode/__init__.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#



================================================
FILE: ljd/bytecode/constants.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

T_NIL = 0
T_FALSE = 1
T_TRUE = 2


class Table():
	def __init__(self):
		self.array = []

		# Use a list so we can keep the original items order in the
		# table
		self.dictionary = []


class Constants():
	def __init__(self):
		self.upvalue_references = []
		self.numeric_constants = []
		self.complex_constants = []


================================================
FILE: ljd/bytecode/debuginfo.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#


class VariableInfo():
	T_VISIBILE = 0
	T_INTERNAL = 1

	def __init__(self):
		self.start_addr = 0
		self.end_addr = 0
		self.type = -1
		self.name = ""


class DebugInformation():
	def __init__(self):
		self.addr_to_line_map = []
		self.upvalue_variable_names = []
		self.variable_info = []

	def lookup_line_number(self, addr):
		try:
			return self.addr_to_line_map[addr]
		except IndexError:
			return 0

	def lookup_local_name(self, addr, slot):
		for info in self.variable_info:
			if info.start_addr > addr:
				break
			if info.end_addr <= addr:
				continue
			elif slot == 0:
				return info
			else:
				slot -= 1

		return None

	def lookup_upvalue_name(self, slot):
		try:
			return self.upvalue_variable_names[slot]
		except IndexError:
			return None


================================================
FILE: ljd/bytecode/helpers.py
================================================
def get_jump_destination(addr, instruction):
	return addr + instruction.CD + 1


def set_jump_destination(addr, instruction, value):
	instruction.CD = value - addr - 1


================================================
FILE: ljd/bytecode/instructions.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

#
# Almost direct wiki-to-code from
# http://wiki.luajit.org/Bytecode-2.0
#


# What the hell is an upvalue?
# It is value from the upper prototype, i.e. a variable stored in a closure.

# What the hell is the MULTRES thing?
# The magical VM's variable that counts the CALL* or VARG returns.
# I.e. it's used to speed-up things like foo(var1, var2, bar(var3, var4)) or
#
# function foo(...)
# 	bla-bla
# 	bar(...)
#

# Argument types

T_VAR = 0  # variable slot number
T_DST = 1  # variable slot number, used as a destination

T_BS = 2  # base slot number, read-write
T_RBS = 3  # base slot number, read-only

T_UV = 4  # upvalue number (slot number, but specific to upvalues)

T_LIT = 5  # literal
T_SLIT = 6  # signed literal

T_PRI = 7  # primitive type (0 = nil, 1 = false, 2 = true)
T_NUM = 8  # numeric constant, index into constant table
T_STR = 9  # string constant, negated index into constant table

T_TAB = 10  # template table, negated index into constant table
T_FUN = 11  # function prototype, negated index into constant table
T_CDT = 12  # cdata constant, negated index into constant table
T_JMP = 13  # branch target, relative to next instruction, biased with 0x8000


class _Instruction():
	def __init__(self, definition):
		for key, value in definition.__dict__.items():
			setattr(self, key, value)

		if self.A_type is not None:
			self.A = 0

		if self.B_type is not None:
			self.B = 0

		if self.CD_type is not None:
			self.CD = 0


class _IDef():
	_LAST_OPCODE = 0

	def __init__(self, name, A_type, B_type, CD_type, description):
		self.name = name
		self.opcode = _IDef._LAST_OPCODE
		self.A_type = A_type
		self.B_type = B_type
		self.CD_type = CD_type
		self.description = description

		self.args_count = (self.A_type is not None)	\
				+ (self.B_type is not None)	\
				+ (self.CD_type is not None)

		_IDef._LAST_OPCODE += 1

	def __call__(self):
		return _Instruction(self)


# Names and order are in sync with luaJIT bytecode for ease of changing

# class = name			A	B	C	description
# Comparison ops

ISLT = _IDef("ISLT", 		T_VAR, 	None, 	T_VAR, 	"if {A} < {D}")
ISGE = _IDef("ISGE", 		T_VAR, 	None, 	T_VAR, 	"if {A} >= {D}")
ISLE = _IDef("ISLE", 		T_VAR, 	None, 	T_VAR, 	"if {A} <= {D}")
ISGT = _IDef("ISGT", 		T_VAR, 	None, 	T_VAR, 	"if {A} > {D}")

ISEQV = _IDef("ISEQV", 		T_VAR, 	None, 	T_VAR, 	"if {A} == {D}")
ISNEV = _IDef("ISNEV", 		T_VAR, 	None, 	T_VAR, 	"if {A} ~= {D}")

ISEQS = _IDef("ISEQS", 		T_VAR, 	None, 	T_STR, 	"if {A} == {D}")
ISNES = _IDef("ISNES", 		T_VAR, 	None, 	T_STR, 	"if {A} ~= {D}")

ISEQN = _IDef("ISEQN", 		T_VAR, 	None, 	T_NUM, 	"if {A} == {D}")
ISNEN = _IDef("ISNEN", 		T_VAR, 	None, 	T_NUM, 	"if {A} ~= {D}")

ISEQP = _IDef("ISEQP", 		T_VAR, 	None, 	T_PRI, 	"if {A} == {D}")
ISNEP = _IDef("ISNEP", 		T_VAR, 	None, 	T_PRI, 	"if {A} ~= {D}")

# Unary test and copy ops

ISTC = _IDef("ISTC", 		T_DST, 	None, 	T_VAR, 	"{A} = {D}; if {D}")
ISFC = _IDef("ISFC", 		T_DST, 	None, 	T_VAR, 	"{A} = {D}; if not {D}")

IST = _IDef("IST", 		None, 	None, 	T_VAR, 	"if {D}")
ISF = _IDef("ISF", 		None, 	None, 	T_VAR, 	"if not {D}")

ISTYPE = _IDef("ISTYPE", 	T_VAR, 	None, 	T_LIT, 	"ISTYPE unknow")
ISNUM = _IDef("ISNUM", 		T_VAR, 	None, 	T_LIT, 	"ISNUM unknow")
# Unary ops

MOV = _IDef("MOV", 		T_DST, 	None, 	T_VAR, 	"{A} = {D}")
NOT = _IDef("NOT", 		T_DST, 	None, 	T_VAR, 	"{A} = not {D}")
UNM = _IDef("UNM", 		T_DST, 	None, 	T_VAR, 	"{A} = -{D}")
LEN = _IDef("LEN", 		T_DST, 	None, 	T_VAR, 	"{A} = #{D}")

# Binary ops

ADDVN = _IDef("ADDVN", 		T_DST, 	T_VAR, 	T_NUM, 	"{A} = {B} + {C}")
SUBVN = _IDef("SUBVN", 		T_DST, 	T_VAR, 	T_NUM, 	"{A} = {B} - {C}")
MULVN = _IDef("MULVN", 		T_DST, 	T_VAR, 	T_NUM, 	"{A} = {B} * {C}")
DIVVN = _IDef("DIVVN", 		T_DST, 	T_VAR, 	T_NUM, 	"{A} = {B} / {C}")
MODVN = _IDef("MODVN", 		T_DST, 	T_VAR, 	T_NUM, 	"{A} = {B} % {C}")

ADDNV = _IDef("ADDNV", 		T_DST, 	T_VAR, 	T_NUM, 	"{A} = {C} + {B}")
SUBNV = _IDef("SUBNV", 		T_DST, 	T_VAR, 	T_NUM, 	"{A} = {C} - {B}")
MULNV = _IDef("MULNV", 		T_DST, 	T_VAR, 	T_NUM, 	"{A} = {C} * {B}")
DIVNV = _IDef("DIVNV", 		T_DST, 	T_VAR, 	T_NUM, 	"{A} = {C} / {B}")
MODNV = _IDef("MODNV", 		T_DST, 	T_VAR, 	T_NUM, 	"{A} = {C} % {B}")

ADDVV = _IDef("ADDVV", 		T_DST, 	T_VAR, 	T_VAR, 	"{A} = {B} + {C}")
SUBVV = _IDef("SUBVV", 		T_DST, 	T_VAR, 	T_VAR, 	"{A} = {B} - {C}")
MULVV = _IDef("MULVV", 		T_DST, 	T_VAR, 	T_VAR, 	"{A} = {B} * {C}")
DIVVV = _IDef("DIVVV", 		T_DST, 	T_VAR, 	T_VAR, 	"{A} = {B} / {C}")
MODVV = _IDef("MODVV", 		T_DST, 	T_VAR, 	T_VAR, 	"{A} = {B} % {C}")

POW = _IDef("POW", 		T_DST, 	T_VAR, 	T_VAR, 	"{A} = {B} ^ {C} (pow)")
CAT = _IDef("CAT", 		T_DST, 	T_RBS, 	T_RBS,
		"{A} = {concat_from_B_to_C}")

# Constant ops.

KSTR = _IDef("KSTR", 		T_DST, 	None, 	T_STR, 	"{A} = {D}")
KCDATA = _IDef("KCDATA", 	T_DST, 	None, 	T_CDT, 	"{A} = {D}")
KSHORT = _IDef("KSHORT", 	T_DST, 	None, 	T_SLIT, "{A} = {D}")
KNUM = _IDef("KNUM", 		T_DST, 	None, 	T_NUM, 	"{A} = {D}")
KPRI = _IDef("KPRI", 		T_DST, 	None, 	T_PRI, 	"{A} = {D}")

KNIL = _IDef("KNIL", 		T_BS, 	None, 	T_BS, 	"{from_A_to_D} = nil")

# Upvalue and function ops.

UGET = _IDef("UGET", 		T_DST, 	None, 	T_UV, 	"{A} = {D}")

USETV = _IDef("USETV", 		T_UV, 	None, 	T_VAR, 	"{A} = {D}")
USETS = _IDef("USETS", 		T_UV, 	None, 	T_STR, 	"{A} = {D}")
USETN = _IDef("USETN", 		T_UV, 	None, 	T_NUM, 	"{A} = {D}")
USETP = _IDef("USETP", 		T_UV, 	None, 	T_PRI, 	"{A} = {D}")

UCLO = _IDef("UCLO", 		T_RBS, 	None, 	T_JMP,
		"nil uvs >= {A}; goto {D}")

FNEW = _IDef("FNEW", 		T_DST, 	None, 	T_FUN, 	"{A} = function {D}")

# Table ops.

TNEW = _IDef("TNEW", 		T_DST, 	None, 	T_LIT, 	"{A} = new table("
							" array: {D_array},"
							" dict: {D_dict})")

TDUP = _IDef("TDUP", 		T_DST, 	None, 	T_TAB, 	"{A} = copy {D}")

GGET = _IDef("GGET", 		T_DST, 	None, 	T_STR, 	"{A} = _env[{D}]")
GSET = _IDef("GSET", 		T_VAR, 	None, 	T_STR, 	"_env[{D}] = {A}")

TGETV = _IDef("TGETV", 		T_DST, 	T_VAR, 	T_VAR, 	"{A} = {B}[{C}]")
TGETS = _IDef("TGETS", 		T_DST, 	T_VAR, 	T_STR, 	"{A} = {B}.{C}")
TGETB = _IDef("TGETB", 		T_DST, 	T_VAR, 	T_LIT, 	"{A} = {B}[{C}]")
TGETR = _IDef("TGETR", 		T_DST, 	T_VAR, 	T_VAR, 	"unkown TGETR")

TSETV = _IDef("TSETV", 		T_VAR, 	T_VAR, 	T_VAR, 	"{B}[{C}] = {A}")
TSETS = _IDef("TSETS", 		T_VAR, 	T_VAR, 	T_STR, 	"{B}.{C} = {A}")
TSETB = _IDef("TSETB", 		T_VAR, 	T_VAR, 	T_LIT, 	"{B}[{C}] = {A}")

TSETM = _IDef("TSETM", 	 	T_BS, 	None, 	T_NUM,
		"for i = 0, MULTRES, 1 do"
		" {A_minus_one}[{D_low} + i] = slot({A} + i)")

TSETR = _IDef("TSETR", 		T_VAR, 	T_VAR, 	T_VAR, 	"unkow TSETR")
# Calls and vararg handling. T = tail call.

CALLM = _IDef("CALLM", 		T_BS, 	T_LIT, 	T_LIT,
		"{from_A_x_B_minus_two} = {A}({from_A_plus_one_x_C}, ...MULTRES)")

CALL = _IDef("CALL", 		T_BS, 	T_LIT, 	T_LIT,
		"{from_A_x_B_minus_two} = {A}({from_A_plus_one_x_C_minus_one})")

CALLMT = _IDef("CALLMT", 	T_BS, 	None, 	T_LIT,
		"return {A}({from_A_plus_one_x_D}, ...MULTRES)")

CALLT = _IDef("CALLT", 		T_BS, 	None, 	T_LIT,
		"return {A}({from_A_plus_one_x_D_minus_one})")

ITERC = _IDef("ITERC", 		T_BS, 	T_LIT, 	T_LIT,
		"{A}, {A_plus_one}, {A_plus_two} ="
			" {A_minus_three}, {A_minus_two}, {A_minus_one};"
		" {from_A_x_B_minus_two} ="
			" {A_minus_three}({A_minus_two}, {A_minus_one})")

ITERN = _IDef("ITERN", 		T_BS, 	T_LIT, 	T_LIT,
		"{A}, {A_plus_one}, {A_plus_two} ="
			" {A_minus_three}, {A_minus_two}, {A_minus_one};"
		" {from_A_x_B_minus_two} ="
			" {A_minus_three}({A_minus_two}, {A_minus_one})")

VARG = _IDef("VARG", 		T_BS, 	T_LIT, 	T_LIT,
		"{from_A_x_B_minus_two} = ...")

ISNEXT = _IDef("ISNEXT", 	 T_BS, 	None, 	T_JMP,
		"Verify ITERN at {D}; goto {D}")

# Returns.

RETM = _IDef("RETM", 		T_BS, 	None, 	T_LIT,
		"return {from_A_x_D_minus_one}, ...MULTRES")

RET = _IDef("RET", 	 	T_RBS, 	None, 	T_LIT,
		"return {from_A_x_D_minus_two}")

RET0 = _IDef("RET0", 		T_RBS, 	None, 	T_LIT, 	"return")
RET1 = _IDef("RET1", 		T_RBS, 	None, 	T_LIT, 	"return {A}")

# Loops and branches. I/J = interp/JIT, I/C/L = init/call/loop.

FORI = _IDef("FORI", 		T_BS, 	None, 	T_JMP,
		"for {A_plus_three} = {A},{A_plus_one},{A_plus_two}"
		" else goto {D}")

JFORI = _IDef("JFORI", 		T_BS, 	None, 	T_JMP,
		"for {A_plus_three} = {A},{A_plus_one},{A_plus_two}"
		" else goto {D}")

FORL = _IDef("FORL", 		T_BS, 	None, 	T_JMP,
		"{A} = {A} + {A_plus_two};"
		" if cmp({A}, sign {A_plus_two},  {A_plus_one}) goto {D}")

IFORL = _IDef("IFORL", 	 	T_BS, 	None, 	T_JMP,
		"{A} = {A} + {A_plus_two};"
		" if cmp({A}, sign {A_plus_two}, {A_plus_one}) goto {D}")

JFORL = _IDef("JFORL", 		T_BS, 	None, 	T_JMP,
		"{A} = {A} + {A_plus_two};"
		" if cmp({A}, sign {A_plus_two}, {A_plus_one}) goto {D}")

ITERL = _IDef("ITERL", 		T_BS, 	None, 	T_JMP,
		"{A_minus_one} = {A}; if {A} != nil goto {D}")

IITERL = _IDef("IITERL", 	T_BS, 	None, 	T_JMP,
		"{A_minus_one} = {A}; if {A} != nil goto {D}")

JITERL = _IDef("JITERL", 	T_BS, 	None, 	T_LIT,
		"{A_minus_one} = {A}; if {A} != nil goto {D}")

LOOP = _IDef("LOOP", 		T_RBS, 	None, 	T_JMP, 	"Noop")
ILOOP = _IDef("ILOOP", 		T_RBS, 	None, 	T_JMP, 	"Noop")
JLOOP = _IDef("JLOOP", 		T_RBS, 	None, 	T_LIT, 	"Noop")

JMP = _IDef("JMP", 		T_RBS, 	None, 	T_JMP, 	"	goto {D}")

# Function headers. I/J = interp/JIT, F/V/C = fixarg/vararg/C func.
# Shouldn't be ever seen - they are not stored in raw dump?

FUNCF = _IDef("FUNCF", 		T_RBS, 	None, 	None,
		"Fixed-arg function with frame size {A}")

IFUNCF = _IDef("IFUNCF", 	T_RBS, 	None, 	None,
		"Interpreted fixed-arg function with frame size {A}")

JFUNCF = _IDef("JFUNCF", 	T_RBS, 	None, 	T_LIT,
		"JIT compiled fixed-arg function with frame size {A}")

FUNCV = _IDef("FUNCV", 		T_RBS, 	None, 	None,
		"Var-arg function with frame size {A}")

IFUNCV = _IDef("IFUNCV", 	T_RBS, 	None, 	None,
		"Interpreted var-arg function with frame size {A}")

JFUNCV = _IDef("JFUNCV", 	T_RBS, 	None, 	T_LIT,
		"JIT compiled var-arg function with frame size {A}")

FUNCC = _IDef("FUNCC", 		T_RBS, 	None, 	None,
		"C function with frame size {A}")
FUNCCW = _IDef("FUNCCW", 	T_RBS, 	None, 	None,
		"Wrapped C function with frame size {A}")

UNKNW = _IDef("UNKNW", 		T_LIT, 	T_LIT, 	T_LIT, "Unknown instruction")


================================================
FILE: ljd/bytecode/prototype.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import ljd.bytecode.constants as constants
import ljd.bytecode.debuginfo as debug


class Flags():
	def __init(self):
		self.has_sub_prototypes = False
		self.is_variadic = False
		self.has_ffi = False
		self.has_jit = True
		self.has_iloop = False


class Prototype():
	def __init__(self):
		self.flags = Flags()

		self.arguments_count = 0

		self.framesize = 0

		self.first_line_number = 0
		self.lines_count = 0

		self.instructions = []
		self.constants = constants.Constants()
		self.debuginfo = debug.DebugInformation()


================================================
FILE: ljd/lua/__init__.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#



================================================
FILE: ljd/lua/writer.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import re

import ljd.ast.nodes as nodes
import ljd.ast.traverse as traverse


CMD_START_STATEMENT = 0
CMD_END_STATEMENT = 1
CMD_END_LINE = 3
CMD_START_BLOCK = 4
CMD_END_BLOCK = 5
CMD_WRITE = 6


OPERATOR_TYPES = (nodes.BinaryOperator, nodes.UnaryOperator)

STATEMENT_NONE = -1

STATEMENT_ASSIGNMENT = 0
STATEMENT_FUNCTION_CALL = 1
STATEMENT_RETURN = 2
STATEMENT_BREAK = 3

STATEMENT_IF = 4

STATEMENT_ITERATOR_FOR = 5
STATEMENT_NUMERIC_FOR = 6
STATEMENT_REPEAT_UNTIL = 7
STATEMENT_WHILE = 8

STATEMENT_FUNCTION = 9

VALID_IDENTIFIER = re.compile(r'^\w[\w\d]*$')


class _State():
	def __init__(self):
		self.current_statement = STATEMENT_NONE
		self.function_name = None
		self.function_local = False


class Visitor(traverse.Visitor):
	def __init__(self):
		traverse.Visitor.__init__(self)

		self.print_queue = []

		self._visited_nodes = [set()]
		self._states = [_State()]

	# ##

	def _start_statement(self, statement):
		assert self._state().current_statement == STATEMENT_NONE
		self._state().current_statement = statement
		self.print_queue.append((CMD_START_STATEMENT, statement))

	def _end_statement(self, statement):
		assert statement == self._state().current_statement
		self._state().current_statement = STATEMENT_NONE
		self.print_queue.append((CMD_END_STATEMENT, statement))

	def _end_line(self):
		self.print_queue.append((CMD_END_LINE,))

	def _start_block(self):
		self.print_queue.append((CMD_START_BLOCK,))

	def _end_block(self):
		self.print_queue.append((CMD_END_BLOCK,))

	def _write(self, fmt, *args, **kargs):
		self.print_queue.append((CMD_WRITE, fmt, args, kargs))

	def _state(self):
		return self._states[-1]

	def _push_state(self):
		return self._states.append(_State())

	def _pop_state(self):
		return self._states.pop()

	# ##

	def visit_function_definition(self, node):
		is_statement = self._state().function_name is not None

		if is_statement:
			self._start_statement(STATEMENT_FUNCTION)

			if self._state().function_local:
				self._write("local ")

			self._write("function ")

			self._visit(self._state().function_name)

			self._write("(")

			self._state().function_name = None
		else:
			self._write("function (")

		self._visit(node.arguments)

		self._write(")")

		self._end_line()

		self._visit(node.statements)

		self._write("end")

		if is_statement:
			self._end_statement(STATEMENT_FUNCTION)

	# ##

	def visit_table_constructor(self, node):
		self._write("{")

		if len(node.records.contents) + len(node.array.contents) > 0:
			self._end_line()

			self._start_block()

			array = node.array.contents
			records = node.records.contents

			all_records = nodes.RecordsList()
			all_records.contents = array + records

			if len(array) > 0:
				first = array[0].value

				all_records.contents.pop(0)

				if not isinstance(first, nodes.Primitive) \
						or first.type != first.T_NIL:
					record = nodes.TableRecord()
					record.key = nodes.Constant()
					record.key.type = nodes.Constant.T_INTEGER
					record.key.value = 0
					record.value = first

					all_records.contents.insert(0, record)

			self._visit(all_records)

			self._skip(node.array)
			self._skip(node.records)

			self._end_block()
		else:
			self._skip(node.array)
			self._skip(node.records)

		self._write("}")

	def visit_table_record(self, node):
		if self._is_valid_name(node.key):
			self._write(node.key.value)

			self._skip(node.key)

			self._write(" = ")
		else:
			self._write("[")

			self._visit(node.key)

			self._write("] = ")

		self._visit(node.value)

	# visit_array_record is a passthough

	# ##

	def visit_assignment(self, node):
		is_local = node.type == node.T_LOCAL_DEFINITION

		dsts = node.destinations.contents
		srcs = node.expressions.contents

		if len(dsts) == 1 and len(srcs) == 1:
			dst = dsts[0]
			src = srcs[0]

			src_is_function = isinstance(src, nodes.FunctionDefinition)
			dst_is_simple = self._is_variable(dst)

			if src_is_function and dst_is_simple:
				self._state().function_name = dst
				self._state().function_local = is_local

				self._visit(src)

				self._skip(node.destinations)
				self._skip(node.expressions)

				return

		if is_local:
			self._write("local ")

		self._start_statement(STATEMENT_ASSIGNMENT)

		self._visit(node.destinations)

		self._write(" = ")

		self._visit(node.expressions)

		self._end_statement(STATEMENT_ASSIGNMENT)

	def _is_variable(self, node):
		if isinstance(node, nodes.Identifier):
			return True

		return self._is_global(node)

	def _is_global(self, node):
		if isinstance(node, nodes.TableElement):
			return self._is_builtin(node.table)

		return False

	def _is_builtin(self, node):
		if not isinstance(node, nodes.Identifier):
			return False

		return node.type == nodes.Identifier.T_BUILTIN

	def _is_valid_name(self, key):
		if not isinstance(key, nodes.Constant) or key.type != key.T_STRING:
			return False

		return VALID_IDENTIFIER.match(key.value)

	# ##

	def visit_binary_operator(self, node):
		is_left_op = isinstance(node.left, OPERATOR_TYPES)
		is_right_op = isinstance(node.right, OPERATOR_TYPES)

		# If the subexpressions are less in order then this expression,
		# they should go with parentheses

		left_parentheses = False
		right_parentheses = False

		binop = nodes.BinaryOperator

		if is_left_op:
			if node.type <= binop.T_LOGICAL_AND:
				left_parentheses = (
					node.left.type <= node.type
					or (node.left.type <= binop.T_LOGICAL_AND
						and node.type <= binop.T_LOGICAL_AND)
				) and node.left.type != node.type
			else:
				left_parentheses = node.left.type < node.type

		if is_right_op:
			if node.type <= nodes.BinaryOperator.T_LOGICAL_AND:
				right_parentheses = (
					node.right.type <= node.type
					or (node.right.type <= binop.T_LOGICAL_AND
						and node.type <= binop.T_LOGICAL_AND)
				) and node.right.type != node.type
			else:
				right_parentheses = node.right.type < node.type

		if left_parentheses:
			self._write("(")

		self._visit(node.left)

		if left_parentheses:
			self._write(")")

		if node.type == nodes.BinaryOperator.T_LOGICAL_OR:
			self._write(" or ")
		elif node.type == nodes.BinaryOperator.T_LOGICAL_AND:
			self._write(" and ")

		elif node.type == nodes.BinaryOperator.T_LESS_THEN:
			self._write(" < ")
		elif node.type == nodes.BinaryOperator.T_GREATER_THEN:
			self._write(" > ")
		elif node.type == nodes.BinaryOperator.T_LESS_OR_EQUAL:
			self._write(" <= ")
		elif node.type == nodes.BinaryOperator.T_GREATER_OR_EQUAL:
			self._write(" >= ")

		elif node.type == nodes.BinaryOperator.T_NOT_EQUAL:
			self._write(" ~= ")
		elif node.type == nodes.BinaryOperator.T_EQUAL:
			self._write(" == ")

		elif node.type == nodes.BinaryOperator.T_CONCAT:
			self._write(" .. ")

		elif node.type == nodes.BinaryOperator.T_ADD:
			self._write(" + ")
		elif node.type == nodes.BinaryOperator.T_SUBTRACT:
			self._write(" - ")

		elif node.type == nodes.BinaryOperator.T_DIVISION:
			self._write("/")
		elif node.type == nodes.BinaryOperator.T_MULTIPLY:
			self._write("*")
		elif node.type == nodes.BinaryOperator.T_MOD:
			self._write("%")

		else:
			assert node.type == nodes.BinaryOperator.T_POW
			self._write("^")

		if right_parentheses:
			self._write("(")

		self._visit(node.right)

		if right_parentheses:
			self._write(")")

	def visit_unary_operator(self, node):
		if node.type == nodes.UnaryOperator.T_LENGTH_OPERATOR:
			self._write("#")
		elif node.type == nodes.UnaryOperator.T_MINUS:
			self._write("-")
		elif node.type == nodes.UnaryOperator.T_NOT:
			self._write("not ")

		has_subexp = isinstance(node.operand, OPERATOR_TYPES)
		need_parentheses = has_subexp and node.operand.type < node.type

		if need_parentheses:
			self._write("(")

		self._visit(node.operand)

		if need_parentheses:
			self._write(")")

	# ##

	def visit_statements_list(self, node):
		if len(self._states) > 1:
			self._start_block()

		self._push_state()

	def leave_statements_list(self, node):
		self._pop_state()

		if len(self._states) > 1:
			self._end_block()

	def _visit_comma_separated_list(self, node):
		if node.contents == []:
			return

		for subnode in node.contents[:-1]:
			self._visit(subnode)
			self._write(", ")

		self._visit(node.contents[-1])

	visit_identifiers_list = _visit_comma_separated_list

	def visit_records_list(self, node):
		if node.contents == []:
			return

		for subnode in node.contents[:-1]:
			self._visit(subnode)

			self._write(",")
			self._end_line()

		self._visit(node.contents[-1])
		self._end_line()

	visit_variables_list = _visit_comma_separated_list
	visit_expressions_list = _visit_comma_separated_list

	# ##

	def visit_identifier(self, node):
		if node.type == nodes.Identifier.T_SLOT:
			self._write("slot{0}", node.slot)
		else:
			self._write(node.name)

	def visit_multres(self, node):
		self._write("MULTRES")

	def visit_table_element(self, node):
		key = node.key
		base = node.table

		is_valid_name = self._is_valid_name(key)

		if self._is_global(node):
			assert is_valid_name

			self._skip(base)
			self._skip(key)

			self._write(key.value)

			return

		base_is_constructor = isinstance(base, nodes.TableConstructor)

		if not base_is_constructor and is_valid_name:
			self._visit(base)
			self._write(".")

			self._write(key.value)
			self._skip(key)
		else:
			if base_is_constructor:
				self._write("(")

			self._visit(base)

			if base_is_constructor:
				self._write(")")

			self._write("[")

			self._visit(key)

			self._write("]")

	def visit_vararg(self, node):
		self._write("...")

	def visit_function_call(self, node):
		is_statement = self._state().current_statement == STATEMENT_NONE

		if is_statement:
			self._start_statement(STATEMENT_FUNCTION_CALL)

		# We are going to modify this list so we can remove the first
		# argument
		args = node.arguments.contents
		is_method = False

		if len(args) > 0 and isinstance(node.function, nodes.TableElement):
			table = node.function.table

			first_arg = node.arguments.contents[0]

			if self._is_valid_name(node.function.key):
				is_method = table == first_arg

		if is_method:
			self._visit(node.function.table)
			self._write(":")
			self._write(node.function.key.value)

			self._skip(node.function)

			args.pop(0)

			self._write("(")
			self._visit(node.arguments)
			self._write(")")

			self._skip(node.arguments)
		else:
			self._visit(node.function)

			self._write("(")
			self._visit(node.arguments)
			self._write(")")

		if is_statement:
			self._end_statement(STATEMENT_FUNCTION_CALL)

	# ##

	def visit_if(self, node):
		self._start_statement(STATEMENT_IF)

		self._write("if ")

		self._visit(node.expression)

		self._write(" then")

		self._end_line()

		self._visit(node.then_block)

		self._visit_list(node.elseifs)

		if len(node.else_block.contents) > 0:
			self._write("else")

			self._end_line()

			self._visit(node.else_block)
		else:
			self._skip(node.else_block)

		self._write("end")

		self._end_statement(STATEMENT_IF)

	def visit_elseif(self, node):
		self._write("elseif ")

		self._visit(node.expression)

		self._write(" then")

		self._end_line()

		self._visit(node.then_block)

	# ##

	def visit_block(self, node):
		self._write("--- BLOCK #{0} {1}-{2}, warpins: {3} ---",
					node.index,
					node.first_address, node.last_address,
					node.warpins_count)

		self._end_line()

		self._visit_list(node.contents)

		self._write("--- END OF BLOCK #{0} ---", node.index)

		self._end_line()

		self._end_line()
		self._visit(node.warp)
		self._end_line()

		self._end_line()

	def visit_unconditional_warp(self, node):
		if node.type == nodes.UnconditionalWarp.T_FLOW:
			self._write("FLOW")
		elif node.type == nodes.UnconditionalWarp.T_JUMP:
			self._write("UNCONDITIONAL JUMP")

		self._write("; TARGET BLOCK #{0}", node.target.index)

		self._end_line()

	def visit_conditional_warp(self, node):
		if hasattr(node, "_slot"):
			self._write("slot" + str(node._slot))
			self._write(" = ")

		self._write("if ")

		self._visit(node.condition)

		self._write(" then")
		self._end_line()

		self._write("JUMP TO BLOCK #{0}", node.true_target.index)

		self._end_line()
		self._write("else")
		self._end_line()

		self._write("JUMP TO BLOCK #{0}", node.false_target.index)

		self._end_line()

		self._write("end")
		self._end_line()

	def visit_iterator_warp(self, node):
		self._write("for ")

		self._visit(node.variables)

		self._write(" in ")

		self._visit(node.controls)

		self._end_line()
		self._write("LOOP BLOCK #{0}", node.body.index)

		self._end_line()
		self._write("GO OUT TO BLOCK #{0}", node.way_out.index)

		self._end_line()

	def visit_numeric_loop_warp(self, node):
		self._write("for ")

		self._visit(node.index)

		self._write("=")

		self._visit(node.controls)

		self._end_line()
		self._write("LOOP BLOCK #{0}", node.body.index)

		self._end_line()
		self._write("GO OUT TO BLOCK #{0}", node.way_out.index)

	# ##

	def visit_return(self, node):
		self._start_statement(STATEMENT_RETURN)

		self._write("return ")

		self._visit(node.returns)

		self._end_statement(STATEMENT_RETURN)

	def visit_break(self, node):
		self._start_statement(STATEMENT_BREAK)

		self._write("break")

		self._end_statement(STATEMENT_BREAK)

	# ##

	def visit_while(self, node):
		self._start_statement(STATEMENT_WHILE)

		self._write("while ")
		self._visit(node.expression)
		self._write(" do")

		self._end_line()

		self._visit(node.statements)

		self._write("end")
		self._end_statement(STATEMENT_WHILE)

	def visit_repeat_until(self, node):
		self._start_statement(STATEMENT_REPEAT_UNTIL)

		self._write("repeat")
		self._end_line()

		self._visit(node.statements)

		self._write("until ")
		self._visit(node.expression)

		self._end_statement(STATEMENT_REPEAT_UNTIL)

	def visit_numeric_for(self, node):
		self._start_statement(STATEMENT_NUMERIC_FOR)

		self._write("for ")
		self._visit(node.variable)
		self._write(" = ")

		self._visit(node.expressions)

		self._write(" do")

		self._end_line()

		self._visit(node.statements)

		self._write("end")
		self._end_statement(STATEMENT_NUMERIC_FOR)

	def visit_iterator_for(self, node):
		self._start_statement(STATEMENT_ITERATOR_FOR)

		self._write("for ")
		self._visit(node.identifiers)
		self._write(" in ")
		self._visit(node.expressions)
		self._write(" do")

		self._end_line()

		self._visit(node.statements)

		self._write("end")
		self._end_statement(STATEMENT_ITERATOR_FOR)

	# ##

	def visit_constant(self, node):
		if node.type != nodes.Constant.T_STRING:
			self._write(node.value)
			return

		lines = node.value.count("\n")

		if lines > 2:
			self._write("[[")

			self._write("\n")

			self._write(node.value)

			self._write("]]")
		else:
			text = node.value

			text = text.replace("\\", "\\\\")
			text = text.replace("\t", "\\t")
			text = text.replace("\n", "\\n")
			text = text.replace("\r", "\\r")
			text = text.replace("\"", "\\\"")

			self._write('"' + text + '"')

	def visit_primitive(self, node):
		if node.type == nodes.Primitive.T_FALSE:
			self._write("false")
		elif node.type == nodes.Primitive.T_TRUE:
			self._write("true")
		else:
			self._write("nil")

	def _skip(self, node):
		self._visited_nodes[-1].add(node)

	def _visit(self, node):
		assert node is not None

		if node in self._visited_nodes[-1]:
			return

		self._visited_nodes[-1].add(node)

		# TODO: add check
		# "It looks like you forgot about some node changes..."

		self._visited_nodes.append(set())

		traverse.Visitor._visit(self, node)

		self._visited_nodes.pop()


def write(fd, ast):
	assert isinstance(ast, nodes.FunctionDefinition)

	visitor = Visitor()

	traverse.traverse(visitor, ast.statements)

	_process_queue(fd, visitor.print_queue)


def _get_next_significant(queue, i):
	i += 1

	while i < len(queue):
		cmd = queue[i]

		if cmd[0] not in (CMD_END_LINE, CMD_WRITE):
			break

		i += 1

	if i < len(queue):
		return queue[i]
	else:
		return (CMD_END_BLOCK,)


def _process_queue(fd, queue):
	indent = 0

	line_broken = True

	for i, cmd in enumerate(queue):
		assert isinstance(cmd, tuple)

		if cmd[0] == CMD_START_STATEMENT:
			# assert line_broken
			pass
		elif cmd[0] == CMD_END_STATEMENT:
			fd.write("\n")
			line_broken = True

			next_cmd = _get_next_significant(queue, i)

			if next_cmd[0] not in (CMD_END_BLOCK, CMD_START_BLOCK):
				assert next_cmd[0] == CMD_START_STATEMENT

				if next_cmd[1] != cmd[1]			\
						or cmd[1] >= STATEMENT_IF	\
						or next_cmd[1] >= STATEMENT_IF:
					fd.write("\n")
		elif cmd[0] == CMD_END_LINE:
			fd.write("\n")
			line_broken = True
		elif cmd[0] == CMD_START_BLOCK:
			indent += 1
		elif cmd[0] == CMD_END_BLOCK:
			indent -= 1

			assert indent >= 0
		else:
			assert cmd[0] == CMD_WRITE

			if line_broken:
				fd.write(indent * '\t')
				line_broken = False

			_id, fmt, args, kargs = cmd

			if len(args) + len(kargs) > 0:
				text = fmt.format(*args, **kargs)
			elif isinstance(fmt, str):
				text = fmt
			else:
				text = str(fmt)

			fd.write(text)


================================================
FILE: ljd/pseudoasm/__init__.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#



================================================
FILE: ljd/pseudoasm/constants.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import ljd.bytecode.constants


def write_tables(writer, prototype):
	i = 0

	for element in prototype.constants.complex_constants:
		if isinstance(element, ljd.bytecode.constants.Table):
			_write_table(writer, i, element)

		i += 1


def _write_table(writer, index, table):
	writer.stream.open_block("ktable#{0} = [", index)

	i = 0

	for element in table.array:
		if i != 0 or element is not None:
			text = _translate_element(element)

			writer.stream.write_line("#{0}: {1},", i, text)

		i += 1

	for key, value in table.dictionary:
		key = _translate_element(key)
		value = _translate_element(value)

		writer.stream.write_line("[{0}] = {1},", key, value)

	writer.stream.close_block("]")
	writer.stream.write_line()
	writer.stream.write_line()


def _translate_element(element):
	if element is True:
		return "true"
	elif element is False:
		return "false"
	elif element is None:
		return "nil"
	elif isinstance(element, bytes):
		return '"' + element.decode("utf8") + '"'
	else:
		return str(element)


================================================
FILE: ljd/pseudoasm/instructions.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import ljd.bytecode.instructions as ins
from ljd.bytecode.constants import T_NIL, T_FALSE, T_TRUE

import ljd.pseudoasm.prototype

_FORMAT = "{addr:3}\t[{line:3}]\t{name:<5}\t{a:3}\t{b}\t{c}\t; {description}"


_DESCRIPTION_HANDLERS = [None] * 255


class _State():
	def __init__(self, writer, prototype, instructions):
		for key, value in writer.__dict__.items():
			setattr(self, key, value)

		self.prototype = prototype
		self.instructions = instructions


def write(writer, prototype):
	global _MAP

	# skip the first function header
	addr = 1

	instructions = prototype.instructions

	writer = _State(writer, prototype, instructions)

	while addr < len(instructions):
		instruction = instructions[addr]
		line = prototype.debuginfo.lookup_line_number(addr)

		if instruction.opcode == ins.FNEW.opcode:
			_write_function(writer, addr, line, instruction)
		else:
			_write_instruction(writer, addr, line, instruction)

		addr += 1


def _write_instruction(writer, addr, line, instruction):
	description = _translate_description(writer, addr, line, instruction)

	writer.stream.write_multiline(_FORMAT,
		addr=addr,
		line=line,
		name=instruction.name,
		a=instruction.A if instruction.A_type is not None else "",
		b=instruction.B	if instruction.B_type is not None else "",
		c=instruction.CD if instruction.CD_type is not None else "",
		description=description
	)


def _write_function(writer, addr, line, instruction):
	prototype = writer.prototype.constants.complex_constants[instruction.CD]

	description = ljd.pseudoasm.prototype.format_header(writer, prototype)

	writer.stream.open_block(_FORMAT,
		addr=addr,
		line=line,
		name="FNEW",
		a=instruction.A,
		b="",
		c=instruction.CD,
		description=description
	)

	writer.stream.write_line()

	ljd.pseudoasm.prototype.write_body(writer, prototype)

	writer.stream.close_block()


def _translate_description(writer, addr, line, instruction):
	global _DESCRIPTION_HANDLERS

	handler = _DESCRIPTION_HANDLERS[instruction.opcode]
	description = instruction.description

	return handler(writer, description, addr, line, instruction)


def _translate(writer, addr, value, attr_type):
	prototype = writer.prototype

	if attr_type == ins.T_DST or attr_type == ins.T_BS:
		return "slot" + str(value)
	if attr_type == ins.T_VAR:
		name = _lookup_variable_name(writer, addr, value)

		if name is not None:
			return name
		else:
			return "slot" + str(value)
	elif attr_type == ins.T_UV:
		name = prototype.debuginfo.lookup_upvalue_name(value)
		return "uv" + str(value) + '"' + name + '"'
	elif attr_type == ins.T_PRI:
		if value is None or value == T_NIL:
			return "nil"
		elif value is True or value == T_TRUE:
			return "true"
		else:
			assert value is False or value == T_FALSE
			return "false"
	elif attr_type == ins.T_NUM:
		return str(prototype.constants.numeric_constants[value])
	elif attr_type == ins.T_STR:
		binary = prototype.constants.complex_constants[value]
		return '"' + binary + '"'
	elif attr_type == ins.T_TAB:
		return "table#k" + str(value)
	elif attr_type == ins.T_CDT:
		return str(prototype.constants.complex_constants[value])
	elif attr_type == ins.T_JMP:
		return str(1 + addr + value)
	elif attr_type == ins.T_LIT or attr_type == ins.T_SLIT:
		return str(value)
	elif attr_type == ins.T_BS or attr_type == ins.T_RBS:
		return "r" + str(value)
	else:
		return " "  # "r" + str(value)


def _lookup_variable_name(writer, addr, slot):
	while True:
		result = _lookup_variable_name_step(writer, addr, slot)

		if isinstance(result, tuple):
			addr = result[0]
			slot = result[1]
			continue

		return result


def _lookup_variable_name_step(writer, addr, slot):
	info = writer.prototype.debuginfo.lookup_local_name(addr, slot)

	if info is not None:
		name = info.name

		if name[0] == '<':
			name = "slot" + str(slot) + name

		return name

	instructions = writer.instructions

	knil_opcode = ins.KNIL.opcode
	constants = writer.prototype.constants.complex_constants

	while addr > 1:
		addr -= 1
		instruction = instructions[addr]

		if instruction.A_type == ins.T_BS:
			if slot >= instruction.A 				\
					and (instruction.opcode == knil_opcode	\
						or slot <= instruction.CD):
				return None

			continue

		if instruction.A_type != ins.T_DST or instruction.A != slot:
			continue

		if instruction.opcode == ins.MOV.opcode:
			# Retry with new addr and slot
			return addr, instruction.CD

		if instruction.opcode == ins.GGET.opcode:
			binary = constants[instruction.CD]
			return binary

		# field or method
		if instruction.opcode == ins.TGETS.opcode:
			table_slot = instruction.B
			table = _lookup_variable_name_step(writer, addr, table_slot)

			if table is None:
				table = "<unknown table>"

			binary = constants[instruction.CD]
			return table + "." + binary

		if instruction.opcode == ins.UGET.opcode:
			uv = instruction.CD
			name = writer.prototype.debuginfo.lookup_upvalue_name(uv)

			return "uv" + str(uv) + '"' + name + '"'

		return None

	return None


def _translate_standard(writer, addr, line, instruction):
	A = None
	B = None
	CD = None

	if instruction.A_type is not None:
		A = _translate(writer, addr, instruction.A, instruction.A_type)

	if instruction.B_type is not None:
		B = _translate(writer, addr, instruction.B, instruction.B_type)

	if instruction.CD_type is not None:
		CD = _translate(writer, addr, instruction.CD, instruction.CD_type)

	return A, B, CD


def _translate_normal(writer, description, addr, line, instruction):
	A, B, CD = _translate_standard(writer, addr, line, instruction)

	return description.format(A=A, B=B, C=CD, D=CD)


def _translate_concat(writer, description, addr, line, instruction):
	A = _translate(writer, addr, instruction.A, instruction.A_type)

	args = []

	start = instruction.B
	end = instruction.CD + 1

	while start != end:
		var = _translate(writer, addr, start, ins.T_VAR)
		args.append(var)
		start += 1

	return description.format(A=A, concat_from_B_to_C=" .. ".join(args))


def _translate_nil(writer, description, addr, line, instruction):
	args = []

	start = instruction.A
	end = instruction.CD + 1

	while start != end:
		var = _translate(writer, addr, start, ins.T_VAR)
		args.append(var)
		start += 1

	return description.format(from_A_to_D=", ".join(args))


def _translate_table_str_op(writer, description, addr, line, instruction):
	A, B, CD = _translate_standard(writer, addr, line, instruction)

	C = CD[1:-1]

	return description.format(A=A, B=B, C=C)


def _translate_new_table(writer, description, addr, line, instruction):
	A = _translate(writer, addr, instruction.A, instruction.A_type)

	size = instruction.CD

	array_size = size & 0b0000011111111111
	dict_size = 2 ** (size >> 11)

	return description.format(
		A=A,
		D_array=array_size,
		D_dict=dict_size,
	)


def _translate_mass_set(writer, description, addr, line, instruction):
	base = instruction.A

	table_var = _translate(writer, addr, base - 1, ins.T_VAR)

	first = instruction.CD

	return description.format(
		A_minus_one=table_var,
		A=base,
		D_low=first,
	)


def _translate_varg_call(writer, description, addr, line, instruction):
	base = instruction.A
	argn = instruction.CD
	retn = instruction.B - 1

	args = []
	returns = []

	i = 0
	while i < argn:
		args.append(_translate(writer, addr, base + i + 1, ins.T_VAR))
		i += 1

	i = 0
	while i < retn:
		returns.append(_translate(writer, addr, base + i, ins.T_DST))
		i += 1

	func_var = _translate(writer, addr, base, ins.T_VAR)

	return description.format(
		A=func_var,
		from_A_x_B_minus_two=", ".join(returns)  if retn >= 0 else "MULTRES",
		from_A_plus_one_x_C=", ".join(args)
	)


def _translate_call(writer, description, addr, line, instruction):
	base = instruction.A
	argn = instruction.CD - 1
	retn = instruction.B - 1

	args = []
	returns = []

	i = 0
	while i < argn:
		args.append(_translate(writer, addr, base + i + 1, ins.T_VAR))
		i += 1

	i = 0
	while i < retn:
		returns.append(_translate(writer, addr, base + i, ins.T_DST))
		i += 1

	func_var = _translate(writer, addr, base, ins.T_VAR)

	return description.format(
		A=func_var,
		from_A_x_B_minus_two=", ".join(returns) if retn >= 0 else "MULTRES",
		from_A_plus_one_x_C_minus_one=", ".join(args)
	)


def _translate_varg_tailcall(writer, description, addr, line, instruction):
	base = instruction.A
	argn = instruction.CD - 1

	args = []

	i = 0
	while i < argn:
		args.append(_translate(writer, addr, base + i + 1, ins.T_VAR))
		i += 1

	func_var = _translate(writer, addr, base, ins.T_VAR)

	return description.format(
		A=func_var,
		from_A_plus_one_x_D=", ".join(args)
	)


def _translate_tailcall(writer, description, addr, line, instruction):
	base = instruction.A
	argn = instruction.CD - 1

	args = []

	i = 0
	while i < argn:
		args.append(_translate(writer, addr, base + i + 1, ins.T_VAR))
		i += 1

	func_var = _translate(writer, addr, base, ins.T_VAR)

	return description.format(
		A=func_var,
		from_A_plus_one_x_D_minus_one=", ".join(args)
	)


def _translate_iterator(writer, description, addr, line, instruction):
	base = instruction.A

	A = _translate(writer, addr, instruction.A, ins.T_DST)
	A_plus_one = _translate(writer, addr, instruction.A + 1, ins.T_DST)
	A_plus_two = _translate(writer, addr, instruction.A + 2, ins.T_DST)

	A_minus_three = _translate(writer, addr, instruction.A - 3, ins.T_VAR)
	A_minus_two = _translate(writer, addr, instruction.A - 2, ins.T_VAR)
	A_minus_one = _translate(writer, addr, instruction.A - 1, ins.T_VAR)

	retn = instruction.B - 1

	returns = []

	i = 0
	while i < retn:
		returns.append(_translate(writer, addr, base + i, ins.T_DST))
		i += 1

	return description.format(
		A=A,
		A_plus_one=A_plus_one,
		A_plus_two=A_plus_two,
		A_minus_three=A_minus_three,
		A_minus_two=A_minus_two,
		A_minus_one=A_minus_one,
		from_A_x_B_minus_two=", ".join(returns)
	)


def _translate_vararg(writer, description, addr, line, instruction):
	returns = []

	base = instruction.A

	count = instruction.B - 2

	if count < 0:
		return description.format(from_A_x_B_minus_two="MULTRES")

	i = 0
	while i <= count:
		returns.append(_translate(writer, addr, base + i, ins.T_DST))
		i += 1

	return description.format(
		from_A_x_B_minus_two=", ".join(returns)
	)


def _translate_return_mult(writer, description, addr, line, instruction):
	returns = []

	base = instruction.A

	count = instruction.CD - 1

	i = 0
	while i < count:
		returns.append(_translate(writer, addr, base + i, ins.T_VAR))
		i += 1

	return description.format(
		from_A_x_D_minus_one=", ".join(returns)
	)


def _translate_return_many(writer, description, addr, line, instruction):
	returns = []

	base = instruction.A

	count = instruction.CD - 2

	i = 0
	while i < count:
		returns.append(_translate(writer, addr, base + i, ins.T_VAR))
		i += 1

	return description.format(
		from_A_x_D_minus_two=", ".join(returns)
	)


def _translate_return_one(writer, description, addr, line, instruction):
	A = _translate(writer, addr, instruction.A, ins.T_VAR)

	return description.format(A=A)


def _translate_for_init(writer, description, addr, line, instruction):
	idx = _translate(writer, addr, instruction.A, ins.T_BS)
	stop = _translate(writer, addr, instruction.A + 1, ins.T_BS)
	step = _translate(writer, addr, instruction.A + 2, ins.T_BS)
	ext_idx = _translate(writer, addr, instruction.A + 3, ins.T_VAR)

	return description.format(
		A=idx,
		A_plus_one=stop,
		A_plus_two=step,
		A_plus_three=ext_idx,
		D=_translate(writer, addr, instruction.CD, ins.T_JMP)
	)


def _translate_numeric_loop(writer, description, addr, line, instruction):
	stop = _translate(writer, addr, instruction.A + 1, ins.T_VAR)
	step = _translate(writer, addr, instruction.A + 2, ins.T_VAR)
	ext_idx = _translate(writer, addr, instruction.A + 3, ins.T_VAR)

	# ext_idx isn't correct var here, but for the visualisation sake we will
	# omit all the stuff with the internal idx var

	return description.format(
		A=ext_idx,
		A_plus_one=stop,
		A_plus_two=step,
		D=_translate(writer, addr, instruction.CD, ins.T_JMP)
	)


def _translate_iter_loop(writer, description, addr, line, instruction):
	A_minus_one = _translate(writer, addr, instruction.A - 1, ins.T_VAR)
	A = _translate(writer, addr, instruction.A, ins.T_VAR)

	return description.format(
		A_minus_one=A_minus_one,
		A=A,
		D=_translate(writer, addr, instruction.CD, ins.T_JMP)
	)


_HANDLERS_MAP = (
	# Comparison ops

	(ins.ISLT.opcode, 	_translate_normal),
	(ins.ISGE.opcode, 	_translate_normal),
	(ins.ISLE.opcode, 	_translate_normal),
	(ins.ISGT.opcode, 	_translate_normal),

	(ins.ISEQV.opcode, 	_translate_normal),
	(ins.ISNEV.opcode, 	_translate_normal),

	(ins.ISEQS.opcode, 	_translate_normal),
	(ins.ISNES.opcode, 	_translate_normal),

	(ins.ISEQN.opcode, 	_translate_normal),
	(ins.ISNEN.opcode, 	_translate_normal),

	(ins.ISEQP.opcode, 	_translate_normal),
	(ins.ISNEP.opcode, 	_translate_normal),

	# Unary test and copy ops

	(ins.ISTC.opcode, 	_translate_normal),
	(ins.ISFC.opcode, 	_translate_normal),

	(ins.IST.opcode, 	_translate_normal),
	(ins.ISF.opcode, 	_translate_normal),

	# Unary ops

	(ins.MOV.opcode, 	_translate_normal),
	(ins.NOT.opcode, 	_translate_normal),
	(ins.UNM.opcode, 	_translate_normal),
	(ins.LEN.opcode, 	_translate_normal),

	# Binary ops

	(ins.ADDVN.opcode, 	_translate_normal),
	(ins.SUBVN.opcode, 	_translate_normal),
	(ins.MULVN.opcode, 	_translate_normal),
	(ins.DIVVN.opcode, 	_translate_normal),
	(ins.MODVN.opcode, 	_translate_normal),

	(ins.ADDNV.opcode, 	_translate_normal),
	(ins.SUBNV.opcode, 	_translate_normal),
	(ins.MULNV.opcode, 	_translate_normal),
	(ins.DIVNV.opcode, 	_translate_normal),
	(ins.MODNV.opcode, 	_translate_normal),

	(ins.ADDVV.opcode, 	_translate_normal),
	(ins.SUBVV.opcode, 	_translate_normal),
	(ins.MULVV.opcode, 	_translate_normal),
	(ins.DIVVV.opcode, 	_translate_normal),
	(ins.MODVV.opcode, 	_translate_normal),

	(ins.POW.opcode, 	_translate_normal),
	(ins.CAT.opcode, 	_translate_concat),

	# Constant ops

	(ins.KSTR.opcode, 	_translate_normal),
	(ins.KCDATA.opcode, 	_translate_normal),
	(ins.KSHORT.opcode, 	_translate_normal),
	(ins.KNUM.opcode, 	_translate_normal),
	(ins.KPRI.opcode, 	_translate_normal),

	(ins.KNIL.opcode, 	_translate_nil),

	# Upvalue and function ops

	(ins.UGET.opcode, 	_translate_normal),

	(ins.USETV.opcode, 	_translate_normal),
	(ins.USETS.opcode, 	_translate_normal),
	(ins.USETN.opcode, 	_translate_normal),
	(ins.USETP.opcode, 	_translate_normal),

	(ins.UCLO.opcode, 	_translate_normal),

	(ins.FNEW.opcode, 	_translate_normal),

	# Table ops

	(ins.TNEW.opcode, 	_translate_new_table),

	(ins.TDUP.opcode, 	_translate_normal),

	(ins.GGET.opcode, 	_translate_normal),
	(ins.GSET.opcode, 	_translate_normal),

	(ins.TGETV.opcode, 	_translate_normal),
	(ins.TGETS.opcode, 	_translate_table_str_op),
	(ins.TGETB.opcode, 	_translate_normal),

	(ins.TSETV.opcode, 	_translate_normal),
	(ins.TSETS.opcode, 	_translate_table_str_op),
	(ins.TSETB.opcode, 	_translate_normal),

	(ins.TSETM.opcode, 	_translate_mass_set),

	# Calls and vararg handling

	(ins.CALLM.opcode, 	_translate_varg_call),
	(ins.CALL.opcode, 	_translate_call),
	(ins.CALLMT.opcode, 	_translate_varg_tailcall),
	(ins.CALLT.opcode, 	_translate_tailcall),

	(ins.ITERC.opcode, 	_translate_iterator),
	(ins.ITERN.opcode, 	_translate_iterator),

	(ins.VARG.opcode, 	_translate_vararg),

	(ins.ISNEXT.opcode, 	_translate_normal),

	# Returns

	(ins.RETM.opcode, 	_translate_return_mult),
	(ins.RET.opcode, 	_translate_return_many),
	(ins.RET0.opcode, 	_translate_normal),
	(ins.RET1.opcode, 	_translate_return_one),

	# Loops and branches

	(ins.FORI.opcode, 	_translate_for_init),
	(ins.JFORI.opcode, 	_translate_for_init),

	(ins.FORL.opcode, 	_translate_numeric_loop),
	(ins.IFORL.opcode, 	_translate_numeric_loop),
	(ins.JFORL.opcode, 	_translate_numeric_loop),

	(ins.ITERL.opcode, 	_translate_iter_loop),
	(ins.IITERL.opcode, 	_translate_iter_loop),
	(ins.JITERL.opcode, 	_translate_iter_loop),

	(ins.LOOP.opcode, 	_translate_normal),
	(ins.ILOOP.opcode, 	_translate_normal),
	(ins.JLOOP.opcode, 	_translate_normal),

	(ins.JMP.opcode, 	_translate_normal),

	# Function headers

	(ins.FUNCF.opcode, 	_translate_normal),
	(ins.IFUNCF.opcode, 	_translate_normal),
	(ins.JFUNCF.opcode, 	_translate_normal),

	(ins.FUNCV.opcode, 	_translate_normal),
	(ins.IFUNCV.opcode, 	_translate_normal),
	(ins.JFUNCV.opcode, 	_translate_normal),

	(ins.FUNCC.opcode, 	_translate_normal),
	(ins.FUNCCW.opcode, 	_translate_normal)
)


def _init():
	global _HANDLERS_MAP, _DESCRIPTION_HANDLERS

	for opcode, handler in _HANDLERS_MAP:
		_DESCRIPTION_HANDLERS[opcode] = handler

	del globals()["_init"]
	del globals()["_HANDLERS_MAP"]

_init()


================================================
FILE: ljd/pseudoasm/prototype.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import ljd.pseudoasm.constants
import ljd.pseudoasm.instructions


def write(writer, prototype):
	_write_header(writer, prototype)
	write_body(writer, prototype)

	writer.stream.close_block("")


def _write_header(writer, prototype):
	writer.stream.open_block("main {0}", format_header(writer, prototype))


def format_header(writer, prototype):
	return "{s}:{start}-{end}: {argn}{varg} args,"	\
			" {uvs} upvalues, {slots} slots".format(
		s=writer.source,
		start=prototype.first_line_number,
		end=prototype.first_line_number + prototype.lines_count,
		argn=prototype.arguments_count,
		varg="+" if prototype.flags.is_variadic else "",
		uvs=len(prototype.constants.upvalue_references),
		slots=prototype.framesize
	)


def write_body(writer, prototype):
	writer.stream.write_line(";;;; constant tables ;;;;")
	ljd.pseudoasm.constants.write_tables(writer, prototype)

	writer.stream.write_line(";;;; instructions ;;;;")
	ljd.pseudoasm.instructions.write(writer, prototype)


================================================
FILE: ljd/pseudoasm/writer.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import ljd.util.indentedstream

import ljd.pseudoasm.prototype


class _State():
	def __init__(self):
		self.flags = None
		self.stream = None
		self.source = None


def write(fd, header, prototype):
	writer = _State()

	writer.stream = ljd.util.indentedstream.IndentedStream(fd)
	writer.flags = header.flags
	writer.source = "N/A" if header.flags.is_stripped else header.name

	_write_header(writer, header)

	ljd.pseudoasm.prototype.write(writer, prototype)


def _write_header(writer, header):
	writer.stream.write_multiline("""
;
; Disassemble of {origin}
;
; Source file: {source}
;
; Flags:
;	Stripped: {stripped}
;	Endianness: {endianness}
;	FFI: {ffi}
;

""", 		origin=header.origin,
		source=writer.source,
		stripped="Yes" if header.flags.is_stripped else "No",
		endianness="Big" if header.flags.is_big_endian else "Little",
		ffi="Present" if header.flags.has_ffi else "Not present")


================================================
FILE: ljd/rawdump/__init__.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#



================================================
FILE: ljd/rawdump/code.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

from ljd.util.log import errprint

import ljd.bytecode.instructions as instructions


_OPCODES = (
	# Comparison ops

	(0x00, 	instructions.ISLT),  # @UndefinedVariable
	(0x01, 	instructions.ISGE),  # @UndefinedVariable
	(0x02, 	instructions.ISLE),  # @UndefinedVariable
	(0x03, 	instructions.ISGT),  # @UndefinedVariable

	(0x04, 	instructions.ISEQV),  # @UndefinedVariable
	(0x05, 	instructions.ISNEV),  # @UndefinedVariable

	(0x06, 	instructions.ISEQS),  # @UndefinedVariable
	(0x07, 	instructions.ISNES),  # @UndefinedVariable

	(0x08, 	instructions.ISEQN),  # @UndefinedVariable
	(0x09, 	instructions.ISNEN),  # @UndefinedVariable

	(0x0A, 	instructions.ISEQP),  # @UndefinedVariable
	(0x0B, 	instructions.ISNEP),  # @UndefinedVariable

	# Unary test and copy ops

	(0x0C, 	instructions.ISTC),  # @UndefinedVariable
	(0x0D, 	instructions.ISFC),  # @UndefinedVariable

	(0x0E, 	instructions.IST),  # @UndefinedVariable
	(0x0F, 	instructions.ISF),  # @UndefinedVariable

	(0x0E, 	instructions.ISTYPE),  # @UndefinedVariable
	(0x0F, 	instructions.ISNUM),  # @UndefinedVariable

	# Unary ops

	(0x10, 	instructions.MOV),  # @UndefinedVariable
	(0x11, 	instructions.NOT),  # @UndefinedVariable
	(0x12, 	instructions.UNM),  # @UndefinedVariable
	(0x13, 	instructions.LEN),  # @UndefinedVariable

	# Binary ops

	(0x14, 	instructions.ADDVN),  # @UndefinedVariable
	(0x15, 	instructions.SUBVN),  # @UndefinedVariable
	(0x16, 	instructions.MULVN),  # @UndefinedVariable
	(0x17, 	instructions.DIVVN),  # @UndefinedVariable
	(0x18, 	instructions.MODVN),  # @UndefinedVariable

	(0x19, 	instructions.ADDNV),  # @UndefinedVariable
	(0x1A, 	instructions.SUBNV),  # @UndefinedVariable
	(0x1B, 	instructions.MULNV),  # @UndefinedVariable
	(0x1C, 	instructions.DIVNV),  # @UndefinedVariable
	(0x1D, 	instructions.MODNV),  # @UndefinedVariable

	(0x1E, 	instructions.ADDVV),  # @UndefinedVariable
	(0x1F, 	instructions.SUBVV),  # @UndefinedVariable
	(0x20, 	instructions.MULVV),  # @UndefinedVariable
	(0x21, 	instructions.DIVVV),  # @UndefinedVariable
	(0x22, 	instructions.MODVV),  # @UndefinedVariable

	(0x23, 	instructions.POW),  # @UndefinedVariable
	(0x24, 	instructions.CAT),  # @UndefinedVariable

	# Constant ops

	(0x25, 	instructions.KSTR),  # @UndefinedVariable
	(0x26, 	instructions.KCDATA),  # @UndefinedVariable
	(0x27, 	instructions.KSHORT),  # @UndefinedVariable
	(0x28, 	instructions.KNUM),  # @UndefinedVariable
	(0x29, 	instructions.KPRI),  # @UndefinedVariable

	(0x2A, 	instructions.KNIL),  # @UndefinedVariable

	# Upvalue and function ops

	(0x2B, 	instructions.UGET),  # @UndefinedVariable

	(0x2C, 	instructions.USETV),  # @UndefinedVariable
	(0x2D, 	instructions.USETS),  # @UndefinedVariable
	(0x2E, 	instructions.USETN),  # @UndefinedVariable
	(0x2F, 	instructions.USETP),  # @UndefinedVariable

	(0x30, 	instructions.UCLO),  # @UndefinedVariable

	(0x31, 	instructions.FNEW),  # @UndefinedVariable

	# Table ops

	(0x32, 	instructions.TNEW),  # @UndefinedVariable

	(0x33, 	instructions.TDUP),  # @UndefinedVariable

	(0x34, 	instructions.GGET),  # @UndefinedVariable
	(0x35, 	instructions.GSET),  # @UndefinedVariable

	(0x36, 	instructions.TGETV),  # @UndefinedVariable
	(0x37, 	instructions.TGETS),  # @UndefinedVariable
	(0x38, 	instructions.TGETB),  # @UndefinedVariable
	(0x38, 	instructions.TGETR),  # @UndefinedVariable

	(0x39, 	instructions.TSETV),  # @UndefinedVariable
	(0x3A, 	instructions.TSETS),  # @UndefinedVariable
	(0x3B, 	instructions.TSETB),  # @UndefinedVariable

	(0x3C, 	instructions.TSETM),  # @UndefinedVariable
	(0x3B, 	instructions.TSETR),  # @UndefinedVariable

	# Calls and vararg handling

	(0x3D, 	instructions.CALLM),  # @UndefinedVariable
	(0x3E, 	instructions.CALL),  # @UndefinedVariable
	(0x3F, 	instructions.CALLMT),  # @UndefinedVariable
	(0x40, 	instructions.CALLT),  # @UndefinedVariable

	(0x41, 	instructions.ITERC),  # @UndefinedVariable
	(0x42, 	instructions.ITERN),  # @UndefinedVariable

	(0x43, 	instructions.VARG),  # @UndefinedVariable

	(0x44, 	instructions.ISNEXT),  # @UndefinedVariable

	# Returns

	(0x45, 	instructions.RETM),  # @UndefinedVariable
	(0x46, 	instructions.RET),  # @UndefinedVariable
	(0x47, 	instructions.RET0),  # @UndefinedVariable
	(0x48, 	instructions.RET1),  # @UndefinedVariable

	# Loops and branches

	(0x49, 	instructions.FORI),  # @UndefinedVariable
	(0x4A, 	instructions.JFORI),  # @UndefinedVariable

	(0x4B, 	instructions.FORL),  # @UndefinedVariable
	(0x4C, 	instructions.IFORL),  # @UndefinedVariable
	(0x4D, 	instructions.JFORL),  # @UndefinedVariable

	(0x4E, 	instructions.ITERL),  # @UndefinedVariable
	(0x4F, 	instructions.IITERL),  # @UndefinedVariable
	(0x50, 	instructions.JITERL),  # @UndefinedVariable

	(0x51, 	instructions.LOOP),  # @UndefinedVariable
	(0x52, 	instructions.ILOOP),  # @UndefinedVariable
	(0x53, 	instructions.JLOOP),  # @UndefinedVariable

	(0x54, 	instructions.JMP),  # @UndefinedVariable

	# Function headers

	(0x55, 	instructions.FUNCF),  # @UndefinedVariable
	(0x56, 	instructions.IFUNCF),  # @UndefinedVariable
	(0x57, 	instructions.JFUNCF),  # @UndefinedVariable

	(0x58, 	instructions.FUNCV),  # @UndefinedVariable
	(0x59, 	instructions.IFUNCV),  # @UndefinedVariable
	(0x5A, 	instructions.JFUNCV),  # @UndefinedVariable

	(0x5B, 	instructions.FUNCC),  # @UndefinedVariable
	(0x5C, 	instructions.FUNCCW)  # @UndefinedVariable
)


_MAP = [None] * 256


def read(parser):
	global _MAP

	codeword = parser.stream.read_uint(4)

	opcode = codeword & 0xFF

	instruction_class = _MAP[opcode]

	if instruction_class is None:
		errprint("Warning: unknown opcode {0:08x}", opcode)
		instruction_class = instructions.UNKNW  # @UndefinedVariable #zzw.20180714

	instruction = instruction_class()

	if instruction_class.opcode != opcode:
		instruction.opcode = opcode

	_set_instruction_operands(parser, codeword, instruction)

	return instruction


def _set_instruction_operands(parser, codeword, instruction):
	if instruction.args_count == 3:
		A = (codeword >> 8) & 0xFF
		CD = (codeword >> 16) & 0xFF
		B = (codeword >> 24) & 0xFF
	else:
		A = (codeword >> 8) & 0xFF
		CD = (codeword >> 16) & 0xFFFF

	if instruction.A_type is not None:
		instruction.A = _process_operand(parser, instruction.A_type, A)

	if instruction.B_type is not None:
		instruction.B = _process_operand(parser, instruction.B_type, B)

	if instruction.CD_type is not None:
		instruction.CD = _process_operand(parser, instruction.CD_type, CD)


def _process_operand(parser, operand_type, operand):
	if operand_type == instructions.T_STR			\
			or operand_type == instructions.T_TAB	\
			or operand_type == instructions.T_FUN	\
			or operand_type == instructions.T_CDT:
		return parser.complex_constants_count - operand - 1
	elif operand_type == instructions.T_JMP:
		return operand - 0x8000
	else:
		return operand


def _init():
	global _OPCODES, _MAP
	opcode = 0
	for instruction in _OPCODES:
		_MAP[opcode] = instruction[1]
		opcode = opcode + 1

	del globals()["_init"]
	del globals()["_OPCODES"]

_init()


================================================
FILE: ljd/rawdump/constants.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import sys
import struct

import ljd.bytecode.constants

import gconfig

BCDUMP_KGC_CHILD = 0
BCDUMP_KGC_TAB = 1
BCDUMP_KGC_I64 = 2
BCDUMP_KGC_U64 = 3
BCDUMP_KGC_COMPLEX = 4
BCDUMP_KGC_STR = 5


BCDUMP_KTAB_NIL = 0
BCDUMP_KTAB_FALSE = 1
BCDUMP_KTAB_TRUE = 2
BCDUMP_KTAB_INT = 3
BCDUMP_KTAB_NUM = 4
BCDUMP_KTAB_STR = 5


def read(parser, constants):
	r = True

	r = r and _read_upvalue_references(parser, constants.upvalue_references)
	r = r and _read_complex_constants(parser, constants.complex_constants)
	r = r and _read_numeric_constants(parser, constants.numeric_constants)

	return r


def _read_upvalue_references(parser, references):
	i = 0

	while i < parser.upvalues_count:
		i += 1
		upvalue = parser.stream.read_uint(2)
		references.append(upvalue)

	return True


def _read_complex_constants(parser, complex_constants):
	i = 0

	while i < parser.complex_constants_count:
		constant_type = parser.stream.read_uleb128()

		if constant_type >= BCDUMP_KGC_STR:
			length = constant_type - BCDUMP_KGC_STR

			string = parser.stream.read_bytes(length)
			#print (string.decode("unicode-escape"))
			#print(str(complex_constants))
			#zzw 20180714 support str encode
			complex_constants.append(string.decode(gconfig.gFlagDic['strEncode']))
		elif constant_type == BCDUMP_KGC_TAB:
			table = ljd.bytecode.constants.Table()

			if not _read_table(parser, table):
				return False

			complex_constants.append(table)
		elif constant_type != BCDUMP_KGC_CHILD:
			number = _read_number(parser)

			if constant_type == BCDUMP_KGC_COMPLEX:
				imaginary = _read_number(parser)
				complex_constants.append((number, imaginary))
			else:
				complex_constants.append(number)
		else:
			complex_constants.append(parser.prototypes.pop())

		i += 1

	return True


def _read_numeric_constants(parser, numeric_constants):
	i = 0

	while i < parser.numeric_constants_count:
		isnum, lo = parser.stream.read_uleb128_from33bit()

		if isnum:
			hi = parser.stream.read_uleb128()

			number = _assemble_number(lo, hi)
		else:
			number = _process_sign(lo)

		numeric_constants.append(number)

		i += 1

	return True


def _read_number(parser):
	lo = parser.stream.read_uleb128()
	hi = parser.stream.read_uleb128()

	return _assemble_number(lo, hi)


def _read_signed_int(parser):
	number = parser.stream.read_uleb128()

	return _process_sign(number)


def _assemble_number(lo, hi):
	if sys.byteorder == 'big':
		float_as_int = lo << 32 | hi
	else:
		float_as_int = hi << 32 | lo

	raw_bytes = struct.pack("=Q", float_as_int)
	return struct.unpack("=d", raw_bytes)[0]


def _process_sign(number):
	if number & 0x80000000:
		return -0x100000000 + number
	else:
		return number


def _read_table(parser, table):
	array_items_count = parser.stream.read_uleb128()
	hash_items_count = parser.stream.read_uleb128()

	while array_items_count > 0:
		constant = _read_table_item(parser)

		table.array.append(constant)

		array_items_count -= 1

	while hash_items_count > 0:
		key = _read_table_item(parser)
		value = _read_table_item(parser)

		table.dictionary.append((key, value))

		hash_items_count -= 1

	return True


def _read_table_item(parser):
	data_type = parser.stream.read_uleb128()

	if data_type >= BCDUMP_KTAB_STR:
		length = data_type - BCDUMP_KTAB_STR
		# zzw 20180714 support str encode
		return parser.stream.read_bytes(length).decode(gconfig.gFlagDic['strEncode'])

	elif data_type == BCDUMP_KTAB_INT:
		return _read_signed_int(parser)

	elif data_type == BCDUMP_KTAB_NUM:
		return _read_number(parser)

	elif data_type == BCDUMP_KTAB_TRUE:
		return True

	elif data_type == BCDUMP_KTAB_FALSE:
		return False

	else:
		assert data_type == BCDUMP_KTAB_NIL

		return None


================================================
FILE: ljd/rawdump/debuginfo.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import sys

import ljd.bytecode.debuginfo


VARNAME_END = 0
VARNAME_FOR_IDX = 1
VARNAME_FOR_STOP = 2
VARNAME_FOR_STEP = 3
VARNAME_FOR_GEN = 4
VARNAME_FOR_STATE = 5
VARNAME_FOR_CTL = 6
VARNAME__MAX = 7


INTERNAL_VARNAMES = [
	None,
	"<index>",
	"<limit>",
	"<step>",
	"<generator>",
	"<state>",
	"<control>"
]


def read(parser, line_offset, debuginfo):
	r = True

	r = r and _read_lineinfo(parser, line_offset, debuginfo.addr_to_line_map)
	r = r and _read_upvalue_names(parser, debuginfo.upvalue_variable_names)
	r = r and _read_variable_infos(parser, debuginfo.variable_info)

	return r


def _read_lineinfo(parser, line_offset, lineinfo):
	if parser.lines_count >= 65536:
		lineinfo_size = 4
	elif parser.lines_count >= 256:
		lineinfo_size = 2
	else:
		lineinfo_size = 1

	lineinfo.append(0)

	while len(lineinfo) < parser.instructions_count + 1:
		line_number = parser.stream.read_uint(lineinfo_size)
		lineinfo.append(line_offset + line_number)

	return True


def _read_upvalue_names(parser, names):
	while len(names) < parser.upvalues_count:
		string = parser.stream.read_zstring()
		names.append(string.decode("utf-8"))

	return True


def _read_variable_infos(parser, infos):
	# pc - program counter
	last_addr = 0

	while True:
		info = ljd.bytecode.debuginfo.VariableInfo()

		internal_vartype = parser.stream.read_byte()

		if internal_vartype >= VARNAME__MAX:
			prefix = internal_vartype.to_bytes(1, sys.byteorder)
			suffix = parser.stream.read_zstring()

			info.name = (prefix + suffix).decode("utf-8")
			info.type = info.T_VISIBILE

		elif internal_vartype == VARNAME_END:
			break
		else:
			index = internal_vartype
			info.name = INTERNAL_VARNAMES[index]
			info.type = info.T_INTERNAL

		start_addr = last_addr + parser.stream.read_uleb128()
		end_addr = start_addr + parser.stream.read_uleb128()

		info.start_addr = start_addr
		info.end_addr = end_addr

		last_addr = start_addr

		infos.append(info)

	return True


================================================
FILE: ljd/rawdump/header.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

from ljd.util.log import errprint


_MAGIC = b'\x1bLJ'

_MAX_VERSION = 0x80

_FLAG_IS_BIG_ENDIAN = 0b00000001
_FLAG_IS_STRIPPED = 0b00000010
_FLAG_HAS_FFI = 0b00000100


class Flags():
    def __init__(self):
        self.is_big_endian = False
        self.is_stripped = False
        self.has_ffi = False


class Header():
    def __init__(self):
        self.version = 0
        self.flags = Flags()
        self.origin = b''
        self.name = b''


def read(state, header):
    r = True

    header.origin = state.stream.name

    r = r and _check_magic(state)

    r = r and _read_version(state, header)
    r = r and _read_flags(state, header)
    r = r and _read_name(state, header)
    #print("header good!")

    return r


def _check_magic(state):
    if state.stream.read_bytes(3) != _MAGIC:
        errprint("Invalid magic, not a LuaJIT format")
        return False

    return True


def _read_version(state, header):
    header.version = state.stream.read_byte()

    if header.version > _MAX_VERSION:
        errprint("Version {0}: propritary modifications",
                        header.version)
        return False

    return True


def _read_flags(parser, header):
    bits = parser.stream.read_uleb128()

    header.flags.is_big_endian = bits & _FLAG_IS_BIG_ENDIAN
    bits &= ~_FLAG_IS_BIG_ENDIAN

    header.flags.is_stripped = bits & _FLAG_IS_STRIPPED
    bits &= ~_FLAG_IS_STRIPPED

    header.flags.has_ffi = bits & _FLAG_HAS_FFI
    bits &= ~_FLAG_HAS_FFI

    # zzw.20180714 pitch: flag value is according to parser.flag when parse proto, not by header.flags
    parser.flags.is_big_endian = header.flags.is_big_endian
    parser.flags.is_stripped = header.flags.is_stripped
    parser.flags.is_big_endian = header.flags.has_ffi

    if bits != 0:
        errprint("Unknown flags set: {0:08b}", bits)
        return False

    return True


def _read_name(state, header):
    if header.flags.is_stripped:
        header.name = state.stream.name
    else:
        length = state.stream.read_uleb128()
        header.name = state.stream.read_bytes(length).decode("utf8")

    return True


================================================
FILE: ljd/rawdump/parser.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

#!/usr/bin/python3

import ljd.util.binstream
from ljd.util.log import errprint

import ljd.bytecode.prototype

import ljd.rawdump.header
import ljd.rawdump.prototype


class _State():
    def __init__(self):
        self.stream = ljd.util.binstream.BinStream()
        self.flags = ljd.rawdump.header.Flags()
        self.prototypes = []


def parse(filename):
    parser = _State()

    parser.stream.open(filename)

    header = ljd.rawdump.header.Header()

    r = True

    try:
        r = r and _read_header(parser, header)
        #print("good1")
        r = r and _read_prototypes(parser, parser.prototypes)
        #print("good2")
    except IOError as e:
        errprint("I/O error while reading dump: {0}", str(e))
        r = False

    if r and not parser.stream.eof():
        errprint("Stopped before whole file was read, something wrong")
        r = False

    if r and len(parser.prototypes) != 1:
        errprint("Invalid prototypes stack order")
        r = False

    parser.stream.close()

    if r:
        return header, parser.prototypes[0]
    else:
        return None, None

#zzw 20180716 解析字节码文件头部结构header
'''
| magic(3 bytes)| version(1 byte) | flag(1 uleb128) [| name(1 uleb128) |]
'''
def _read_header(parser, header):
    if not ljd.rawdump.header.read(parser, header):
        errprint("Failed to read raw-dump header")
        return False

    if header.flags.is_big_endian:
        parser.stream.data_byteorder = 'big'
    else:
        parser.stream.data_byteorder = 'little'

    return True


def _read_prototypes(state, prototypes):
    while not state.stream.eof():
        prototype = ljd.bytecode.prototype.Prototype()
        #print ("good rwsdump->parser->read_prototypes")

        if not ljd.rawdump.prototype.read(state, prototype):
            if state.stream.eof():
                break
            else:
                errprint("Failed to read prototype")
                return False

        prototypes.append(prototype)

    return True


================================================
FILE: ljd/rawdump/prototype.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

from ljd.util.log import errprint

import ljd.bytecode.instructions as ins

import ljd.rawdump.constants
import ljd.rawdump.debuginfo
import ljd.rawdump.code


FLAG_HAS_CHILD = 0b00000001
FLAG_IS_VARIADIC = 0b00000010
FLAG_HAS_FFI = 0b00000100
FLAG_JIT_DISABLED = 0b00001000
FLAG_HAS_ILOOP = 0b00010000


class _State():
    def __init__(self, parser):
        for key, value in parser.__dict__.items():
            setattr(self, key, value)

        self.upvalues_count = 0
        self.complex_constants_count = 0
        self.numeric_constants_count = 0
        self.instructions_count = 0
        self.debuginfo_size = 0
'''
zzw 20180716
| size(1 uleb128) | flag(1 byte) | arguments_count(1 byte) 
| framesize(1 byte) | upvalues_count(1 byte) | complex_constants_count(1 uleb128) 
| numeric_constants_count(1 uleb128) | instructions_count(1 uleb128) [| debuginfo_size(1 uleb128)
| first_line_number(1 uleb128) | lines_count(1 uleb128) ] | instructions(protoheader define) 
| constants(protoheader define) | debginfo(protoheader flage define) |
'''

def read(parser, prototype):
    parser = _State(parser)

    size = parser.stream.read_uleb128()

    if size == 0:
        return False

    if not parser.stream.check_data_available(size):
        errprint("File truncated")
        return False

    start = parser.stream.pos

    r = True

    r = r and _read_flags(parser, prototype)
    r = r and _read_counts_and_sizes(parser, prototype)
    r = r and _read_instructions(parser, prototype)
    r = r and _read_constants(parser, prototype)
    r = r and _read_debuginfo(parser, prototype)

    end = parser.stream.pos

    if r:
        assert end - start == size,                     \
            "Incorrectly read: from {0} to {1} ({2}) instead of {3}"\
            .format(start, end, end - start, size)

    return r


def _read_flags(parser, prototype):
    bits = parser.stream.read_byte()

    prototype.flags.has_ffi = bool(bits & FLAG_HAS_FFI)
    bits &= ~FLAG_HAS_FFI

    prototype.flags.has_iloop = bool(bits & FLAG_HAS_ILOOP)
    bits &= ~FLAG_HAS_ILOOP

    prototype.flags.has_jit = not (bits & FLAG_JIT_DISABLED)
    bits &= ~FLAG_JIT_DISABLED

    prototype.flags.has_sub_prototypes = bool(bits & FLAG_HAS_CHILD)
    bits &= ~FLAG_HAS_CHILD

    prototype.flags.is_variadic = bool(bits & FLAG_IS_VARIADIC)
    bits &= ~FLAG_IS_VARIADIC

    if bits != 0:
        errprint("Unknown prototype flags: {0:08b}", bits)
        return False

    return True


def _read_counts_and_sizes(parser, prototype):
    prototype.arguments_count = parser.stream.read_byte()
    prototype.framesize = parser.stream.read_byte()

    parser.upvalues_count = parser.stream.read_byte()
    parser.complex_constants_count = parser.stream.read_uleb128()
    parser.numeric_constants_count = parser.stream.read_uleb128()
    parser.instructions_count = parser.stream.read_uleb128()

    if parser.flags.is_stripped:
        parser.debuginfo_size = 0
    else:
        parser.debuginfo_size = parser.stream.read_uleb128()

    if parser.debuginfo_size == 0:
        return True

    prototype.first_line_number = parser.stream.read_uleb128()
    prototype.lines_count = parser.stream.read_uleb128()

    parser.lines_count = prototype.lines_count

    return True


def _read_instructions(parser, prototype):
    i = 0

    if prototype.flags.is_variadic:
        header = ins.FUNCV()
    else:
        header = ins.FUNCF()

    header.A = prototype.framesize
    prototype.instructions.append(header)

    while i < parser.instructions_count:
        instruction = ljd.rawdump.code.read(parser)

        if not instruction:
            return False

        #print ("inst:%s" % instruction.name)
        #print ("opcode:%x" % instruction.opcode)
        #print ("A:%x" % instruction.A)
        #print ("B:%x" % instruction.B)
        #print ("CD:%x" % instruction.CD)
        prototype.instructions.append(instruction)

        i += 1

    return True


def _read_constants(parser, prototype):
    return ljd.rawdump.constants.read(parser, prototype.constants)


def _read_debuginfo(stream, prototype):
    if stream.debuginfo_size == 0:
        return True

    return ljd.rawdump.debuginfo.read(stream,
                        prototype.first_line_number,
                        prototype.debuginfo)


================================================
FILE: ljd/util/__init__.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#



================================================
FILE: ljd/util/binstream.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import io
import os
import sys


class BinStream():
	def __init__(self):
		self.fd = None

		self.size = 0
		self.pos = 0
		self.name = ""

		self.data_byteorder = sys.byteorder

	def open(self, filename):
		self.name = filename
		self.fd = io.open(filename, 'rb')
		self.size = os.stat(filename).st_size

	def close(self):
		self.fd.close()
		self.size = 0
		self.pos = 0

	def eof(self):
		return self.pos >= self.size

	def check_data_available(self, size=1):
		return self.pos + size <= self.size

	def read_bytes(self, size=1):
		if not self.check_data_available(size):
			raise IOError("Unexpected EOF while trying to read {0} bytes"
									.format(size))

		data = self.fd.read(size)
		self.pos += len(data)#size

		return data

	def read_byte(self):
		if not self.check_data_available(1):
			raise IOError("Unexpected EOF while trying to read 1 byte")

		data = self.fd.read(1)
		self.pos += 1

		return int.from_bytes(data,
					byteorder=sys.byteorder,
					signed=False)

	def read_zstring(self):
		string = b''

		while not self.eof():
			byte = self.read_bytes(1)

			if byte == b'\x00':
				return string
			else:
				string += byte

		return string

	def read_uleb128(self):
		value = self.read_byte()

		if value >= 0x80:
			bitshift = 0
			value &= 0x7f

			while True:
				byte = self.read_byte()

				bitshift += 7
				value |= (byte & 0x7f) << bitshift

				if byte < 0x80:
					break

		return value

	def read_uleb128_from33bit(self):
		first_byte = self.read_byte()

		is_number_bit = first_byte & 0x1
		value = first_byte >> 1

		if value >= 0x40:
			bitshift = -1
			value &= 0x3f

			while True:
				byte = self.read_byte()

				bitshift += 7
				value |= (byte & 0x7f) << bitshift

				if byte < 0x80:
					break

		return is_number_bit, value

	def read_uint(self, size=4):
		value = self.read_bytes(size)

		return int.from_bytes(value, byteorder=self.data_byteorder,
								signed=False)


================================================
FILE: ljd/util/indentedstream.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

_TAB_WIDTH = " " * 8


class IndentedStream():
	def __init__(self, fd):
		self.fd = fd

		self.indent = 0
		self.line_open = False

	def write_multiline(self, fmt, *args, **kargs):
		assert not self.line_open

		if len(args) + len(kargs) > 0:
			text = fmt.format(*args, **kargs)
		else:
			text = fmt

		lines = text.split("\n")

		if lines[0] == "":
			lines.pop(0)

		if lines[-1] == "":
			lines.pop(-1)

		spaces = "\t" * self.indent

		for line in lines:
			self.fd.write(spaces + line + "\n")

	def start_line(self):
		assert not self.line_open
		self.line_open = True

		self.fd.write("\t" * self.indent)

	def write(self, fmt="", *args, **kargs):
		assert self.line_open

		if len(args) + len(kargs) > 0:
			text = fmt.format(*args, **kargs)
		elif isinstance(fmt, str):
			text = fmt
		else:
			text = str(fmt)

		assert "\n" not in text

		self.fd.write(text)

	def end_line(self):
		assert self.line_open

		self.fd.write("\n")

		self.line_open = False

	def write_line(self, *args, **kargs):
		self.start_line()
		self.write(*args, **kargs)
		self.end_line()

	def open_block(self, *args, **kargs):
		if len(args) + len(kargs) > 0:
			self.write_line(*args, **kargs)

		self.indent += 1

	def close_block(self, *args, **kargs):
		if len(args) + len(kargs) > 0:
			self.write_line(*args, **kargs)

		self.indent -= 1


================================================
FILE: ljd/util/log.py
================================================
#
# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py
#

import sys


def errprint(*args):
	fmt = None

	args = list(args)

	if isinstance(args[0], str):
		fmt = args.pop(0)

	if fmt:
		print(fmt.format(*args), file=sys.stderr)
	else:
		strs = [repr(x) for x in args]
		print(" ".join(strs), file=sys.stderr)


================================================
FILE: main.py
================================================
#!/usr/bin/python3
#
# The MIT License (MIT)
#
# Copyright (c) 2013 Andrian Nord
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#

import sys

import ljd.rawdump.parser
import ljd.pseudoasm.writer
import ljd.ast.builder
import ljd.ast.validator
import ljd.ast.locals
import ljd.ast.slotworks
import ljd.ast.unwarper
import ljd.ast.mutator
import ljd.lua.writer
#zzw 20180714 support str encode
import gconfig

def dump(name, obj, level=0):
    indent = level * '\t'

    if name is not None:
        prefix = indent + name + " = "
    else:
        prefix = indent

    if isinstance(obj, (int, float, str)):
        print(prefix + str(obj))
    elif isinstance(obj, list):
        print (prefix + "[")

        for value in obj:
            dump(None, value, level + 1)

        print (indent + "]")
    elif isinstance(obj, dict):
        print (prefix + "{")

        for key, value in obj.items():
            dump(key, value, level + 1)

        print (indent + "}")
    else:
        print (prefix + obj.__class__.__name__)

        for key in dir(obj):
            if key.startswith("__"):
                continue

            val = getattr(obj, key)
            dump(key, val, level + 1)


def main():
    file_in = sys.argv[1]

    header, prototype = ljd.rawdump.parser.parse(file_in)
    #print ("good")
    if not prototype:
        return 1

    # TODO: args
    # ljd.pseudoasm.writer.write(sys.stdout, header, prototype)

    ast = ljd.ast.builder.build(prototype)

    assert ast is not None

    ljd.ast.validator.validate(ast, warped=True)

    ljd.ast.mutator.pre_pass(ast)

    # ljd.ast.validator.validate(ast, warped=True)

    ljd.ast.locals.mark_locals(ast)

    # ljd.ast.validator.validate(ast, warped=True)

    ljd.ast.slotworks.eliminate_temporary(ast)

    # ljd.ast.validator.validate(ast, warped=True)

    if True:
        ljd.ast.unwarper.unwarp(ast)

        # ljd.ast.validator.validate(ast, warped=False)

        if True:
            ljd.ast.locals.mark_local_definitions(ast)

            # ljd.ast.validator.validate(ast, warped=False)

            ljd.ast.mutator.primary_pass(ast)

            ljd.ast.validator.validate(ast, warped=False)

    ljd.lua.writer.write(sys.stdout, ast)

    return 0


if __name__ == "__main__":
    # zzw 20180714 support str encode
    gconfig.gFlagDic['strEncode'] = 'utf-8'
    retval = main()
    sys.exit(retval)

# vim: ts=8 noexpandtab nosmarttab softtabstop=8 shiftwidth=8


================================================
FILE: test/breaks.lua
================================================
--[[
--]]

for i=1,2,3 do
	if x and y then
		print("Then")
	else
		break
	end
end

for i=1,2,3 do
	if x then
		print("Then")
	else
		break
	end

	print ("Something")

	if y then
		break
	end
end

for i=1,2,3 do
	if x then
		print("Then")
	else
		if y then
			break
		end
	end

	print ("Something")

	if y then
		break
	end
end

for i=1,2,3 do
	if x then
		print("Then")
		if y then
			break
		end
	else
		break
	end
end

for i=1,2,3 do
	if x then
		print("Then")
		if y then
			print ("Nested then")
		else
			break
		end
	else
		break
	end
end

for i=1,2,3 do
	if x then
		print("Then")
	else
		break
	end

	if y then
		print ("Y then")
	else
		break
	end
end

print ("Too bad")

for i=1,2,3 do
	if x then
		print("Then")
		if y then
			print ("Y then")
			if z then
				print ("Z then")
			end
		end
		break
	end

	if y then
		print ("Y then")
	else
		break
	end
end

for i=1,2,3 do
	if x then
		if y then
			if z then
				if xi then
					print ("Xi then")
				end
			end
		end
		break
	else
		break
	end

	if y then
		print ("Y then")
	else
		break
	end
end

for i=1,2,3 do
	if x and y and z and xi then
		print ("Xi then")
		break
	else
		break
	end

	if y then
		print ("Y then")
	else
		break
	end
end

for i=1,2,3 do
	if x then
		print ("X then")
		if y then
			print ("Y then")
			if z then
				print ("Z then")
				if xi then
					print ("Xi then")
				end
			end
		end
		break
	else
		break
	end

	if y then
		print ("Y then")
	else
		break
	end
end

for i=1,2,3 do
	if x then
		if z then
			print("Z Then")
		else
			break
		end

		print ("Something")

		if y then
			print ("Y then")
		else
			break
		end
	else
		break
	end

end

for i=1,2,3 do
	if x then
		if z then
			print("Z Then")
		else
			break
		end

		print ("Something")
	else
		break
	end

	if y then
		print ("Y then")
	else
		break
	end
end

function updateAnimation()
	local currentTime = getElapsedTime()
	local startSequence = private.currentSequence + 1
	local endSequence = #private.sequence

	for i=startSequence, endSequence, 1 do
		local entry = private.sequence[i]

		if entry.time <= currentTime then
			if entry.command == "start" then
				goToSlide(entry.element, 2)
				goToTime(entry.element, entry.animationTime)
				play(entry.element)
			else
				if entry.command == "step" then
					goToSlide(entry.element, 2)
					goToTime(entry.element, entry.animationTime)
				else
					pause(entry.element)
				end
			end

			if entry.name ~= nil then
				setAttribute(getElement("name", entry.element), "textstring", entry.name)
			end

			private.currentSequence = i

			break
		else 
			break
		end
	end 

	if #private.sequence == private.currentSequence then
		private.playing = false
	end

	return 
end

--[[
--]]


================================================
FILE: test/expression.lua
================================================
--[[
--]]

print ("true or true")

b = true or true

print ("false and false")

b = false and false

print ("false and or")

b = false and (x or y)

print ("false and ((and) or)")

b = false and ((x and z) or y)

print ("precalculated true expression")

c = true or (x and y) or true

print ("precalculated false expression")

d = false and ((x and y) or true)

print ("precalculated false expression with function")

e = error() and false and ((x and y) or true)

print ("precalculated true expression with function")

e = error() and true and ((x and y) or true)

print ("precalculated? false expression with variable")

local z = false

f = z and ((x and y) or true)

print ("precalculated false expression with nil")

f = nil and ((x and y) or true)

print ("simple or expression")
b = x or y

print ("simple or not expression")

b = not x or y
print ("simple and expression")

b = x and y

print ("simple or expression with binary comparison")

b = (x < 100) or y

print ("simple and expression with binary comparison")

b = (x < 100) and y

print ("simple and expression with binary comparison and function call")

b = (x < 100) and print(y)

print ("simple and expression with double binary comparison")

b = (x < 100) and (y > 100)


print ("(and) or expression")

b = (x and y) or z

print ("(or) and expression")

b = (x or y) and z

print ("(and) and expression")

b = (x and y) and z

print ("(or) or expression")
b = (x or y) or z

print ("or (and) expression")

b = x or (y and x)
b = x < 100 or (y < 100 and x < 100)

print ("and (or) expression")

b = x and (y or x)

print ("and (and) expression")

b = x and (y and x)

print ("or (or) expression")

b = x or (y or x)

print ("ond (or) and expression")

b = x and (y or x) and z

print ("or (and) or expression")

b = x or (y and x) or z

print ("and of two ors")

b = (x or z) and (y or z)
print ("or of two ands")

b = (x and z) or (y and z)

print ("x or string")
local xi = "nothing"
xi = x or "something"

local xi

print ("x and string")
xi = x and "something"

print ("and (or) and (or) expression with comparisons")

b = x < 100 and (y < 100 or x < 100) and (z < 100 or x < 100)

print ("and (or) and or or expression with comparisons")

b = x < 100 and (y < 100 or x < 100) and z < 100 or x < 100 or y < 100

print ("and (or) and and and expression with comparisons")

b = x < 100 and (y < 100 or x < 100) and z < 100 and x < 100 and y < 100

print ("or (and) or (and) expression with comparisons")

b = x < 100 or (y < 100 and x < 100) or (z < 100 and x < 100)

print ("and (and) and (and) expression with comparisons")

b = x < 100 and (y < 100 and x < 100) and (z < 100 and x < 100)

print ("or (or) or (or) expression with comparisons")

b = x < 100 or (y < 100 or x < 100) or (z < 100 or x < 100)

print ("4 and expression with comparisons")

b = x < 100 and y < 100 and x < 100 and z < 100 and x < 100

print ("4 or expression with comparisons")

b = x < 100 or y < 100 or x < 100 or z < 100 or x < 100

print ("and (or or) and (or or) expression with comparisons")

b = x < 100 and (y < 100 or x < 100 or z < 100)
		and (y < 100 or x < 100 or z < 100)
print ("and (or and or) and (or and or) expression with comparisons")

b = x < 100 and (y < 100 or (x < 100 and x > 100) or z < 100)
		and (y < 100 or (x <
Download .txt
gitextract_2i5rc5bq/

├── .gitignore
├── LICENSE
├── README.md
├── README.md.txt
├── gconfig.py
├── ljd/
│   ├── __init__.py
│   ├── ast/
│   │   ├── __init__.py
│   │   ├── builder.py
│   │   ├── helpers.py
│   │   ├── locals.py
│   │   ├── mutator.py
│   │   ├── nodes.py
│   │   ├── slotworks.py
│   │   ├── traverse.py
│   │   ├── unwarper.py
│   │   └── validator.py
│   ├── bytecode/
│   │   ├── __init__.py
│   │   ├── constants.py
│   │   ├── debuginfo.py
│   │   ├── helpers.py
│   │   ├── instructions.py
│   │   └── prototype.py
│   ├── lua/
│   │   ├── __init__.py
│   │   └── writer.py
│   ├── pseudoasm/
│   │   ├── __init__.py
│   │   ├── constants.py
│   │   ├── instructions.py
│   │   ├── prototype.py
│   │   └── writer.py
│   ├── rawdump/
│   │   ├── __init__.py
│   │   ├── code.py
│   │   ├── constants.py
│   │   ├── debuginfo.py
│   │   ├── header.py
│   │   ├── parser.py
│   │   └── prototype.py
│   └── util/
│       ├── __init__.py
│       ├── binstream.py
│       ├── indentedstream.py
│       └── log.py
├── main.py
└── test/
    ├── breaks.lua
    ├── expression.lua
    ├── ifs.lua
    ├── loop.lua
    └── primitive.lua
Download .txt
SYMBOL INDEX (584 symbols across 29 files)

FILE: ljd/ast/builder.py
  class _State (line 13) | class _State():
    method __init__ (line 14) | def __init__(self):
    method _warp_in_block (line 21) | def _warp_in_block(self, addr):
  function build (line 29) | def build(prototype):
  function _build_function_definition (line 33) | def _build_function_definition(prototype):
  function _build_function_arguments (line 57) | def _build_function_arguments(state, prototype):
  function _build_function_blocks (line 72) | def _build_function_blocks(state, instructions):
  function _blockenize (line 116) | def _blockenize(state, instructions):
  function _establish_warps (line 167) | def _establish_warps(state, instructions):
  function _build_warp (line 190) | def _build_warp(state, last_addr, instructions):
  function _build_jump_warp (line 207) | def _build_jump_warp(state, last_addr, instructions):
  function _build_conditional_warp (line 218) | def _build_conditional_warp(state, last_addr, instructions):
  function _build_unconditional_warp (line 255) | def _build_unconditional_warp(state, addr, instruction):
  function _build_iterator_warp (line 273) | def _build_iterator_warp(state, last_addr, instructions):
  function _build_numeric_loop_warp (line 308) | def _build_numeric_loop_warp(state, addr, instruction):
  function _build_flow_warp (line 328) | def _build_flow_warp(state, addr, instruction):
  function _build_statement (line 339) | def _build_statement(state, addr, instruction):
  function _build_var_assignment (line 386) | def _build_var_assignment(state, addr, instruction):
  function _build_knil (line 449) | def _build_knil(state, addr, instruction):
  function _build_global_assignment (line 457) | def _build_global_assignment(state, addr, instruction):
  function _build_table_assignment (line 469) | def _build_table_assignment(state, addr, instruction):
  function _build_table_mass_assignment (line 481) | def _build_table_mass_assignment(state, addr, instruction):
  function _build_call (line 496) | def _build_call(state, addr, instruction):
  function _build_vararg (line 522) | def _build_vararg(state, addr, instruction):
  function _build_return (line 537) | def _build_return(state, addr, instruction):
  function _build_call_arguments (line 560) | def _build_call_arguments(state, addr, instruction):
  function _build_range_assignment (line 585) | def _build_range_assignment(state, addr, from_slot, to_slot):
  function _build_binary_expression (line 611) | def _build_binary_expression(state, addr, instruction):
  function _build_concat_expression (line 639) | def _build_concat_expression(state, addr, instruction):
  function _build_const_expression (line 663) | def _build_const_expression(state, addr, instruction):
  function _build_table_element (line 686) | def _build_table_element(state, addr, instruction):
  function _build_function (line 698) | def _build_function(state, slot):
  function _build_table_copy (line 704) | def _build_table_copy(state, slot):
  function _build_table_record_item (line 729) | def _build_table_record_item(value):
  function _build_comparison_expression (line 777) | def _build_comparison_expression(state, addr, instruction):
  function _build_unary_expression (line 799) | def _build_unary_expression(state, addr, instruction):
  function _build_slot (line 826) | def _build_slot(state, addr, slot):
  function _build_upvalue (line 830) | def _build_upvalue(state, addr, slot):
  function _build_identifier (line 834) | def _build_identifier(state, addr, slot, want_type):
  function _build_global_variable (line 851) | def _build_global_variable(state, addr, slot):
  function _build_string_constant (line 862) | def _build_string_constant(state, index):
  function _build_cdata_constant (line 870) | def _build_cdata_constant(state, index):
  function _build_numeric_constant (line 878) | def _build_numeric_constant(state, index):
  function _build_primitive (line 892) | def _build_primitive(state, value):
  function _build_literal (line 907) | def _build_literal(state, value):

FILE: ljd/ast/helpers.py
  function insert_table_record (line 5) | def insert_table_record(constructor, key, value):
  function has_same_table (line 57) | def has_same_table(node, table):
  function is_equal (line 81) | def is_equal(a, b):

FILE: ljd/ast/locals.py
  function mark_locals (line 10) | def mark_locals(ast):
  function mark_local_definitions (line 14) | def mark_local_definitions(ast):
  class _LocalsMarker (line 18) | class _LocalsMarker(traverse.Visitor):
    class _State (line 19) | class _State():
      method __init__ (line 20) | def __init__(self):
    method __init__ (line 25) | def __init__(self):
    method _push_state (line 30) | def _push_state(self):
    method _pop_state (line 33) | def _pop_state(self):
    method _state (line 36) | def _state(self):
    method _process_slots (line 39) | def _process_slots(self, addr):
    method _reset_slot (line 65) | def _reset_slot(self, slot):
    method _reset_all (line 68) | def _reset_all(self, slots):
    method visit_function_definition (line 75) | def visit_function_definition(self, node):
    method leave_function_definition (line 79) | def leave_function_definition(self, node):
    method visit_variables_list (line 87) | def visit_variables_list(self, node):
    method visit_identifiers_list (line 92) | def visit_identifiers_list(self, node):
    method visit_numeric_loop_warp (line 95) | def visit_numeric_loop_warp(self, node):
    method visit_identifier (line 98) | def visit_identifier(self, node):
    method _process_worthy_node (line 107) | def _process_worthy_node(self, node):
    method _leave_node (line 119) | def _leave_node(self, handler, node):
    method _visit_node (line 124) | def _visit_node(self, handler, node):
  class _LocalDefinitionsMarker (line 130) | class _LocalDefinitionsMarker(traverse.Visitor):
    class _State (line 131) | class _State():
      method __init__ (line 132) | def __init__(self):
    method __init__ (line 136) | def __init__(self):
    method _push_state (line 139) | def _push_state(self):
    method _pop_state (line 142) | def _pop_state(self):
    method _state (line 145) | def _state(self):
    method _update_known_locals (line 148) | def _update_known_locals(self, local, addr):
    method visit_function_definition (line 163) | def visit_function_definition(self, node):
    method leave_function_definition (line 170) | def leave_function_definition(self, node):
    method visit_iterator_for (line 173) | def visit_iterator_for(self, node):
    method visit_numeric_for (line 180) | def visit_numeric_for(self, node):
    method visit_assignment (line 188) | def visit_assignment(self, node):
    method _visit (line 214) | def _visit(self, node):

FILE: ljd/ast/mutator.py
  class SimpleLoopWarpSwapper (line 10) | class SimpleLoopWarpSwapper(traverse.Visitor):
    method visit_statements_list (line 11) | def visit_statements_list(self, node):
    method _create_dummy_block (line 59) | def _create_dummy_block(self, block, slot):
    method _fix_uclo_return (line 85) | def _fix_uclo_return(self, block, next_block):
    method _swap_iterator_warps (line 104) | def _swap_iterator_warps(self, blocks, end):
    method _swap_numeric_loop_warps (line 130) | def _swap_numeric_loop_warps(self, blocks, end):
  class MutatorVisitor (line 158) | class MutatorVisitor(traverse.Visitor):
    method leave_if (line 161) | def leave_if(self, node):
    method visit_statements_list (line 178) | def visit_statements_list(self, node):
    method _fill_constructor (line 205) | def _fill_constructor(self, table, constructor, statements):
  function pre_pass (line 236) | def pre_pass(ast):
  function primary_pass (line 242) | def primary_pass(ast):

FILE: ljd/ast/nodes.py
  class FunctionDefinition (line 9) | class FunctionDefinition():
    method __init__ (line 10) | def __init__(self):
    method _accept (line 18) | def _accept(self, visitor):
  class TableConstructor (line 27) | class TableConstructor():
    method __init__ (line 28) | def __init__(self):
    method _accept (line 32) | def _accept(self, visitor):
  class ArrayRecord (line 41) | class ArrayRecord():
    method __init__ (line 42) | def __init__(self):
    method _accept (line 45) | def _accept(self, visitor):
  class TableRecord (line 53) | class TableRecord():
    method __init__ (line 54) | def __init__(self):
    method _accept (line 58) | def _accept(self, visitor):
  class Assignment (line 67) | class Assignment():
    method __init__ (line 71) | def __init__(self):
    method _accept (line 76) | def _accept(self, visitor):
  class BinaryOperator (line 85) | class BinaryOperator():
    method __init__ (line 108) | def __init__(self):
    method _accept (line 113) | def _accept(self, visitor):
  class UnaryOperator (line 122) | class UnaryOperator():
    method __init__ (line 127) | def __init__(self):
    method _accept (line 131) | def _accept(self, visitor):
  class StatementsList (line 139) | class StatementsList():
    method __init__ (line 140) | def __init__(self):
    method _accept (line 143) | def _accept(self, visitor):
  class IdentifiersList (line 151) | class IdentifiersList():
    method __init__ (line 152) | def __init__(self):
    method _accept (line 155) | def _accept(self, visitor):
  class RecordsList (line 163) | class RecordsList():
    method __init__ (line 164) | def __init__(self):
    method _accept (line 167) | def _accept(self, visitor):
  class VariablesList (line 175) | class VariablesList():
    method __init__ (line 176) | def __init__(self):
    method _accept (line 179) | def _accept(self, visitor):
  class ExpressionsList (line 187) | class ExpressionsList():
    method __init__ (line 188) | def __init__(self):
    method _accept (line 191) | def _accept(self, visitor):
  class Identifier (line 200) | class Identifier():
    method __init__ (line 206) | def __init__(self):
    method _accept (line 211) | def _accept(self, visitor):
  class MULTRES (line 218) | class MULTRES():
    method _accept (line 219) | def _accept(self, visitor):
  class TableElement (line 224) | class TableElement():
    method __init__ (line 225) | def __init__(self):
    method _accept (line 229) | def _accept(self, visitor):
  class Vararg (line 238) | class Vararg():
    method _accept (line 239) | def _accept(self, visitor):
  class FunctionCall (line 244) | class FunctionCall():
    method __init__ (line 245) | def __init__(self):
    method _accept (line 249) | def _accept(self, visitor):
  class If (line 258) | class If():
    method __init__ (line 259) | def __init__(self):
    method _accept (line 265) | def _accept(self, visitor):
  class ElseIf (line 278) | class ElseIf():
    method __init__ (line 279) | def __init__(self):
    method _accept (line 283) | def _accept(self, visitor):
  class Block (line 294) | class Block():
    method __init__ (line 295) | def __init__(self):
    method _accept (line 303) | def _accept(self, visitor):
  class UnconditionalWarp (line 312) | class UnconditionalWarp():
    method __init__ (line 316) | def __init__(self):
    method _accept (line 321) | def _accept(self, visitor):
  class ConditionalWarp (line 329) | class ConditionalWarp():
    method __init__ (line 330) | def __init__(self):
    method _accept (line 335) | def _accept(self, visitor):
  class IteratorWarp (line 345) | class IteratorWarp():
    method __init__ (line 346) | def __init__(self):
    method _accept (line 352) | def _accept(self, visitor):
  class NumericLoopWarp (line 363) | class NumericLoopWarp():
    method __init__ (line 364) | def __init__(self):
    method _accept (line 370) | def _accept(self, visitor):
  class EndWarp (line 381) | class EndWarp():
    method _accept (line 382) | def _accept(self, visitor):
  class Return (line 390) | class Return():
    method __init__ (line 391) | def __init__(self):
    method _accept (line 394) | def _accept(self, visitor):
  class Break (line 402) | class Break():
    method _accept (line 403) | def _accept(self, visitor):
  class While (line 408) | class While():
    method __init__ (line 409) | def __init__(self):
    method _accept (line 413) | def _accept(self, visitor):
  class RepeatUntil (line 422) | class RepeatUntil():
    method __init__ (line 423) | def __init__(self):
    method _accept (line 427) | def _accept(self, visitor):
  class NumericFor (line 436) | class NumericFor():
    method __init__ (line 437) | def __init__(self):
    method _accept (line 442) | def _accept(self, visitor):
  class IteratorFor (line 452) | class IteratorFor():
    method __init__ (line 453) | def __init__(self):
    method _accept (line 458) | def _accept(self, visitor):
  class Constant (line 468) | class Constant():
    method __init__ (line 474) | def __init__(self):
    method _accept (line 478) | def _accept(self, visitor):
  class Primitive (line 483) | class Primitive():
    method __init__ (line 488) | def __init__(self):
    method _accept (line 491) | def _accept(self, visitor):

FILE: ljd/ast/slotworks.py
  function eliminate_temporary (line 10) | def eliminate_temporary(ast):
  function _eliminate_temporary (line 23) | def _eliminate_temporary(slots):
  function _fill_massive_refs (line 56) | def _fill_massive_refs(info, simple, massive, iterators):
  function _fill_simple_refs (line 90) | def _fill_simple_refs(info, simple, tables):
  function _get_holder (line 128) | def _get_holder(path):
  function _eliminate_simple_cases (line 136) | def _eliminate_simple_cases(simple):
  function _eliminate_into_table_constructors (line 156) | def _eliminate_into_table_constructors(tables):
  function _eliminate_mass_assignments (line 174) | def _eliminate_mass_assignments(massive):
  function _replace_node (line 184) | def _replace_node(holder, original, replacement):
  function _replace_node_in_list (line 193) | def _replace_node_in_list(nodes, original, replacement):
  function _eliminate_iterators (line 203) | def _eliminate_iterators(iterators):
  function _mark_invalidated (line 222) | def _mark_invalidated(node):
  function _is_invalidated (line 226) | def _is_invalidated(node):
  function _remove_unused (line 230) | def _remove_unused(unused):
  function _collect_slots (line 234) | def _collect_slots(ast):
  function _eliminate_multres (line 241) | def _eliminate_multres(ast):
  class _MultresEliminator (line 246) | class _MultresEliminator(traverse.Visitor):
    method __init__ (line 247) | def __init__(self):
    method leave_assignment (line 250) | def leave_assignment(self, node):
    method _process_multres_in_list (line 277) | def _process_multres_in_list(self, nodes_list):
    method visit_function_call (line 289) | def visit_function_call(self, node):
    method visit_return (line 292) | def visit_return(self, node):
  class _SlotReference (line 296) | class _SlotReference():
    method __init__ (line 297) | def __init__(self):
  class _SlotInfo (line 302) | class _SlotInfo():
    method __init__ (line 303) | def __init__(self):
  class _SlotsCollector (line 313) | class _SlotsCollector(traverse.Visitor):
    class _State (line 314) | class _State():
      method __init__ (line 315) | def __init__(self):
    method __init__ (line 321) | def __init__(self):
    method _state (line 333) | def _state(self):
    method _push_state (line 336) | def _push_state(self):
    method _pop_state (line 339) | def _pop_state(self):
    method _commit_info (line 342) | def _commit_info(self, info):
    method _commit_slot (line 350) | def _commit_slot(self, slot, node):
    method _register_slot (line 362) | def _register_slot(self, slot, node):
    method _register_all_slots (line 372) | def _register_all_slots(self, node, slots):
    method _commit_all_slots (line 382) | def _commit_all_slots(self, slots, node):
    method _register_slot_reference (line 389) | def _register_slot_reference(self, slot, node):
    method visit_assignment (line 405) | def visit_assignment(self, node):
    method leave_assignment (line 411) | def leave_assignment(self, node):
    method visit_identifier (line 414) | def visit_identifier(self, node):
    method visit_function_definition (line 420) | def visit_function_definition(self, node):
    method leave_function_definition (line 424) | def leave_function_definition(self, node):
    method leave_block (line 427) | def leave_block(self, node):
    method visit_iterator_warp (line 433) | def visit_iterator_warp(self, node):
    method visit_numeric_loop_warp (line 436) | def visit_numeric_loop_warp(self, node):
    method _visit_node (line 441) | def _visit_node(self, handler, node):
    method _leave_node (line 446) | def _leave_node(self, handler, node):
    method _visit (line 451) | def _visit(self, node):
  function _cleanup_invalid_nodes (line 458) | def _cleanup_invalid_nodes(ast):
  class _TreeCleanup (line 462) | class _TreeCleanup(traverse.Visitor):
    method visit_block (line 463) | def visit_block(self, node):

FILE: ljd/ast/traverse.py
  class Visitor (line 1) | class Visitor():
    method __init__ (line 2) | def __init__(self):
    method visit_function_definition (line 7) | def visit_function_definition(self, node):
    method leave_function_definition (line 10) | def leave_function_definition(self, node):
    method visit_table_constructor (line 15) | def visit_table_constructor(self, node):
    method leave_table_constructor (line 18) | def leave_table_constructor(self, node):
    method visit_table_record (line 21) | def visit_table_record(self, node):
    method leave_table_record (line 24) | def leave_table_record(self, node):
    method visit_array_record (line 27) | def visit_array_record(self, node):
    method leave_array_record (line 30) | def leave_array_record(self, node):
    method visit_assignment (line 35) | def visit_assignment(self, node):
    method leave_assignment (line 38) | def leave_assignment(self, node):
    method visit_binary_operator (line 43) | def visit_binary_operator(self, node):
    method leave_binary_operator (line 46) | def leave_binary_operator(self, node):
    method visit_unary_operator (line 49) | def visit_unary_operator(self, node):
    method leave_unary_operator (line 52) | def leave_unary_operator(self, node):
    method visit_statements_list (line 57) | def visit_statements_list(self, node):
    method leave_statements_list (line 60) | def leave_statements_list(self, node):
    method visit_identifiers_list (line 63) | def visit_identifiers_list(self, node):
    method leave_identifiers_list (line 66) | def leave_identifiers_list(self, node):
    method visit_records_list (line 69) | def visit_records_list(self, node):
    method leave_records_list (line 72) | def leave_records_list(self, node):
    method visit_variables_list (line 75) | def visit_variables_list(self, node):
    method leave_variables_list (line 78) | def leave_variables_list(self, node):
    method visit_expressions_list (line 81) | def visit_expressions_list(self, node):
    method leave_expressions_list (line 84) | def leave_expressions_list(self, node):
    method visit_identifier (line 89) | def visit_identifier(self, node):
    method leave_identifier (line 92) | def leave_identifier(self, node):
    method visit_multres (line 95) | def visit_multres(self, node):
    method leave_multres (line 98) | def leave_multres(self, node):
    method visit_table_element (line 101) | def visit_table_element(self, node):
    method leave_table_element (line 104) | def leave_table_element(self, node):
    method visit_vararg (line 107) | def visit_vararg(self, node):
    method leave_vararg (line 110) | def leave_vararg(self, node):
    method visit_function_call (line 113) | def visit_function_call(self, node):
    method leave_function_call (line 116) | def leave_function_call(self, node):
    method visit_if (line 121) | def visit_if(self, node):
    method leave_if (line 124) | def leave_if(self, node):
    method visit_elseif (line 127) | def visit_elseif(self, node):
    method leave_elseif (line 130) | def leave_elseif(self, node):
    method visit_block (line 135) | def visit_block(self, node):
    method leave_block (line 138) | def leave_block(self, node):
    method visit_unconditional_warp (line 141) | def visit_unconditional_warp(self, node):
    method leave_unconditional_warp (line 144) | def leave_unconditional_warp(self, node):
    method visit_conditional_warp (line 147) | def visit_conditional_warp(self, node):
    method leave_conditional_warp (line 150) | def leave_conditional_warp(self, node):
    method visit_iterator_warp (line 153) | def visit_iterator_warp(self, node):
    method leave_iterator_warp (line 156) | def leave_iterator_warp(self, node):
    method visit_numeric_loop_warp (line 159) | def visit_numeric_loop_warp(self, node):
    method leave_numeric_loop_warp (line 162) | def leave_numeric_loop_warp(self, node):
    method visit_end_warp (line 165) | def visit_end_warp(self, node):
    method leave_end_warp (line 168) | def leave_end_warp(self, node):
    method visit_return (line 173) | def visit_return(self, node):
    method leave_return (line 176) | def leave_return(self, node):
    method visit_break (line 179) | def visit_break(self, node):
    method leave_break (line 182) | def leave_break(self, node):
    method visit_while (line 187) | def visit_while(self, node):
    method leave_while (line 190) | def leave_while(self, node):
    method visit_repeat_until (line 193) | def visit_repeat_until(self, node):
    method leave_repeat_until (line 196) | def leave_repeat_until(self, node):
    method visit_numeric_for (line 199) | def visit_numeric_for(self, node):
    method leave_numeric_for (line 202) | def leave_numeric_for(self, node):
    method visit_iterator_for (line 205) | def visit_iterator_for(self, node):
    method leave_iterator_for (line 208) | def leave_iterator_for(self, node):
    method visit_constant (line 213) | def visit_constant(self, node):
    method leave_constant (line 216) | def leave_constant(self, node):
    method visit_primitive (line 219) | def visit_primitive(self, node):
    method leave_primitive (line 222) | def leave_primitive(self, node):
    method _visit_node (line 227) | def _visit_node(self, handler, node):
    method _leave_node (line 230) | def _leave_node(self, handler, node):
    method _visit (line 235) | def _visit(self, node):
    method _visit_list (line 240) | def _visit_list(self, nodes_list):
  function traverse (line 247) | def traverse(visitor, node):

FILE: ljd/ast/unwarper.py
  class _StatementsCollector (line 20) | class _StatementsCollector(traverse.Visitor):
    method __init__ (line 21) | def __init__(self):
    method visit_statements_list (line 24) | def visit_statements_list(self, node):
  function unwarp (line 29) | def unwarp(node):
  function _run_step (line 41) | def _run_step(step, node, **kargs):
  function _gather_statements_lists (line 51) | def _gather_statements_lists(node):
  function _glue_flows (line 57) | def _glue_flows(node):
  function _unwarp_expressions (line 83) | def _unwarp_expressions(blocks):
  function _find_endest_end (line 129) | def _find_endest_end(expressions):
  function _unwarp_ifs (line 139) | def _unwarp_ifs(blocks, top_end=None, topmost_end=None):
  function _extract_if_body (line 176) | def _extract_if_body(start_index, blocks, topmost_end):
  function _unwarp_expressions_pack (line 192) | def _unwarp_expressions_pack(blocks, pack):
  function _split_by_slot_use (line 244) | def _split_by_slot_use(statements, min_i, warp, slot):
  function _extract_destination_slots (line 275) | def _extract_destination_slots(statement):
  function _gather_slots (line 288) | def _gather_slots(node):
  function _find_expressions (line 304) | def _find_expressions(start, body, end):
  function _find_subexpressions (line 447) | def _find_subexpressions(start, body):
  function _get_simple_local_assignment_slot (line 460) | def _get_simple_local_assignment_slot(start, body, end):
  function _find_expression_slot (line 473) | def _find_expression_slot(body):
  function _unwarp_logical_expression (line 486) | def _unwarp_logical_expression(start, end, body):
  function _compile_expression (line 504) | def _compile_expression(body, end, true, false):
  function _unwarp_expression (line 570) | def _unwarp_expression(body, end, true, false):
  function _get_target (line 691) | def _get_target(warp, allow_end=False):
  function _set_target (line 702) | def _set_target(warp, target):
  function _get_operator (line 710) | def _get_operator(block, true, end):
  function _get_last_assignment_source (line 740) | def _get_last_assignment_source(block):
  function _get_and_remove_last_assignment_source (line 749) | def _get_and_remove_last_assignment_source(block):
  function _compile_subexpression (line 755) | def _compile_subexpression(subexpression, operator,
  function _is_inverted (line 785) | def _is_inverted(warp, true, end):
  function _invert (line 813) | def _invert(expression):
  function _get_terminators (line 836) | def _get_terminators(body):
  function _assemble_expression (line 868) | def _assemble_expression(parts):
  function _make_explicit_subexpressions (line 901) | def _make_explicit_subexpressions(parts):
  function _unwarp_if_statement (line 936) | def _unwarp_if_statement(start, body, end, topmost_end):
  function _extract_if_expression (line 989) | def _extract_if_expression(start, body, end, topmost_end):
  function _search_expression_end (line 1034) | def _search_expression_end(expression, falses):
  function _find_branching_end (line 1055) | def _find_branching_end(blocks, topmost_end):
  function _remove_processed_blocks (line 1076) | def _remove_processed_blocks(blocks, boundaries):
  function _unwarp_loops (line 1099) | def _unwarp_loops(blocks, repeat_until):
  function _cleanup_breaks_and_if_ends (line 1139) | def _cleanup_breaks_and_if_ends(loops, blocks):
  function _replace_targets (line 1200) | def _replace_targets(blocks, original, replacement):
  function _unwarp_loop (line 1223) | def _unwarp_loop(start, end, body):
  function _create_next_block (line 1356) | def _create_next_block(original):
  function _set_flow_to (line 1366) | def _set_flow_to(block, target):
  function _set_end (line 1372) | def _set_end(block):
  function _is_flow (line 1383) | def _is_flow(warp):
  function _is_jump (line 1388) | def _is_jump(warp):
  function _fix_nested_ifs (line 1393) | def _fix_nested_ifs(blocks, start):
  function _fix_expression (line 1413) | def _fix_expression(blocks, start, end):
  function _gather_possible_ends (line 1424) | def _gather_possible_ends(block):
  function _unwarp_breaks (line 1442) | def _unwarp_breaks(start, blocks, next_block):
  function _find_all_loops (line 1556) | def _find_all_loops(blocks, repeat_until):
  function _get_previous_block (line 1628) | def _get_previous_block(block, blocks):

FILE: ljd/ast/validator.py
  class TypeRestriction (line 9) | class TypeRestriction():
    method __init__ (line 10) | def __init__(self, default, specific):
    method check (line 18) | def check(self, node):
  class Visitor (line 70) | class Visitor(traverse.Visitor):
    method __init__ (line 71) | def __init__(self, warped=True):
    method _set_restrictions (line 76) | def _set_restrictions(self, default, specific={}):
    method visit_function_definition (line 81) | def visit_function_definition(self, node):
    method visit_table_constructor (line 89) | def visit_table_constructor(self, node):
    method visit_array_record (line 92) | def visit_array_record(self, node):
    method visit_table_record (line 95) | def visit_table_record(self, node):
    method visit_assignment (line 100) | def visit_assignment(self, node):
    method visit_binary_operator (line 115) | def visit_binary_operator(self, node):
    method visit_unary_operator (line 140) | def visit_unary_operator(self, node):
    method visit_statements_list (line 149) | def visit_statements_list(self, node):
    method visit_identifiers_list (line 157) | def visit_identifiers_list(self, node):
    method visit_records_list (line 161) | def visit_records_list(self, node):
    method visit_variables_list (line 178) | def visit_variables_list(self, node):
    method visit_expressions_list (line 181) | def visit_expressions_list(self, node):
    method visit_identifier (line 186) | def visit_identifier(self, node):
    method visit_table_element (line 195) | def visit_table_element(self, node):
    method visit_function_call (line 198) | def visit_function_call(self, node):
    method visit_if (line 206) | def visit_if(self, node):
    method visit_elseif (line 213) | def visit_elseif(self, node):
    method visit_block (line 221) | def visit_block(self, node):
    method visit_unconditional_warp (line 234) | def visit_unconditional_warp(self, node):
    method visit_conditional_warp (line 240) | def visit_conditional_warp(self, node):
    method visit_iterator_warp (line 253) | def visit_iterator_warp(self, node):
    method visit_numeric_loop_warp (line 264) | def visit_numeric_loop_warp(self, node):
    method visit_return (line 275) | def visit_return(self, node):
    method visit_while (line 280) | def visit_while(self, node):
    method visit_repeat_until (line 286) | def visit_repeat_until(self, node):
    method visit_numeric_for (line 292) | def visit_numeric_for(self, node):
    method visit_iterator_for (line 299) | def visit_iterator_for(self, node):
    method visit_constant (line 308) | def visit_constant(self, node):
    method visit_primitive (line 314) | def visit_primitive(self, node):
    method _visit (line 321) | def _visit(self, node):
  function validate (line 336) | def validate(ast, warped=True):

FILE: ljd/bytecode/constants.py
  class Table (line 10) | class Table():
    method __init__ (line 11) | def __init__(self):
  class Constants (line 19) | class Constants():
    method __init__ (line 20) | def __init__(self):

FILE: ljd/bytecode/debuginfo.py
  class VariableInfo (line 6) | class VariableInfo():
    method __init__ (line 10) | def __init__(self):
  class DebugInformation (line 17) | class DebugInformation():
    method __init__ (line 18) | def __init__(self):
    method lookup_line_number (line 23) | def lookup_line_number(self, addr):
    method lookup_local_name (line 29) | def lookup_local_name(self, addr, slot):
    method lookup_upvalue_name (line 42) | def lookup_upvalue_name(self, slot):

FILE: ljd/bytecode/helpers.py
  function get_jump_destination (line 1) | def get_jump_destination(addr, instruction):
  function set_jump_destination (line 5) | def set_jump_destination(addr, instruction, value):

FILE: ljd/bytecode/instructions.py
  class _Instruction (line 46) | class _Instruction():
    method __init__ (line 47) | def __init__(self, definition):
  class _IDef (line 61) | class _IDef():
    method __init__ (line 64) | def __init__(self, name, A_type, B_type, CD_type, description):
    method __call__ (line 78) | def __call__(self):

FILE: ljd/bytecode/prototype.py
  class Flags (line 9) | class Flags():
    method __init (line 10) | def __init(self):
  class Prototype (line 18) | class Prototype():
    method __init__ (line 19) | def __init__(self):

FILE: ljd/lua/writer.py
  class _State (line 40) | class _State():
    method __init__ (line 41) | def __init__(self):
  class Visitor (line 47) | class Visitor(traverse.Visitor):
    method __init__ (line 48) | def __init__(self):
    method _start_statement (line 58) | def _start_statement(self, statement):
    method _end_statement (line 63) | def _end_statement(self, statement):
    method _end_line (line 68) | def _end_line(self):
    method _start_block (line 71) | def _start_block(self):
    method _end_block (line 74) | def _end_block(self):
    method _write (line 77) | def _write(self, fmt, *args, **kargs):
    method _state (line 80) | def _state(self):
    method _push_state (line 83) | def _push_state(self):
    method _pop_state (line 86) | def _pop_state(self):
    method visit_function_definition (line 91) | def visit_function_definition(self, node):
    method visit_table_constructor (line 125) | def visit_table_constructor(self, node):
    method visit_table_record (line 166) | def visit_table_record(self, node):
    method visit_assignment (line 186) | def visit_assignment(self, node):
    method _is_variable (line 223) | def _is_variable(self, node):
    method _is_global (line 229) | def _is_global(self, node):
    method _is_builtin (line 235) | def _is_builtin(self, node):
    method _is_valid_name (line 241) | def _is_valid_name(self, key):
    method visit_binary_operator (line 249) | def visit_binary_operator(self, node):
    method visit_unary_operator (line 335) | def visit_unary_operator(self, node):
    method visit_statements_list (line 356) | def visit_statements_list(self, node):
    method leave_statements_list (line 362) | def leave_statements_list(self, node):
    method _visit_comma_separated_list (line 368) | def _visit_comma_separated_list(self, node):
    method visit_records_list (line 380) | def visit_records_list(self, node):
    method visit_identifier (line 398) | def visit_identifier(self, node):
    method visit_multres (line 404) | def visit_multres(self, node):
    method visit_table_element (line 407) | def visit_table_element(self, node):
    method visit_vararg (line 446) | def visit_vararg(self, node):
    method visit_function_call (line 449) | def visit_function_call(self, node):
    method visit_if (line 494) | def visit_if(self, node):
    method visit_elseif (line 522) | def visit_elseif(self, node):
    method visit_block (line 535) | def visit_block(self, node):
    method visit_unconditional_warp (line 555) | def visit_unconditional_warp(self, node):
    method visit_conditional_warp (line 565) | def visit_conditional_warp(self, node):
    method visit_iterator_warp (line 590) | def visit_iterator_warp(self, node):
    method visit_numeric_loop_warp (line 607) | def visit_numeric_loop_warp(self, node):
    method visit_return (line 624) | def visit_return(self, node):
    method visit_break (line 633) | def visit_break(self, node):
    method visit_while (line 642) | def visit_while(self, node):
    method visit_repeat_until (line 656) | def visit_repeat_until(self, node):
    method visit_numeric_for (line 669) | def visit_numeric_for(self, node):
    method visit_iterator_for (line 687) | def visit_iterator_for(self, node):
    method visit_constant (line 705) | def visit_constant(self, node):
    method visit_primitive (line 731) | def visit_primitive(self, node):
    method _skip (line 739) | def _skip(self, node):
    method _visit (line 742) | def _visit(self, node):
  function write (line 760) | def write(fd, ast):
  function _get_next_significant (line 770) | def _get_next_significant(queue, i):
  function _process_queue (line 787) | def _process_queue(fd, queue):

FILE: ljd/pseudoasm/constants.py
  function write_tables (line 8) | def write_tables(writer, prototype):
  function _write_table (line 18) | def _write_table(writer, index, table):
  function _translate_element (line 42) | def _translate_element(element):

FILE: ljd/pseudoasm/instructions.py
  class _State (line 16) | class _State():
    method __init__ (line 17) | def __init__(self, writer, prototype, instructions):
  function write (line 25) | def write(writer, prototype):
  function _write_instruction (line 47) | def _write_instruction(writer, addr, line, instruction):
  function _write_function (line 61) | def _write_function(writer, addr, line, instruction):
  function _translate_description (line 83) | def _translate_description(writer, addr, line, instruction):
  function _translate (line 92) | def _translate(writer, addr, value, attr_type):
  function _lookup_variable_name (line 134) | def _lookup_variable_name(writer, addr, slot):
  function _lookup_variable_name_step (line 146) | def _lookup_variable_name_step(writer, addr, slot):
  function _translate_standard (line 207) | def _translate_standard(writer, addr, line, instruction):
  function _translate_normal (line 224) | def _translate_normal(writer, description, addr, line, instruction):
  function _translate_concat (line 230) | def _translate_concat(writer, description, addr, line, instruction):
  function _translate_nil (line 246) | def _translate_nil(writer, description, addr, line, instruction):
  function _translate_table_str_op (line 260) | def _translate_table_str_op(writer, description, addr, line, instruction):
  function _translate_new_table (line 268) | def _translate_new_table(writer, description, addr, line, instruction):
  function _translate_mass_set (line 283) | def _translate_mass_set(writer, description, addr, line, instruction):
  function _translate_varg_call (line 297) | def _translate_varg_call(writer, description, addr, line, instruction):
  function _translate_call (line 324) | def _translate_call(writer, description, addr, line, instruction):
  function _translate_varg_tailcall (line 351) | def _translate_varg_tailcall(writer, description, addr, line, instruction):
  function _translate_tailcall (line 370) | def _translate_tailcall(writer, description, addr, line, instruction):
  function _translate_iterator (line 389) | def _translate_iterator(writer, description, addr, line, instruction):
  function _translate_vararg (line 420) | def _translate_vararg(writer, description, addr, line, instruction):
  function _translate_return_mult (line 440) | def _translate_return_mult(writer, description, addr, line, instruction):
  function _translate_return_many (line 457) | def _translate_return_many(writer, description, addr, line, instruction):
  function _translate_return_one (line 474) | def _translate_return_one(writer, description, addr, line, instruction):
  function _translate_for_init (line 480) | def _translate_for_init(writer, description, addr, line, instruction):
  function _translate_numeric_loop (line 495) | def _translate_numeric_loop(writer, description, addr, line, instruction):
  function _translate_iter_loop (line 511) | def _translate_iter_loop(writer, description, addr, line, instruction):
  function _init (line 677) | def _init():

FILE: ljd/pseudoasm/prototype.py
  function write (line 9) | def write(writer, prototype):
  function _write_header (line 16) | def _write_header(writer, prototype):
  function format_header (line 20) | def format_header(writer, prototype):
  function write_body (line 33) | def write_body(writer, prototype):

FILE: ljd/pseudoasm/writer.py
  class _State (line 10) | class _State():
    method __init__ (line 11) | def __init__(self):
  function write (line 17) | def write(fd, header, prototype):
  function _write_header (line 29) | def _write_header(writer, header):

FILE: ljd/rawdump/code.py
  function read (line 173) | def read(parser):
  function _set_instruction_operands (line 196) | def _set_instruction_operands(parser, codeword, instruction):
  function _process_operand (line 215) | def _process_operand(parser, operand_type, operand):
  function _init (line 227) | def _init():

FILE: ljd/rawdump/constants.py
  function read (line 28) | def read(parser, constants):
  function _read_upvalue_references (line 38) | def _read_upvalue_references(parser, references):
  function _read_complex_constants (line 49) | def _read_complex_constants(parser, complex_constants):
  function _read_numeric_constants (line 86) | def _read_numeric_constants(parser, numeric_constants):
  function _read_number (line 106) | def _read_number(parser):
  function _read_signed_int (line 113) | def _read_signed_int(parser):
  function _assemble_number (line 119) | def _assemble_number(lo, hi):
  function _process_sign (line 129) | def _process_sign(number):
  function _read_table (line 136) | def _read_table(parser, table):
  function _read_table_item (line 158) | def _read_table_item(parser):

FILE: ljd/rawdump/debuginfo.py
  function read (line 31) | def read(parser, line_offset, debuginfo):
  function _read_lineinfo (line 41) | def _read_lineinfo(parser, line_offset, lineinfo):
  function _read_upvalue_names (line 58) | def _read_upvalue_names(parser, names):
  function _read_variable_infos (line 66) | def _read_variable_infos(parser, infos):

FILE: ljd/rawdump/header.py
  class Flags (line 17) | class Flags():
    method __init__ (line 18) | def __init__(self):
  class Header (line 24) | class Header():
    method __init__ (line 25) | def __init__(self):
  function read (line 32) | def read(state, header):
  function _check_magic (line 47) | def _check_magic(state):
  function _read_version (line 55) | def _read_version(state, header):
  function _read_flags (line 66) | def _read_flags(parser, header):
  function _read_name (line 90) | def _read_name(state, header):

FILE: ljd/rawdump/parser.py
  class _State (line 16) | class _State():
    method __init__ (line 17) | def __init__(self):
  function parse (line 23) | def parse(filename):
  function _read_header (line 60) | def _read_header(parser, header):
  function _read_prototypes (line 73) | def _read_prototypes(state, prototypes):

FILE: ljd/rawdump/prototype.py
  class _State (line 21) | class _State():
    method __init__ (line 22) | def __init__(self, parser):
  function read (line 40) | def read(parser, prototype):
  function _read_flags (line 72) | def _read_flags(parser, prototype):
  function _read_counts_and_sizes (line 97) | def _read_counts_and_sizes(parser, prototype):
  function _read_instructions (line 122) | def _read_instructions(parser, prototype):
  function _read_constants (line 151) | def _read_constants(parser, prototype):
  function _read_debuginfo (line 155) | def _read_debuginfo(stream, prototype):

FILE: ljd/util/binstream.py
  class BinStream (line 10) | class BinStream():
    method __init__ (line 11) | def __init__(self):
    method open (line 20) | def open(self, filename):
    method close (line 25) | def close(self):
    method eof (line 30) | def eof(self):
    method check_data_available (line 33) | def check_data_available(self, size=1):
    method read_bytes (line 36) | def read_bytes(self, size=1):
    method read_byte (line 46) | def read_byte(self):
    method read_zstring (line 57) | def read_zstring(self):
    method read_uleb128 (line 70) | def read_uleb128(self):
    method read_uleb128_from33bit (line 88) | def read_uleb128_from33bit(self):
    method read_uint (line 109) | def read_uint(self, size=4):

FILE: ljd/util/indentedstream.py
  class IndentedStream (line 8) | class IndentedStream():
    method __init__ (line 9) | def __init__(self, fd):
    method write_multiline (line 15) | def write_multiline(self, fmt, *args, **kargs):
    method start_line (line 36) | def start_line(self):
    method write (line 42) | def write(self, fmt="", *args, **kargs):
    method end_line (line 56) | def end_line(self):
    method write_line (line 63) | def write_line(self, *args, **kargs):
    method open_block (line 68) | def open_block(self, *args, **kargs):
    method close_block (line 74) | def close_block(self, *args, **kargs):

FILE: ljd/util/log.py
  function errprint (line 8) | def errprint(*args):

FILE: main.py
  function dump (line 40) | def dump(name, obj, level=0):
  function main (line 75) | def main():
Condensed preview — 46 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (249K chars).
[
  {
    "path": ".gitignore",
    "chars": 332,
    "preview": "*.lua[co]\n*.py[cod]\n\n# C extensions\n*.so\n\n# Packages\n*.egg\n*.egg-info\ndist\nbuild\neggs\nparts\nbin\nvar\nsdist\ndevelop-eggs\n."
  },
  {
    "path": "LICENSE",
    "chars": 1079,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2013 Andrian Nord\n\nPermission is hereby granted, free of charge, to any person obta"
  },
  {
    "path": "README.md",
    "chars": 503,
    "preview": "代码在fork https://github.com/NightNord/ljd 的基础上修改的\n\n主要修正了原版本没有将_read_header函数解析过的header中的flag值带进_read_protoypes。\n\n解决了原代码只能"
  },
  {
    "path": "README.md.txt",
    "chars": 2336,
    "preview": "LuaJIT raw-bytecode decompiler (LJD)\n===\n\nThe original name was _ljwthgnd_ as in _LuaJIT 'What The Hell is Going On'\nDec"
  },
  {
    "path": "gconfig.py",
    "chars": 68,
    "preview": "\n#zzw 20180714 support str encode\ngFlagDic = {'strEncode' : 'ascii'}"
  },
  {
    "path": "ljd/__init__.py",
    "chars": 72,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n"
  },
  {
    "path": "ljd/ast/__init__.py",
    "chars": 72,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n"
  },
  {
    "path": "ljd/ast/builder.py",
    "chars": 23725,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport ljd.bytecode.instructions as ins\n\nfrom lj"
  },
  {
    "path": "ljd/ast/helpers.py",
    "chars": 1991,
    "preview": "import ljd.ast.nodes as nodes\nimport ljd.ast.traverse as traverse\n\n\ndef insert_table_record(constructor, key, value):\n\ta"
  },
  {
    "path": "ljd/ast/locals.py",
    "chars": 4865,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n\nimport ljd.ast.nodes as nodes\nimport ljd.ast.tr"
  },
  {
    "path": "ljd/ast/mutator.py",
    "chars": 5294,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport copy\n\nfrom ljd.ast.helpers import *\n\n\ncla"
  },
  {
    "path": "ljd/ast/nodes.py",
    "chars": 10816,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n\n# We should visit stuff in it's execution order"
  },
  {
    "path": "ljd/ast/slotworks.py",
    "chars": 10423,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport ljd.ast.nodes as nodes\nimport ljd.ast.tra"
  },
  {
    "path": "ljd/ast/traverse.py",
    "chars": 3488,
    "preview": "class Visitor():\n\tdef __init__(self):\n\t\tpass\n\n\t# ##\n\n\tdef visit_function_definition(self, node):\n\t\tpass\n\n\tdef leave_func"
  },
  {
    "path": "ljd/ast/unwarper.py",
    "chars": 37226,
    "preview": "import copy\n\nimport ljd.ast.nodes as nodes\nimport ljd.ast.traverse as traverse\nimport ljd.ast.slotworks as slotworks\n\nbi"
  },
  {
    "path": "ljd/ast/validator.py",
    "chars": 8310,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport ljd.ast.nodes as nodes\nimport ljd.ast.tra"
  },
  {
    "path": "ljd/bytecode/__init__.py",
    "chars": 72,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n"
  },
  {
    "path": "ljd/bytecode/constants.py",
    "chars": 390,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nT_NIL = 0\nT_FALSE = 1\nT_TRUE = 2\n\n\nclass Table()"
  },
  {
    "path": "ljd/bytecode/debuginfo.py",
    "chars": 838,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n\nclass VariableInfo():\n\tT_VISIBILE = 0\n\tT_INTERN"
  },
  {
    "path": "ljd/bytecode/helpers.py",
    "chars": 168,
    "preview": "def get_jump_destination(addr, instruction):\n\treturn addr + instruction.CD + 1\n\n\ndef set_jump_destination(addr, instruct"
  },
  {
    "path": "ljd/bytecode/instructions.py",
    "chars": 10153,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n#\n# Almost direct wiki-to-code from\n# http://wik"
  },
  {
    "path": "ljd/bytecode/prototype.py",
    "chars": 600,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport ljd.bytecode.constants as constants\nimpor"
  },
  {
    "path": "ljd/lua/__init__.py",
    "chars": 72,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n"
  },
  {
    "path": "ljd/lua/writer.py",
    "chars": 16967,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport re\n\nimport ljd.ast.nodes as nodes\nimport "
  },
  {
    "path": "ljd/pseudoasm/__init__.py",
    "chars": 72,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n"
  },
  {
    "path": "ljd/pseudoasm/constants.py",
    "chars": 1082,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport ljd.bytecode.constants\n\n\ndef write_tables"
  },
  {
    "path": "ljd/pseudoasm/instructions.py",
    "chars": 16726,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport ljd.bytecode.instructions as ins\nfrom ljd"
  },
  {
    "path": "ljd/pseudoasm/prototype.py",
    "chars": 1049,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport ljd.pseudoasm.constants\nimport ljd.pseudo"
  },
  {
    "path": "ljd/pseudoasm/writer.py",
    "chars": 968,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport ljd.util.indentedstream\n\nimport ljd.pseud"
  },
  {
    "path": "ljd/rawdump/__init__.py",
    "chars": 72,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n"
  },
  {
    "path": "ljd/rawdump/code.py",
    "chars": 7085,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nfrom ljd.util.log import errprint\n\nimport ljd.by"
  },
  {
    "path": "ljd/rawdump/constants.py",
    "chars": 3743,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport sys\nimport struct\n\nimport ljd.bytecode.co"
  },
  {
    "path": "ljd/rawdump/debuginfo.py",
    "chars": 2014,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport sys\n\nimport ljd.bytecode.debuginfo\n\n\nVARN"
  },
  {
    "path": "ljd/rawdump/header.py",
    "chars": 2190,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nfrom ljd.util.log import errprint\n\n\n_MAGIC = b'\\"
  },
  {
    "path": "ljd/rawdump/parser.py",
    "chars": 2069,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n#!/usr/bin/python3\n\nimport ljd.util.binstream\nfr"
  },
  {
    "path": "ljd/rawdump/prototype.py",
    "chars": 4380,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nfrom ljd.util.log import errprint\n\nimport ljd.by"
  },
  {
    "path": "ljd/util/__init__.py",
    "chars": 72,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n"
  },
  {
    "path": "ljd/util/binstream.py",
    "chars": 1993,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport io\nimport os\nimport sys\n\n\nclass BinStream"
  },
  {
    "path": "ljd/util/indentedstream.py",
    "chars": 1402,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\n_TAB_WIDTH = \" \" * 8\n\n\nclass IndentedStream():\n\t"
  },
  {
    "path": "ljd/util/log.py",
    "chars": 324,
    "preview": "#\n# Copyright (C) 2013 Andrian Nord. See Copyright Notice in main.py\n#\n\nimport sys\n\n\ndef errprint(*args):\n\tfmt = None\n\n\t"
  },
  {
    "path": "main.py",
    "chars": 3465,
    "preview": "#!/usr/bin/python3\n#\n# The MIT License (MIT)\n#\n# Copyright (c) 2013 Andrian Nord\n#\n# Permission is hereby granted, free "
  },
  {
    "path": "test/breaks.lua",
    "chars": 2703,
    "preview": "--[[\n--]]\n\nfor i=1,2,3 do\n\tif x and y then\n\t\tprint(\"Then\")\n\telse\n\t\tbreak\n\tend\nend\n\nfor i=1,2,3 do\n\tif x then\n\t\tprint(\"Th"
  },
  {
    "path": "test/expression.lua",
    "chars": 5544,
    "preview": "--[[\n--]]\n\nprint (\"true or true\")\n\nb = true or true\n\nprint (\"false and false\")\n\nb = false and false\n\nprint (\"false and o"
  },
  {
    "path": "test/ifs.lua",
    "chars": 11360,
    "preview": "--[[\n--]]\n\nif true then\n\tprint(\"if true\")\nend\n\nprint (\"something\")\n\nif false then\n\tprint(\"if false\")\nend\n\nprint (\"someth"
  },
  {
    "path": "test/loop.lua",
    "chars": 5765,
    "preview": "--[[\n--]]\n\nfor i=1, 100 do\n\tprint (\"Numeric loop\")\nend\n\nfor i=1, 100, 2 do\n\tprint (\"numeric for with step\")\nend\n\nfor i=1"
  },
  {
    "path": "test/primitive.lua",
    "chars": 8790,
    "preview": "function assignment()\n\tfunction generator()\n\t\treturn 1, 2, 3\n\tend\n\n\ta = 1\n\tb, c, d = nil\n\te, f, g = 1, 2, 3\n\tlocal i, k,"
  }
]

About this extraction

This page contains the full source code of the zzwlpx/ljd GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 46 files (217.5 KB), approximately 64.5k tokens, and a symbol index with 584 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!