Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Handle multifunction candidates ambiguity. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
ac195a58b81a4d4deae2cc0af7588a03 |
| User & Date: | imagic 2021-11-14 18:01:47.975 |
| Original User & Date: | imagic 2021-11-14 18:01:48.000 |
Context
|
2021-11-14
| ||
| 18:01 | Add micro benchmarks and improve performances. ... (check-in: 304a118a3a user: imagic tags: trunk) | |
| 18:01 | Handle multifunction candidates ambiguity. ... (check-in: ac195a58b8 user: imagic tags: trunk) | |
| 18:01 | Minor doc improvements. ... (check-in: 90e4f5b8b9 user: imagic tags: trunk) | |
Changes
Changes to README.adoc.
| ︙ | ︙ | |||
44 45 46 47 48 49 50 | A non-primitive type can inherit from other types, building a stack of types. This stack defines the order in which the types are evaluated, from the terminal type to the least specific inherited types. === Multifunction A multifunction is a function that can have multiple definitions, each associated to a specific signature. When called, it resolves the call by selecting a matching definition from the call signature. | < < | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | A non-primitive type can inherit from other types, building a stack of types. This stack defines the order in which the types are evaluated, from the terminal type to the least specific inherited types. === Multifunction A multifunction is a function that can have multiple definitions, each associated to a specific signature. When called, it resolves the call by selecting a matching definition from the call signature. ==== Metaprogramming We can use Lua as its own metaprogramming language: we can generate Lua code from Lua. A multifunction can have generators that are called when no definitions could be found, allowing to generate definitions for specific call signatures on-the-fly. == API |
| ︙ | ︙ |
Changes to examples/test_error.lua.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 31 |
mf(5,5)
errcheck("unresolved call signature", mf)
errcheck("unresolved call signature", mf, 5)
errcheck("unresolved call signature", mf, 5, 5, 5)
errcheck("unresolved call signature", mf, 5, "5")
mf:define(nil, "number", "number")
errcheck("unresolved call signature", mf, 5, 5)
end
| > > > > > > > < | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
mf(5,5)
errcheck("unresolved call signature", mf)
errcheck("unresolved call signature", mf, 5)
errcheck("unresolved call signature", mf, 5, 5, 5)
errcheck("unresolved call signature", mf, 5, "5")
mf:define(nil, "number", "number")
errcheck("unresolved call signature", mf, 5, 5)
-- test ambiguity
local A = xtype.create("A")
local B = xtype.create("B")
local C = xtype.create("C", A, B)
mf:define(function() end, A, B)
mf:define(function() end, B, A)
errcheck("ambiguous call signature", mf.resolve, mf, C, C)
end
|
Changes to src/xtype.lua.
| ︙ | ︙ | |||
259 260 261 262 263 264 265 | local node = self.hsign for _, t in ipairs(sign) do node = node[t] end return node[1] end multifunction.hashSign = mf_hash_sign -- Find candidate. | | | > > > | | | > > > > > > > > > | | | | | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 |
local node = self.hsign
for _, t in ipairs(sign) do node = node[t] end
return node[1]
end
multifunction.hashSign = mf_hash_sign
-- Find candidate.
-- return candidate or nothing if none found
--- candidate: {.sign, .dist, .def}
---- sign: call signature
---- dist: distance from call signature
---- def: candidate definition
local function mf_find_candidate(self, sign)
-- find candidates
local candidates = {}
for hash, def in pairs(self.definitions) do
if #sign == #def.sign then -- same number of parameters
local dist = sign_dist(sign, def.sign)
if dist then table.insert(candidates, {sign = sign, def = def, dist = dist}) end
end
end
-- sort candidates
table.sort(candidates, function(a, b) return a.dist < b.dist end)
-- check for ambiguity
if candidates[1] and candidates[2] and candidates[1].dist == candidates[2].dist then
-- generate ambiguity error
local candidate_signs = {}
for _, candidate in ipairs(candidates) do
if candidate.dist ~= candidates[1].dist then break end
table.insert(candidate_signs, "\t"..format_sign(candidate.def.sign))
end
error("ambiguous call signature "..format_sign(sign).."\ncandidates:\n"..table.concat(candidate_signs, "\n"))
end
return candidates[1]
end
local function mf_resolve_sign(self, sign)
local hash = mf_hash_sign(self, sign)
local candidate = self.candidates[hash]
if not candidate then
candidate = mf_find_candidate(self, sign)
if not candidate then -- re-try
-- call generators
for generator in pairs(self.generators) do generator(self, table_unpack(sign)) end
candidate = mf_find_candidate(self, sign)
end
self.candidates[hash] = candidate
end
return candidate and candidate.def.f
end
-- Unoptimized multifunction call.
local function mf_call(self, ...)
local sign = table_pack(...)
for i=1, sign.n do sign[i] = xtype_get(sign[i]) end
local f = mf_resolve_sign(self, sign)
if not f then error("unresolved call signature "..format_sign(sign)) end
return f(...)
end
local mfcalls = {}
-- Generate optimized multifunction call.
-- n: maximum number of parameters
local function gen_opt_mfcall(n)
-- cache check
local mfcall = mfcalls[n]
if mfcall then return mfcall end
-- generate code
local main = [=[
local select = select
local xtype_get, mf_call = ...
return function(self, ...)
-- optimized path
local n = select("#", ...)
local hash
--
$hash_code
--
local candidate = self.candidates[hash]
if candidate then return candidate.def.f(...) end
-- fallback to unoptimized path
return mf_call(self, ...)
end
]=]
-- generate table/vararg-less hashing for each arguments count
local hcode = "if n == 0 then hash = self.hsign[1]\n"
for i=1,n do
hcode = hcode.."elseif n == "..i.." then\n"
hcode = hcode.."local "..xtype.tpllist("a$", 1, i, ", ").." = ...\n"
hcode = hcode.."hash = self.hsign"..xtype.tpllist("[xtype_get(a$)]", 1, i, "").."[1]\n"
end
hcode = hcode.."end\n"
|
| ︙ | ︙ | |||
362 363 364 365 366 367 368 |
end
-- definition
local def = self.definitions[hash]
if def then def.f = f -- update definition
else -- new definition
def = {f = f, sign = sign}
self.definitions[hash] = def
| | | | > | | > > > | | 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
end
-- definition
local def = self.definitions[hash]
if def then def.f = f -- update definition
else -- new definition
def = {f = f, sign = sign}
self.definitions[hash] = def
-- update candidates (better or equivalent match)
for chash, candidate in pairs(self.candidates) do
local dist = sign_dist(candidate.sign, sign)
if dist then
if dist < candidate.dist then -- update if better candidate
candidate.def, candidate.dist = def, dist
elseif dist == candidate.dist then -- remove candidate on ambiguity
self.candidates[chash] = nil
end
end
end
end
else -- undefine
local def = self.definitions[hash]
if def then
self.definitions[hash] = nil
-- remove candidates (removed definition)
for chash, candidate in pairs(self.candidates) do
if candidate.def == def then self.candidates[chash] = nil end
end
end
end
end
-- Get the resolved function for a specific signature.
-- ...: call signature, list of types
|
| ︙ | ︙ | |||
416 417 418 419 420 421 422 |
__index = multifunction,
__call = default_call
}
return setmetatable({
hsign = new_hsign(),
definitions = {}, -- map of sign hash => {.f, .sign}
generators = {}, -- set of generator functions
| | | 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 |
__index = multifunction,
__call = default_call
}
return setmetatable({
hsign = new_hsign(),
definitions = {}, -- map of sign hash => {.f, .sign}
generators = {}, -- set of generator functions
candidates = {}, -- cached candidates, map of call sign hash => {.sign, .dist, .def}
max_calln = 0, -- maximum call parameters
call = default_call
}, multifunction_mt)
end
-- Code generation tools.
|
| ︙ | ︙ |