示例#1
0
	def create_method(self, line):
		indent = self.tree.find_indent(line) #this should always be zero
		if line[:4] == 'def ': # this prevents us from overlooking consecutively declared methods
			self.tree.indent = indent
			element, attributes = parse_definition(line[4:])
			
			if element[0] not in string.letters + '_' or not (element.replace('_','').isalnum()):
				raise ParserError("Method name must be alphanumeric with underscores and start with a letter or underscore")
			
			if element in self.valid_tags.keys():
				raise ParserError("Can't create method named '%s', it's a reserved markup element name" % element)
			
			self.creating_method = element
			# methods access shadowed variables to prevent overwriting globals
			self.method_map[element] = Method(attributes, True, self.color, element)
				
		else:	# regular line inside a method
			if indent == 0:
				self.creating_method = None
				return True #finished
			else:
				# check if this is a loop:
				if not self.current_verbatim and (self.loop_stack or line.strip()[:4] == 'for '):
					self.create_loop(line[len(self.tree.indent_marker):])
				else:
					if self.need_to_remove_method_vars:
						self.need_to_remove_method_vars = False
						line = '%s%s(%s)' % (self.tree.indent_to(indent), self.current_verbatim, ', '.join(self.verbatim_vars[GLOBAL_VARS]))
				
					# add normal line to method sequence
					self.method_map[self.creating_method].add_line(line[len(self.tree.indent_marker):], 
																self.current_verbatim,
																self.verbatim_vars[METHOD_VARS])
		return False
示例#2
0
	def parse_template_engine_call(self, line, indent):
		compressed = line.replace(' ','')
		try:
			# invoke template engine method
			if indent is None:
				whitespace = ''
			else:
				whitespace = self.tree.indent_to(indent)
			method_pair = line.split('(')[0].rstrip().rsplit('.', 1)
			end_method = self.template_engines[method_pair[0]].end_method(method_pair[1])
			if indent is not None:
				if end_method:
					# full-line declaration
					ending_tag = whitespace + end_method
				else:
					# invocation that's a subset of the line
					ending_tag = None
				self.handle_indent(indent, ending_tag)
			if not self.template_engines[method_pair[0]].is_submethod(method_pair[1], indent):
				# this is a method created using 'create()'
				#end_method = self.template_engines[method_pair[0]].end_method(method_pair[1])
				if end_method is None:
					if indent is not None:
						self.tree.indent = indent
				else:
					if indent is None:
						# this will happen when we try something like:
						# div(src=django.for(a, b))
						raise ParserError("'%s' method requires a closing tag, it can't be used as part of other logic on the line" % method_pair[1])
					#self.handle_indent(indent, whitespace + end_method)
			else:
				if indent is None:
					raise ParserError("Can't use '%s' as part of other logic on the line because it's a branch of an existing method" % method_pair[1])
				# this is a sub-method of a verbatim method (i.e. 'else' is a submethod of 'if')
				# submethods are created using 'append()'
				#note the indent+1, we want to close inner logic, but not the method itself
				endtag = self.template_engines[method_pair[0]].end_method(method_pair[1], False)
				if endtag is None:
					self.handle_indent(indent+1, None)
					self.element_stack.pop()
				else:
					self.handle_indent(indent+1, whitespace + endtag)
			attr = get_attr(line)
			return whitespace + self.template_engines[method_pair[0]].call_method(method_pair[1], attr, indent)
			#self.element_stack.append(whitespace + self.template_engines[method_pair[0]].end_method(method_pair[1]))
		#except (KeyError, IndexError, TypeError):
		except ParserError:
			raise ParserError("Improper TemplateEngine method declaration or invocation")

		# repeated from the definition method, rewrite to make the code more DRY
		# helper method for checking if the line involves variable assignment and/or reassignment
		if line.find(':=') != -1:
			# variable declaration
			self.set_variable(line)
			return
		elif line.find('+=') != -1 or line.find('-=') != -1 or line.find('*=') != -1 or line.find('/=') != -1:
			# variable increment
			res = self.expand_assignment_ops(line)
			line = self.var_map[res]
示例#3
0
	def create_template_engine(self, line):
		# creates a new set of rules for a templating engine, such as Django, Web2py, or Rails
		pair = line.split('=')
		if len(pair) != 2:
			raise ParserError("Improper TemplateEngine declaration")
		elif pair[0].isalnum() and pair[0] not in string.letters:
			raise ParserError("TemplateEngine must have alphanumeric name that starts with a letter")
		template = pair[1][pair[1].find('(')+1:pair[1].rfind(')')-1].strip().strip("'").strip('"')
		self.template_engines[pair[0].rstrip()] = TemplateEngine(template)
