1##
2## This script recursively goes through the matlab directory and its
3## subdirectories in the unstable branch, comparing the calling structures of the
4## functions to those in another branch/commit, in this case the 4.2.5 branch
5## The output is compatible with a Dynare Wiki page
6##
7## NB: To err on the side of safety, this script won't clean your workspace so a
8## git command to checkout a different branch will fail if your workspace is
9## dirty.
10##
11
12##
13## Copyright © 2012 Dynare Team
14##
15## This file is part of Dynare.
16##
17## Dynare is free software: you can redistribute it and/or modify
18## it under the terms of the GNU General Public License as published by
19## the Free Software Foundation, either version 3 of the License, or
20## (at your option) any later version.
21##
22## Dynare is distributed in the hope that it will be useful,
23## but WITHOUT ANY WARRANTY; without even the implied warranty of
24## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25## GNU General Public License for more details.
26##
27## You should have received a copy of the GNU General Public License
28## along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
29##
30
31import os
32import string
33import subprocess
34import sys
35
36def runCompare():
37    unstable = getFuncDict()
38    subprocess.call(['git','checkout','4.2'])
39
40    stable = getFuncDict()
41    subprocess.call(['git','checkout','master'])
42
43    us = set(unstable.keys())
44    ss = set(stable.keys())
45
46    deletedFunctions = sorted(ss - us)
47    makeOldFunctionsWikiTable(deletedFunctions, stable)
48
49    newFunctions = sorted(us - ss)
50    makeNewFunctionsWikiTable(newFunctions, unstable)
51
52    outputChanged = set([])
53    inputChanged = set([])
54    commonFunctions = sorted(ss & us)
55    for le in commonFunctions:
56        sOut = set(stable[le]['output'])
57        uOut = set(unstable[le]['output'])
58        sIn = set(stable[le]['input'])
59        uIn = set(unstable[le]['input'])
60
61        if len(uOut - sOut):
62            outputChanged.add(le)
63
64        if len(uIn - sIn):
65            inputChanged.add(le)
66
67    unionChanged = sorted(outputChanged | inputChanged)
68    makeUnionChangedWikiTable(unionChanged, stable, unstable)
69
70
71def makeUnionChangedWikiTable(keys, stable, unstable):
72    write = sys.stdout.write
73    print "= Functions Whose Arguments Have Changed between Dynare 4.2.5 and " \
74        "Dynare 4.3 ="
75    print "|| '''Location''' || '''Old Output''' || " \
76        "'''New Output''' || '''Old Input''' || '''New Input''' ||"
77    for k in keys:
78        write('|| {{{' + unstable[k]['filename'][10:]  + '}}} || ')
79        write(str(stable[k]['output']) + ' || ')
80        write(str(unstable[k]['output']) + ' || ')
81        write(str(stable[k]['input']) + ' || ')
82        write(str(unstable[k]['input']) + ' ||')
83        print
84
85
86def makeNewFunctionsWikiTable(keys, dictionary):
87    print '= New Functions in Dynare 4.3 ='
88    makeWikiTable(keys, dictionary)
89
90
91def makeOldFunctionsWikiTable(keys, dictionary):
92    print '= Functions Removed in Dynare 4.3 ='
93    makeWikiTable(keys, dictionary)
94
95
96def makeWikiTable(keys, dictionary):
97    write = sys.stdout.write
98    print "|| '''Location''' || '''Output''' || '''Input''' ||"
99    for k in keys:
100        write('|| {{{' + dictionary[k]['filename'][10:]  + '}}} || ')
101        write(str(dictionary[k]['output']) + ' || ')
102        write(str(dictionary[k]['input']) + ' ||')
103        print
104
105
106def printDict(dictionary, title):
107    print
108    print '***********************************'
109    print '** ' + title
110    print '***********************************'
111    for key in sorted(dictionary.iterkeys()):
112        b = dictionary[key]
113        print key + ':' + b['filename']
114    print '***********************************'
115    print '***********************************'
116    print
117
118
119def getFuncDict():
120    functions = {}
121
122    for dirname, dirnames, filenames in os.walk('../matlab'):
123        for filename in filenames:
124            filename = string.strip(filename)
125
126            if filename[-2:] != '.m' or filename == 'msstart2.m' or filename == 'msstart_setup.m' or filename == 'qmc_sequence.m':
127                continue
128
129            fullfilename = os.path.join(dirname, filename)
130            f = open(fullfilename, 'r')
131            funcDef = ''
132            inComment = False
133            while True:
134                funcDef += f.read(1)
135                if inComment:
136                    if funcDef[-1:] == '\n':
137                        inComment = False
138                else:
139                    if funcDef[-1:] == '%':
140                        inComment = True
141                    elif funcDef[-1:] == ')':
142                        break
143            funcDef = funcDef.strip()
144            f.close()
145
146            funcDict = {}
147            funcDict['filename'] = fullfilename
148
149            # OUTPUT
150            spliteq = string.rsplit(funcDef, '=')
151            if len(spliteq) == 1:
152                # no output
153                outputs = ['']
154                funcDict['output'] = outputs
155                rhs = string.split(funcDef, ' ', 1).pop(1).strip()
156            else:
157                outputs = string.split(spliteq.pop(0), ' ', 1).pop(1)
158                outputs = [string.strip(outputs, '[] ')]
159                outputs = getputs(outputs)
160                funcDict['output'] = outputs
161                rhs = string.strip(spliteq.pop(0), ' .\n')
162
163            # FUNCTION NAME
164            splitfn = string.split(rhs, '(')
165            fn = splitfn.pop(0)
166
167            # INPUT
168            inputs = [string.strip(le,')') for le in splitfn]
169            inputs = getputs(inputs)
170            funcDict['input'] = inputs
171
172            functions[fn] = funcDict
173
174    return functions
175
176
177def getputs(puts):
178    ''' Can be of the forms
179    a
180    a
181    a, b
182    a, b   % aoeu
183    a, ...
184       b
185    a, ... % aoeu
186       b
187
188    Return a list of (in/out puts)
189    '''
190    splitout = string.split(string.join(puts), '\n')
191
192    for i,le in enumerate(splitout[:]):
193        indx = string.find(le, '%')
194        if indx != -1:
195            splitout[i] = string.strip(le[:indx], '\t .')
196        else:
197            splitout[i] = string.strip(le, '\t .')
198
199    splitcommas = string.split(string.join(splitout), ',')
200    return [string.strip(le) for le in splitcommas]
201
202
203if __name__ == "__main__":
204    runCompare()
205