From db733081d135cce4943da58316b343b6775c2588 Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Wed, 26 Jun 2019 13:59:49 -0300 Subject: [PATCH] More code coverage --- .travis.yml | 2 +- nelua/astbuilder.lua | 1 + nelua/astnode.lua | 9 +++ nelua/cbuiltins.lua | 6 +- nelua/ccontext.lua | 6 +- nelua/cgenerator.lua | 7 +- nelua/luagenerator.lua | 26 +++---- nelua/pegbuilder.lua | 4 +- nelua/preprocessor.lua | 8 +++ nelua/scope.lua | 4 +- nelua/syntaxdefs.lua | 16 ++--- nelua/typechecker.lua | 22 +++--- nelua/typedefs.lua | 1 - nelua/types.lua | 17 ++--- nelua/utils/stringer.lua | 8 --- spec/03-typechecker_spec.lua | 128 +++++++++++++++++++++------------- spec/04-luagenerator_spec.lua | 2 + spec/05-cgenerator_spec.lua | 48 +++++++++---- spec/06-preprocessor_spec.lua | 105 ++++++++++++++++++++++------ spec/tools/assert.lua | 28 ++++++-- stdlib/math.nelua | 8 +-- stdlib/os.nelua | 72 ++++++++++++++----- 22 files changed, 351 insertions(+), 177 deletions(-) diff --git a/.travis.yml b/.travis.yml index 128f5f10..2f1ee033 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: c services: - docker -dist: trusty +dist: xenial before_install: - sudo apt-get install luarocks - sudo luarocks install luacov-coveralls diff --git a/nelua/astbuilder.lua b/nelua/astbuilder.lua index f2cd3a4a..11da1920 100644 --- a/nelua/astbuilder.lua +++ b/nelua/astbuilder.lua @@ -26,6 +26,7 @@ end function ASTBuilder:register(tag, shape) shape.attr = shapetypes.table:is_optional() + shape.uid = shapetypes.number:is_optional() shape = shapetypes.shape(shape) local klass = class(ASTNode) klass.tag = tag diff --git a/nelua/astnode.lua b/nelua/astnode.lua index 0850d9ae..4f66394d 100644 --- a/nelua/astnode.lua +++ b/nelua/astnode.lua @@ -13,12 +13,19 @@ ASTNode.tag = 'Node' ASTNode.nargs = 0 ASTNode._astnode = true +local uid = 0 +local function genuid() + uid = uid + 1 + return uid +end + function ASTNode:_init(...) local nargs = select('#', ...) for i=1,nargs do self[i] = select(i, ...) end self.attr = {} + self.uid = genuid() end function ASTNode:args() @@ -63,6 +70,7 @@ function ASTNode:clone() node.srcname = self.srcname node.modname = self.modname node.cloned = true + node.uid = genuid() return node end @@ -127,6 +135,7 @@ end ------------------- local ignored_stringfy_keys = { pos = true, src = true, srcname=true, modname=true, + uid = true, processed = true, untyped = true, cloned = true, diff --git a/nelua/cbuiltins.lua b/nelua/cbuiltins.lua index ad06c302..49c9e777 100644 --- a/nelua/cbuiltins.lua +++ b/nelua/cbuiltins.lua @@ -176,9 +176,9 @@ function functions.type(context, node, emitter) typename = 'number' elseif type:is_nilptr() then typename = 'pointer' - elseif type:is_any() then - assert(false, 'type for any values not implemented yet') - else + elseif type:is_any() then --luacov:disable + node:raisef('type() for any values not implemented yet') + else --luacov:enable typename = type.name end emitter:add('&nelua_typestr_', typename) diff --git a/nelua/ccontext.lua b/nelua/ccontext.lua index a85ce13d..68925fde 100644 --- a/nelua/ccontext.lua +++ b/nelua/ccontext.lua @@ -108,9 +108,9 @@ function CContext:typename(type) end elseif type:is_string() then self.has_string = true - elseif type:is_function() then - assert(false, 'ctype for functions not implemented yet') - elseif type:is_any() then + elseif type:is_function() then --luacov:disable + error('ctype for functions not implemented yet') + elseif type:is_any() then --luacov:enable self.has_any = true self.has_string = true self.has_type = true diff --git a/nelua/cgenerator.lua b/nelua/cgenerator.lua index 1244e158..a57ea9dd 100644 --- a/nelua/cgenerator.lua +++ b/nelua/cgenerator.lua @@ -364,9 +364,10 @@ local function visitor_Call(context, node, emitter, argnodes, callee, isblockcal emitter:add_val2type(calleetype.argtypes[1], callee) else if attr.pointercall then - emitter:add('*') + emitter:add('(*', callee, ')(') + else + emitter:add(callee, '(') end - emitter:add(callee, '(') end for i,funcargtype,argnode,argtype,lastcallindex in izipargnodes(callargtypes, argnodes) do @@ -661,6 +662,7 @@ function visitors.ForNum(context, node, emitter) context:pop_scope() end +--[[ function visitors.ForIn(_, node, emitter) local itvarnodes, inexpnodes, blocknode = node:args() emitter:add_indent_ln("{") @@ -672,6 +674,7 @@ function visitors.ForIn(_, node, emitter) emitter:dec_indent() emitter:add_indent_ln("}") end +]] function visitors.Break(_, _, emitter) emitter:add_indent_ln('break;') diff --git a/nelua/luagenerator.lua b/nelua/luagenerator.lua index 99a2bcf7..7b46aeeb 100644 --- a/nelua/luagenerator.lua +++ b/nelua/luagenerator.lua @@ -300,23 +300,19 @@ end -- operators function visitors.UnaryOp(context, node, emitter) local opname, argnode = node:args() - if opname == 'tostring' then - emitter:add('tostring(', argnode, ')') - else - local op = node:assertraisef(luadefs.unary_ops[opname], 'unary operator "%s" not found', opname) - if config.lua_version ~= '5.3' then - local fallop = luadefs.lua51_unary_ops[opname] - if fallop then - context:add_runtime_builtin(fallop.builtin) - emitter:add(fallop.func, '(', argnode, ')') - return - end + local op = node:assertraisef(luadefs.unary_ops[opname], 'unary operator "%s" not found', opname) + if config.lua_version ~= '5.3' then + local fallop = luadefs.lua51_unary_ops[opname] + if fallop then + context:add_runtime_builtin(fallop.builtin) + emitter:add(fallop.func, '(', argnode, ')') + return end - local surround = node.attr.inoperator - if surround then emitter:add('(') end - emitter:add(op, argnode) - if surround then emitter:add(')') end end + local surround = node.attr.inoperator + if surround then emitter:add('(') end + emitter:add(op, argnode) + if surround then emitter:add(')') end end function visitors.BinaryOp(context, node, emitter) diff --git a/nelua/pegbuilder.lua b/nelua/pegbuilder.lua index feab6d0b..a45639a5 100644 --- a/nelua/pegbuilder.lua +++ b/nelua/pegbuilder.lua @@ -32,9 +32,9 @@ function PEGBuilder:add_group_peg(groupname, name, patt, defs, overwrite) self.group_pegs[groupname] = group end local fullname = string.format('%s_%s', groupname, name) - if tabler.ifind(group, fullname) then + if tabler.ifind(group, fullname) then --luacov:disable errorer.assertf(overwrite, 'group peg "%s" already exists', fullname) - else + else --luacov:enable table.insert(group, fullname) end merge_defs(self, defs) diff --git a/nelua/preprocessor.lua b/nelua/preprocessor.lua index fdac107d..1540482a 100644 --- a/nelua/preprocessor.lua +++ b/nelua/preprocessor.lua @@ -52,12 +52,20 @@ function PPContext:tovalue(val, orignode) node = self.aster.String{val} elseif traits.is_number(val) or traits.is_bignumber(val) then local num = bn.new(val) + local neg = false + if num:isneg() then + num = num:abs() + neg = true + end if num:isintegral() then node = self.aster.Number{'dec', num:todec()} else local int, frac = num:todec():match('^(-?%d+).(%d+)$') node = self.aster.Number{'dec', int, frac} end + if neg then + node = self.aster.UnaryOp{'neg', node} + end elseif traits.is_boolean(val) then node = self.aster.Boolean{val} --TODO: table, nil diff --git a/nelua/scope.lua b/nelua/scope.lua index 38e5ae8d..e0a49253 100644 --- a/nelua/scope.lua +++ b/nelua/scope.lua @@ -73,10 +73,8 @@ local function symbol_resolve_type(symbol) end function Scope:add_symbol(symbol) - if not symbol.name then - return - end local name = symbol.name + assert(name) local oldsymbol = self.symbols[name] if oldsymbol then symbol.node:assertraisef(not config.strict, diff --git a/nelua/syntaxdefs.lua b/nelua/syntaxdefs.lua index f73def77..47a26d45 100644 --- a/nelua/syntaxdefs.lua +++ b/nelua/syntaxdefs.lua @@ -318,6 +318,14 @@ local function get_parser(std) ({} '' -> 'Preprocess' ppstring ) -> to_astnode ]]) + grammar:add_group_peg('stat', 'vardecl', [[ + ({} '' -> 'VarDecl' + %LOCAL -> 'local' cnil + {| etyped_idlist |} + (%ASSIGN {| eexpr_list |})? + ) -> to_astnode + ]]) + if not is_luacompat then grammar:add_group_peg('stat', 'vardecl', [[ ({} '' -> 'VarDecl' @@ -350,14 +358,6 @@ local function get_parser(std) grammar:add_group_peg('stat', 'continue', [[ ({} %CONTINUE -> 'Continue') -> to_astnode ]]) - else - grammar:add_group_peg('stat', 'vardecl', [[ - ({} '' -> 'VarDecl' - %LOCAL -> 'local' cnil - {| etyped_idlist |} - (%ASSIGN {| eexpr_list |})? - ) -> to_astnode - ]]) end -- expressions diff --git a/nelua/typechecker.lua b/nelua/typechecker.lua index 3eac7106..76746ac5 100644 --- a/nelua/typechecker.lua +++ b/nelua/typechecker.lua @@ -944,16 +944,18 @@ function visitors.ForIn(context, node) end context:traverse(inexpnodes) context:repeat_scope_until_resolution('loop', function() - if itvarnodes then - for i,itvarnode in ipairs(itvarnodes) do - local itsymbol = context:traverse(itvarnode) - if infunctype and infunctype:is_function() then - local fittype = infunctype:get_return_type(i) - itsymbol:add_possible_type(fittype) - end + --[[ + if itvarnodes then + for i,itvarnode in ipairs(itvarnodes) do + local itsymbol = context:traverse(itvarnode) + if infunctype and infunctype:is_function() then + local fittype = infunctype:get_return_type(i) + itsymbol:add_possible_type(fittype) end end - context:traverse(blocknode) + end + ]] + context:traverse(blocknode) end) end @@ -1196,9 +1198,7 @@ function visitors.FuncDef(context, node) -- function declaration, must create a new symbol local name = varnode[1] local vartype = varnode.attr.type - if not vartype and context.phase == phases.any_inference then - vartype = primtypes.any - end + assert(vartype or context.phase ~= phases.any_inference) symbol = context.scope:add_symbol(Symbol(name, varnode, vartype)) elseif varnode.tag == 'Id' then symbol = context:traverse(varnode) diff --git a/nelua/typedefs.lua b/nelua/typedefs.lua index 4c3fd1c9..c5ffc930 100644 --- a/nelua/typedefs.lua +++ b/nelua/typedefs.lua @@ -45,7 +45,6 @@ local primtypes = { culonglong = Type('culonglong'), csize = Type('csize'), clongdouble = Type('clongdouble'), - --cstring = Type('cstring'), } primtypes.pointer = types.PointerType(nil, primtypes.void) diff --git a/nelua/types.lua b/nelua/types.lua index cb007d0d..ddf25fab 100644 --- a/nelua/types.lua +++ b/nelua/types.lua @@ -17,13 +17,13 @@ local uidcounter = 0 local function genkey(name, node) local uid local srcname - if node and node.pos and not node.cloned then - uid = node.pos - srcname = node.srcname + if node then + uid = node.uid + srcname = node.srcname or '' else uidcounter = uidcounter + 1 uid = uidcounter - srcname = '' + srcname = '__nonode__' end return string.format('%s%s%d', name, srcname, uid) end @@ -459,17 +459,12 @@ function MetaType:set_field(name, symbol) self.fields[name] = symbol end -function MetaType:is_equal(type) - return type.name == self.name and - type.key == self.key -end - function MetaType:__tostring() local ss = sstream('metatype{') local first = true for name,sym in iters.opairs(self.fields) do - if first then ss:add(', ') first = false end - ss:add(name, ':', sym.attr.type) + if not first then ss:add(', ') first = false end + ss:add(name, ': ', sym.attr.type) end ss:add('}') return ss:tostring() diff --git a/nelua/utils/stringer.lua b/nelua/utils/stringer.lua index 3d509ba9..9cd77b6e 100644 --- a/nelua/utils/stringer.lua +++ b/nelua/utils/stringer.lua @@ -25,14 +25,6 @@ function stringer.pconcat(...) return table.concat(t, '\t') end -function stringer.pformat(format, ...) - if select('#', ...) > 0 then - return string.format(format, ...) - else - return format - end -end - function stringer.pformat(format, ...) if select('#', ...) == 0 then return format diff --git a/spec/03-typechecker_spec.lua b/spec/03-typechecker_spec.lua index bc694a3e..8d0f61c9 100644 --- a/spec/03-typechecker_spec.lua +++ b/spec/03-typechecker_spec.lua @@ -30,7 +30,7 @@ it("analyzed ast transform", function() end) it("local variable", function() - assert.c_gencode_equals("local a = 1", "local a: integer = 1") + assert.ast_type_equals("local a = 1", "local a: integer = 1") assert.analyze_error("local a: integer = 'string'", "is not coercible with") assert.analyze_error("local a: byte = 1.0", "is not coercible with") assert.analyze_error("local a: byte = {1.0}", "cannot be initialized using a table literal") @@ -39,18 +39,18 @@ it("local variable", function() end) it("global variable", function() - assert.c_gencode_equals("global a = 1", "global a: integer = 1") + assert.ast_type_equals("global a = 1", "global a: integer = 1") assert.analyze_error("do global a = 1 end", "global variables can only be declarated in top scope") end) it("name collision", function() - assert.c_gencode_equals("local a = 1; local a = 2", "local a: integer = 1; local a: integer = 2") + assert.ast_type_equals("local a = 1; local a = 2", "local a: integer = 1; local a: integer = 2") end) it("compconst variable" , function() assert.analyze_ast([[local compconst N = 255; local a: byte = N]]) assert.analyze_ast([[local a: compconst integer = 1]]) - assert.c_gencode_equals( + assert.ast_type_equals( [[local compconst a = 1; local function f() return a end]], [[local compconst a: integer = 1; local function f() return a end]]) assert.analyze_ast([[local compconst a = 1 * 2 + 3]]) @@ -83,8 +83,8 @@ it("numeric types coercion", function() local b: uint16 = 1 + 1 local b: int16 = -1 ]]) - assert.c_gencode_equals("local a = 1 + 1_u16", "local a: uint16 = 1 + 1_u16") - assert.c_gencode_equals("local a = 1 + 2.0_f32", "local a: float32 = 1 + 2.0_f32") + assert.ast_type_equals("local a = 1 + 1_u16", "local a: uint16 = 1 + 1_u16") + assert.ast_type_equals("local a = 1 + 2.0_f32", "local a: float32 = 1 + 2.0_f32") end) it("narrow casting", function() @@ -148,9 +148,9 @@ it("typed var initialization", function() end) it("loop variables", function() - assert.c_gencode_equals("for i=1,10 do end", "for i:integer=1,10 do end") - assert.c_gencode_equals("for i=1,10,2 do end", "for i:integer=1,10,2 do end") - assert.c_gencode_equals("for i=0_is,1_is-1 do end", "for i:isize=0_is,1_is-1 do end") + assert.ast_type_equals("for i=1,10 do end", "for i:integer=1,10 do end") + assert.ast_type_equals("for i=1,10,2 do end", "for i:integer=1,10,2 do end") + assert.ast_type_equals("for i=0_is,1_is-1 do end", "for i:isize=0_is,1_is-1 do end") assert.analyze_error("for i:byte=1.0,256 do end", "is not coercible with") assert.analyze_error("for i:byte=1_byte,256 do end", "is not coercible with") assert.analyze_error("for i:byte=1_byte,10_byte,2.0 do end", "is not coercible with") @@ -160,83 +160,83 @@ it("loop variables", function() end) it("variable assignments", function() - assert.c_gencode_equals("local a; a = 1", "local a: integer; a = 1") + assert.ast_type_equals("local a; a = 1", "local a: integer; a = 1") assert.analyze_error("local a: integer; a = 's'", "is not coercible with") assert.analyze_error("local a, b; a, b = 1,2,3", "too many expressions in assign") end) it("unary operators", function() - assert.c_gencode_equals("local a = not b", "local a: boolean = not b") - assert.c_gencode_equals("local a = -1", "local a: integer = -1") + assert.ast_type_equals("local a = not b", "local a: boolean = not b") + assert.ast_type_equals("local a = -1", "local a: integer = -1") end) it("binary operator shift", function() - assert.c_gencode_equals("local a = 1_u32 << 1", "local a: uint32 = 1_u32 << 1") - assert.c_gencode_equals("local a = 1_u16 >> 1_u32", "local a: uint16 = 1_u16 >> 1_u32") + assert.ast_type_equals("local a = 1_u32 << 1", "local a: uint32 = 1_u32 << 1") + assert.ast_type_equals("local a = 1_u16 >> 1_u32", "local a: uint16 = 1_u16 >> 1_u32") end) it("binary operator add", function() - assert.c_gencode_equals("local a = 1 + 2", "local a: integer = 1 + 2") - assert.c_gencode_equals("local a = 1 + 2.0", "local a: number = 1 + 2.0") - assert.c_gencode_equals("local a = 1_f32 + 2.0_f32", "local a: float32 = 1_f32 + 2.0_f32") - assert.c_gencode_equals("local a = 1_i8 + 2_u8", "local a: int16 = 1_i8 + 2_u8") + assert.ast_type_equals("local a = 1 + 2", "local a: integer = 1 + 2") + assert.ast_type_equals("local a = 1 + 2.0", "local a: number = 1 + 2.0") + assert.ast_type_equals("local a = 1_f32 + 2.0_f32", "local a: float32 = 1_f32 + 2.0_f32") + assert.ast_type_equals("local a = 1_i8 + 2_u8", "local a: int16 = 1_i8 + 2_u8") assert.analyze_error("local a = 1 + 's'", "is not defined for type") end) it("binary operator pow", function() - assert.c_gencode_equals("local a = 2 ^ 2", "local a: number = 2 ^ 2") + assert.ast_type_equals("local a = 2 ^ 2", "local a: number = 2 ^ 2") end) it("binary operator idiv", function() - assert.c_gencode_equals("local a = 2 // 2", "local a: integer = 2 // 2") + assert.ast_type_equals("local a = 2 // 2", "local a: integer = 2 // 2") assert.analyze_error("local a = 1 // 0", "divizion by zero") end) it("binary operator div", function() - assert.c_gencode_equals("local a = 2 / 2", "local a: number = 2 / 2") - assert.c_gencode_equals( + assert.ast_type_equals("local a = 2 / 2", "local a: number = 2 / 2") + assert.ast_type_equals( "local x = 1; local a = x / 2_f32", "local x = 1; local a: float32 = x / 2_f32") assert.analyze_error("local a = 1 / 0", "divizion by zero") assert.analyze_error("local a = 1 / -0", "divizion by zero") - assert.c_gencode_equals( + assert.ast_type_equals( "local a, b = 1, 2; a = b / 1", "local a: number, b: integer = 1, 2; a = b / 1") end) it("binary operator mod", function() - assert.c_gencode_equals("local a = 2_u32 % 2_u32", "local a: uint32 = 2_u32 % 2_u32") + assert.ast_type_equals("local a = 2_u32 % 2_u32", "local a: uint32 = 2_u32 % 2_u32") assert.analyze_error("local a = 1 % 0", "divizion by zero") end) it("binary operator eq", function() - assert.c_gencode_equals("local a = 1 == 2", "local a: boolean = 1 == 2") - assert.c_gencode_equals("local a = 1 == 'a'", "local a: boolean = 1 == 'a'") + assert.ast_type_equals("local a = 1 == 2", "local a: boolean = 1 == 2") + assert.ast_type_equals("local a = 1 == 'a'", "local a: boolean = 1 == 'a'") end) it("binary operator ne", function() - assert.c_gencode_equals("local a = 1 ~= 2", "local a: boolean = 1 ~= 2") - assert.c_gencode_equals("local a = 1 ~= 'a'", "local a: boolean = 1 ~= 'a'") + assert.ast_type_equals("local a = 1 ~= 2", "local a: boolean = 1 ~= 2") + assert.ast_type_equals("local a = 1 ~= 'a'", "local a: boolean = 1 ~= 'a'") end) it("binary conditional and", function() - assert.c_gencode_equals("local a = 1 and 2", "local a: integer = 1 and 2") - assert.c_gencode_equals("local a = 1_i8 and 2_u8", "local a: int16 = 1_i8 and 2_u8") - assert.c_gencode_equals("local a = 1 and true", "local a: any = 1 and true") - assert.c_gencode_equals("local a = 1 and 2 or 3", "local a: integer = 1 and 2 or 3") + assert.ast_type_equals("local a = 1 and 2", "local a: integer = 1 and 2") + assert.ast_type_equals("local a = 1_i8 and 2_u8", "local a: int16 = 1_i8 and 2_u8") + assert.ast_type_equals("local a = 1 and true", "local a: any = 1 and true") + assert.ast_type_equals("local a = 1 and 2 or 3", "local a: integer = 1 and 2 or 3") end) it("binary conditional or", function() - assert.c_gencode_equals("local a = 1 or true", "local a: any = 1 or true") - assert.c_gencode_equals("local a = nilptr and false or 1", "local a: any = nilptr and false or 1") + assert.ast_type_equals("local a = 1 or true", "local a: any = 1 or true") + assert.ast_type_equals("local a = nilptr and false or 1", "local a: any = nilptr and false or 1") end) it("operation with parenthesis", function() - assert.c_gencode_equals("local a = -(1)", "local a: integer = -(1)") + assert.ast_type_equals("local a = -(1)", "local a: integer = -(1)") end) it("recursive late deduction", function() - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local a, b, c a = 1 b = 2 @@ -247,14 +247,14 @@ it("recursive late deduction", function() b = 2 c = a + b ]]) - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local a = 1_integer local b = a + 1 ]],[[ local a: integer = 1_integer local b: integer = a + 1 ]]) - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local limit = 1_integer for i=1,limit do end ]],[[ @@ -303,16 +303,16 @@ it("function multiple argument types", function() local function f(x: integer | boolean): void end local function g(x: integer | boolean) end f(1) - --g(1) + g(1) ]]) end) it("function return", function() - assert.c_gencode_equals( + assert.ast_type_equals( "local function f() return 0 end", "local function f(): integer return 0 end" ) - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local function f() local a, b, c a = 1; b = 2; c = a + b @@ -325,6 +325,13 @@ it("function return", function() return c end ]]) + assert.ast_type_equals([[ + local f + local x = f() + ]],[[ + local f: any + local x: any = f() + ]]) assert.analyze_ast([[ local function f(): integer return 1 end local function f(): integer if true then return 1 else return 2 end end @@ -349,6 +356,11 @@ it("function return", function() assert.analyze_error([[ local function f(): integer if a then return 1 end end ]], "return statement is missing") + assert.analyze_error([[ + local function f(x: boolean): integer + if x then assert(true) else return 1 end + end + ]], "return statement is missing") assert.analyze_error([[ local function f(): integer return 1, 2 end ]], "invalid return expression at index") @@ -380,7 +392,7 @@ it("function multiple return", function() local function g(): boolean,integer return f() end local a: boolean, b: integer = g() ]]) - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local function f() return true,1 end local function g() return f() end local a, b = g() @@ -389,6 +401,15 @@ it("function multiple return", function() local function g(): boolean,integer return f() end local a: boolean, b: integer = g() ]]) + assert.ast_type_equals([[ + local function f() return true,1 end + local function g() return 's', f() end + local a, b = g() + ]],[[ + local function f() return true,1 end + local function g(): string,boolean,integer return 's', f() end + local a: string, b: boolean = g() + ]]) assert.analyze_error([[ local function f(): integer, boolean return 1,false end local a, b, c @@ -419,7 +440,7 @@ it("function multiple return", function() end) it("function call", function() - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local function f() return 0 end local a = f() ]],[[ @@ -443,13 +464,15 @@ it("for in", function() assert.analyze_error( [[for i in a,b,c,d do end]], "`in` expression can have at most") - assert.c_gencode_equals([[ + --[=[ + assert.ast_type_equals([[ local function iter() return 1 end for i in iter do end ]],[[ - local function iter() return 1 end + local function iter():integer return 1 end for i:integer in iter do end ]]) + ]=] end) it("array tables", function() @@ -471,7 +494,7 @@ it("array tables", function() local function f(a: arraytable) end f({false, true}) ]]) - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local a: arraytable local b = a[0] ]],[[ @@ -581,16 +604,16 @@ it("records", function() local Record = @record{x: boolean} local compconst a = Record{x = b} ]], "can only assign to constant expressions") - assert.c_gencode_equals( + assert.ast_type_equals( "local a: record {x: boolean}; local b = a.x", "local a: record {x: boolean}; local b: boolean = a.x") - assert.c_gencode_equals( + assert.ast_type_equals( "local a: record {x: boolean}; local b; b = a.x", "local a: record {x: boolean}; local b: boolean; b = a.x") end) it("dependent functions resolution", function() - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local A = @record{x:number} function A:foo() return self.x end local a = A{} @@ -715,7 +738,7 @@ it("pointers", function() ]]) assert.analyze_ast([[ local a: pointer - local b: pointer + local b: pointer b = a ]]) assert.analyze_ast([[ @@ -723,6 +746,11 @@ it("pointers", function() local b: pointer b = a ]]) + assert.analyze_ast([[ + local a: cstring + local b: pointer + b = a + ]]) assert.analyze_error([[ local a: pointer local b: pointer diff --git a/spec/04-luagenerator_spec.lua b/spec/04-luagenerator_spec.lua index 199de465..77761e57 100644 --- a/spec/04-luagenerator_spec.lua +++ b/spec/04-luagenerator_spec.lua @@ -110,6 +110,7 @@ it("for", function() assert.generate_lua("for i=1,10,2 do\nend") assert.generate_lua("for i in a, f() do\nend") assert.generate_lua("for i, j, k in f() do\nend") + assert.generate_lua("in f() do\nend", "for _ in f() do\nend") end) it("break", function() assert.generate_lua("while true do\n break\nend") @@ -122,6 +123,7 @@ it("variable declaration", function() assert.generate_lua("local a = 1") assert.generate_lua("local a, b, c = 1, 2, nil") assert.generate_lua("local a, b, c = 1, 2, 3") + assert.generate_lua("local a, b = 1", "local a, b = 1, nil") assert.generate_lua("function f() local a end", "function f()\n local a\nend") end) it("assignment", function() diff --git a/spec/05-cgenerator_spec.lua b/spec/05-cgenerator_spec.lua index 502c1b81..900cfbc1 100644 --- a/spec/05-cgenerator_spec.lua +++ b/spec/05-cgenerator_spec.lua @@ -654,8 +654,13 @@ it("records", function() assert.run_c([[ local Point = @record {x: integer, y: integer} local p: Point + local pptr = &p p.x = 1 assert(p.x == 1 and p.y == 0) + assert(pptr.x == 1 and pptr.y == 0) + pptr.x = 2 + assert(p.x == 2 and p.y == 0) + assert(pptr.x == 2 and pptr.y == 0) p = Point{} assert(p.x == 0 and p.y == 0) assert(Point({1,2}).x == 1) @@ -664,15 +669,17 @@ it("records", function() assert(Point({1,y=2}).x == 1) ]]) assert.run_c([[ - local Point = @record {x: integer, y: integer} - local p = Point{x=1, y=2} - print(p.x, p.y) - ]], "1\t2") - assert.run_c([[ - local Point = @record {x: integer, y: integer} - local p: Point = {x=1, y=2} - print(p.x, p.y) - ]], "1\t2") + do + local Point = @record {x: integer, y: integer} + local p = Point{x=1, y=2} + assert(p.x == 1 and p.y == 2) + end + do + local Point = @record {x: integer, y: integer} + local p: Point = {x=1, y=2} + assert(p.x == 1 and p.y == 2) + end + ]]) assert.run_c([[ local r: record {x: array} = {x={1}} assert(r.x[0] == 1) @@ -704,9 +711,12 @@ it("record methods", function() assert(v:length4() == 3) assert(vec2.length4(v) == 3) - local math = @record{} - function math.abs(x: number): number !cimport('fabs', '') end - assert(math.abs(-1) == 1) + function vec2:lenmul(a: integer, b: integer) return (self.x + self.y)*a*b end + assert(v:lenmul(2,3) == 18) + + local Math = @record{} + function Math.abs(x: number): number !cimport('fabs', '') end + assert(Math.abs(-1) == 1) ]]) end) @@ -749,6 +759,14 @@ it("pointers", function() ]], "1\n2\n3") end) +it("function pointers", function() + assert.run_c([[ + local function f() return 1 end + assert((&f)() == 1) + assert(f() == 1) + ]]) +end) + it("automatic reference", function() assert.run_c([[ local p: integer* @@ -885,6 +903,12 @@ it("assert builtin", function() ]], "assertion failed!") end) +it("error builtin", function() + assert.run_error_c([[ + error 'got an error!' + ]], 'got an error!') +end) + it("type builtin", function() assert.run_c([[ local function f() end diff --git a/spec/06-preprocessor_spec.lua b/spec/06-preprocessor_spec.lua index 9cd115e5..fdef85f1 100644 --- a/spec/06-preprocessor_spec.lua +++ b/spec/06-preprocessor_spec.lua @@ -6,7 +6,7 @@ local config = require 'nelua.configer'.get() describe("Nelua preprocessor should", function() it("evaluate expressions", function() - assert.c_gencode_equals([=[ + assert.ast_type_equals([=[ local a = #['he' .. 'llo'] local b = #[math.sin(-math.pi/2)] local c = #[true] @@ -19,7 +19,7 @@ it("evaluate expressions", function() local d = 3.1415926535898 local e = 1 ]]) - assert.c_gencode_equals([=[ + assert.ast_type_equals([=[ local a: integer[10] a[#[0]] = 1 ]=], [[ @@ -30,7 +30,7 @@ it("evaluate expressions", function() end) it("evaluate names", function() - assert.c_gencode_equals([[ + assert.ast_type_equals([[ #('print') 'hello' ]], [[ print 'hello' @@ -38,14 +38,14 @@ it("evaluate names", function() end) it("parse if", function() - assert.c_gencode_equals("[##[ if true then ]##] local a = 1 [##[ end ]##]", "local a = 1") - assert.c_gencode_equals("[##[ if false then ]##] local a = 1 [##[ end ]##]", "") - assert.c_gencode_equals([[ + assert.ast_type_equals("[##[ if true then ]##] local a = 1 [##[ end ]##]", "local a = 1") + assert.ast_type_equals("[##[ if false then ]##] local a = 1 [##[ end ]##]", "") + assert.ast_type_equals([[ local function f() [##[ if true then ]##] return 1 [##[ end ]##] end ]],[[ local function f() return 1 end ]]) - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local function f() ## if true then return 1 @@ -60,7 +60,7 @@ it("parse if", function() end) it("parse loops", function() - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local a = 2 ## for i=1,4 do a = a * 2 @@ -72,7 +72,7 @@ it("parse loops", function() a = a * 2 a = a * 2 ]]) - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local a = 0 ## for i=1,3 do do @@ -91,21 +91,25 @@ it("parse loops", function() do a = a + 2 end do a = a + 3 end ]]) - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local a = 0 ## for i=1,3 do a = a + #[i] + for i=1,4,2 do end ## end ]], [[ local a = 0 a = a + 1 + for i=1,4,2 do end a = a + 2 + for i=1,4,2 do end a = a + 3 + for i=1,4,2 do end ]]) end) it("inject other symbol type", function() - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local a: uint8 = 1 local b: #[symbols['a'].attr.type] ]], [[ @@ -115,14 +119,18 @@ it("inject other symbol type", function() end) it("print symbol", function() - assert.c_gencode_equals([=[ + assert.ast_type_equals([=[ local a: compconst integer = 1 + local b: const integer = 2 print #[tostring(symbols.a)] + print #[tostring(symbols.b)] ]=], [[ - local compconst a = 1 + local a: compconst = 1 + local b: const = 2 print 'symbol' + print 'symbol' ]]) - assert.c_gencode_equals([=[ + assert.ast_type_equals([=[ for i:integer=1,2 do print(i, #[tostring(symbols.i)]) end @@ -131,20 +139,20 @@ it("print symbol", function() print(i, 'symbol') end ]]) - assert.c_gencode_equals([[ + assert.ast_type_equals([[ ## local aval = 1 ## if true then local #('a'): compconst #('integer') = #[aval] print #[tostring(scope:get_symbol('a'))] ## end ]], [[ - local compconst a = 1 + local a: compconst = 1 print 'symbol' ]]) end) it("print enums", function() - assert.c_gencode_equals([[ + assert.ast_type_equals([[ local Weekends = @enum { Friday=0, Saturday, Sunda } ## symbols.Weekends.attr.holdedtype.fields[3].name = 'Sunday' ## for i,field in ipairs(symbols.Weekends.attr.holdedtype.fields) do @@ -158,8 +166,63 @@ it("print enums", function() ]]) end) +it("print ast", function() + assert.ast_type_equals([[ + local a = #[tostring(ast)] + ]], [=[ + local a = [[Block { + { + } +}]] + ]=]) +end) + +it("print types", function() + assert.ast_type_equals([[ + local n: float64 + local s: string + local b: boolean + local a: int64[2] + local function f(a: int64, b: int64): int64, int64 return 0,0 end + local function g(a: boolean | string) end + local R: type = @record{a: integer, b: integer} + function R:foo() return 1 end + global R.v: integer = 1 + local r: R + local tn = #[tostring(symbols.n.attr.type)] + local ts = #[tostring(symbols.s.attr.type)] + local tb = #[tostring(symbols.b.attr.type)] + local ta = #[tostring(symbols.a.attr.type)] + local tf = #[tostring(symbols.f.attr.type)] + local tg = #[tostring(symbols.g.attr.type)] + local tR = #[tostring(symbols.R.attr.type)] + local tRmt = #[tostring(symbols.R.attr.holdedtype.metatype)] + local tr = #[tostring(symbols.r.attr.type)] + ]], [=[ + local n: float64 + local s: string + local b: boolean + local a: int64[2] + local function f(a: int64, b: int64): int64, int64 return 0,0 end + local function g(a: boolean | string) end + local R: type = @record{a: integer, b: integer} + function R:foo() return 1 end + global R.v: integer = 1 + local r: R + local tn = 'float64' + local ts = 'string' + local tb = 'boolean' + local ta = 'array' + local tf = 'function<(int64, int64): int64, int64>' + local tg = 'function<(boolean | string)>' + local tR = 'type' + local tRmt = 'metatype{foo: function<(pointer): int64>v: int64}' + local tr = 'record{a:int64, b:int64}' + ]=]) +end) + it("generate functions", function() - assert.c_gencode_equals([[ + assert.ast_type_equals([[ ## local function make_pow(N) local function #('pow' .. N)(x: integer) local r = 1 @@ -192,7 +255,7 @@ it("generate functions", function() end) it("print symbol", function() - assert.c_gencode_equals([=[ + assert.ast_type_equals([=[ ## local a = 1 do do @@ -202,7 +265,7 @@ it("print symbol", function() ]=], [[ do do print(1) end end ]]) - assert.c_gencode_equals([=[ + assert.ast_type_equals([=[ ## local MIN, MAX = 1, 2 for i:integer=#[MIN],#[MAX] do print(i, #[tostring(symbols.i)]) @@ -216,7 +279,7 @@ end) it("strict mode", function() config.strict = true - assert.c_gencode_equals([=[ + assert.ast_type_equals([=[ ## local dummy = 1 local function f(a: integer) ## if true then diff --git a/spec/tools/assert.lua b/spec/tools/assert.lua index 265358d6..bbb6c8fb 100644 --- a/spec/tools/assert.lua +++ b/spec/tools/assert.lua @@ -200,15 +200,35 @@ function assert.analyze_ast(code, expected_ast) end end ---[[ -function assert.analyze_ast_equals(code, expected_code) +local function filter_ast_for_check(t) + for k,v in pairs(t) do + if type(k) == 'number' then + if traits.is_astnode(v) and v.attr.type and v.attr.type:is_type() then + -- remove type nodes because they are optional + t[k] = nil + elseif type(v) == 'table' then + filter_ast_for_check(v) + end + elseif k == 'attr' and traits.is_astnode(t) then + -- remove generated strings + v.codename = nil + v.name = nil + v.symbol = nil + v.pseudoargtypes = nil + end + end + return t +end + +function assert.ast_type_equals(code, expected_code) local ast = assert.parse_ast(nelua_parser, code) ast = assert(typechecker.analyze(ast, nelua_parser.astbuilder)) local expected_ast = assert.parse_ast(nelua_parser, expected_code) expected_ast = assert(typechecker.analyze(expected_ast, nelua_parser.astbuilder)) - assert.same(tostring(expected_ast), tostring(ast)) + filter_ast_for_check(ast) + filter_ast_for_check(expected_ast) + assert.same_string(tostring(expected_ast), tostring(ast)) end -]] function assert.analyze_error(code, expected_error) local ast = assert.parse_ast(nelua_parser, code) diff --git a/stdlib/math.nelua b/stdlib/math.nelua index dc214027..70aaebb3 100644 --- a/stdlib/math.nelua +++ b/stdlib/math.nelua @@ -65,10 +65,6 @@ function math.modf(x: number) return i, f end -function math.ult(m: integer, n: integer): boolean - return @uinteger(m) < @uinteger(n) -end - function math.tointeger(x: number) return @integer(x) end @@ -77,6 +73,10 @@ function math.type(x: number) return 'float' end +function math.ult(m: integer, n: integer): boolean + return @uinteger(m) < @uinteger(n) +end + -- Pseudo Random Number Generator based on xoshiro256** local xoshiro256 = @record{state: uint64[4]} diff --git a/stdlib/os.nelua b/stdlib/os.nelua index c9103e72..f9d926a4 100644 --- a/stdlib/os.nelua +++ b/stdlib/os.nelua @@ -1,5 +1,7 @@ !!strict +--TODO: optional params/returns + -------------------------------------------------------------------------------- -- C imports @@ -18,6 +20,8 @@ local LC_CTYPE: cint !cimport local LC_MONETARY: cint !cimport local LC_NUMERIC: cint !cimport local LC_TIME: cint !cimport +local EXIT_SUCCESS: cint !cimport +local EXIT_FAILURE: cint !cimport local errno: cint !cimport local function clock(): clong !cimport end local function getenv(name: cstring): cstring !cimport end @@ -45,22 +49,20 @@ local function fileresult(status: cint): boolean, string, integer end end -function os.clock() +function os.clock(): number return clock() / CLOCKS_PER_SEC end -function os.date() +function os.date(): string --TODO: implement os date return 'NIY (not implementet yet)' end -function os.difftime(t1: integer, t2: integer) +function os.difftime(t1: integer, t2: integer): integer return t2 - t1 end function os.execute(command: string): boolean, string, integer - --TODO: optional string param - --TODO: on error return nil instead of false local status = system(command) if status == -1 then return fileresult(status) @@ -68,16 +70,39 @@ function os.execute(command: string): boolean, string, integer return true,'exit',status end +function os.execute_check(): boolean + local status = system(nilptr) + return status ~= 0 +end + function os.exit(code: integer) - --TODO: handle boolean argument exit(@cint(code)) end +function os.exit_boolean(code: boolean) + local ecode + if code then + ecode = EXIT_SUCCESS + else + ecode = EXIT_FAILURE + end + exit(ecode) +end + +function os.exit_default() + exit(EXIT_SUCCESS) +end + function os.getenv(varname: string): string - return @string(getenv(varname)) + local s = getenv(varname) + if s then + return @string(s) + else + return '' + end end -function os.remove(filename: string) +function os.remove(filename: string): boolean, string, integer return fileresult(remove(filename)) end @@ -86,7 +111,6 @@ function os.rename(oldname: string, newname: string): boolean, string, integer end function os.setlocale(locale: string, category: string): string - --TODO: make arguments optional local cat if category == 'all' then cat = LC_ALL elseif category == 'collate' then cat = LC_COLLATE @@ -98,10 +122,19 @@ function os.setlocale(locale: string, category: string): string --TODO: string format with option name error "invalid option '%s'" end - return @string(setlocale(cat, locale)) + local s = setlocale(cat, locale) + if s then + return @string(s) + else + return '' + end +end + +function os.setlocale_default(locale: string): string + return os.setlocale(locale, 'all') end -function os.time(): integer +function os.time_default(): integer --TODO: optional table argument local t = ctime(nilptr) if t == -1 then @@ -128,14 +161,17 @@ end assert(os.clock() >= 0) assert(os.difftime(0,0) == 0 and os.difftime(0,1) == 1) -print(os.date()) +--print(os.date()) assert(type(os.getenv('PATH')) == 'string') -print(os.tmpname()) -print(os.execute('my_invalid_command')) -print(os.rename('my_invalid_file', 'a')) -print(os.remove('my_invalid_command')) -assert(os.setlocale('C','all') == 'C') +assert(type(os.tmpname()) == 'string') +assert(os.execute_check() == true) +--assert(os.execute('my_invalid_command')) +assert(os.rename('my_invalid_file', 'my_invalid_file') == false) +assert(os.remove('my_invalid_file') == false) +assert(os.setlocale_default('C') == 'C') assert(os.setlocale('C','numeric') == 'C') -print(os.time()) +assert(os.time_default() >= 0) os.exit(0) +os.exit_boolean(true) +os.exit_default() assert(false)