示例#4
0
	def set_variable(self, tag):
		# sets the variable(s) in the system
		vars = tag.split(':=')
		if len(vars) == 1:
			raise ParserError("You're trying to declare variable %s without assignment" % vars[0].strip())
			
		text = vars[-1].lstrip()
		for var in vars[:-1]:
			if var[0] == '$':
				self.var_map[var.rstrip()] = self.get_variables(text)
			else:
				raise ParserError("Illegal assignment to a constant '%s'" % var.rstrip())
示例#5
0
	def create_loop(self, line):
		# we can just piggy-back on create_method, since a loop is essentially a repeated function
		# the only tricky part is that loops can be nested
		tag = line.strip()
		indent = self.tree.find_indent(line)
		if tag[:4] == 'for ':	# new loop started (either within old loop, or outside)
			self.handle_indent(indent, None)
			var = tag.split()[1] #[for,$var,in,...]
			if var in self.var_map.keys():
				raise ParserError("Can't reuse previously defined variable %s as loop iterator" % var)
			
			#array = self.get_variables(tag.split('[')[1].split(']')[0]).split(',')
			#iterator = re.findall('^for[ ]+(\$[a-zA-Z_][a-zA-Z0-9_]*)[ ]+in', tag)[0]
			#array = self.get_variables(tag, [var]).split('[', 1)[1].rsplit(']', 1)[0].split(',')
			array = get_attr('(%s)' % self.get_variables(tag, [var]).split('[', 1)[1].rsplit(']', 1)[0])
			
			for i in range(len(array)):
				array[i] = array[i].strip()
			loop_name = 'rapydml_loop_def_%s' % self.loop_index
			self.loop_stack.append((loop_name, indent, array))
			self.method_map[loop_name] = Method([var], False, self.color, loop_name) # loops see/access global var space
			self.loop_index += 1
		else:					# command inside the loop or outside (loop termination)
			loop_name = self.loop_stack[-1][0]
			loop_indent = self.loop_stack[-1][1]
			indent_diff = indent-loop_indent
			if indent_diff < 1:
				# loop terminated, unroll it and execute this line as normal
				self.unroll_loop()
				self.handle_line(line)
			else:
				self.tree.indent = indent
				self.method_map[loop_name].add_line(line[len(self.tree.indent_to(loop_indent+1)):])
示例#6
0
	def close_last_element(self):
		# closes last html tag
		tag = self.element_stack.pop()
			
		if tag is None:
			return # this is not an element that requires closing
		
		# in order to use short-hand <tag /> we need to make sure that tag is not a special tag, 
		# and that the name matches as well as indent
		if self.last_opened_element is None:
			tag_type = -1
		else:
			try:
				tag_type = self.valid_tags[self.last_opened_element][0]
			except KeyError:
				try:
					tag_type = self.valid_tags['*'][0]
				except KeyError:
					# WE SHOULD NOT GET IN HERE UNLESS SOMETHING IS WRONG
					print "This logic should not trigger, please inform RapydML developers, provide the contents of your .pyml file as well."
					raise ParserError("'%s' is not a valid markup tag or method name." % self.last_opened_element)
		
		if tag_type == NORMAL \
		and re.search('^%s</%s>' % (self.tree.indent_to(self.tree.indent), self.last_opened_element), tag):
			self.write(' />\n', -2)
		elif tag_type != SINGLE or self.last_opened_element is None:
			self.write(tag)
示例#7
0
def parse_template_engine_method_declaration(line):
	command_pair = line.split('=', 1)
	if len(command_pair) != 2:
		raise ParserError("Improper TemplateEngine method declaration, variable assigned to must follow format of TemplateEngine.MethodName")
	method_pair = command_pair[0].rstrip().rsplit('.', 1)
	method, attributes = parse_definition(command_pair[1])
	return method_pair[0], method_pair[1], method, attributes
示例#8
0
	def expand_assignment_ops(self, tag, perform=True):
		# takes operation of form '$a += 3', converts it to '$a := $a + 3' and evaluates it, assigning new value to $a
		result, tag = expand_assignment(tag)
		pair = tag.split(':=')
		if len(pair) > 2:
			raise ParserError("Command '%s' has multiple assignment operators, invalid syntax" % tag)
		self.var_map[result] = do_arithmetic(self.get_variables(pair[1].strip()))
		return result
