scansion/stanzacmp.lua

changeset 170
db73c4c317ce
parent 153
f83ea6e5c3d8
--- a/scansion/stanzacmp.lua	Sat May 08 13:21:56 2021 +0200
+++ b/scansion/stanzacmp.lua	Thu Mar 23 11:43:25 2023 +0000
@@ -21,21 +21,38 @@
 	error("Unexpected scansion:strict value: "..opt);
 end
 
-local function stanzas_strict_match(stanza1, stanza2)
+local function is_wildcard(k, v)
+	if v == "{scansion:any}" then
+		return "attr:"..k;
+	end
+	return (v:match("^{scansion:capture:([^}]+)}$"));
+end
+
+-- stanza1 == expected, stanza2 == variable
+-- captures is an optional table to store captures (captures["foo"] == {scansion:capture:foo})
+local function stanzas_strict_match(stanza1, stanza2, captures)
 	if stanza1.name ~= stanza2.name or stanza1.attr.xmlns ~= stanza2.attr.xmlns then
 		return false;
 	end
 	
 	for k, v in pairs(stanza1.attr) do
-		if not k:match("^scansion:") and v ~= "{scansion:any}" and stanza2.attr[k] ~= v then
+		local wildcard = is_wildcard(k, v);
+		if not k:match("^scansion:") and not wildcard and stanza2.attr[k] ~= v then
 			return false;
 		end
+		if wildcard and captures then
+			captures[wildcard] = stanza2.attr[k];
+		end
 	end
 	
 	for k, v in pairs(stanza2.attr) do
-		if stanza1.attr[k] ~= "{scansion:any}" and stanza1.attr[k] ~= v then
+		local wildcard = is_wildcard(k, stanza1.attr[k]);
+		if not wildcard and stanza1.attr[k] ~= v then
 			return false;
 		end
+		if wildcard and captures then
+			captures[wildcard] = v;
+		end
 	end
 	
 	if #stanza1.tags ~= #stanza2.tags then
@@ -56,7 +73,7 @@
 			end
 			if type(child) == "table" and child2.name == child.name and child2.attr.xmlns == child.attr.xmlns then
 				-- Strict deep match
-				match = stanzas_strict_match(child, child2);
+				match = stanzas_strict_match(child, child2, captures);
 			elseif type(child) == "string" then -- Text nodes, must be equal, ignoring leading/trailing whitespace
 				match = trim(child) == trim(child2);
 			end
@@ -71,18 +88,22 @@
 
 -- Everything in stanza1 should be present in stanza2
 
-local function stanzas_match(stanza1, stanza2)
+local function stanzas_match(stanza1, stanza2, captures)
 	if wants_strict(stanza1, stanza1.attr.xmlns == nil and "no" or "yes") then
-		return stanzas_strict_match(stanza1, stanza2);
+		return stanzas_strict_match(stanza1, stanza2, captures);
 	end
 	if stanza1.name ~= stanza2.name or stanza1.attr.xmlns ~= stanza2.attr.xmlns then
 		return false;
 	end
 	
 	for k, v in pairs(stanza1.attr) do
-		if not k:match("^scansion:") and v ~= "{scansion:any}" and stanza2.attr[k] ~= v then
+		local wildcard = is_wildcard(k, v);
+		if not k:match("^scansion:") and not wildcard and stanza2.attr[k] ~= v then
 			return false;
 		end
+		if wildcard and captures then
+			captures[wildcard] = stanza2.attr[k];
+		end
 	end
 	
 	local matched_children = {};
@@ -98,7 +119,7 @@
 
 					if type(child2) == type(child) then
 						if type(child) == "table" and child2.name == child.name and child2.attr.xmlns == child.attr.xmlns then
-							match = stanzas_match(child, child2);
+							match = stanzas_match(child, child2, captures);
 						elseif type(child) == "string" then -- Text nodes, must be equal, ignoring leading/trailing whitespace
 							match = trim(child) == trim(child2);
 						end

mercurial