1#!/usr/bin/env lua
2
3--[[
4Takes two mandatory and one optiona argument
5* a file containing a dump of the old API
6* a file containing a dump of the new API
7* optionally "true" or "nodes"
8  true : show differences
9  nodes : also dump the content of added nodes
10
11This script will compare the two API and report any differences it sees
12optionally printing details of the difference
13
14Use this to quickly assess what has changed between two versions of DT
15
16Note that dumps are obtained via lua_doc/dumper.lua
17]]
18
19table = require "table"
20args = {...}
21oldapi = require(string.sub(args[1],1,-5))
22newapi = require(string.sub(args[2],1,-5))
23verbose = args[3]
24
25
26local function sorted_pairs (t, f)
27  local a = {}
28  for n in pairs(t) do table.insert(a, n) end
29  table.sort(a, f)
30  local i = 0      -- iterator variable
31  local iter = function ()   -- iterator function
32    i = i + 1
33    if a[i] == nil then return nil
34    else return a[i], t[a[i]]
35    end
36  end
37  return iter
38end
39
40local function length(ptree)
41  if #ptree == 0 then return 0 end
42  return length(ptree[2])+1
43end
44
45
46function find_parent(node,ptree)
47  if type(node) ~= "table" then return end
48
49  if length(ptree) == 0 then
50    node.__parent = {}
51    for k,v in sorted_pairs(node) do
52      find_parent(v,{k,node.__parent})
53    end
54    return
55  end
56
57  if ptree[1] == "__parent" then return end
58
59  if not node.__parent  or length(node.__parent) > length(ptree) then
60    node.__parent = ptree
61    for k,v in sorted_pairs(node) do
62      find_parent(v,{k,ptree})
63    end
64  end
65end
66
67
68
69local function build_name(ptree)
70  if #ptree == 0 then return "" end
71  if #ptree[2] == 0 then return ptree[1] end
72  return build_name(ptree[2]).."."..ptree[1]
73end
74
75
76local function specific_filter(old,new,name,ptree)
77  if ptree[1] == "read" and ptree[2][1] == "__attributes" then
78    return true
79  end
80  if ptree[1] == "write" and ptree[2][1] == "__attributes" then
81    return true
82  end
83  if ptree[1] == "is_attribute" and ptree[2][1] == "__attributes" then
84    return true
85  end
86  if ptree[1] == "is_self" and ptree[2][1] == "__attributes" then
87    return true
88  end
89  if ptree[1] == "parent" and ptree[2][1] == "__attributes" then
90    return true
91  end
92  return false
93end
94
95
96local known = {}
97function dump_node(node,prefix)
98  if type(node) ~= "table" then
99    return tostring(node)
100  elseif known[node] then
101    return build_name(node.__parent)
102  elseif verbose== "nodes" then
103    known[node] = true
104    local result="{\n"
105    for k,v in sorted_pairs(node) do
106      if k ~= "__parent" and k ~="__done" then
107        result = result..prefix.."   "..k.." = "..dump_node(v,prefix.."      ").."\n"
108      end
109    end
110    result = result ..prefix.."}"
111    return result
112  else
113    return build_name(node.__parent)
114  end
115end
116
117function trace_change(name,change_type,old,new)
118  print(name.." : "..change_type)
119  if verbose then
120    print("**************** old ************************")
121    known = {}
122    print(dump_node(old,""))
123    print("**************** new ************************")
124    known = {}
125    print(dump_node(new,""))
126    print("*********************************************")
127  end
128end
129
130local function compare_any(old,new,ptree)
131  local name
132  if ptree[1] == "__done" then return end
133  if ptree[1] == "__parent" then return end
134  if type(old) == "table" and old.__done then return end
135  if type(old) == "table" then
136    name = build_name(old.__parent)
137    old.__done = true;
138  else
139    name = build_name(ptree)
140  end
141  if specific_filter(old,new,name,ptree) then
142    return
143  end
144  if old == nil then
145    trace_change(name,"Node added",old,new)
146    return
147  end
148  if new == nil then
149    trace_change(name,"Node deleted",old,new)
150    return
151  end
152  if type(old) ~= type(new) then
153    trace_change(name,"Type changed from "..type(old).." to "..type(new),old,new)
154    return
155  end
156  if type(old) ~= "table" then
157    if old ~= new then
158      trace_change(name,"("..type(old)..") value changed",old,new)
159    end
160    return
161  else
162    for k,v in sorted_pairs(old) do
163      compare_any(v,new[k],{k,old.__parent})
164    end
165    for k,v in sorted_pairs(new) do
166      if not old[k] then
167        compare_any(old[k],v,{k,new.__parent})
168      end
169    end
170  end
171
172end
173
174
175
176find_parent(oldapi,{})
177find_parent(newapi,{})
178
179compare_any(oldapi,newapi,{})
180
181-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
182