blob: d694ba8baebe233b8f51c4894c7c9bb133614eed [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("^@")
13
14class Processor:
15 def __init__(self, filename):
16 self.items = {}
17 self.filename = filename
18 self.fp = open(filename)
19 self.lineno = 0
20 self.resolving = {}
21 self.resolved = {}
22 self.pushback = None
23
24 def _readline(self):
25 """Read a line. Allow for pushback"""
26 if self.pushback:
27 rv = self.pushback
28 self.pushback = None
29 return rv
30 self.lineno = self.lineno + 1
Jack Jansen61c64c92000-12-14 23:35:01 +000031 return self.lineno, self.fp.readline()
Jack Jansen581fa782000-12-14 22:29:58 +000032
Jack Jansen61c64c92000-12-14 23:35:01 +000033 def _linedirective(self, lineno):
34 """Return a #line cpp directive for this file position"""
35 return '#line %d "%s"\n'%(lineno-3, os.path.split(self.filename)[1])
Jack Jansen581fa782000-12-14 22:29:58 +000036
37 def _readitem(self):
38 """Read the definition of an item. Insert #line where needed. """
Jack Jansen61c64c92000-12-14 23:35:01 +000039 rv = []
Jack Jansen581fa782000-12-14 22:29:58 +000040 while 1:
Jack Jansen61c64c92000-12-14 23:35:01 +000041 lineno, line = self._readline()
Jack Jansen581fa782000-12-14 22:29:58 +000042 if not line:
43 break
44 if ENDDEFINITION.search(line):
45 break
46 if BEGINDEFINITION.match(line):
Jack Jansen61c64c92000-12-14 23:35:01 +000047 self.pushback = lineno, line
Jack Jansen581fa782000-12-14 22:29:58 +000048 break
49 mo = USEDEFINITION.match(line)
50 if mo:
51 pre = mo.group('pre')
52 if pre:
Jack Jansen61c64c92000-12-14 23:35:01 +000053 rv.append((lineno, pre+'\n'))
54 rv.append((lineno, line))
Jack Jansen581fa782000-12-14 22:29:58 +000055 # For simplicity we add #line directives now, if
56 # needed.
57 if mo:
Jack Jansen61c64c92000-12-14 23:35:01 +000058 post = mo.group('post')
59 if post and post != '\n':
60 rv.append((lineno, post))
Jack Jansen581fa782000-12-14 22:29:58 +000061 return rv
62
63 def _define(self, name, value):
64 """Define an item, or append to an existing definition"""
65 if self.items.has_key(name):
66 self.items[name] = self.items[name] + value
67 else:
68 self.items[name] = value
69
70 def read(self):
71 """Read the source file and store all definitions"""
72 while 1:
Jack Jansen61c64c92000-12-14 23:35:01 +000073 lineno, line = self._readline()
Jack Jansen581fa782000-12-14 22:29:58 +000074 if not line: break
75 mo = BEGINDEFINITION.search(line)
76 if mo:
77 name = mo.group('name')
78 value = self._readitem()
79 self._define(name, value)
80 else:
81 pass # We could output the TeX code but we don't bother.
82
83 def resolve(self):
84 """Resolve all references"""
85 for name in self.items.keys():
86 self._resolve_one(name)
87
88 def _resolve_one(self, name):
89 """Resolve references in one definition, recursively"""
90 # First check for unknown macros and recursive calls
91 if not self.items.has_key(name):
92 print "Undefined macro:", name
93 return ['<<%s>>'%name]
94 if self.resolving.has_key(name):
95 print "Recursive macro:", name
96 return ['<<%s>>'%name]
97 # Then check that we haven't handled this one before
98 if self.resolved.has_key(name):
99 return self.items[name]
100 # No rest for the wicked: we have work to do.
101 self.resolving[name] = 1
102 result = []
Jack Jansen61c64c92000-12-14 23:35:01 +0000103 for lineno, line in self.items[name]:
Jack Jansen581fa782000-12-14 22:29:58 +0000104 mo = USEDEFINITION.search(line)
105 if mo:
106 # We replace the complete line. Is this correct?
107 macro = mo.group('name')
108 replacement = self._resolve_one(macro)
109 result = result + replacement
110 else:
Jack Jansen61c64c92000-12-14 23:35:01 +0000111 result.append((lineno, line))
Jack Jansen581fa782000-12-14 22:29:58 +0000112 self.items[name] = result
113 self.resolved[name] = 1
114 del self.resolving[name]
115 return result
116
117 def save(self, dir, pattern):
118 """Save macros that match pattern to folder dir"""
119 # Compile the pattern, if needed
120 if type(pattern) == type(''):
121 pattern = re.compile(pattern)
122 # If the directory is relative it is relative to the sourcefile
123 if not os.path.isabs(dir):
124 sourcedir = os.path.split(self.filename)[0]
125 dir = os.path.join(sourcedir, dir)
126 for name in self.items.keys():
127 if pattern.search(name):
128 pathname = os.path.join(dir, name)
Jack Jansen61c64c92000-12-14 23:35:01 +0000129 data = self._addlinedirectives(self.items[name])
Jack Jansen581fa782000-12-14 22:29:58 +0000130 self._dosave(pathname, data)
131
Jack Jansen61c64c92000-12-14 23:35:01 +0000132 def _addlinedirectives(self, data):
133 curlineno = -100
134 rv = []
135 for lineno, line in data:
136 curlineno = curlineno + 1
137 if line and line != '\n' and lineno != curlineno:
138 rv.append(self._linedirective(lineno))
139 curlineno = lineno
140 rv.append(line)
141 return rv
Jack Jansen581fa782000-12-14 22:29:58 +0000142
143 def _dosave(self, pathname, data):
144 """Save data to pathname, unless it is identical to what is there"""
145 if os.path.exists(pathname):
146 olddata = open(pathname).readlines()
147 if olddata == data:
148 return
Jack Jansen61c64c92000-12-14 23:35:01 +0000149 macostools.mkdirs(os.path.split(pathname)[0])
Jack Jansen581fa782000-12-14 22:29:58 +0000150 fp = open(pathname, "w").writelines(data)
151
Jack Jansen61c64c92000-12-14 23:35:01 +0000152def process(file, config):
Jack Jansen581fa782000-12-14 22:29:58 +0000153 pr = Processor(file)
154 pr.read()
155 pr.resolve()
Jack Jansen61c64c92000-12-14 23:35:01 +0000156 for pattern, folder in config:
157 pr.save(folder, pattern)
158
159def readconfig():
160 """Read a configuration file, if it doesn't exist create it."""
161 configname = sys.argv[0] + '.config'
162 if not os.path.exists(configname):
163 confstr = """config = [
164 ("^.*\.cp$", ":unweave-src"),
165 ("^.*\.h$", ":unweave-include"),
166]"""
167 open(configname, "w").write(confstr)
168 print "Created config file", configname
169## print "Please check and adapt (if needed)"
170## sys.exit(0)
171 namespace = {}
172 execfile(configname, namespace)
173 return namespace['config']
Jack Jansen581fa782000-12-14 22:29:58 +0000174
175def main():
Jack Jansen61c64c92000-12-14 23:35:01 +0000176 config = readconfig()
Jack Jansen581fa782000-12-14 22:29:58 +0000177 if len(sys.argv) > 1:
Jack Jansen61c64c92000-12-14 23:35:01 +0000178 for file in sys.argv[1:]:
179 if file[-3:] == '.nw':
180 print "Processing", file
181 process(file, config)
182 else:
183 print "Skipping", file
Jack Jansen581fa782000-12-14 22:29:58 +0000184 else:
185 fss, ok = macfs.PromptGetFile("Select .nw source file", "TEXT")
186 if not ok:
187 sys.exit(0)
188 process(fss.as_pathname())
189
190if __name__ == "__main__":
191 main()