1 """An attempt at an unweave script.
2 Jack Jansen, jack@oratrix.com, 13-Dec-00
10 BEGINDEFINITION
=re
.compile("^<<(?P<name>.*)>>=\s*")
11 USEDEFINITION
=re
.compile("^(?P<pre>.*)<<(?P<name>.*)>>(?P<post>[^=].*)")
12 ENDDEFINITION
=re
.compile("^@")
13 GREMLINS
=re
.compile("[\xa0\xca]")
17 ("^.*\.cp$", ":unweave-src"),
18 ("^.*\.h$", ":unweave-include"),
25 def __init__(self
, filename
, config
={}):
27 self
.filename
= filename
28 self
.fp
= open(filename
)
34 if config
.has_key("genlinedirectives"):
35 self
.genlinedirectives
= config
["genlinedirectives"]
37 self
.genlinedirectives
= 1
38 if config
.has_key("gencomments"):
39 self
.gencomments
= config
["gencomments"]
42 if config
.has_key("filepatterns"):
43 self
.filepatterns
= config
["filepatterns"]
45 self
.filepatterns
= []
46 self
.filepattern_relist
= []
47 for pat
, dummy
in self
.filepatterns
:
48 self
.filepattern_relist
.append(re
.compile(pat
))
51 """Read a line. Allow for pushback"""
56 self
.lineno
= self
.lineno
+ 1
57 return self
.lineno
, self
.fp
.readline()
59 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])
64 """Read the definition of an item. Insert #line where needed. """
67 lineno
, line
= self
._readline
()
70 if ENDDEFINITION
.search(line
):
72 if BEGINDEFINITION
.match(line
):
73 self
.pushback
= lineno
, line
75 mo
= USEDEFINITION
.match(line
)
79 ## rv.append((lineno, pre+'\n'))
80 rv
.append((lineno
, pre
))
81 rv
.append((lineno
, line
))
83 post
= mo
.group('post')
84 if post
and post
!= '\n':
85 rv
.append((lineno
, post
))
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
93 self
.items
[name
] = value
96 """Read the source file and store all definitions"""
99 lineno
, line
= self
._readline
()
101 mo
= BEGINDEFINITION
.search(line
)
103 name
= mo
.group('name')
104 value
= self
._readitem
()
106 defline
= [(lineno
, '// <%s>=\n'%name
)]
108 savedcomment
= savedcomment
+ [(lineno
, '//\n')] + defline
110 savedcomment
= defline
111 savedcomment
= self
._processcomment
(savedcomment
)
112 value
= savedcomment
+ value
115 for rexp
in self
.filepattern_relist
:
116 if rexp
.search(name
):
119 if 0 and not isfilepattern
:
120 value
= self
._addspace
(value
)
121 self
._define
(name
, value
)
124 # It seems initial blank lines are ignored:-(
125 if savedcomment
or line
.strip():
126 savedcomment
.append((lineno
, '// '+line
))
128 def _processcomment(self
, comment
):
129 # This routine mimicks some artefact of Matthias' code.
131 for lineno
, line
in comment
:
133 line
= GREMLINS
.subn(' ', line
)[0]
135 line
= line
+ (75-len(line
))*' '
137 rv
.append((lineno
, line
))
140 def _addspace(self
, value
, howmany
):
141 # Yet another routine to mimick yet another artefact
143 for lineno
, line
in value
[1:]:
144 rv
.append((lineno
, (' '*howmany
)+line
))
148 """Resolve all references"""
149 for name
in self
.items
.keys():
150 self
._resolve
_one
(name
)
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
167 lastlineincomplete
= 0
168 for lineno
, line
in self
.items
[name
]:
169 mo
= USEDEFINITION
.search(line
)
171 # We replace the complete line. Is this correct?
172 macro
= mo
.group('name')
173 replacement
= self
._resolve
_one
(macro
)
174 if lastlineincomplete
:
175 replacement
= self
._addspace
(replacement
, lastlineincomplete
)
176 result
= result
+ replacement
178 result
.append((lineno
, line
))
180 lastlineincomplete
= 0
182 lastlineincomplete
= len(line
)
183 self
.items
[name
] = result
184 self
.resolved
[name
] = 1
185 del self
.resolving
[name
]
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
)
200 data
= self
._addlinedirectives
(self
.items
[name
])
201 self
._dosave
(pathname
, data
)
203 def _addlinedirectives(self
, data
):
206 for lineno
, line
in data
:
207 curlineno
= curlineno
+ 1
208 if self
.genlinedirectives
and line
and line
!= '\n' and lineno
!= curlineno
:
209 rv
.append(self
._linedirective
(lineno
))
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()
220 macostools
.mkdirs(os
.path
.split(pathname
)[0])
221 fp
= open(pathname
, "w").writelines(data
)
223 def process(file, config
):
224 pr
= Processor(file, config
)
227 for pattern
, folder
in config
['filepatterns']:
228 pr
.save(folder
, pattern
)
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
):
234 confstr
= DEFAULT_CONFIG
235 open(configname
, "w").write(confstr
)
236 print "Created config file", configname
237 ## print "Please check and adapt (if needed)"
240 execfile(configname
, namespace
)
244 config
= readconfig()
245 if len(sys
.argv
) > 1:
246 for file in sys
.argv
[1:]:
247 if file[-3:] == '.nw':
248 print "Processing", file
249 process(file, config
)
251 print "Skipping", file
253 fss
, ok
= macfs
.PromptGetFile("Select .nw source file", "TEXT")
256 process(fss
.as_pathname(), config
)
258 if __name__
== "__main__":