blob: 8a5e80fcb7f6a331e6dff6c9560af1a80162f758 [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
8
9BEGINDEFINITION=re.compile("^<<(?P<name>.*)>>=\s*")
10USEDEFINITION=re.compile("^(?P<pre>.*)<<(?P<name>.*)>>[^=]")
11ENDDEFINITION=re.compile("^@")
12
13class Processor:
14 def __init__(self, filename):
15 self.items = {}
16 self.filename = filename
17 self.fp = open(filename)
18 self.lineno = 0
19 self.resolving = {}
20 self.resolved = {}
21 self.pushback = None
22
23 def _readline(self):
24 """Read a line. Allow for pushback"""
25 if self.pushback:
26 rv = self.pushback
27 self.pushback = None
28 return rv
29 self.lineno = self.lineno + 1
30 return self.fp.readline()
31
32 def _linedirective(self):
33 """Return a #line cpp directive for the current input file position"""
34 return '#line %d "%s"\n'%(self.lineno-2, os.path.split(self.filename)[1])
35
36 def _readitem(self):
37 """Read the definition of an item. Insert #line where needed. """
38 rv = [self._linedirective()]
39 while 1:
40 line = self._readline()
41 if not line:
42 break
43 if ENDDEFINITION.search(line):
44 break
45 if BEGINDEFINITION.match(line):
46 self.pushback = line
47 break
48 mo = USEDEFINITION.match(line)
49 if mo:
50 pre = mo.group('pre')
51 if pre:
52 rv.append(pre+'\n')
53 rv.append(line)
54 # For simplicity we add #line directives now, if
55 # needed.
56 if mo:
57 rv.append(self._linedirective())
58 return rv
59
60 def _define(self, name, value):
61 """Define an item, or append to an existing definition"""
62 if self.items.has_key(name):
63 self.items[name] = self.items[name] + value
64 else:
65 self.items[name] = value
66
67 def read(self):
68 """Read the source file and store all definitions"""
69 while 1:
70 line = self._readline()
71 if not line: break
72 mo = BEGINDEFINITION.search(line)
73 if mo:
74 name = mo.group('name')
75 value = self._readitem()
76 self._define(name, value)
77 else:
78 pass # We could output the TeX code but we don't bother.
79
80 def resolve(self):
81 """Resolve all references"""
82 for name in self.items.keys():
83 self._resolve_one(name)
84
85 def _resolve_one(self, name):
86 """Resolve references in one definition, recursively"""
87 # First check for unknown macros and recursive calls
88 if not self.items.has_key(name):
89 print "Undefined macro:", name
90 return ['<<%s>>'%name]
91 if self.resolving.has_key(name):
92 print "Recursive macro:", name
93 return ['<<%s>>'%name]
94 # Then check that we haven't handled this one before
95 if self.resolved.has_key(name):
96 return self.items[name]
97 # No rest for the wicked: we have work to do.
98 self.resolving[name] = 1
99 result = []
100 for line in self.items[name]:
101 mo = USEDEFINITION.search(line)
102 if mo:
103 # We replace the complete line. Is this correct?
104 macro = mo.group('name')
105 replacement = self._resolve_one(macro)
106 result = result + replacement
107 else:
108 result.append(line)
109 self.items[name] = result
110 self.resolved[name] = 1
111 del self.resolving[name]
112 return result
113
114 def save(self, dir, pattern):
115 """Save macros that match pattern to folder dir"""
116 # Compile the pattern, if needed
117 if type(pattern) == type(''):
118 pattern = re.compile(pattern)
119 # If the directory is relative it is relative to the sourcefile
120 if not os.path.isabs(dir):
121 sourcedir = os.path.split(self.filename)[0]
122 dir = os.path.join(sourcedir, dir)
123 for name in self.items.keys():
124 if pattern.search(name):
125 pathname = os.path.join(dir, name)
126 data = self._stripduplines(self.items[name])
127 self._dosave(pathname, data)
128
129 def _stripduplines(self, data):
130 for i in range(len(data)-1, 0, -1):
131 if data[i][:5] == '#line' and data[i-1][:5] == '#line':
132 del data[i-1]
133 if data[-1][:5] == '#line':
134 del data[-1]
135 return data
136
137 def _dosave(self, pathname, data):
138 """Save data to pathname, unless it is identical to what is there"""
139 if os.path.exists(pathname):
140 olddata = open(pathname).readlines()
141 if olddata == data:
142 return
143 fp = open(pathname, "w").writelines(data)
144
145def process(file):
146 pr = Processor(file)
147 pr.read()
148 pr.resolve()
149 pr.save(":jacktest:src", "^.*\.cp$")
150 pr.save(":jacktest:include", "^.*\.h")
151
152def main():
153 if len(sys.argv) > 1:
154 for file in sys.argv:
155 print "Processing", file
156 process(file)
157 else:
158 fss, ok = macfs.PromptGetFile("Select .nw source file", "TEXT")
159 if not ok:
160 sys.exit(0)
161 process(fss.as_pathname())
162
163if __name__ == "__main__":
164 main()