| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | """An attempt at an unweave script.
 | 
					
						
							|  |  |  | Jack Jansen, jack@oratrix.com, 13-Dec-00 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import macfs | 
					
						
							|  |  |  | import os | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | import macostools | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | BEGINDEFINITION=re.compile("^<<(?P<name>.*)>>=\s*") | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | USEDEFINITION=re.compile("^(?P<pre>.*)<<(?P<name>.*)>>(?P<post>[^=].*)") | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | ENDDEFINITION=re.compile("^@") | 
					
						
							| 
									
										
										
										
											2001-02-17 22:00:18 +00:00
										 |  |  | GREMLINS=re.compile("[\xa0\xca]") | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | DEFAULT_CONFIG="""
 | 
					
						
							| 
									
										
										
										
											2001-02-17 22:00:18 +00:00
										 |  |  | filepatterns = [ | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 	("^.*\.cp$", ":unweave-src"), | 
					
						
							|  |  |  | 	("^.*\.h$", ":unweave-include"), | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | genlinedirectives = 0 | 
					
						
							|  |  |  | gencomments = 1 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | class Processor: | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 	def __init__(self, filename, config={}): | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 		self.items = {} | 
					
						
							|  |  |  | 		self.filename = filename | 
					
						
							|  |  |  | 		self.fp = open(filename) | 
					
						
							|  |  |  | 		self.lineno = 0 | 
					
						
							|  |  |  | 		self.resolving = {} | 
					
						
							|  |  |  | 		self.resolved = {} | 
					
						
							|  |  |  | 		self.pushback = None | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 		# Options | 
					
						
							|  |  |  | 		if config.has_key("genlinedirectives"): | 
					
						
							|  |  |  | 			self.genlinedirectives = config["genlinedirectives"] | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			self.genlinedirectives = 1 | 
					
						
							|  |  |  | 		if config.has_key("gencomments"): | 
					
						
							|  |  |  | 			self.gencomments = config["gencomments"] | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			self.gencomments = 0 | 
					
						
							| 
									
										
										
										
											2001-02-17 22:00:18 +00:00
										 |  |  | 		if config.has_key("filepatterns"): | 
					
						
							|  |  |  | 			self.filepatterns = config["filepatterns"] | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			self.filepatterns = [] | 
					
						
							|  |  |  | 		self.filepattern_relist = [] | 
					
						
							|  |  |  | 		for pat, dummy in self.filepatterns: | 
					
						
							|  |  |  | 			self.filepattern_relist.append(re.compile(pat)) | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 		 | 
					
						
							|  |  |  | 	def _readline(self): | 
					
						
							|  |  |  | 		"""Read a line. Allow for pushback""" | 
					
						
							|  |  |  | 		if self.pushback: | 
					
						
							|  |  |  | 			rv = self.pushback | 
					
						
							|  |  |  | 			self.pushback = None | 
					
						
							|  |  |  | 			return rv | 
					
						
							|  |  |  | 		self.lineno = self.lineno + 1 | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 		return self.lineno, self.fp.readline() | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 		 | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 	def _linedirective(self, lineno): | 
					
						
							|  |  |  | 		"""Return a #line cpp directive for this file position""" | 
					
						
							|  |  |  | 		return '#line %d "%s"\n'%(lineno-3, os.path.split(self.filename)[1]) | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 		 | 
					
						
							|  |  |  | 	def _readitem(self): | 
					
						
							|  |  |  | 		"""Read the definition of an item. Insert #line where needed. """ | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 		rv = [] | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 		while 1: | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 			lineno, line = self._readline() | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 			if not line: | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			if ENDDEFINITION.search(line): | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			if BEGINDEFINITION.match(line): | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 				self.pushback = lineno, line | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 				break | 
					
						
							|  |  |  | 			mo = USEDEFINITION.match(line) | 
					
						
							|  |  |  | 			if mo: | 
					
						
							|  |  |  | 				pre = mo.group('pre') | 
					
						
							|  |  |  | 				if pre: | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | ##					rv.append((lineno, pre+'\n')) | 
					
						
							|  |  |  | 					rv.append((lineno, pre)) | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 			rv.append((lineno, line)) | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 			if mo: | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 				post = mo.group('post') | 
					
						
							|  |  |  | 				if post and post != '\n': | 
					
						
							|  |  |  | 					rv.append((lineno, post)) | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 		return rv | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 	def _define(self, name, value): | 
					
						
							|  |  |  | 		"""Define an item, or append to an existing definition""" | 
					
						
							|  |  |  | 		if self.items.has_key(name): | 
					
						
							|  |  |  | 			self.items[name] = self.items[name] + value | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			self.items[name] = value | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 	def read(self): | 
					
						
							|  |  |  | 		"""Read the source file and store all definitions""" | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 		savedcomment = [] | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 		while 1: | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 			lineno, line = self._readline() | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 			if not line: break | 
					
						
							|  |  |  | 			mo = BEGINDEFINITION.search(line) | 
					
						
							|  |  |  | 			if mo: | 
					
						
							|  |  |  | 				name = mo.group('name') | 
					
						
							|  |  |  | 				value = self._readitem() | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 				if self.gencomments: | 
					
						
							|  |  |  | 					defline = [(lineno, '// <%s>=\n'%name)] | 
					
						
							|  |  |  | 					if savedcomment: | 
					
						
							|  |  |  | 						savedcomment = savedcomment + [(lineno, '//\n')] + defline | 
					
						
							|  |  |  | 					else: | 
					
						
							|  |  |  | 						savedcomment = defline | 
					
						
							| 
									
										
										
										
											2001-02-17 22:00:18 +00:00
										 |  |  | 					savedcomment = self._processcomment(savedcomment) | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 					value = savedcomment + value | 
					
						
							|  |  |  | 					savedcomment = [] | 
					
						
							| 
									
										
										
										
											2001-02-17 22:00:18 +00:00
										 |  |  | 				isfilepattern = 0 | 
					
						
							|  |  |  | 				for rexp in self.filepattern_relist: | 
					
						
							|  |  |  | 					if rexp.search(name): | 
					
						
							|  |  |  | 						isfilepattern = 1 | 
					
						
							|  |  |  | 						break | 
					
						
							|  |  |  | 				if 0 and not isfilepattern: | 
					
						
							|  |  |  | 					value = self._addspace(value) | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 				self._define(name, value) | 
					
						
							|  |  |  | 			else: | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 				if self.gencomments: | 
					
						
							|  |  |  | 					# It seems initial blank lines are ignored:-( | 
					
						
							|  |  |  | 					if savedcomment or line.strip(): | 
					
						
							|  |  |  | 						savedcomment.append((lineno, '// '+line)) | 
					
						
							|  |  |  | 						 | 
					
						
							| 
									
										
										
										
											2001-02-17 22:00:18 +00:00
										 |  |  | 	def _processcomment(self, comment): | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 		# This routine mimicks some artefact of Matthias' code. | 
					
						
							|  |  |  | 		rv = [] | 
					
						
							|  |  |  | 		for lineno, line in comment: | 
					
						
							|  |  |  | 			line = line[:-1] | 
					
						
							| 
									
										
										
										
											2001-02-17 22:00:18 +00:00
										 |  |  | 			line = GREMLINS.subn(' ', line)[0] | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 			if len(line) < 75: | 
					
						
							|  |  |  | 				line = line + (75-len(line))*' ' | 
					
						
							|  |  |  | 			line = line + '\n' | 
					
						
							|  |  |  | 			rv.append((lineno, line)) | 
					
						
							|  |  |  | 		return rv | 
					
						
							|  |  |  | 		 | 
					
						
							| 
									
										
										
										
											2001-02-17 22:00:18 +00:00
										 |  |  | 	def _addspace(self, value, howmany): | 
					
						
							|  |  |  | 		# Yet another routine to mimick yet another artefact | 
					
						
							|  |  |  | 		rv = value[0:1] | 
					
						
							|  |  |  | 		for lineno, line in value[1:]: | 
					
						
							|  |  |  | 			rv.append((lineno, (' '*howmany)+line)) | 
					
						
							|  |  |  | 		return rv | 
					
						
							|  |  |  | 		 | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 	def resolve(self): | 
					
						
							|  |  |  | 		"""Resolve all references""" | 
					
						
							|  |  |  | 		for name in self.items.keys(): | 
					
						
							|  |  |  | 			self._resolve_one(name) | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 	def _resolve_one(self, name): | 
					
						
							|  |  |  | 		"""Resolve references in one definition, recursively""" | 
					
						
							|  |  |  | 		# First check for unknown macros and recursive calls | 
					
						
							|  |  |  | 		if not self.items.has_key(name): | 
					
						
							|  |  |  | 			print "Undefined macro:", name | 
					
						
							|  |  |  | 			return ['<<%s>>'%name] | 
					
						
							|  |  |  | 		if self.resolving.has_key(name): | 
					
						
							|  |  |  | 			print "Recursive macro:", name | 
					
						
							|  |  |  | 			return ['<<%s>>'%name] | 
					
						
							|  |  |  | 		# Then check that we haven't handled this one before | 
					
						
							|  |  |  | 		if self.resolved.has_key(name): | 
					
						
							|  |  |  | 			return self.items[name] | 
					
						
							|  |  |  | 		# No rest for the wicked: we have work to do. | 
					
						
							|  |  |  | 		self.resolving[name] = 1 | 
					
						
							|  |  |  | 		result = [] | 
					
						
							| 
									
										
										
										
											2001-02-17 22:00:18 +00:00
										 |  |  | 		lastlineincomplete = 0 | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 		for lineno, line in self.items[name]: | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 			mo = USEDEFINITION.search(line) | 
					
						
							|  |  |  | 			if mo: | 
					
						
							|  |  |  | 				# We replace the complete line. Is this correct? | 
					
						
							|  |  |  | 				macro = mo.group('name') | 
					
						
							|  |  |  | 				replacement = self._resolve_one(macro) | 
					
						
							| 
									
										
										
										
											2001-02-17 22:00:18 +00:00
										 |  |  | 				if lastlineincomplete: | 
					
						
							|  |  |  | 					replacement = self._addspace(replacement, lastlineincomplete) | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 				result = result + replacement | 
					
						
							|  |  |  | 			else: | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 				result.append((lineno, line)) | 
					
						
							| 
									
										
										
										
											2001-02-17 22:00:18 +00:00
										 |  |  | 			if line[-1] == '\n': | 
					
						
							|  |  |  | 				lastlineincomplete = 0 | 
					
						
							|  |  |  | 			else: | 
					
						
							|  |  |  | 				lastlineincomplete = len(line) | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 		self.items[name] = result | 
					
						
							|  |  |  | 		self.resolved[name] = 1 | 
					
						
							|  |  |  | 		del self.resolving[name] | 
					
						
							|  |  |  | 		return result | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 	def save(self, dir, pattern): | 
					
						
							|  |  |  | 		"""Save macros that match pattern to folder dir""" | 
					
						
							|  |  |  | 		# Compile the pattern, if needed | 
					
						
							|  |  |  | 		if type(pattern) == type(''): | 
					
						
							|  |  |  | 			pattern = re.compile(pattern) | 
					
						
							|  |  |  | 		# If the directory is relative it is relative to the sourcefile | 
					
						
							|  |  |  | 		if not os.path.isabs(dir): | 
					
						
							|  |  |  | 			sourcedir = os.path.split(self.filename)[0] | 
					
						
							|  |  |  | 			dir = os.path.join(sourcedir, dir) | 
					
						
							|  |  |  | 		for name in self.items.keys(): | 
					
						
							|  |  |  | 			if pattern.search(name): | 
					
						
							|  |  |  | 				pathname = os.path.join(dir, name) | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 				data = self._addlinedirectives(self.items[name]) | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 				self._dosave(pathname, data) | 
					
						
							|  |  |  | 				 | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 	def _addlinedirectives(self, data): | 
					
						
							|  |  |  | 		curlineno = -100 | 
					
						
							|  |  |  | 		rv = [] | 
					
						
							|  |  |  | 		for lineno, line in data: | 
					
						
							|  |  |  | 			curlineno = curlineno + 1 | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 			if self.genlinedirectives and line and line != '\n' and lineno != curlineno: | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 				rv.append(self._linedirective(lineno)) | 
					
						
							|  |  |  | 				curlineno = lineno | 
					
						
							|  |  |  | 			rv.append(line) | 
					
						
							|  |  |  | 		return rv | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 		 | 
					
						
							|  |  |  | 	def _dosave(self, pathname, data): | 
					
						
							|  |  |  | 		"""Save data to pathname, unless it is identical to what is there""" | 
					
						
							|  |  |  | 		if os.path.exists(pathname): | 
					
						
							|  |  |  | 			olddata = open(pathname).readlines() | 
					
						
							|  |  |  | 			if olddata == data: | 
					
						
							|  |  |  | 				return | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 		macostools.mkdirs(os.path.split(pathname)[0]) | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 		fp = open(pathname, "w").writelines(data) | 
					
						
							|  |  |  | 		 | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | def process(file, config): | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 	pr = Processor(file, config) | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 	pr.read() | 
					
						
							|  |  |  | 	pr.resolve() | 
					
						
							| 
									
										
										
										
											2001-02-17 22:00:18 +00:00
										 |  |  | 	for pattern, folder in config['filepatterns']: | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 		pr.save(folder, pattern) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | def readconfig(): | 
					
						
							|  |  |  | 	"""Read a configuration file, if it doesn't exist create it.""" | 
					
						
							|  |  |  | 	configname = sys.argv[0] + '.config' | 
					
						
							|  |  |  | 	if not os.path.exists(configname): | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 		confstr = DEFAULT_CONFIG | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 		open(configname, "w").write(confstr) | 
					
						
							|  |  |  | 		print "Created config file", configname | 
					
						
							|  |  |  | ##		print "Please check and adapt (if needed)" | 
					
						
							|  |  |  | ##		sys.exit(0) | 
					
						
							|  |  |  | 	namespace = {} | 
					
						
							|  |  |  | 	execfile(configname, namespace) | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 	return namespace | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def main(): | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 	config = readconfig() | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 	if len(sys.argv) > 1: | 
					
						
							| 
									
										
										
										
											2000-12-14 23:35:01 +00:00
										 |  |  | 		for file in sys.argv[1:]: | 
					
						
							|  |  |  | 			if file[-3:] == '.nw': | 
					
						
							|  |  |  | 				print "Processing", file | 
					
						
							|  |  |  | 				process(file, config) | 
					
						
							|  |  |  | 			else: | 
					
						
							|  |  |  | 				print "Skipping", file | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 	else: | 
					
						
							|  |  |  | 		fss, ok = macfs.PromptGetFile("Select .nw source file", "TEXT") | 
					
						
							|  |  |  | 		if not ok: | 
					
						
							|  |  |  | 			sys.exit(0) | 
					
						
							| 
									
										
										
										
											2001-02-13 22:58:56 +00:00
										 |  |  | 		process(fss.as_pathname(), config) | 
					
						
							| 
									
										
										
										
											2000-12-14 22:29:58 +00:00
										 |  |  | 		 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  | 	main() |