示例#9
0
	def resolve_indexes(self, line):
		# replace all indexes with corresponding values
		while line.count(']['):
			try:
				parts = line.split('][')
				array, start_index = parse_array_part(parts[0]+']')
				index = parts[1].split(']')[0]	#this can trigger IndexError
				num = int(index)				#this can trigger ValueError
				line = line.replace(line[start_index:len('%s[%s]' % (parts[0],index))+1], array[num])
			except ValueError:
				# this occurs if we try something like array['abcd'] or array[3.5]
				raise ParserError("Invalid index '%s', index must be an integer" % index)
			except IndexError:
				# this occurs if we try some improperly terminated line like array[ or ][
				raise ParserError("Syntax error while trying to parse index")
		
		return line
示例#10
0
	def call_method(self, method, vars, indent):
		# this returns an actual template tag to use for this method call
		if len(vars) != self.methods[method][1]:
			raise ParserError("TemplateEngine method %s takes %d variables, %d given" % (method, self.methods[method][1], len(vars)))
		
		self.handle_indent(indent, method)
		vars = tuple(vars)
		contents = self.methods[method][0] % vars
		return self.tag_format % contents
示例#11
0
def do_arithmetic(operation):
	# this function solves simple arithmetic such as +/-/*//
	# if we detect # or non-numeric variable, that's not a method, try to run it through color converter
	# valid color formats we expect are '#ffffff', '#000', or 'white' (note the quotes, we assume them necessary)
	# what about style?
	try:
		result = eval(operation)
		return repr(result)
	except SyntaxError, NameError:
		raise ParserError("Command '%s' is not a valid mathematical operation" % operation.strip())
示例#12
0
	def handle_verbatim_declaration(self, tag):
		assignment_pair = tag.split('=', 1)
		newtag = assignment_pair[0].strip()
		element, attributes = parse_definition(assignment_pair[1])
		length = len(attributes)
		
		# code_block follows same format as verbatim, but always takes additional argument
		if element == 'code_block':
			length -= 1
			sys_command = attributes.pop()[1:-1]
		
		if length == 0:
			# no args were passed, this version has no outer tags wrapping the text
			starttag = endtag = ''
		elif length == 1:
			# received 1 argument, it's a tag to use as a template
			
			# TODO: eventually we want this to also check declared methods, if available, first
			#if attributes[0] in self.template_engines.keys():
			#	endtag = self.template_engines[attributes[0]].tag_format %
			#else:
			#	endtag = '</%s>\n' % attributes[0]
			
			tagname, tagattr = parse_definition(attributes[0])
			starttag, endtag = create_tag(tagname, tagattr)
		elif length == 2:
			# received 2 quoted arguments for beginning and end tags
			starttag = attributes[0][1:-1] + '\n'
			endtag = attributes[1][1:-1] + '\n'
		else:
			if element == 'code_block':
				raise ParserError("Code Block definition takes 1, 2, or 3 arguments, %s arguments were given" % length)
			else:
				raise ParserError("Verbatim definition takes 0, 1, or 2 arguments, %s arguments were given" % length)
		
		# append to verbatim format in (start_tag, end_tag, type) format
		if element == 'verbatim_line':
			self.verbatim[newtag] = (starttag, endtag, SINGLE_LINE)
		elif element == 'verbatim':
			self.verbatim[newtag] = (starttag, endtag, MULTI_LINE)
		else:
			self.verbatim[newtag] = (starttag, endtag, CODE_BLOCK, sys_command)
示例#13
0
	def import_module(self, line):
		tokens = line.split()
		if len(tokens) != 2 or tokens[0] != 'import':
			raise ParserError("Invalid import statement: %s" % line.strip())
		
		if tokens[1] not in self.imported_files:
			try:
				self.imported_files.append(tokens[1])
				self.parse(tokens[1].replace('.', '/') +'.pyml', True)
			except IOError:
				# failed importing from working directory, try importing from rapydml directory
				cur_dir = os.getcwd()
				try:
					# we have to rely on __file__, because cwd could be different if invoked by another script
					os.chdir(os.path.dirname(__file__))
					self.imported_files.append(tokens[1])
					self.parse(tokens[1].replace('.', '/') +'.pyml', True)
				except IOError:
					raise ParserError("Can't import '%s', module doesn't exist" % tokens[1])
				finally:
					os.chdir(cur_dir)
