Skip to content

Commit

Permalink
Many micro optimizations in the compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
edubart committed Jul 23, 2020
1 parent bd6ef10 commit eeb9fd6
Show file tree
Hide file tree
Showing 18 changed files with 220 additions and 188 deletions.
6 changes: 3 additions & 3 deletions docs/pages/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -1029,9 +1029,9 @@ You can even manipulate what has already been processed:

```nelua
local Person = @record{name: string}
## Person.value.fields[1].name = 'nick' -- rename field 'name' to 'nick'
local p: Person = {nick='Joe'}
print(p.nick) -- outputs 'Joe'
## Person.value:add_field('age', primtypes.integer) -- add field 'age' to 'Person'
local p: Person = {name='Joe', age=21}
print(p.age) -- outputs '21'
```

The above code compile exactly as:
Expand Down
6 changes: 3 additions & 3 deletions examples/overview.nelua
Original file line number Diff line number Diff line change
Expand Up @@ -496,9 +496,9 @@ do -- Processing on the fly
## end

local Person = @record{name: string}
## Person.value.fields[1].name = 'nick' -- rename field 'name' to 'nick'
local p: Person = {nick='Joe'}
print(p.nick) -- outputs 'Joe'
## Person.value:add_field('age', primtypes.integer) -- add field 'age' to 'Person'
local p: Person = {name='Joe', age=21}
print(p.age) -- outputs '21'
end

