SoUI 0.5版本占坑
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

449 lines
11 KiB

local grammar = require 'parser.grammar'
local ast
local parse_exp
local parse_lines
local function parser_error(str)
error(('[%s]第[%d]行: %s'):format(ast.file, ast.current_line, str))
end
local function base_type(type)
while ast.types[type].extends do
type = ast.types[type].extends
end
return type
end
local function get_var(name)
if ast.current_function then
if ast.current_function.locals[name] then
return ast.current_function.locals[name]
end
if ast.current_function.args and ast.current_function.args[name] then
return ast.current_function.args[name]
end
end
return ast.globals[name]
end
local function get_function(name)
return ast.functions[name]
end
local function get_var_type(exp)
local var = get_var(exp.name)
return var.type
end
local function get_vari_type(exp)
local var = get_var(exp.name)
parse_exp(exp[1], 'integer')
return var.type
end
local function get_call(exp)
local func = ast.functions[exp.name]
if not func then
parser_error(('函数[%s]不存在'):format(exp.name))
end
for _, arg in ipairs(exp) do
parse_exp(arg)
end
return func.returns or 'null'
end
local function get_op(exp)
local t1 = parse_exp(exp[1])
local t2 = parse_exp(exp[2])
if (t1 == 'integer' or t1 == 'real') and (t2 == 'integer' or t2 == 'real') then
if t1 == 'real' or t2 == 'real' then
return 'real'
else
return 'integer'
end
end
return nil, t1, t2
end
local function get_add(exp)
local type, t1, t2 = get_op(exp)
if type then
return type
end
if (t1 == 'string' or t1 == 'null') and (t2 == 'string' or t2 == 'null') then
return 'string'
end
parser_error(('不能对[%s]与[%s]做加法运算'):format(t1, t2))
end
local function get_sub(exp)
local type, t1, t2 = get_op(exp)
if type then
return type
end
parser_error(('不能对[%s]与[%s]做减法运算'):format(t1, t2))
end
local function get_mul(exp)
local type, t1, t2 = get_op(exp)
if type then
return type
end
parser_error(('不能对[%s]与[%s]做乘法运算'):format(t1, t2))
end
local function get_div(exp)
local type, t1, t2 = get_op(exp)
if type then
return type
end
parser_error(('不能对[%s]与[%s]做除法运算'):format(t1, t2))
end
local function get_neg(exp)
local t = parse_exp(exp[1])
if t == 'real' or t == 'integer' then
return t
end
parser_error(('不能对[%s]做负数运算'):format(t))
end
local function get_equal(exp)
local t1 = parse_exp(exp[1])
local t2 = parse_exp(exp[2])
if t1 == 'null' or t2 == 'null' then
return 'boolean'
end
if (t1 == 'integer' or t1 == 'real') and (t2 == 'integer' or t2 == 'real') then
return 'boolean'
end
local b1 = base_type(t1)
local b2 = base_type(t2)
if b1 == b2 then
return 'boolean'
end
parser_error(('不能比较[%s]与[%s]是否相等'):format(t1, t2))
end
local function get_compare(exp)
local t1 = parse_exp(exp[1])
local t2 = parse_exp(exp[2])
if (t1 == 'integer' or t1 == 'real') and (t2 == 'integer' or t2 == 'real') then
return 'boolean'
end
parser_error(('不能比较[%s]与[%s]的大小'):format(t1, t2))
end
local function get_and(exp)
parse_exp(exp[1], 'boolean')
parse_exp(exp[2], 'boolean')
return 'boolean'
end
local function get_or(exp)
parse_exp(exp[1], 'boolean')
parse_exp(exp[2], 'boolean')
return 'boolean'
end
local function get_not(exp)
parse_exp(exp[1], 'boolean')
return 'boolean'
end
local function get_code(exp)
return 'code'
end
function parse_exp(exp, expect)
if exp.type == 'null' then
exp.vtype = 'null'
elseif exp.type == 'integer' then
exp.vtype = 'integer'
elseif exp.type == 'real' then
exp.vtype = 'real'
elseif exp.type == 'string' then
exp.vtype = 'string'
elseif exp.type == 'boolean' then
exp.vtype = 'boolean'
elseif exp.type == 'var' then
exp.vtype = get_var_type(exp)
elseif exp.type == 'vari' then
exp.vtype = get_vari_type(exp)
elseif exp.type == 'call' then
exp.vtype = get_call(exp)
elseif exp.type == '+' then
exp.vtype = get_add(exp)
elseif exp.type == '-' then
exp.vtype = get_sub(exp)
elseif exp.type == '*' then
exp.vtype = get_mul(exp)
elseif exp.type == '/' then
exp.vtype = get_div(exp)
elseif exp.type == 'neg' then
exp.vtype = get_neg(exp)
elseif exp.type == 'paren' then
exp.vtype = parse_exp(exp[1])
elseif exp.type == '==' then
exp.vtype = get_equal(exp)
elseif exp.type == '!=' then
exp.vtype = get_equal(exp)
elseif exp.type == '>' then
exp.vtype = get_compare(exp)
elseif exp.type == '<' then
exp.vtype = get_compare(exp)
elseif exp.type == '>=' then
exp.vtype = get_compare(exp)
elseif exp.type == '<=' then
exp.vtype = get_compare(exp)
elseif exp.type == 'and' then
exp.vtype = get_and(exp)
elseif exp.type == 'or' then
exp.vtype = get_or(exp)
elseif exp.type == 'not' then
exp.vtype = get_not(exp)
elseif exp.type == 'code' then
exp.vtype = get_code(exp)
else
print('解析未定义的表达式类型:', exp.type)
end
if not exp.vtype then
print('没有解析到类型:', exp.type)
end
return exp.vtype
end
local function parse_type(data)
ast.current_line = data.line
if not ast.types[data.extends] then
parser_error(('类型[%s]未定义'):format(data.extends))
end
if ast.types[data.name] and not ast.types[data.name].extends then
parser_error('不能重新定义本地类型')
end
if ast.types[data.name] then
parser_error(('类型[%s]重复定义 --> 已经定义在[%s]第[%d]行'):format(data.name, ast.types[data.name].file, ast.types[data.name].line))
end
ast.types[data.name] = data
end
local function parse_global(data)
ast.current_line = data.line
if ast.globals[data.name] then
parser_error(('全局变量[%s]重复定义 --> 已经定义在[%s]第[%d]行'):format(data.name, ast.globals[data.name].file, ast.globals[data.name].line))
end
if data.constant and not data[1] then
parser_error('常量必须初始化')
end
if not ast.types[data.type] then
parser_error(('类型[%s]未定义'):format(data.type))
end
if data.array and data[1] then
parser_error('数组不能直接初始化')
end
if data[1] then
parse_exp(data[1], data.type)
end
table.insert(ast.globals, data)
ast.globals[data.name] = data
end
local function parse_globals(chunk)
for _, func in ipairs(ast.functions) do
if not func.native then
ast.current_line = chunk.line
parser_error '全局变量必须在函数前定义'
end
end
for _, data in ipairs(chunk) do
parse_global(data)
end
end
local function parse_arg(data, args)
args[data.name] = data
end
local function parse_args(chunk)
if not chunk.args then
return
end
for _, arg in ipairs(chunk.args) do
parse_arg(arg, chunk.args)
end
end
local function parse_local(data, locals, args)
ast.current_line = data.line
if not ast.types[data.type] then
parser_error(('类型[%s]未定义'):format(data.type))
end
if data.array and data[1] then
parser_error('数组不能直接初始化')
end
if args and args[data.name] then
parser_error(('局部变量[%s]和函数参数重名'):format(data.name))
end
if data[1] then
parse_exp(data[1], data.type)
end
locals[data.name] = data
end
local function parse_locals(chunk)
for _, data in ipairs(chunk.locals) do
parse_local(data, chunk.locals, chunk.args)
end
end
local function parse_loop(chunk)
ast.loop_count = ast.loop_count + 1
parse_lines(chunk)
ast.loop_count = ast.loop_count - 1
end
local function parse_if(data)
for _, chunk in ipairs(data) do
if chunk.type == 'if' or chunk.type == 'elseif' then
parse_exp(chunk.condition, 'boolean')
end
parse_lines(chunk)
end
end
local function parse_call(line)
local func = get_function(line.name)
if not func.args then
return
end
for i, arg in ipairs(func.args) do
parse_exp(line[i], arg.type)
end
end
local function parse_set(line)
local var = get_var(line.name)
parse_exp(line[1], var.vtype)
end
local function parse_seti(line)
local var = get_var(line.name)
parse_exp(line[1], 'integer')
parse_exp(line[2], var.vtype)
end
local function parse_return(line)
local func = ast.current_function
if not func.returns then
return
end
parse_exp(line[1], func.returns)
end
local function parse_exit(line)
if ast.loop_count == 0 then
parser_error '不能在循环外使用exitwhen'
end
parse_exp(line[1], 'boolean')
end
local function parse_line(line)
ast.current_line = line.line
if line.type == 'loop' then
parse_loop(line)
elseif line.type == 'if' then
parse_if(line)
elseif line.type == 'call' then
parse_call(line)
elseif line.type == 'set' then
parse_set(line)
elseif line.type == 'seti' then
parse_seti(line)
elseif line.type == 'return' then
parse_return(line)
elseif line.type == 'exit' then
parse_exit(line)
else
parser_error('未知的语句类型:'..line.type)
end
end
function parse_lines(chunk)
for i, line in ipairs(chunk) do
parse_line(line)
end
end
local function parse_function(chunk)
table.insert(ast.functions, chunk)
ast.functions[chunk.name] = chunk
if chunk.native then
return
end
ast.current_function = chunk
ast.loop_count = 0
parse_args(chunk)
parse_locals(chunk)
parse_lines(chunk)
ast.current_function = nil
end
local function parser_gram(gram)
for i, chunk in ipairs(gram) do
if chunk.type == 'globals' then
parse_globals(chunk)
elseif chunk.type == 'function' then
parse_function(chunk)
elseif chunk.type == 'type' then
parse_type(chunk)
else
parser_error('未知的区块类型:'..chunk.type)
end
end
end
return function (jass, file, _ast)
if _ast then
ast = _ast
for i = 1, #ast.functions do
ast.functions[i] = nil
end
for i = 1, #ast.globals do
ast.globals[i] = nil
end
else
ast = {}
ast.types = {
null = {type = 'type'},
handle = {type = 'type'},
code = {type = 'type'},
integer = {type = 'type'},
real = {type = 'type'},
boolean = {type = 'type'},
string = {type = 'type'},
}
ast.globals = {}
ast.functions = {}
end
ast.file = file
local gram, comments = grammar(jass, file)
parser_gram(gram)
ast.current_line = nil
ast.loop_count = nil
ast.file = nil
ast.comments = comments
return ast, gram
end