diff --git a/docs/assets/js/highlightjs-nelua.js b/docs/assets/js/highlightjs-nelua.js index c0faa466..f097692b 100644 --- a/docs/assets/js/highlightjs-nelua.js +++ b/docs/assets/js/highlightjs-nelua.js @@ -21,8 +21,8 @@ hljs.registerLanguage("nelua", function(e) { //Lua "function and break do else elseif end for goto if in local not or repeat return then until while " + //Extended lua - "switch case continue defer record enum" + - "var const compconst auto" + "switch case continue compconst const global" + + "defer record enum" , built_in: //Metatags and globals: diff --git a/docs/pages/nelua.md b/docs/pages/nelua.md index 843a7e04..00986431 100644 --- a/docs/pages/nelua.md +++ b/docs/pages/nelua.md @@ -4,7 +4,7 @@ [![Coverage Status](https://coveralls.io/repos/github/edubart/nelua-lang/badge.svg?branch=master)](https://coveralls.io/github/edubart/nelua-lang?branch=master) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?label=license)](https://opensource.org/licenses/MIT) [![Documentation](https://img.shields.io/website/https/edubart.github.io/nelua-lang.svg?label=docs&color=blue)](https://edubart.github.io/nelua-lang/overview/) -[![Join the chat at Gitter](https://badges.gitter.im/euluna-lang/Lobby.svg)](https://gitter.im/nelua-lang/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Gitter](https://badges.gitter.im/nelua-lang/community.svg)](https://gitter.im/nelua-lang/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) ![Project Status](https://img.shields.io/badge/status-alpha-red.svg) Nelua is a minimalistic, efficient, safe, optionally typed, ahead of time compiled, meta programmable, @@ -192,7 +192,7 @@ Lua Generator: C Generator: - [x] Generate basic C code and compile - [x] Primitives (integer, number, boolean) -- [x] Control structures +- [x] Control flow statements - [x] Primitives operators - [x] Functions - [x] Static string diff --git a/docs/pages/overview.md b/docs/pages/overview.md index 38d00233..d9ebd2e3 100644 --- a/docs/pages/overview.md +++ b/docs/pages/overview.md @@ -132,7 +132,6 @@ local function f(x: const integer) end ``` - -------------------------------------------------------------------------------- ## Control flow diff --git a/examples/math.nelua b/examples/math.nelua index 9b9610d3..4e569ecc 100644 --- a/examples/math.nelua +++ b/examples/math.nelua @@ -1,7 +1,5 @@ --TODO: function type overload ---TODO: implement using namespaces or method syntax sugar --TODO: become a module ---TODO: export functions --TODO: string compare !!cinclude '' @@ -9,10 +7,10 @@ local math = @record{} -local math_pi: compconst = 3.141592653589793 -local math_maxinteger: compconst = 9223372036854775807 -local math_mininteger: compconst = -9223372036854775807-1 -local math_huge: number !cimport 'HUGE_VAL' +global math.pi: compconst = 3.141592653589793 +global math.maxinteger: compconst = 9223372036854775807 +global math.mininteger: compconst = -9223372036854775807-1 +global math.huge: number !cimport 'HUGE_VAL' function math.abs(x: number): number !cimport('fabs') end function math.ceil(x: number): number !cimport('ceil') end @@ -40,7 +38,7 @@ local function csrand(x: uint32) !cimport('srand') end function math.atan2(x: number, y: number): number !cimport('atan2') end -function math.logbase(x: number, base: number): number +local function math_logbase(x: number, base: number): number if base == 2 then return clog2(x) elseif base == 10 then @@ -50,11 +48,11 @@ function math.logbase(x: number, base: number): number end function math.deg(x: number) - return x * (180.0 / math_pi) + return x * (180.0 / math.pi) end function math.rad(x: number) - return x * (math_pi / 180.0) + return x * (math.pi / 180.0) end function math.modf(x: number) @@ -108,28 +106,28 @@ assert_equal(math.min(1.0, -1.0), -1) assert_equal(math.min(-1.0, 1.0), -1) assert_equal(math.max(1.0, -1.0), 1) assert_equal(math.max(-1.0, 1.0), 1) -assert(math.min(math_huge, -math_huge) == -math_huge) -assert(math.max(math_huge, -math_huge) == math_huge) +assert(math.min(math.huge, -math.huge) == -math.huge) +assert(math.max(math.huge, -math.huge) == math.huge) -assert_equal(math.acos(-1.0), math_pi) +assert_equal(math.acos(-1.0), math.pi) assert_equal(math.acos(1.0), 0) assert_equal(math.asin(0.0), 0) -assert_equal(math.asin(1.0), math_pi/2) +assert_equal(math.asin(1.0), math.pi/2) assert_equal(math.atan(0.0), 0) -assert_equal(math.atan(1.0), math_pi/4) +assert_equal(math.atan(1.0), math.pi/4) assert_equal(math.atan2(0.0, 1.0), 0) -assert_equal(math.atan2(0.0, -1.0), math_pi) +assert_equal(math.atan2(0.0, -1.0), math.pi) -assert_equal(math.cos(math_pi), -1) +assert_equal(math.cos(math.pi), -1) assert_equal(math.cos(0.0), 1) -assert_equal(math.sin(math_pi/2), 1) +assert_equal(math.sin(math.pi/2), 1) assert_equal(math.sin(0.0), 0) -assert_equal(math.tan(math_pi/4), 1) +assert_equal(math.tan(math.pi/4), 1) assert_equal(math.tan(0.0), 0) -assert_equal(math.deg(math_pi / 2), 90) +assert_equal(math.deg(math.pi / 2), 90) assert_equal(math.deg(0), 0) -assert_equal(math.rad(90), math_pi / 2) +assert_equal(math.rad(90), math.pi / 2) assert_equal(math.rad(0), 0) assert_equal(math.sqrt(4.0), 2) @@ -138,10 +136,10 @@ assert_equal(math.exp(0), 1) assert_equal(math.exp(1), e) assert_equal(math.log(1), 0) assert_equal(math.log(e), 1) -assert_equal(math.logbase(1, 2), 0) -assert_equal(math.logbase(2, 2), 1) -assert_equal(math.logbase(1, 10), 0) -assert_equal(math.logbase(10, 10), 1) +assert_equal(math_logbase(1, 2), 0) +assert_equal(math_logbase(2, 2), 1) +assert_equal(math_logbase(1, 10), 0) +assert_equal(math_logbase(10, 10), 1) assert_equal(math.fmod(5, 2), 1) assert_equal(math.fmod(2.3, 5.7), 2.3) @@ -151,8 +149,8 @@ i, f = math.modf( 5.0) assert_equal(i, 5) assert_equal(f, 0.0) i, f = math.modf( 5.3) assert_equal(i, 5) assert_equal(f, 0.3) i, f = math.modf(-5.3) assert_equal(i,-5) assert_equal(f,-0.3) -assert(not (math_maxinteger < math_mininteger)) -assert(math.ult(math_maxinteger, math_mininteger)) +assert(not (math.maxinteger < math.mininteger)) +assert(math.ult(math.maxinteger, math.mininteger)) assert(math.tointeger(1.0) == 1_integer) math.randomseed(0) diff --git a/nelua/astdefs.lua b/nelua/astdefs.lua index 9f87f9e0..644c91ce 100644 --- a/nelua/astdefs.lua +++ b/nelua/astdefs.lua @@ -33,6 +33,20 @@ astbuilder:register('PreprocessName', { stypes.string, -- code }) +-- indexing +astbuilder:register('DotIndex', { + stypes.string + ntypes.PreprocessName, -- name + ntypes.Node -- expr +}) +astbuilder:register('ColonIndex', { + stypes.string + ntypes.PreprocessName, -- name + ntypes.Node -- expr +}) +astbuilder:register('ArrayIndex', { + ntypes.Node, -- index expr + ntypes.Node -- expr +}) + -- table astbuilder:register('Table', { stypes.array_of(ntypes.Node) -- pair or exprs @@ -53,7 +67,7 @@ astbuilder:register('Id', { stypes.string + ntypes.PreprocessName, -- name }) astbuilder:register('IdDecl', { - stypes.string + ntypes.PreprocessName, -- name + stypes.string + ntypes.PreprocessName + ntypes.DotIndex, -- name stypes.one_of{"const", "compconst"}:is_optional(), -- mutability ntypes.Node:is_optional(), -- typexpr stypes.array_of(ntypes.Pragma):is_optional(), -- pragmas @@ -110,20 +124,6 @@ astbuilder:register('Function', { ntypes.Node, -- block }) --- indexing -astbuilder:register('DotIndex', { - stypes.string + ntypes.PreprocessName, -- name - ntypes.Node -- expr -}) -astbuilder:register('ColonIndex', { - stypes.string + ntypes.PreprocessName, -- name - ntypes.Node -- expr -}) -astbuilder:register('ArrayIndex', { - ntypes.Node, -- index expr - ntypes.Node -- expr -}) - -- calls astbuilder:register('Call', { stypes.array_of(ntypes.Node), -- args exprs @@ -182,7 +182,7 @@ astbuilder:register('Goto', { stypes.string + ntypes.PreprocessName -- label name }) astbuilder:register('VarDecl', { - stypes.one_of{"local"}:is_optional(), -- scope + stypes.one_of{"local","global"}:is_optional(), -- scope stypes.one_of{"const", "compconst"}:is_optional(), -- mutability stypes.array_of(ntypes.IdDecl), -- var names with types stypes.array_of(ntypes.Node):is_optional(), -- expr list, initial assignments values diff --git a/nelua/ccontext.lua b/nelua/ccontext.lua index e16a2a49..a85ce13d 100644 --- a/nelua/ccontext.lua +++ b/nelua/ccontext.lua @@ -32,13 +32,13 @@ function CContext:declname(node) if self.scope:is_main() and traits.is_astnode(node) then local modname = self.attr.modname or node.modname if modname ~= '' then - declname = modname .. '_' .. declname + declname = string.format('%s_%s', modname, declname) end end declname = cdefs.quotename(declname) end if attr.shadowcount then - declname = declname .. '__' .. attr.shadowcount + declname = string.format('%s__%d', declname, attr.shadowcount) end end attr.declname = declname diff --git a/nelua/cgenerator.lua b/nelua/cgenerator.lua index 90070f7e..1e274f89 100644 --- a/nelua/cgenerator.lua +++ b/nelua/cgenerator.lua @@ -55,16 +55,16 @@ local function visit_assignments(context, emitter, varnodes, valnodes, decl) end local multiretvalname for _,varnode,valnode,valtype,lastcallindex in izipargnodes(varnodes, valnodes or {}) do - local vartype = varnode.attr.type - if not vartype:is_type() and not varnode.attr.nodecl then + local varattr = varnode.attr + local vartype = varattr.type + if not vartype:is_type() and not varattr.nodecl then local declared, defined = false, false - if decl and context.scope:is_main() then + if decl and (context.scope:is_main()) then -- declare main variables in the top scope local decemitter = CEmitter(context) - if not context.attr.no_static then - decemitter:add_indent('static ') - else - decemitter:add_indent() + decemitter:add_indent() + if not context.attr.no_static and not varattr.global then + decemitter:add('static ') end decemitter:add(varnode) if valnode and valnode.attr.compconst then @@ -120,9 +120,9 @@ local function visit_assignments(context, emitter, varnodes, valnodes, decl) end defemitter:add_ln(';') end - elseif varnode.attr.cinclude then + elseif varattr.cinclude then -- not declared, might be an imported variable from C - context:add_include(varnode.attr.cinclude) + context:add_include(varattr.cinclude) end end if usetemporary then @@ -250,6 +250,7 @@ function visitors.IdDecl(context, node, emitter) if attr.volatile then emitter:add('volatile ') end if attr.restrict then emitter:add('restrict ') end if attr.register then emitter:add('register ') end + if attr.static then emitter:add('static ') end if attr.cqualifier then emitter:add(attr.cqualifier, ' ') end emitter:add(type, ' ', context:declname(node)) if attr.cattribute then emitter:add(' __attribute__((', attr.cattribute, '))') end @@ -691,7 +692,6 @@ end function visitors.VarDecl(context, node, emitter) local varscope, mutability, varnodes, valnodes = node:args() - node:assertraisef(varscope == 'local', 'global variables not supported yet') visit_assignments(context, emitter, varnodes, valnodes, true) end @@ -709,7 +709,7 @@ function visitors.FuncDef(context, node) local type = attr.type local numrets = type:get_return_count() local qualifier = '' - if not attr.entrypoint and not context.attr.no_static then + if not attr.entrypoint and not context.attr.no_static and not attr.global then qualifier = 'static ' end local declare, define = not attr.nodecl, true diff --git a/nelua/symbol.lua b/nelua/symbol.lua index 5bcbd90c..1de574ef 100644 --- a/nelua/symbol.lua +++ b/nelua/symbol.lua @@ -12,7 +12,9 @@ function Symbol:_init(name, node, type) self.attr = attr attr.name = name attr.codename = name - attr.type = type + if type then + attr.type = type + end end function Symbol:add_possible_type(type, required) @@ -33,20 +35,19 @@ function Symbol:link_node(node) if node.attr == self.attr then return end - if not self.node then - -- this is symbol without a parent node, merge attrs into others nodes and link - local attr = node.attr - for k,v in pairs(self.attr) do + if next(node.attr) == nil then + node.attr = self.attr + else + -- merge attrs into others node and link + local attr = self.attr + for k,v in pairs(node.attr) do if attr[k] == nil then attr[k] = v else assert(attr[k] == v, 'cannot link to a node with different attributes') end end - self.attr = attr - else - assert(next(node.attr) == nil, 'cannot link to a node with attributes') - node.attr = self.attr + node.attr = attr end end diff --git a/nelua/syntaxdefs.lua b/nelua/syntaxdefs.lua index e5241716..f73def77 100644 --- a/nelua/syntaxdefs.lua +++ b/nelua/syntaxdefs.lua @@ -50,7 +50,7 @@ local function get_parser(std) if not is_luacompat then parser:add_keywords({ -- nelua additional keywords - "switch", "case", "continue", "compconst", "const", "var" + "switch", "case", "continue", "compconst", "const", "global" }) end @@ -266,7 +266,7 @@ local function get_parser(std) ) -> to_astnode for_in <- - ({} '' -> 'ForIn' {| typed_idlist |} %IN {| eexpr_list |} eDO block eEND) -> to_astnode + ({} '' -> 'ForIn' {| etyped_idlist |} %IN {| eexpr_list |} eDO block eEND) -> to_astnode for_in_empty <- ({} %IN -> 'ForIn' cnil {| eexpr_list |} eDO block eEND) -> to_astnode @@ -284,16 +284,8 @@ local function get_parser(std) ({} %GOTO -> 'Goto' ename) -> to_astnode ]]) - grammar:add_group_peg('stat', 'vardecl', [[ - ({} '' -> 'VarDecl' - var_scope cnil - {| typed_idlist |} - (%ASSIGN {| eexpr_list |})? - ) -> to_astnode - ]]) - grammar:add_group_peg('stat', 'funcdef', [[ - ({} '' -> 'FuncDef' var_scope %FUNCTION id function_body) -> to_astnode / + ({} '' -> 'FuncDef' %LOCAL -> 'local' %FUNCTION id function_body) -> to_astnode / ({} %FUNCTION -> 'FuncDef' cnil func_name function_body) -> to_astnode func_name <- (id {| (dot_index* colon_index / dot_index)* |}) -> to_chain_index_or_call @@ -328,10 +320,20 @@ local function get_parser(std) if not is_luacompat then grammar:add_group_peg('stat', 'vardecl', [[ - ({} '' -> 'VarDecl' - (((var_scope / %VAR -> 'local') (var_mutability / cnil)) / (cnil var_mutability)) - {| typed_idlist |} - (%ASSIGN {| eexpr_list |})? + ({} '' -> 'VarDecl' + ( %LOCAL -> 'local' (var_mut/cnil) + {| etyped_idlist |} + / (%GLOBAL ->'global' (var_mut/cnil) / ''->'global' var_mut) + {| eglobal_typed_idlist |} + ) (%ASSIGN {| eexpr_list |})? + ) -> to_astnode + + eglobal_typed_idlist <- + (global_typed_id / %{ExpectedName}) (%COMMA global_typed_id)* + global_typed_id <- ({} '' -> 'IdDecl' + ((id {| dot_index+ |}) -> to_chain_index_or_call / name) + ((%COLON (var_mut (typexpr/cnil) / cnil etypexpr)) / cnil cnil) + (&%EXCL {| var_pragma* |})? ) -> to_astnode ]], nil, true) @@ -348,6 +350,14 @@ 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 @@ -413,13 +423,14 @@ local function get_parser(std) {| (%COLON etypexpr_list)? |} {| var_pragma* |} block eEND - var_mutability <- + var_mut <- %TCOMPCONST -> 'compconst' / %TCONST -> 'const' typed_idlist <- typed_id (%COMMA typed_id)* + etyped_idlist <- (typed_id / %{ExpectedName}) (%COMMA typed_id)* typed_id <- ({} '' -> 'IdDecl' name - ((%COLON (var_mutability (typexpr /cnil) / cnil etypexpr)) / cnil cnil) + ((%COLON (var_mut (typexpr /cnil) / cnil etypexpr)) / cnil cnil) (&%EXCL {| var_pragma* |})? ) -> to_astnode @@ -429,7 +440,6 @@ local function get_parser(std) expr_list <- (expr (%COMMA expr)*)? eexpr_list <- eexpr (%COMMA expr)* - var_scope <- %LOCAL -> 'local' var_pragma <- ({} %EXCL -> 'Pragma' epragma_expr) -> to_astnode epragma_expr <- @@ -594,7 +604,7 @@ local function get_parser(std) -- grammar errors parser:add_syntax_errors({ - UnexpectedSyntaxAtEOF = 'unexpected syntax, was expecting EOF', + UnexpectedSyntaxAtEOF = 'unexpected syntax', UnclosedParenthesis = "unclosed parenthesis, did you forget a `)`?", UnclosedBracket = "unclosed bracket, did you forget a `]`?", UnclosedCurly = "unclosed curly brace, did you forget a `}`?", diff --git a/nelua/typechecker.lua b/nelua/typechecker.lua index 98111360..a7c97dd1 100644 --- a/nelua/typechecker.lua +++ b/nelua/typechecker.lua @@ -302,8 +302,8 @@ function visitors.Id(context, node) end function visitors.IdDecl(context, node) - local name, mut, typenode, pragmanodes = node:args() - node:assertraisef(not (mut and node.mut), "cannot declare mutability twice for '%s'", name) + local namenode, mut, typenode, pragmanodes = node:args() + node:assertraisef(not (mut and node.mut), "cannot declare mutability twice") mut = mut or node.mut node:assertraisef(not mut or typedefs.mutabilities[mut], 'mutability %s not supported yet', mut) @@ -317,11 +317,23 @@ function visitors.IdDecl(context, node) type = primtypes.any end end - local symbol = context.scope:add_symbol(Symbol(name, node, type)) + local symbol + if traits.is_string(namenode) then + symbol = context.scope:add_symbol(Symbol(namenode, node, type)) + else + -- global record field + assert(namenode.tag == 'DotIndex') + context.inglobaldecl = node + symbol = context:traverse(namenode) + context.inglobaldecl = nil + end local attr = symbol.attr if mut then attr[mut] = true end + if type then + attr.type = type + end if pragmanodes then context:traverse(pragmanodes, symbol) end @@ -514,6 +526,7 @@ local function visitor_FieldIndex(context, node) local attr = node.attr local name, objnode = node:args() if attr.type then + -- type already known, return early local objtype = objnode.attr.type if objtype:is_type() then objtype = objnode.attr.holdedtype @@ -549,13 +562,22 @@ local function visitor_FieldIndex(context, node) elseif objtype:is_record() then symbol = objtype:get_metafield(name) if not symbol then - if context.infuncdef == context:get_parent_node() then - local symname = string.format('%s_%s', objtype.codename, name) + local parentnode = context:get_parent_node() + local symname = string.format('%s_%s', objtype.codename, name) + if context.infuncdef == parentnode then + -- declaration of record global function symbol = Symbol(symname, node) symbol.attr.const = true symbol.attr.metafunc = true symbol.attr.metarecordtype = objtype objtype:set_metafield(name, symbol) + elseif context.inglobaldecl == parentnode then + -- declaration of record global variable + symbol = Symbol(symname, parentnode) + objtype:set_metafield(name, symbol) + + -- add symbol to scope to enable type deduction + context.scope:add_symbol(symbol) else node:raisef('cannot index record meta field "%s"', name) end @@ -1002,6 +1024,10 @@ function visitors.VarDecl(context, node) #varnodes, #valnodes) for _,varnode,valnode,valtype in izipargnodes(varnodes, valnodes) do assert(varnode.tag == 'IdDecl') + if varscope == 'global' then + varnode:assertraisef(context.scope:is_main(), 'global variables can only be declarated in top scope') + varnode.attr.global = true + end varnode.mut = mut local symbol = context:traverse(varnode) assert(symbol) @@ -1473,13 +1499,15 @@ function typechecker.analyze(ast, astbuilder) -- phase 1 traverse: infer and check types context.phase = phases.type_inference - context:repeat_scope_until_resolution('function', function() + context:repeat_scope_until_resolution('function', function(scope) + scope.main = true context:traverse(ast) end) -- phase 2 traverse: infer non set types to 'any' type context.phase = phases.any_inference - context:repeat_scope_until_resolution('function', function() + context:repeat_scope_until_resolution('function', function(scope) + scope.main = true context:traverse(ast) end) diff --git a/nelua/typedefs.lua b/nelua/typedefs.lua index b383b550..de5c38ad 100644 --- a/nelua/typedefs.lua +++ b/nelua/typedefs.lua @@ -344,7 +344,8 @@ typedefs.block_pragmas = { local common_pragmas = { cimport = shaper.shape{shaper.string:is_optional(), (shaper.boolean + shaper.string):is_optional()}, - onestring = shaper.shape{shaper.string} + onestring = shaper.shape{shaper.string}, + oneinteger = shaper.shape{shaper.integer} } typedefs.function_pragmas = { cimport = common_pragmas.cimport, @@ -364,14 +365,15 @@ typedefs.variable_pragmas = { codename = common_pragmas.onestring, cqualifier = common_pragmas.onestring, cattribute = common_pragmas.onestring, - aligned = shaper.shape{shaper.integer}, + aligned = common_pragmas.oneinteger, + static = true, register = true, restrict = true, volatile = true, nodecl = true, } typedefs.type_pragmas = { - aligned = shaper.shape{shaper.integer}, + aligned = common_pragmas.oneinteger, cimport = common_pragmas.cimport, codename = common_pragmas.onestring, nodecl = true, diff --git a/nelua/utils/console.lua b/nelua/utils/console.lua index 7d8d85aa..b2b40b26 100644 --- a/nelua/utils/console.lua +++ b/nelua/utils/console.lua @@ -22,12 +22,12 @@ local pformat, pconcat = stringer.pformat, stringer.pconcat function console.warnf(format, ...) logcf(io.stderr, color_warn, pformat(format, ...)) end function console.errorf(format, ...) logcf(io.stderr, color_error, pformat(format, ...)) end -function console.debugf(format, ...) logcf(io.stderr, color_debug, pformat(format, ...)) end +function console.debugf(format, ...) logcf(io.stdout, color_debug, pformat(format, ...)) end function console.infof(format, ...) logcf(io.stdout, color_info, pformat(format, ...)) end function console.warn(...) logcf(io.stderr, color_warn, pconcat(...)) end function console.error(...) logcf(io.stderr, color_error, pconcat(...)) end -function console.debug(...) logcf(io.stderr, color_debug, pconcat(...)) end +function console.debug(...) logcf(io.stdout, color_debug, pconcat(...)) end function console.info(...) logcf(io.stdout, color_info, pconcat(...)) end function console.log(...) logcf(io.stdout, nil, pconcat(...)) end diff --git a/spec/astbuilder_spec.lua b/spec/01-astbuilder_spec.lua similarity index 100% rename from spec/astbuilder_spec.lua rename to spec/01-astbuilder_spec.lua diff --git a/spec/syntaxdefs_spec.lua b/spec/02-syntaxdefs_spec.lua similarity index 98% rename from spec/syntaxdefs_spec.lua rename to spec/02-syntaxdefs_spec.lua index 91c2b5fa..c8c74531 100644 --- a/spec/syntaxdefs_spec.lua +++ b/spec/02-syntaxdefs_spec.lua @@ -838,9 +838,9 @@ describe("statement variable declaration", function() }}) end) it("non local variable", function() - assert.parse_ast(nelua_parser, "var a", + assert.parse_ast(nelua_parser, "global a: integer", n.Block{{ - n.VarDecl{'local', nil, {n.IdDecl{'a'}}} + n.VarDecl{'global', nil, {n.IdDecl{'a', nil, n.Type{'integer'}}}} }}) end) it("variable mutabilities inside types", function() @@ -857,13 +857,17 @@ describe("statement variable declaration", function() end) it("variable mutabilities", function() assert.parse_ast(nelua_parser, [[ - var a = b - const a = b + local const a = b + local a: const = b + global const a = b + global a: const = b local compconst a = b ]], n.Block{{ - n.VarDecl{'local', nil, {n.IdDecl{'a'}}, {n.Id{'b'}}}, - n.VarDecl{nil, 'const', {n.IdDecl{'a'}}, {n.Id{'b'}}}, + n.VarDecl{'local', 'const', {n.IdDecl{'a'}}, {n.Id{'b'}}}, + n.VarDecl{'local', nil, {n.IdDecl{'a', 'const'}}, {n.Id{'b'}}}, + n.VarDecl{'global', 'const', {n.IdDecl{'a'}}, {n.Id{'b'}}}, + n.VarDecl{'global', nil, {n.IdDecl{'a', 'const'}}, {n.Id{'b'}}}, n.VarDecl{'local', 'compconst', {n.IdDecl{'a'}}, {n.Id{'b'}}}, }}) end) @@ -875,6 +879,12 @@ describe("statement variable declaration", function() { n.Id{'x'}, n.Id{'y'}, n.Id{'z'} }}, }}) end) + it("record global variables", function() + assert.parse_ast(nelua_parser, "global a.b: integer", + n.Block{{ + n.VarDecl{'global', nil, {n.IdDecl{n.DotIndex{'b',n.Id{'a'}}, nil, n.Type{'integer'}}}} + }}) + end) end) -------------------------------------------------------------------------------- diff --git a/spec/typechecker_spec.lua b/spec/03-typechecker_spec.lua similarity index 98% rename from spec/typechecker_spec.lua rename to spec/03-typechecker_spec.lua index 7fd6a746..65854b6b 100644 --- a/spec/typechecker_spec.lua +++ b/spec/03-typechecker_spec.lua @@ -38,6 +38,11 @@ it("local variable", function() assert.analyze_error("local a: void", "variable declaration cannot be of the type") end) +it("global variable", function() + assert.c_gencode_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") end) @@ -584,6 +589,19 @@ it("record methods", function() ]], "cannot index record meta field") end) +it("record globals", function() + assert.analyze_ast([[ + local Math = @record{} + global Math.PI = 3.14 + local a = Math.PI + ]]) + assert.analyze_error([[ + local Math = @record{} + global Math.PI: integer = 3 + Math.PI = 3.14 + ]], "is not coercible with") +end) + it("type neasting", function() assert.analyze_ast([[ local a: record{x: record{y: integer}} diff --git a/spec/luagenerator_spec.lua b/spec/04-luagenerator_spec.lua similarity index 96% rename from spec/luagenerator_spec.lua rename to spec/04-luagenerator_spec.lua index 3db34f1a..199de465 100644 --- a/spec/luagenerator_spec.lua +++ b/spec/04-luagenerator_spec.lua @@ -122,10 +122,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("var a", "local a") - assert.generate_lua("var a, b = 1", "local a, b = 1, nil") - assert.generate_lua("var a, b = 1, 2", "local a, b = 1, 2") - assert.generate_lua("function f() var a end", "function f()\n local a\nend") + assert.generate_lua("function f() local a end", "function f()\n local a\nend") end) it("assignment", function() assert.generate_lua("a = 1") diff --git a/spec/cgenerator_spec.lua b/spec/05-cgenerator_spec.lua similarity index 97% rename from spec/cgenerator_spec.lua rename to spec/05-cgenerator_spec.lua index e8df1079..f22440a2 100644 --- a/spec/cgenerator_spec.lua +++ b/spec/05-cgenerator_spec.lua @@ -23,6 +23,14 @@ int nelua_main() { assert.generate_c("return (1)") end) +it("local variable", function() + assert.generate_c("do local a = 1 end", " int64_t a = 1;") +end) + +it("global variable", function() + assert.generate_c("global a = 1", "\nint64_t mymod_a = 1;\n") +end) + it("number", function() assert.generate_c("do local a = 99 end", "99") assert.generate_c("do local a = 1.2 end", "1.2") @@ -484,10 +492,10 @@ it("reserved names quoting", function() assert.generate_c("do local default: integer end", "int64_t default_ = 0;") assert.generate_c("do local NULL: integer = 0 end", "int64_t NULL_ = 0;") assert.run_c([[ - local function struct(static: integer) + local function struct(double: integer) local default: integer default = 1 - return default + static + return default + double end print(struct(1)) ]], "2") @@ -672,6 +680,16 @@ it("record methods", function() ]]) end) +it("record globals", function() + assert.run_c([[ + local Math = @record{} + global Math.PI = 3.14 + assert(Math.PI == 3.14) + Math.PI = 3 + assert(Math.PI == 3) + ]]) +end) + it("enums", function() assert.generate_c( "local e: enum{A=0}", @@ -760,6 +778,7 @@ it("pragmas", function() assert.generate_c("local a: int64 !register", "register int64_t mymod_a") assert.generate_c("local a: int64 !restrict", "restrict int64_t mymod_a") assert.generate_c("local a: int64 !nodecl", "") + assert.generate_c("do local a !static = 1 end", "static int64_t a = 1;", true) assert.generate_c("local a: int64 !cattribute 'vector_size(16)'", "int64_t mymod_a __attribute__((vector_size(16)))") assert.generate_c("local a: number !cqualifier 'in' = 1", "in double mymod_a = 1.0;") assert.generate_c("local R !aligned(16) = @record{x: integer}; local r: R", "} __attribute__((aligned(16))) ") diff --git a/spec/preprocessor_spec.lua b/spec/06-preprocessor_spec.lua similarity index 100% rename from spec/preprocessor_spec.lua rename to spec/06-preprocessor_spec.lua diff --git a/spec/runner_spec.lua b/spec/07-runner_spec.lua similarity index 96% rename from spec/runner_spec.lua rename to spec/07-runner_spec.lua index 1c3a2e41..7f29270b 100644 --- a/spec/runner_spec.lua +++ b/spec/07-runner_spec.lua @@ -12,7 +12,7 @@ it("compile simple programs" , function() end) it("run simple programs", function() - assert.run({'--generator', 'c', '--no-cache', '--eval', "return 0"}) + assert.run({'--generator', 'c', '--no-cache', '--timing', '--eval', "return 0"}) assert.run('--generator lua examples/helloworld.nelua', 'hello world') assert.run('--generator c examples/helloworld.nelua', 'hello world') assert.run({'--generator', 'lua', '--eval', ""}, '') diff --git a/spec/tools/assert.lua b/spec/tools/assert.lua index 28593b9d..265358d6 100644 --- a/spec/tools/assert.lua +++ b/spec/tools/assert.lua @@ -90,7 +90,7 @@ local function run(args) end local tmperr, tmpout = io.tmpfile(), io.tmpfile() local function rprint(...) - return tmpout:write(stringer.print_concat(...) .. "\n") + return tmpout:write(stringer.pconcat(...) .. "\n") end -- hook print, stderr and stdout local ostderr, ostdout, oprint = io.stderr, io.stdout, _G.print