diff --git a/nelua/cbuiltins.lua b/nelua/cbuiltins.lua index 02db9064..5fec909d 100644 --- a/nelua/cbuiltins.lua +++ b/nelua/cbuiltins.lua @@ -398,6 +398,40 @@ function builtins.nelua_lt_(context, ltype, rtype) end end +function builtins.nelua_eq_(context, type) + assert(type.is_record) + local name = string.format('nelua_eq_%s', type.codename) + if context.usedbuiltins[name] then return name end + local ctype = context:ctype(type) + local defemitter = CEmitter(context) + defemitter:add_ln('{') + defemitter:inc_indent() + defemitter:add_indent('return ') + for i,field in ipairs(type.fields) do + if i > 1 then + defemitter:add(' && ') + end + if field.type.is_record then + local op = context:ensure_runtime_builtin('nelua_eq_', field.type) + defemitter:add(op, '(a.', field.name, ', b.', field.name, ')') + elseif field.type.is_array then + context:add_include('') + defemitter:add('memcmp(a.', field.name, ', ', 'b.', field.name, ', sizeof(', type, ')) == 0') + else + defemitter:add('a.', field.name, ' == ', 'b.', field.name) + end + end + defemitter:add_ln(';') + defemitter:dec_indent() + defemitter:add_ln('}') + define_inline_builtin(context, name, + 'bool', + string.format('(%s a, %s b)', ctype, ctype), + defemitter:generate()) + return name +end + + function builtins.nelua_idiv_(context, type) local name = string.format('nelua_idiv_i%d', type.bitsize) if context.usedbuiltins[name] then return name end @@ -547,9 +581,18 @@ end function operators.eq(_, emitter, lnode, rnode, lname, rname) local ltype, rtype = lnode.attr.type, rnode.attr.type - if ltype.is_stringview and rtype.is_stringview then + if ltype.is_stringview then + assert(rtype.is_stringview) emitter:add_builtin('nelua_stringview_eq') emitter:add('(', lname, ', ', rname, ')') + elseif ltype.is_record then + assert(ltype == rtype) + local op = emitter.context:ensure_runtime_builtin('nelua_eq_', ltype) + emitter:add(op, '(', lname, ', ', rname, ')') + elseif ltype.is_array then + assert(ltype == rtype) + emitter.context:add_include('') + emitter:add('(memcmp(&', lname, '.data[0], &', rname, '.data[0], sizeof(', ltype, ')) == 0)') else emitter:add(lname, ' == ', rname) end @@ -560,6 +603,14 @@ function operators.ne(_, emitter, lnode, rnode, lname, rname) if ltype.is_stringview and rtype.is_stringview then emitter:add_builtin('nelua_stringview_ne') emitter:add('(', lname, ', ', rname, ')') + elseif ltype.is_record then + assert(ltype == rtype) + local op = emitter.context:ensure_runtime_builtin('nelua_eq_', ltype) + emitter:add('!', op, '(', lname, ', ', rname, ')') + elseif ltype.is_array then + assert(ltype == rtype) + emitter.context:add_include('') + emitter:add('(memcmp(&', lname, '.data[0], &', rname, '.data[0], sizeof(', ltype, ')) != 0)') else emitter:add(lname, ' != ', rname) end diff --git a/nelua/configer.lua b/nelua/configer.lua index 7f5e57dd..83fcf67d 100644 --- a/nelua/configer.lua +++ b/nelua/configer.lua @@ -43,8 +43,6 @@ local function merge_configs(conf, pconf) for k,v in pairs(pconf) do if conf[k] == nil then conf[k] = v - elseif type(conf[k]) == 'table' and type(v) == 'table' and #v > 0 then - tabler.insertvalues(conf[k], 1, v) end end @@ -52,10 +50,10 @@ local function merge_configs(conf, pconf) local ss = sstream() for _,libpath in ipairs(conf.lib_path) do if libpath:find('?') then - ss:add(libpath) + ss:add(libpath, ';') else - ss:add(libpath .. '/?.nelua;') - ss:add(libpath .. '/?/init.nelua;') + ss:add(libpath, '/?.nelua;') + ss:add(libpath, '/?/init.nelua;') end end ss:add(conf.path) @@ -82,11 +80,11 @@ local function create_parser(args) argparser:flag('-t --timing', 'Debug compile timing information', defconfig.timing) argparser:flag('--no-cache', "Don't use any cached compilation", defconfig.no_cache) argparser:option('-D --define', 'Define values in the preprocessor') - :count("*"):convert(convert_define, defconfig.define) + :count("*"):convert(convert_define, tabler.copy(defconfig.define or {})) argparser:option('-g --generator', "Code generator to use (lua/c)", defconfig.generator) argparser:option('-d --standard', "Source standard (default/luacompat)", defconfig.standard) argparser:option('-p --path', "Set module search path", defconfig.path) - argparser:option('-L --lib-path', "Add module search path", defconfig.lib_path) + argparser:option('-L --lib-path', "Add module search path", tabler.copy(defconfig.lib_path or {})) :count("*"):convert(convert_add_path) argparser:option('--cc', "C compiler to use", defconfig.cc) argparser:option('--cpu-bits', "Target CPU architecture bit size (64/32)", defconfig.cpu_bits) diff --git a/spec/05-cgenerator_spec.lua b/spec/05-cgenerator_spec.lua index 641e9d29..adc88731 100644 --- a/spec/05-cgenerator_spec.lua +++ b/spec/05-cgenerator_spec.lua @@ -830,6 +830,59 @@ it("string comparisons", function() ]]) end) +it("array comparisons", function() + assert.run_c([[ + local A = @integer[4] + local a: A = {1,2,3,4} + local b: A = {1,2,3,4} + local c: A = {1,2,3,5} + assert(a == a) + assert(a == (@integer[4]){1,2,3,4}) + assert(a == b) + assert(not (a ~= b)) + assert(not (a == c)) + assert(a ~= c) +]]) +end) + +it("record comparisons", function() + assert.run_c([[ + local R = @record{x: integer, y: integer} + local a: R = {1,1} + local b: R = {1,1} + local c: R = {2,2} + + assert(a == a) + assert(a == R{1,1}) + assert(a == b) + assert(not (a ~= b)) + assert(not (a == c)) + assert(a ~= c) + + + local P = @record{x: R} + local a: P = {{1}} + local b: P = {{1}} + local c: P = {{2}} + assert(a == a) + assert(a == b) + assert(not (a ~= b)) + assert(not (a == c)) + assert(a ~= c) + + + local Q = @record{x: integer[2]} + local a: Q = {{1,2}} + local b: Q = {{1,2}} + local c: Q = {{1,3}} + assert(a == a) + assert(a == b) + assert(not (a ~= b)) + assert(not (a == c)) + assert(a ~= c) +]]) +end) + it("binary conditional operators", function() assert.generate_c("local a, b; do return a or b end", [[({ nelua_any t1_ = a; diff --git a/spec/08-runner_spec.lua b/spec/08-runner_spec.lua index 91990537..e73fc51f 100644 --- a/spec/08-runner_spec.lua +++ b/spec/08-runner_spec.lua @@ -97,7 +97,7 @@ it("configure module search paths", function() defconfig.lib_path = oldlibpath assert.run({'--path', './examples', '--analyze', '--eval',[[ - ## assert(config.path == './examples') + ## assert(config.path:match('examples')) ]]}) end)