Module:Sandbox/AbstractWikipedia/UnifiableFeatures
This module is part of user:AGutman-WMF's work to create a prototype implementation of Abstract Wikipedia's template language in Scribunto. The main module is Module:Sandbox/AbstractWikipedia.
This module specifically implements the data-structure needed to support unifiable features, which is similar to a disjoint-set data structure. These unifiable features are then used by the Module:Sandbox/AbstractWikipedia/Lexemes module.
local p = {}
-- All features are stored in an array of features, similar to a
-- disjoint-set data structure (union-find data structure)
-- The features themselves are strings, but to support unification features
-- can point to other features using numeric indexes to other array cells.
local features = {}
-- Add a new feature to the array, and return its index
function p.createNewFeature ( feature )
if type(feature) ~= 'string' then
error("Features must be strings. Got: "..tostring(feature))
end
table.insert(features, feature)
return #features
end
-- Helper function to find the index that actually points to a feature
-- As a side effect, updates all indexes on the path to point to the
-- representative index (containing the actual feature)
local function findRepresentative ( index )
if type(features[index]) == 'number' then
features[index] = findRepresentative(features[index])
return features[index]
else
return index
end
end
-- retrieves the feature at a given index
function p.getFeature ( index )
return features[findRepresentative(index)]
end
-- Returns true if feature1 subsumes (is more general than) feature2.
-- Ideally there should be a type hierarchy of features, but for the prototype
-- only empty features are considered more general than any other feature
-- For more information about subsumption and unification, see
-- http://www.ale.cs.toronto.edu/docs/man/ale_trale_man/ale_trale_man-node21.html
local function subsumes ( feature1, feature2 )
if (feature1 == '') or (feature1 == feature2) then
return true
else
return false
end
end
-- same as above, but operates on indexes
function p.subsumes (index1, index2)
index1 = findRepresentative(index1)
index2 = findRepresentative(index2)
return subsumes(features[index1], features[index2])
end
-- Returns whether two features are unifiable
-- In this prototype unification is only possible through subsumption
function p.unifiable (index1, index2)
return (p.subsumes(index1, index2) or p.subsumes(index2, index1))
end
-- Unifies two features, returning nil if they are incompatible
-- Ideally there should be a type hierarchy of features, but for the prototype
-- we allow only unification of identical features or unification with an empty
-- feature
local function unifyFeatures ( feature1, feature2 )
mw.log("Unify '"..tostring(feature1).."' with '"..tostring(feature2).."'")
if subsumes(feature2, feature1) then
return feature1
elseif subsumes(feature1, feature2) then
return feature2
else
-- In theory, unification could result in a third type, but this is not
-- possible in this prototype.
return nil
end
end
-- Unify the features in index1 and index2 and return result
-- This is achieved by pointing one index to the other and possibly updating
-- the value
function p.unify( index1, index2 )
index1 = findRepresentative(index1)
index2 = findRepresentative(index2)
if (index1 == index2) then
return features[index1]
end
local feature1 = features[index1]
local feature2 = features[index2]
local result = unifyFeatures(feature1, feature2)
if result == nil then
return nil
end
if result == feature1 then
features[index2] = index1
elseif result == feature2 then
features[index1] = index2
else -- result is different than both: update one cell
features[index2] = result
features[index1] = index2
end
return result
end
-- Unify the feature at index with a new feature, updating its value
function p.unifyWithFeature ( index, feature )
index = findRepresentative(index)
feature_i = features[index]
local result = unifyFeatures(feature, feature_i)
if result ~= nil then
features[index] = result
end
return result
end
-- For debugging purposes
function p.listFeatures ()
mw.log("Size: "..#features)
for index, feature in ipairs(features) do
if type(feature) == 'string' then
mw.log(index.." == '"..feature.."'")
else
mw.log(index.." -> "..feature)
end
end
end
return p