scansion/stanzacmp.lua

changeset 170
db73c4c317ce
parent 153
f83ea6e5c3d8
equal deleted inserted replaced
169:ecae87f5aaba 170:db73c4c317ce
19 return true; 19 return true;
20 end 20 end
21 error("Unexpected scansion:strict value: "..opt); 21 error("Unexpected scansion:strict value: "..opt);
22 end 22 end
23 23
24 local function stanzas_strict_match(stanza1, stanza2) 24 local function is_wildcard(k, v)
25 if v == "{scansion:any}" then
26 return "attr:"..k;
27 end
28 return (v:match("^{scansion:capture:([^}]+)}$"));
29 end
30
31 -- stanza1 == expected, stanza2 == variable
32 -- captures is an optional table to store captures (captures["foo"] == {scansion:capture:foo})
33 local function stanzas_strict_match(stanza1, stanza2, captures)
25 if stanza1.name ~= stanza2.name or stanza1.attr.xmlns ~= stanza2.attr.xmlns then 34 if stanza1.name ~= stanza2.name or stanza1.attr.xmlns ~= stanza2.attr.xmlns then
26 return false; 35 return false;
27 end 36 end
28 37
29 for k, v in pairs(stanza1.attr) do 38 for k, v in pairs(stanza1.attr) do
30 if not k:match("^scansion:") and v ~= "{scansion:any}" and stanza2.attr[k] ~= v then 39 local wildcard = is_wildcard(k, v);
40 if not k:match("^scansion:") and not wildcard and stanza2.attr[k] ~= v then
31 return false; 41 return false;
42 end
43 if wildcard and captures then
44 captures[wildcard] = stanza2.attr[k];
32 end 45 end
33 end 46 end
34 47
35 for k, v in pairs(stanza2.attr) do 48 for k, v in pairs(stanza2.attr) do
36 if stanza1.attr[k] ~= "{scansion:any}" and stanza1.attr[k] ~= v then 49 local wildcard = is_wildcard(k, stanza1.attr[k]);
50 if not wildcard and stanza1.attr[k] ~= v then
37 return false; 51 return false;
52 end
53 if wildcard and captures then
54 captures[wildcard] = v;
38 end 55 end
39 end 56 end
40 57
41 if #stanza1.tags ~= #stanza2.tags then 58 if #stanza1.tags ~= #stanza2.tags then
42 return false; 59 return false;
54 if type(child) ~= type(child2) then 71 if type(child) ~= type(child2) then
55 return false; 72 return false;
56 end 73 end
57 if type(child) == "table" and child2.name == child.name and child2.attr.xmlns == child.attr.xmlns then 74 if type(child) == "table" and child2.name == child.name and child2.attr.xmlns == child.attr.xmlns then
58 -- Strict deep match 75 -- Strict deep match
59 match = stanzas_strict_match(child, child2); 76 match = stanzas_strict_match(child, child2, captures);
60 elseif type(child) == "string" then -- Text nodes, must be equal, ignoring leading/trailing whitespace 77 elseif type(child) == "string" then -- Text nodes, must be equal, ignoring leading/trailing whitespace
61 match = trim(child) == trim(child2); 78 match = trim(child) == trim(child2);
62 end 79 end
63 if not match then 80 if not match then
64 return false; 81 return false;
69 return true; 86 return true;
70 end 87 end
71 88
72 -- Everything in stanza1 should be present in stanza2 89 -- Everything in stanza1 should be present in stanza2
73 90
74 local function stanzas_match(stanza1, stanza2) 91 local function stanzas_match(stanza1, stanza2, captures)
75 if wants_strict(stanza1, stanza1.attr.xmlns == nil and "no" or "yes") then 92 if wants_strict(stanza1, stanza1.attr.xmlns == nil and "no" or "yes") then
76 return stanzas_strict_match(stanza1, stanza2); 93 return stanzas_strict_match(stanza1, stanza2, captures);
77 end 94 end
78 if stanza1.name ~= stanza2.name or stanza1.attr.xmlns ~= stanza2.attr.xmlns then 95 if stanza1.name ~= stanza2.name or stanza1.attr.xmlns ~= stanza2.attr.xmlns then
79 return false; 96 return false;
80 end 97 end
81 98
82 for k, v in pairs(stanza1.attr) do 99 for k, v in pairs(stanza1.attr) do
83 if not k:match("^scansion:") and v ~= "{scansion:any}" and stanza2.attr[k] ~= v then 100 local wildcard = is_wildcard(k, v);
101 if not k:match("^scansion:") and not wildcard and stanza2.attr[k] ~= v then
84 return false; 102 return false;
103 end
104 if wildcard and captures then
105 captures[wildcard] = stanza2.attr[k];
85 end 106 end
86 end 107 end
87 108
88 local matched_children = {}; 109 local matched_children = {};
89 for _, child in ipairs(stanza1) do 110 for _, child in ipairs(stanza1) do
96 local child2 = stanza2[stanza2_pos]; 117 local child2 = stanza2[stanza2_pos];
97 stanza2_pos = stanza2_pos + 1; 118 stanza2_pos = stanza2_pos + 1;
98 119
99 if type(child2) == type(child) then 120 if type(child2) == type(child) then
100 if type(child) == "table" and child2.name == child.name and child2.attr.xmlns == child.attr.xmlns then 121 if type(child) == "table" and child2.name == child.name and child2.attr.xmlns == child.attr.xmlns then
101 match = stanzas_match(child, child2); 122 match = stanzas_match(child, child2, captures);
102 elseif type(child) == "string" then -- Text nodes, must be equal, ignoring leading/trailing whitespace 123 elseif type(child) == "string" then -- Text nodes, must be equal, ignoring leading/trailing whitespace
103 match = trim(child) == trim(child2); 124 match = trim(child) == trim(child2);
104 end 125 end
105 if match then 126 if match then
106 break; 127 break;

mercurial