示例#14
0
	def to_num(self, color):
		# first we standardize colors to 6-digit hex format
		color = color.lower()
		if color in self.color_map.keys():
			color = self.color_map[color]
		elif len(color) == 3:
			color = '%s%s%s%s%s%s' % (color[0], color[0], color[1], color[1], color[2], color[2])
		elif len(color) != 6:
			raise ParserError("Color '%s' is not a valid HTML color" % color)
		
		# now we return base 10 representation of the number
		return int(color, 16)
示例#15
0
	def parse_template_engine_definition(self, line):
		compressed = line.replace(' ','')
		if compressed.find('create(') != -1:
			# add a template to existing template engine
			engine, template, method_call, attr = parse_template_engine_method_declaration(line)
			try:
				if len(attr) == 1:
					self.template_engines[engine].add_method(template, attr[0][1:-1])
				else:
					self.template_engines[engine].add_method(template, attr[0][1:-1], attr[1][1:-1])
			except KeyError:
				raise ParserError("Attempting to add a method to a TemplateEngine prior to declaration")
		else: # append()
			# append additional logic to a template
			engine, template, method_call, attr = parse_template_engine_method_declaration(line)
			original_pair = method_call.split('.') # we only care about 1st 2 args
			if len(original_pair) != 3:
				raise ParserError("Method being appended to must follow TemplateEngine.MethodName format")
			try:
				self.template_engines[original_pair[0]].enhance_method(original_pair[1], template)
			except KeyError:
				raise ParserError("Attempting to append functionality to a non-existing method")
			try:
				self.template_engines[engine].add_method(template, attr[0][1:-1])
			except KeyError:
				raise ParserError("Attempting to add a method to a TemplateEngine prior to declaration")
		
		# helper method for checking if the line involves variable assignment and/or reassignment
		if line.find(':=') != -1:
			# variable declaration
			self.set_variable(line)
			return
		elif line.find('+=') != -1 or line.find('-=') != -1 or line.find('*=') != -1 or line.find('/=') != -1:
			# variable increment
			res = self.expand_assignment_ops(line)
			line = self.var_map[res]
示例#16
0
	def handle_indent(self, indent, method_name):
		# push to or pop from the stack, depending on indent
		
		# first method call
		if self.method_stack:
			indent_diff = indent-self.method_stack[-1][1]
		else:
			self.method_stack.append((method_name, indent))
			return
			
		if self.is_submethod(method_name, indent): #indent_diff == 0 and method_name in self.methods[self.method_stack[-1][0]][3]:
			# this is a submethod of current method
			#self.method_stack.append((self.method_stack[-1][0], indent))
			pass
		elif indent_diff < 1: #pop
			while indent_diff < 1:
				self.method_stack.pop()
				indent_diff += 1
			self.method_stack.append((method_name, indent))
		elif indent_diff > 1:
			raise ParserError('Incorrect indentation')
示例#17
0
def replace_variables(code, var_hash, ignore_list=[]):
	#plugs the variables into the line
	vars = re.findall('(?<!\\\\)\$[A-Za-z_][A-Za-z0-9_]*', code)
	
	for var in ignore_list:
		try:
			vars.remove(var)
		except ValueError:
			pass # variable doesn't appear on this line
	
	for var in vars:
		try:
			# the first version will not replace 2nd occurence in strings like '$a$a'
			#code = re.sub('(?<!(\\\\|[A-Za-z0-9_]))\%s(?![A-Za-z0-9_])' % var, var_hash[var], code)
			code = re.sub('(?<!\\\\)\%s(?![A-Za-z0-9_])' % var, var_hash[var], code)
		except KeyError:
			raise ParserError("Variable '%s' used prior to definition" % var)
	
	if code.find('python.') != -1:
		# use of python method
		code = eval_python(code)		
	
	return code
