|
|
|
@ -27,10 +27,33 @@ local CONSTANT_VALUE_TOKEN = {
|
|
|
|
|
NIL = function() return nil end
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local function table_indexing (t, k) return t[k] end
|
|
|
|
|
|
|
|
|
|
local function safe_index (t, index)
|
|
|
|
|
assert(type(t) == 'table')
|
|
|
|
|
|
|
|
|
|
-- Attempt rawget first
|
|
|
|
|
local value = rawget(t, index)
|
|
|
|
|
if value ~= nil then return value end
|
|
|
|
|
|
|
|
|
|
local mt = debug.getmetatable(t)
|
|
|
|
|
if mt.__index then
|
|
|
|
|
-- If weird indexing, use pcall
|
|
|
|
|
local success, value = pcall(table_indexing, t, index)
|
|
|
|
|
if success then return value end
|
|
|
|
|
return nil
|
|
|
|
|
else
|
|
|
|
|
-- Then attempt normal indexing, if no weirdness
|
|
|
|
|
return t[index]
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function get_variable (var_name, info)
|
|
|
|
|
--
|
|
|
|
|
assert(type(var_name) == 'string')
|
|
|
|
|
assert(type(info) == 'table')
|
|
|
|
|
-- Assertions
|
|
|
|
|
assert(type(var_name) == 'string')
|
|
|
|
|
assert(type(info) == 'table')
|
|
|
|
|
assert(type(info.func) == 'function')
|
|
|
|
|
assert(type(info.locals) == 'table')
|
|
|
|
|
|
|
|
|
|
-- Local
|
|
|
|
|
if info.locals[var_name] then
|
|
|
|
@ -47,7 +70,7 @@ local function get_variable (var_name, info)
|
|
|
|
|
until not name
|
|
|
|
|
|
|
|
|
|
-- Global
|
|
|
|
|
local env = getfenv(info.func)[var_name]
|
|
|
|
|
local env = safe_index(getfenv(info.func), var_name)
|
|
|
|
|
return env, 'global'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
@ -251,6 +274,28 @@ local function for_each_node_in_ast (ast, func)
|
|
|
|
|
return func(ast)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local CONSTANT_BINOP = {}
|
|
|
|
|
local CONSTANT_UNOP = {}
|
|
|
|
|
|
|
|
|
|
function CONSTANT_BINOP.DOT (node) return node[1].value[ node[2].value ] end -- TODO
|
|
|
|
|
function CONSTANT_BINOP.AND (node) return node[1].value and node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.OR (node) return node[1].value or node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.PLUS (node) return node[1].value + node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.MINUS (node) return node[1].value - node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.TIMES (node) return node[1].value * node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.DIVIDE (node) return node[1].value / node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.MODULO (node) return node[1].value % node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.CARET (node) return node[1].value ^ node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.EQ (node) return node[1].value == node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.NEQ (node) return node[1].value ~= node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.LEQ (node) return node[1].value <= node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.GEQ (node) return node[1].value >= node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.LE (node) return node[1].value < node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.GT (node) return node[1].value > node[2].value end
|
|
|
|
|
function CONSTANT_BINOP.CONCAT (node) return node[1].value .. node[2].value end
|
|
|
|
|
|
|
|
|
|
function CONSTANT_UNOP.HASHTAG (node) return #node[1].value end
|
|
|
|
|
|
|
|
|
|
local function populate_ast_with_semantics (ast, info)
|
|
|
|
|
assert(type(ast) == 'table')
|
|
|
|
|
assert(type(info) == 'table')
|
|
|
|
@ -260,16 +305,26 @@ local function populate_ast_with_semantics (ast, info)
|
|
|
|
|
node.exp, node.token = node.token, nil
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
--print 'Semantics!'
|
|
|
|
|
return for_each_node_in_ast(ast, function(node)
|
|
|
|
|
--print(require'pretty'(node))
|
|
|
|
|
if node.exp == 'IDENTIFIER' then
|
|
|
|
|
node.value, node.scope, node.function_local = get_variable(node.text, info)
|
|
|
|
|
elseif CONSTANT_VALUE_TOKEN[node.exp] then
|
|
|
|
|
node.value = CONSTANT_VALUE_TOKEN[node.exp](node.text)
|
|
|
|
|
elseif node.exp == 'OP' and node.binop == 'DOT' then
|
|
|
|
|
assert(node[1].value and node[2].value)
|
|
|
|
|
node.value = node[1].value[ node[2].value ] --TODO
|
|
|
|
|
node.is_constant = true
|
|
|
|
|
elseif node.exp == 'OP' and CONSTANT_UNOP[node.binop] and node[1].value then
|
|
|
|
|
assert(node[1].value)
|
|
|
|
|
node.value = CONSTANT_UNOP[node.binop](node)
|
|
|
|
|
node.is_constant = node[1].is_constant
|
|
|
|
|
elseif node.exp == 'OP' and CONSTANT_BINOP[node.binop] and node[1].value and node[2] and node[2].value then
|
|
|
|
|
assert(node[1].value and (not node[2] or node[2].value))
|
|
|
|
|
node.value = CONSTANT_BINOP[node.binop](node)
|
|
|
|
|
node.is_constant = node[1].is_constant and (not node[2] or node[2].is_constant)
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
@ -382,6 +437,13 @@ local function fmt_lvalue (node, with_scope)
|
|
|
|
|
return ('key %s in %s'):format(fmt_val(node[2].value), base), function_local
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
|
if node.exp == 'OP' and node.binop == 'HASHTAG' and #node == 1 then
|
|
|
|
|
local base, is_local = fmt_lvalue(node[1], with_scope)
|
|
|
|
|
return ('length of %s'):format(base), is_local
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--print(require'pretty'(node))
|
|
|
|
|
error 'Not implemented yet!'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
@ -389,7 +451,8 @@ local function fmt_prefix (ast, call_info)
|
|
|
|
|
assert(type(ast) == 'table')
|
|
|
|
|
--
|
|
|
|
|
local name, is_function_local = fmt_lvalue(ast, true)
|
|
|
|
|
local func_name = is_function_local and (' to '..get_function_name(call_info)) or ''
|
|
|
|
|
local binder = ast.node == 'argument' and 'to' or 'in'
|
|
|
|
|
local func_name = is_function_local and (' '..binder..' '..get_function_name(call_info)) or ''
|
|
|
|
|
return ('bad %s%s'):format(name, func_name)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
@ -656,6 +719,7 @@ local function determine_error_message (call_info, msg, condition)
|
|
|
|
|
elseif not ast.exp then
|
|
|
|
|
error(('[assert-gooder/internal]: Root node did not have expression type.'))
|
|
|
|
|
else
|
|
|
|
|
--print(require'pretty'(ast))
|
|
|
|
|
error(('[assert-gooder/internal]: Unknown expression type %s'):format(ast.exp))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|