do -- Preprocessing lazy functions
Expand Down
18 changes: 11 additions & 7 deletions nelua/analyzer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ local function visitor_Record_literal(context, node, littype)
if not traits.is_string(fieldname) then
childnode:raisef("only string literals are allowed in record's field names")
end
field, fieldindex = littype:get_field(fieldname)
field = littype:get_field(fieldname)
fieldindex = field and field.index or nil
parent = childnode
parentindex = 2
else
Expand Down Expand Up @@ -398,7 +399,7 @@ function visitors.Id(context, node)
symbol:link_node(node)
return symbol
end
local symbol = context.scope:get_symbol(name)
local symbol = context.scope.symbols[name]
if not symbol then
node:raisef("undeclared symbol '%s'", name)
end
Expand Down Expand Up @@ -468,7 +469,7 @@ function visitors.Type(context, node)
local tyname = node[1]
local value = typedefs.primtypes[tyname]
if not value then
local symbol = context.scope:get_symbol(tyname)
local symbol = context.scope.symbols[tyname]
if not (symbol and symbol.type == primtypes.type) then
node:raisef("symbol '%s' is an invalid type", tyname)
end
Expand Down Expand Up @@ -646,7 +647,7 @@ function visitors.GenericType(context, node)
local attr = node.attr
local name, argnodes = node[1], node[2]
if attr.type then return end
local symbol = context.scope:get_symbol(name)
local symbol = context.scope.symbols[name]
if not symbol or not symbol.type or not symbol.type.is_type or not symbol.value.is_generic then
node:raisef("symbol '%s' doesn't hold a generic type", name)
end
Expand Down Expand Up @@ -823,8 +824,8 @@ local function visitor_Call(context, node, argnodes, calleetype, calleesym, call
local pseudoargtypes = funcargtypes
local pseudoargattrs = funcargattrs
if calleeobjnode then
pseudoargtypes = tabler.copy(funcargtypes)
pseudoargattrs = tabler.copy(funcargattrs)
pseudoargtypes = tabler.icopy(funcargtypes)
pseudoargattrs = tabler.icopy(funcargattrs)
local ok, err = funcargtypes[1]:is_convertible_from(calleeobjnode)
if not ok then
node:raisef("in call of function '%s' at argument %d: %s",
Expand Down Expand Up @@ -1021,16 +1022,19 @@ function visitors.CallMethod(context, node)
end

local function visitor_Record_FieldIndex(_, node, objtype, name)
local attr = node.attr
if attr.type then return end
local field = objtype:get_field(name)
local type = field and field.type
if not type then
node:raisef("cannot index field '%s' on record '%s'", name, objtype)
end
node.attr.type = type
attr.type = type
end

local function visitor_EnumType_FieldIndex(_, node, objtype, name)
local attr = node.attr
if attr.type then return end
local field = objtype:get_field(name)
if not field then
node:raisef("cannot index field '%s' on enum '%s'", name, objtype)
Expand Down
4 changes: 3 additions & 1 deletion nelua/astbuilder.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ end

function ASTBuilder:create(tag, ...)
local klass = self.nodes[tag]
errorer.assertf(klass, "AST with name '%s' is not registered", tag)
if not klass then
errorer.errorf("AST with name '%s' is not registered", tag)
end
local node = klass(...)
if config.check_ast_shape then
local shape = self.shapes[tag]
Expand Down
22 changes: 5 additions & 17 deletions nelua/astnode.lua
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,11 @@ clone_nodetable = function(t)
local ct = {}
for i=1,t.n or #t do
local v = t[i]
local tv = type(v)
if tv == 'table' then
if v._astnode then
ct[i] = clone_node(v)
elseif getmetatable(v) == nil then
ct[i] = clone_nodetable(v)
else --luacov:disable
errorer.errorf("invalid table metatable in node clone")
end --luacov:enable
else --luacov:disable
if tv == 'number' or tv == 'userdata' or tv == 'string' or
tv == 'boolean' or tv == 'function' then
ct[i] = v
else
errorer.errorf("invalid value type '%s' in node clone", tv)
end
end --luacov:enable
if v._astnode then
ct[i] = clone_node(v)
else
ct[i] = clone_nodetable(v)
end
end
-- in case of packed tables
ct.n = t.n
Expand Down
4 changes: 0 additions & 4 deletions nelua/attr.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ function Attr:merge(attr)
return self
end

function Attr:is_empty()
return next(self) == nil
end

function Attr:is_static_vardecl()
if self.vardecl and self.staticstorage and not self.comptime then
if not self.type or self.type.size > 0 then
Expand Down
2 changes: 1 addition & 1 deletion nelua/ccompiler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ function compiler.get_run_command(binaryfile, runargs)
end
end --luacov:enable

return fs.abspath(binaryfile), tabler.copy(runargs)
return fs.abspath(binaryfile), tabler.icopy(runargs)
end

return compiler
2 changes: 1 addition & 1 deletion nelua/configer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ end
function configer.parse(args)
defconfig.data_path = fs.getdatapath(args[0])
defconfig.path = get_search_path(defconfig.data_path)
local argparser = create_parser(tabler.copy(args))
local argparser = create_parser(tabler.icopy(args))
local ok, options = argparser:pparse(args)
except.assertraise(ok, options)
merge_configs(options, defconfig)
Expand Down
37 changes: 20 additions & 17 deletions nelua/emitter.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
local class = require 'nelua.utils.class'
local traits = require 'nelua.utils.traits'
local errorer = require 'nelua.utils.errorer'
local bn = require 'nelua.utils.bn'

Expand Down Expand Up @@ -27,19 +26,17 @@ function Emitter:dec_indent(count)
end

function Emitter:add_indent(what, ...)
local depth = math.max(self.depth, 0)
local indent = string.rep(self.indent, depth)
self:add(indent, what, ...)
self:add(string.rep(self.indent, math.max(self.depth, 0)), what, ...)
end

function Emitter:add_indent_ln(what, ...)
self:add_indent()
self:add_ln(what, ...)
self:add_ln(string.rep(self.indent, math.max(self.depth, 0)), what, ...)
end

function Emitter:add_ln(what, ...)
self:add(what, ...)
self:add('\n')
local codes = self.codes
codes[#codes+1] = '\n'
end

function Emitter:get_pos()
Expand All @@ -57,16 +54,23 @@ function Emitter:remove_until_pos(pos)
end

function Emitter:add_one(what)
if traits.is_string(what) then
local ty = type(what)
local codes = self.codes
local pos = #codes+1
if ty == 'string' then
if #what > 0 then
table.insert(self.codes, what)
codes[pos] = what
end
elseif ty == 'number' then
codes[pos] = tostring(what)
elseif ty == 'table' then
if what._astnode then
self:add_traversal(what)
-- elseif what._bn then
-- codes[pos] = tostring(what)
else
self:add_traversal_list(what)
end
elseif bn.isnumeric(what) then
table.insert(self.codes, tostring(what))
elseif traits.is_astnode(what) then
self:add_traversal(what)
elseif traits.is_table(what) then
self:add_traversal_list(what)
--elseif traits.is_function(what) then
--what(self)
else --luacov:disable
Expand All @@ -78,8 +82,7 @@ function Emitter:add(what, ...)
if what then
self:add_one(what)
end
local numargs = select('#', ...)
if numargs > 0 then
if select('#', ...) > 0 then
self:add(...)
end
end
Expand Down
2 changes: 1 addition & 1 deletion nelua/preprocessor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ local traverse_node = VisitorContext.traverse_node
local function pp_default_visitor(self, node, emitter, ...)
for i=1,node.nargs or #node do
local arg = node[i]
if type(arg) == 'table' then
if arg and type(arg) == 'table' then
if arg._astnode then
traverse_node(self, arg, emitter, node, i, ...)
else
Expand Down
78 changes: 38 additions & 40 deletions nelua/scope.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
local class = require 'nelua.utils.class'
local metamagic = require 'nelua.utils.metamagic'
local types = require 'nelua.types'
local typedefs = require 'nelua.typedefs'
local symdefs = require 'nelua.symdefs'
Expand All @@ -18,7 +17,6 @@ function Scope:_init(parent, kind, node)
table.insert(parent.children, self)
end
self.children = {}
self.checkpointstack = {}
self:clear_symbols()
end

Expand All @@ -30,20 +28,23 @@ function Scope:is_topscope()
return self.parent and self.parent.kind == 'root'
end

local default_symbols_mt = {
__index = function(symbols, key)
-- return predefined symbol definition if nothing is found
local symbol = symdefs[key]
if symbol then
symbol = symbol:clone()
symbols[key] = symbol
return symbol
end
end
}

function Scope:clear_symbols()
self.symbols = {}
if self.parent then
metamagic.setmetaindex(self.symbols, self.parent.symbols)
self.symbols = setmetatable({}, {__index = self.parent.symbols})
else
metamagic.setmetaindex(self.symbols, function(symbols, key)
-- return predefined symbol definition if nothing is found
local symbol = symdefs[key]
if symbol then
symbol = symbol:clone()
symbols[key] = symbol
return symbol
end
end)
self.symbols = setmetatable({}, default_symbols_mt)
end
self.possible_returntypes = {}
self.resolved_returntypes = {}
Expand Down Expand Up @@ -80,10 +81,6 @@ function Scope:get_parent_of_kind(kind)
return parent
end

function Scope:get_symbol(name)
return self.symbols[name]
end

function Scope:make_checkpoint()
local checkpoint = {
symbols = tabler.copy(self.symbols),
Expand Down Expand Up @@ -121,6 +118,9 @@ function Scope:merge_checkpoint(checkpoint)
end

function Scope:push_checkpoint(checkpoint)
if not self.checkpointstack then
self.checkpointstack = {}
end
table.insert(self.checkpointstack, self:make_checkpoint())
self:set_checkpoint(checkpoint)
end
Expand All @@ -133,16 +133,16 @@ end

function Scope:add_symbol(symbol)
local key
if symbol.annonymous then
key = symbol
else
if not symbol.annonymous then
key = symbol.name
else
key = symbol
end
local oldsymbol = self.symbols[key]
if oldsymbol == symbol then
return true
end
if oldsymbol then
if oldsymbol == symbol then
return true
end
-- shadowing a symbol with the same name
if oldsymbol == self.context.state.inlazydef then
-- symbol definition of a lazy function
Expand Down Expand Up @@ -171,10 +171,12 @@ function Scope:resolve_symbols()
-- first resolve any symbol with known possible types
for i=1,#self.symbols do
local symbol = self.symbols[i]
if symbol:resolve_type() then
count = count + 1
elseif count == 0 and symbol.type == nil then
table.insert(unknownlist, symbol)
if symbol.type == nil then
if symbol:resolve_type() then
count = count + 1
elseif count == 0 then
table.insert(unknownlist, symbol)
end
end
end
-- if nothing was resolved previously then try resolve symbol with unknown possible types
Expand All @@ -197,28 +199,24 @@ function Scope:resolve_symbols()
end

function Scope:add_return_type(index, type)
if not type then
self.has_unknown_return = true
end
local returntypes = self.possible_returntypes[index]
if not returntypes then
returntypes = {}
self.possible_returntypes[index] = returntypes
elseif type and tabler.ifind(returntypes, type) then
return
end
if type then
self.possible_returntypes[index] = {[1] = type}
elseif type and not tabler.ifind(returntypes, type) then
table.insert(returntypes, type)
else
self.has_unknown_return = true
end
end

function Scope:resolve_returntypes()
local resolved_returntypes = {}
for i,returntypes in pairs(self.possible_returntypes) do
if #self.possible_returntypes == 0 then return end
local resolved_returntypes = self.resolved_returntypes
resolved_returntypes.has_unknown = self.has_unknown_return
for i,returntypes in ipairs(self.possible_returntypes) do
resolved_returntypes[i] = types.find_common_type(returntypes) or typedefs.primtypes.any
end
resolved_returntypes.has_unknown = self.has_unknown_return
self.resolved_returntypes = resolved_returntypes
return resolved_returntypes
end

function Scope:resolve()
Expand Down
Loading

0 comments on commit eeb9fd6

Please sign in to comment.