blob: f14287edbda72b44c8e1b02cdf864350938c6b26 [file] [log] [blame]
Jack Jansen581fa782000-12-14 22:29:58 +00001"""An attempt at an unweave script.
2Jack Jansen, jack@oratrix.com, 13-Dec-00
3"""
4import re
5import sys
6import macfs
7import os
Jack Jansen61c64c92000-12-14 23:35:01 +00008import macostools
Jack Jansen581fa782000-12-14 22:29:58 +00009
10BEGINDEFINITION=re.compile("^<<(?P<name>.*)>>=\s*")
Jack Jansen61c64c92000-12-14 23:35:01 +000011USEDEFINITION=re.compile("^(?P<pre>.*)<<(?P<name>.*)>>(?P<post>[^=].*)")
Jack Jansen581fa782000-12-14 22:29:58 +000012ENDDEFINITION=re.compile("^@")
Jack Jansenf3eaf012001-02-17 22:00:18 +000013GREMLINS=re.compile("[\xa0\xca]")
Jack Jansen581fa782000-12-14 22:29:58 +000014
Jack Jansen0eea1662001-02-13 22:58:56 +000015DEFAULT_CONFIG="""
Jack Jansenf3eaf012001-02-17 22:00:18 +000016filepatterns = [
Jack Jansen0eea1662001-02-13 22:58:56 +000017 ("^.*\.cp$", ":unweave-src"),
18 ("^.*\.h$", ":unweave-include"),
19]
20genlinedirectives = 0
21gencomments = 1
22"""
23
Jack Jansen581fa782000-12-14 22:29:58 +000024class Processor:
Jack Jansen0eea1662001-02-13 22:58:56 +000025 def __init__(self, filename, config={}):
Jack Jansen581fa782000-12-14 22:29:58 +000026 self.items = {}
27 self.filename = filename
28 self.fp = open(filename)
29 self.lineno = 0
30 self.resolving = {}
31 self.resolved = {}
32 self.pushback = None
Jack Jansen0eea1662001-02-13 22:58:56 +000033 # Options
34 if config.has_key("genlinedirectives"):
35 self.genlinedirectives = config["genlinedirectives"]
36 else:
37 self.genlinedirectives = 1
38 if config.has_key("gencomments"):
39 self.gencomments = config["gencomments"]
40 else:
41 self.gencomments = 0
Jack Jansenf3eaf012001-02-17 22:00:18 +000042 if config.has_key("filepatterns"):
43 self.filepatterns = config["filepatterns"]
44 else:
45 self.filepatterns = []
46 self.filepattern_relist = []
47 for pat, dummy in self.filepatterns:
48 self.filepattern_relist.append(re.compile(pat))
Jack Jansen581fa782000-12-14 22:29:58 +000049
50 def _readline(self):
51 """Read a line. Allow for pushback"""
52 if self.pushback:
53 rv = self.pushback
54 self.pushback = None
55 return rv
56 self.lineno = self.lineno + 1
Jack Jansen61c64c92000-12-14 23:35:01 +000057 return self.lineno, self.fp.readline()
Jack Jansen581fa782000-12-14 22:29:58 +000058
Jack Jansen61c64c92000-12-14 23:35:01 +000059 def _linedirective(self, lineno):
60 """Return a #line cpp directive for this file position"""
61 return '#line %d "%s"\n'%(lineno-3, os.path.split(self.filename)[1])
Jack Jansen581fa782000-12-14 22:29:58 +000062
63 def _readitem(self):
64 """Read the definition of an item. Insert #line where needed. """
Jack Jansen61c64c92000-12-14 23:35:01 +000065 rv = []
Jack Jansen581fa782000-12-14 22:29:58 +000066 while 1:
Jack Jansen61c64c92000-12-14 23:35:01 +000067 lineno, line = self._readline()
Jack Jansen581fa782000-12-14 22:29:58 +000068 if not line:
69 break
70 if ENDDEFINITION.search(line):
71 break
72 if BEGINDEFINITION.match(line):
Jack Jansen61c64c92000-12-14 23:35:01 +000073 self.pushback = lineno, line
Jack Jansen581fa782000-12-14 22:29:58 +000074 break
75 mo = USEDEFINITION.match(line)
76 if mo:
77 pre = mo.group('pre')
78 if pre:
Jack Jansen0eea1662001-02-13 22:58:56 +000079## rv.append((lineno, pre+'\n'))
80 rv.append((lineno, pre))
Jack Jansen61c64c92000-12-14 23:35:01 +000081 rv.append((lineno, line))
Jack Jansen581fa782000-12-14 22:29:58 +000082 if mo:
Jack Jansen61c64c92000-12-14 23:35:01 +000083 post = mo.group('post')
84 if post and post != '\n':
85 rv.append((lineno, post))
Jack Jansen581fa782000-12-14 22:29:58 +000086 return rv
87
88 def _define(self, name, value):
89 """Define an item, or append to an existing definition"""
90 if self.items.has_key(name):
91 self.items[name] = self.items[name] + value
92 else:
93 self.items[name] = value
94
95 def read(self):
96 """Read the source file and store all definitions"""
Jack Jansen0eea1662001-02-13 22:58:56 +000097 savedcomment = []
Jack Jansen581fa782000-12-14 22:29:58 +000098 while 1:
Jack Jansen61c64c92000-12-14 23:35:01 +000099 lineno, line = self._readline()
Jack Jansen581fa782000-12-14 22:29:58 +0000100 if not line: break
101 mo = BEGINDEFINITION.search(line)
102 if mo:
103 name = mo.group('name')
104 value = self._readitem()
Jack Jansen0eea1662001-02-13 22:58:56 +0000105 if self.gencomments:
106 defline = [(lineno, '// <%s>=\n'%name)]
107 if savedcomment:
108 savedcomment = savedcomment + [(lineno, '//\n')] + defline
109 else:
110 savedcomment = defline
Jack Jansenf3eaf012001-02-17 22:00:18 +0000111 savedcomment = self._processcomment(savedcomment)
Jack Jansen0eea1662001-02-13 22:58:56 +0000112 value = savedcomment + value
113 savedcomment = []
Jack Jansenf3eaf012001-02-17 22:00:18 +0000114 isfilepattern = 0
115 for rexp in self.filepattern_relist:
116 if rexp.search(name):
117 isfilepattern = 1
118 break
119 if 0 and not isfilepattern:
120 value = self._addspace(value)
Jack Jansen581fa782000-12-14 22:29:58 +0000121 self._define(name, value)
122 else:
Jack Jansen0eea1662001-02-13 22:58:56 +0000123 if self.gencomments:
124 # It seems initial blank lines are ignored:-(
125 if savedcomment or line.strip():
126 savedcomment.append((lineno, '// '+line))
127
Jack Jansenf3eaf012001-02-17 22:00:18 +0000128 def _processcomment(self, comment):
Jack Jansen0eea1662001-02-13 22:58:56 +0000129 # This routine mimicks some artefact of Matthias' code.
130 rv = []
131 for lineno, line in comment:
132 line = line[:-1]
Jack Jansenf3eaf012001-02-17 22:00:18 +0000133 line = GREMLINS.subn(' ', line)[0]
Jack Jansen0eea1662001-02-13 22:58:56 +0000134 if len(line) < 75:
135 line = line + (75-len(line))*' '
136 line = line + '\n'
137 rv.append((lineno, line))
138 return rv
139
Jack Jansenf3eaf012001-02-17 22:00:18 +0000140 def _addspace(self, value, howmany):
141 # Yet another routine to mimick yet another artefact
142 rv = value[0:1]
143 for lineno, line in value[1:]:
144 rv.append((lineno, (' '*howmany)+line))
145 return rv
146
Jack Jansen581fa782000-12-14 22:29:58 +0000147 def resolve(self):
148 """Resolve all references"""
149 for name in self.items.keys():
150 self._resolve_one(name)
151
152 def _resolve_one(self, name):
153 """Resolve references in one definition, recursively"""
154 # First check for unknown macros and recursive calls
155 if not self.items.has_key(name):
156 print "Undefined macro:", name
157 return ['<<%s>>'%name]
158 if self.resolving.has_key(name):
159 print "Recursive macro:", name
160 return ['<<%s>>'%name]
161 # Then check that we haven't handled this one before
162 if self.resolved.has_key(name):
163 return self.items[name]
164 # No rest for the wicked: we have work to do.
165 self.resolving[name] = 1
166 result = []
Jack Jansenf3eaf012001-02-17 22:00:18 +0000167 lastlineincomplete = 0
Jack Jansen61c64c92000-12-14 23:35:01 +0000168 for lineno, line in self.items[name]:
Jack Jansen581fa782000-12-14 22:29:58 +0000169 mo = USEDEFINITION.search(line)
170 if mo:
171 # We replace the complete line. Is this correct?
172 macro = mo.group('name')
173 replacement = self._resolve_one(macro)
Jack Jansenf3eaf012001-02-17 22:00:18 +0000174 if lastlineincomplete:
175 replacement = self._addspace(replacement, lastlineincomplete)
Jack Jansen581fa782000-12-14 22:29:58 +0000176 result = result + replacement
177 else:
Jack Jansen61c64c92000-12-14 23:35:01 +0000178 result.append((lineno, line))
Jack Jansenf3eaf012001-02-17 22:00:18 +0000179 if line[-1] == '\n':
180 lastlineincomplete = 0
181 else:
182 lastlineincomplete = len(line)
Jack Jansen581fa782000-12-14 22:29:58 +0000183 self.items[name] = result
184 self.resolved[name] = 1
185 del self.resolving[name]
186 return result
187
188 def save(self, dir, pattern):
189 """Save macros that match pattern to folder dir"""
190 # Compile the pattern, if needed
191 if type(pattern) == type(''):
192 pattern = re.compile(pattern)
193 # If the directory is relative it is relative to the sourcefile
194 if not os.path.isabs(dir):
195 sourcedir = os.path.split(self.filename)[0]
196 dir = os.path.join(sourcedir, dir)
197 for name in self.items.keys():
198 if pattern.search(name):
199 pathname = os.path.join(dir, name)
Jack Jansen61c64c92000-12-14 23:35:01 +0000200 data = self._addlinedirectives(self.items[name])
Jack Jansen581fa782000-12-14 22:29:58 +0000201 self._dosave(pathname, data)
202
Jack Jansen61c64c92000-12-14 23:35:01 +0000203 def _addlinedirectives(self, data):
204 curlineno = -100
205 rv = []
206 for lineno, line in data:
207 curlineno = curlineno + 1
Jack Jansen0eea1662001-02-13 22:58:56 +0000208 if self.genlinedirectives and line and line != '\n' and lineno != curlineno:
Jack Jansen61c64c92000-12-14 23:35:01 +0000209 rv.append(self._linedirective(lineno))
210 curlineno = lineno
211 rv.append(line)
212 return rv
Jack Jansen581fa782000-12-14 22:29:58 +0000213
214 def _dosave(self, pathname, data):
215 """Save data to pathname, unless it is identical to what is there"""
216 if os.path.exists(pathname):
217 olddata = open(pathname).readlines()
218 if olddata == data:
219 return
Jack Jansen61c64c92000-12-14 23:35:01 +0000220 macostools.mkdirs(os.path.split(pathname)[0])
Jack Jansen581fa782000-12-14 22:29:58 +0000221 fp = open(pathname, "w").writelines(data)
222
Jack Jansen61c64c92000-12-14 23:35:01 +0000223def process(file, config):
Jack Jansen0eea1662001-02-13 22:58:56 +0000224 pr = Processor(file, config)
Jack Jansen581fa782000-12-14 22:29:58 +0000225 pr.read()
226 pr.resolve()
Jack Jansenf3eaf012001-02-17 22:00:18 +0000227 for pattern, folder in config['filepatterns']:
Jack Jansen61c64c92000-12-14 23:35:01 +0000228 pr.save(folder, pattern)
229
230def readconfig():
231 """Read a configuration file, if it doesn't exist create it."""
232 configname = sys.argv[0] + '.config'
233 if not os.path.exists(configname):
Jack Jansen0eea1662001-02-13 22:58:56 +0000234 confstr = DEFAULT_CONFIG
Jack Jansen61c64c92000-12-14 23:35:01 +0000235 open(configname, "w").write(confstr)
236 print "Created config file", configname
237## print "Please check and adapt (if needed)"
238## sys.exit(0)
239 namespace = {}
240 execfile(configname, namespace)
Jack Jansen0eea1662001-02-13 22:58:56 +0000241 return namespace
Jack Jansen581fa782000-12-14 22:29:58 +0000242
243def main():
Jack Jansen61c64c92000-12-14 23:35:01 +0000244 config = readconfig()
Jack Jansen581fa782000-12-14 22:29:58 +0000245 if len(sys.argv) > 1:
Jack Jansen61c64c92000-12-14 23:35:01 +0000246 for file in sys.argv[1:]:
247 if file[-3:] == '.nw':
248 print "Processing", file
249 process(file, config)
250 else:
251 print "Skipping", file
Jack Jansen581fa782000-12-14 22:29:58 +0000252 else:
253 fss, ok = macfs.PromptGetFile("Select .nw source file", "TEXT")
254 if not ok:
255 sys.exit(0)
Jack Jansen0eea1662001-02-13 22:58:56 +0000256 process(fss.as_pathname(), config)
Jack Jansen581fa782000-12-14 22:29:58 +0000257
258if __name__ == "__main__":
259 main()