示例#18
0
	def run_method(self, args, heap):
		if self.copy_heap:
			self.heap = heap.copy()
		else:
			self.heap = heap
		#var_hash = {} #if we 'clutter' the global heap, it makes some logic easier and allows more Python-like reuse of variables after loop terminates
		for i in range(len(self.attributes)):
			#TEMP: the \ replacing is a temporary quickfix for misunderstood problem of Python interpreting the string when it shouldn't
			#var_hash[self.attributes[i]] = args[i].replace('\\','\\\\')
			try:
				self.heap[self.attributes[i]] = args[i].replace('\\','\\\\')
			except IndexError:
				raise ParserError("Method '%s' expects %s attributes, %s given." % \
									(self.name, len(self.attributes), len(args)))
		for line in self.lines:
			if line[0] == VERBATIM:
				verbatim_line = line[1]
				
				# replace variables we specified
				for variable in line[2]:
					verbatim_line = re.sub(r'\%s(?![A-Za-z0-9_])' % variable, self.heap[variable], verbatim_line)
				
				yield verbatim_line		# don't run any logic on verbatim lines
			else:
				line = line[1]
				
				# this is where we handle replacing method arguments
				assignments = line.count(':=')
				if assignments == 1:
					operands = line.split(':=')
					self.heap[operands[0].strip()] = self.eval_line(operands[1].strip())
					yield None # this line produces no output
				elif assignments > 1:
					raise "Multiple assignments on same line aren't allowed"
				else:
					yield self.eval_line(line)
示例#19
0
	def handle_line(self, line):
		indent = self.tree.find_indent(line)
		if DEBUG:
			print self.element_stack, '|%s|' % self.creating_method, indent, repr(line)
		whitespace = self.tree.indent_to(indent)
		
		#parse the tag
		tag = line.strip()
		
		#if tag[0] == '$' and tag.find(':=') == -1:
		#	tag = self.get_variables(tag)
		
		if self.current_verbatim is not None or \
		tag.split('(')[0].strip(':') in self.verbatim.keys():
			# verbatim call
			if self.loop_stack and tag.split('(')[0].strip(':') in self.verbatim.keys():
				# in case verbatim ended a loop, we want to handle that
				# we can return right after, since the loop terminator will automatically
				# call handle_line() again with verbatim tag
				self.create_loop(line)
				return
			else:
				# note: even comments inside verbatim block get treated verbatim
				last_line_processed = self.handle_verbatim_call(line)
			
			# if we're inside method creation, we want to keep going, so this line gets added to
			# the method
			if not self.creating_method or last_line_processed:
				return
		elif not tag or tag[0] == '#':
			# strip comments and blank lines
			return
		
		line = expand_arrays(line)
		tag = line.strip()
		
		# first check is a quick pre-qualifier to avoid expensive regex, second one avoids
		# false positives like: this_is_not_verbatim_call()
		if (tag.find('verbatim') != -1 or tag.find('code_block') != -1) \
		and re.search(r'\b(verbatim(_line)?|code_block)\b', tag):
			# verbatim/code_block declaration
			self.handle_verbatim_declaration(tag)
			return
		elif self.creating_method is None and (self.loop_stack or tag[:4] == 'for '):
			# loop
			self.create_loop(line)
			return
		elif self.creating_method is not None or line[:4] == 'def ':
			# method definition
			finished = self.create_method(line)
			if not finished:
				return
		if tag.find(':=') != -1:
			# variable declaration
			self.set_variable(tag)
			return
		elif line.find('+=') != -1 or line.find('-=') != -1 or line.find('*=') != -1 or line.find('/=') != -1:
			# variable increment
			res = self.expand_assignment_ops(line)
			tag = self.var_map[res]
		elif line[:7] == 'import ':
			# import
			self.import_module(line)
			return
		elif line.find('TemplateEngine') != -1:
			# template class declaration
			self.create_template_engine(tag)
			return
		elif tag.find('.') != -1:
			if tag.find('python.') != -1:
				# use of python method
				line = eval_python(line)
				tag = line.strip()
			
			# continue parsing, there could be other instances of '.'
			if tag.find('.append(') != -1 or tag.find('create(') != -1:
				# template engine declaration
				self.parse_template_engine_definition(tag)
				return
			else:
				# this regex will find all occurences of template engine calls in the form of:
				# template_engine.template_method(.*)
				# as long as the method is not in a string
				# and matching the parentheses correctly even if items inside use parentheses, up to 1 level deep
				substitutions = re.findall(r'(\b[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*\b%s)' % REGEX_NESTED_PAREN, tag)
				if len(substitutions) == 1 and tag.find(substitutions[0][1]) == 0:
					line = self.parse_template_engine_call(tag, indent)
				else:
					for item in substitutions:
						pattern = item.replace('(', '\(').replace(')', '\)').replace('.', '\.')
						if re.match(r'%s\b%s' % (NOT_SINGLE_QUOTED, pattern), tag) \
								and item.split('.')[0] in self.template_engines:
							# template method declaration or call
							tag = tag.replace(item, self.parse_template_engine_call(item, None).strip('\n'))
				
#				if substitutions:
#					self.write(line)
#					return
		elif line == EOF_MARKER:
			return
		
		tag = self.get_variables(tag)
		
		if tag[0] in ('"', "'"):
			# handle quoted strings as plain-text
			starttag = tag[1:-1] + '\n'
			element = None
			htmlend = None
		else:
			# test if this tag is a method call, if so execute it
			element, attributes = parse_definition(tag)
			if element in self.method_map.keys():
				self.handle_indent(indent, None)
				for method_line in self.method_map[element].run_method(attributes, self.var_map):
					if method_line is not None:
						self.handle_line(whitespace+method_line)
				return
			else:
				# this is a regular tag, not a method, let's make sure the element and attributes are valid
				try:
					self.valid_tags[element]
					hash_key = element
				except KeyError:
					try:
						self.valid_tags['*'] # if we can't access this, wildcard element was not declared
						hash_key = '*'
					except KeyError:
						raise ParserError("'%s' is not a valid markup tag or method name." % element)
				
				if self.valid_tags[hash_key][1] is not None:
					for attr in attributes:
						attr_name = attr.split('=', 1)[0]
						if attr_name not in self.valid_tags[hash_key][1]:
							raise ParserError("'%s' is not one of allowed attributes for '%s' element" % (attr_name, hash_key))
		
			starttag, endtag = create_tag(element, attributes)
			htmlend = whitespace + endtag
		
		# check indent difference, close old tags if indent < 1
		self.handle_indent(indent, htmlend)
		
		# update variables
		self.last_opened_element = element
		
		# dump the current line to file
		self.write(whitespace + starttag)
示例#20
0
	def handle_verbatim_call(self, line):
		indent = self.tree.find_indent(line)
		if self.current_verbatim is None:
			# this is the first line of verbatim logic
			
			# set verbatim, get replaceable variables and check that they exist
			self.current_verbatim, verbatim_vars = parse_definition(line.strip())
			self.verbatim_vars = ([], [])
			for variable in verbatim_vars:
				try:
					self.var_map[variable]
					self.verbatim_vars[GLOBAL_VARS].append(variable)
				except KeyError:
					if self.creating_method \
					and (variable in self.method_map[self.creating_method].attributes \
					or variable in self.method_map[self.creating_method].local_vars):
						self.verbatim_vars[METHOD_VARS].append(variable)
						self.need_to_remove_method_vars = True
						continue
					raise ParserError("Variable '%s' used prior to declaration." % variable)
			
			self.verbatim_indent = self.tree.find_indent(line)
			if not self.creating_method:
				self.handle_indent(indent, None)
				self.last_opened_element = None
				self.element_stack.append(None)
		else:
			# we're continuing to parse existing verbatim logic
			verbatim_properties = self.verbatim[self.current_verbatim]
			if indent > self.verbatim_indent:
				# still inside verbatim block
				if not self.creating_method:
					line = line[self.verbatim_indent+1:]
				
					# plug in the variables, if they appear on this line
					for variable in self.verbatim_vars[GLOBAL_VARS]:
						line = re.sub(r'\%s(?![A-Za-z0-9_])' % variable, self.var_map[variable], line)
					
					self.verbatim_buffer += line
			else:
				# end of verbatim logic
				if not self.creating_method:
					if verbatim_properties[2] == SINGLE_LINE:
						self.verbatim_buffer = re.sub('\n[ 	]*', ' ', self.verbatim_buffer)
						self.verbatim_buffer += '\n'
					elif verbatim_properties[2] == CODE_BLOCK:
						result = Popen('echo %s' % pipes.quote(self.verbatim_buffer) + verbatim_properties[3],
										stdout=PIPE, stderr=PIPE, shell=True).communicate()
						if result[1]:
							raise ShellError("'%s' code_block tag triggered the following OS error: %s" %
											(self.current_verbatim, result[1]))
						self.verbatim_buffer = result[0] + '\n'
					if verbatim_properties[1] != '':
						vindent = self.tree.indent_to(self.verbatim_indent)
						self.verbatim_buffer = '%s%s%s%s%s' % (\
							vindent,
							verbatim_properties[0],
							self.verbatim_buffer,
							vindent,
							verbatim_properties[1])
					self.write(self.verbatim_buffer)
					self.verbatim_buffer = ''
					self.close_last_element() # close verbatim element so it does not screw up the stack
				self.current_verbatim = None
				self.handle_line(line)
				return True
		return False