| 7 | | class TemplateSyntaxError(Exception): pass |
| 8 | | |
| 9 | | |
| 10 | | """ |
| 11 | | VARIABLE_NAME -> '[a-zA-Z]+' |
| 12 | | TEXT -> '(?:[^\$#\\]|\\\\|\\\$|\\#)+' |
| 13 | | TEMPLATE -> BLOCK |
| 14 | | BLOCK -> TEXT |
| 15 | | | PLACEHOLDER |
| 16 | | | IF_DIRECTIVE |
| 17 | | | BLOCK_DIRECTIVE |
| 18 | | REFERENCE -> '\$' VARIABLE_VALUE |
| 19 | | SILENT_REFERENCE -> '\$!' VARIABLE_VALUE |
| 20 | | VARIABLE_VALUE -> VARIABLE_NAME |
| 21 | | | VARIABLE_NAME '\.' VARIABLE_VALUE |
| 22 | | |
| 23 | | |
| 24 | | """ |
| 25 | | |
| 26 | | |
| 27 | | |
| 28 | | class Tokeniser: |
| 29 | | PLAIN, IF, PLACEHOLDER, FOREACH, END, SET, ELSE = range(7) |
| 30 | | |
| 31 | | UP_TO_NEXT_TEMPLATE_BIT = re.compile('^(.*?)((?:#|\$).*)', re.MULTILINE + re.DOTALL) |
| 32 | | REST = '(.*)$' |
| 33 | | NAME = '[a-z0-9_]+' |
| 34 | | NAME_OR_CALL = NAME + '(?:\(\))?' |
| 35 | | RE_FLAGS = re.IGNORECASE + re.DOTALL + re.MULTILINE |
| 36 | | EXPRESSION = '(' + NAME_OR_CALL + '(?:\.' + NAME_OR_CALL + ')*)' |
| 37 | | STRING_LITERAL = "'(?:\\\\|\\'|\\n|\\b|\\t)'" |
| 38 | | PLACEHOLDER_PATTERN = re.compile('^\$(!?)({?)' + EXPRESSION + '(}?)' + REST, RE_FLAGS) |
| 39 | | SET_PATTERN = re.compile('^#set[ \t]*\([ \t]*\$(' + NAME + ')[ \t]*=[ \t]*(\d+|"[^"]+")[ \t]*\)' + REST, RE_FLAGS) |
| 40 | | BEGIN_IF_PATTERN = re.compile('^#if[ \t]*\([ \t]*\$' + EXPRESSION + '[ \t]*\)' + REST, RE_FLAGS) |
| 41 | | BEGIN_FOREACH_PATTERN = re.compile('^#foreach[ \t]*\([ \t]*\$(' + NAME + ')[ \t]+in[ \t]+\$' + EXPRESSION + '[ \t]*\)' + REST, RE_FLAGS) |
| 42 | | END_PATTERN = re.compile('^#end' + REST, RE_FLAGS) |
| 43 | | ELSE_PATTERN = re.compile('^#else' + REST, RE_FLAGS) |
| 44 | | COMMENT_PATTERN = re.compile('^##.*?(?:\n|$)' + REST, RE_FLAGS) |
| 45 | | MULTI_LINE_COMMENT_PATTERN = re.compile('^#\*.*?\*#(?:[ \t]*\n)?' + REST, RE_FLAGS) |
| 46 | | |
| 47 | | def tokenise(self, text): |
| 48 | | while True: |
| 49 | | m = self.UP_TO_NEXT_TEMPLATE_BIT.match(text) |
| | 7 | class TemplateSyntaxError(Exception): |
| | 8 | def __init__(self, element, expected, got): |
| | 9 | if len(got) > 40: |
| | 10 | got = got[:36] + ' ...' |
| | 11 | Exception.__init__(self,"%s: expected %s, got: %s ..." % (element.__class__.__name__, expected, got)) |
| | 12 | class NoMatch(Exception): pass |
| | 13 | |
| | 14 | |
| | 15 | class LocalNamespace(dict): |
| | 16 | def __init__(self, parent): |
| | 17 | dict.__init__(self) |
| | 18 | self.parent = parent |
| | 19 | def __getitem__(self, key): |
| | 20 | try: return dict.__getitem__(self, key) |
| | 21 | except KeyError: return self.parent[key] |
| | 22 | |
| | 23 | class TextElement: |
| | 24 | MY_PATTERN = re.compile(r'^((?:[^\\\$#]|\\[\$#])+|\$[^!\{\}a-z0-9_])(.*)$', re.S + re.I) |
| | 25 | def __init__(self, text): |
| | 26 | m = self.MY_PATTERN.match(text) |
| | 27 | if not m: raise NoMatch() |
| | 28 | self.text, self.remaining_text = m.groups() |
| | 29 | |
| | 30 | def evaluate(self, namespace, stream): |
| | 31 | stream.write(self.text) |
| | 32 | |
| | 33 | |
| | 34 | class IntegerLiteralElement: |
| | 35 | MY_PATTERN = re.compile(r'^(\d+)(.*)', re.S) |
| | 36 | def __init__(self, text): |
| | 37 | m = self.MY_PATTERN.match(text) |
| | 38 | if not m: raise NoMatch() |
| | 39 | self.value = int(m.group(1)) |
| | 40 | self.remaining_text = m.group(2) |
| | 41 | |
| | 42 | def calculate(self, namespace): |
| | 43 | return self.value |
| | 44 | |
| | 45 | |
| | 46 | class StringLiteralElement: |
| | 47 | MY_PATTERN = re.compile(r'^"((?:\\["nrbt\\]|[^"\n\r"\\])+)"(.*)', re.S) |
| | 48 | ESCAPED_CHAR = re.compile(r'\\([nrbt"\\])') |
| | 49 | def __init__(self, text): |
| | 50 | m = self.MY_PATTERN.match(text) |
| | 51 | if not m: raise NoMatch() |
| | 52 | def unescape(match): |
| | 53 | return {'n': '\n', 'r': '\r', 'b': '\b', 't': '\t', '"': '"', '\\': '\\'}[match.group(1)] |
| | 54 | self.value = self.ESCAPED_CHAR.sub(unescape, m.group(1)) |
| | 55 | self.remaining_text = m.group(2) |
| | 56 | |
| | 57 | def calculate(self, namespace): |
| | 58 | return self.value |
| | 59 | |
| | 60 | |
| | 61 | class ValueElement: |
| | 62 | def __init__(self, text): |
| | 63 | if text.startswith('$'): |
| | 64 | self.expression = ExpressionElement(text[1:]) |
| | 65 | else: |
| | 66 | try: |
| | 67 | self.expression = IntegerLiteralElement(text) |
| | 68 | except NoMatch: |
| | 69 | self.expression = StringLiteralElement(text) |
| | 70 | self.remaining_text = self.expression.remaining_text |
| | 71 | |
| | 72 | def calculate(self, namespace): |
| | 73 | return self.expression.calculate(namespace) |
| | 74 | |
| | 75 | |
| | 76 | class ExpressionElement: |
| | 77 | NAME_PATTERN = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)(.*)$', re.S) |
| | 78 | def __init__(self, text): |
| | 79 | self.names_and_calls = [] |
| | 80 | try: text = self.read_name_or_call(text) |
| | 81 | except NoMatch: raise TemplateSyntaxError(self, 'name or call', text) |
| | 82 | while text.startswith('.'): |
| | 83 | try: |
| | 84 | text = self.read_name_or_call(text[1:]) |
| | 85 | except NoMatch: # for the '$name. blah' case |
| | 86 | break |
| | 87 | self.remaining_text = text |
| | 88 | |
| | 89 | def read_name_or_call(self, text): |
| | 90 | m = self.NAME_PATTERN.match(text) |
| | 91 | if not m: raise NoMatch() |
| | 92 | name, text = m.groups() |
| | 93 | parameter_list = None |
| | 94 | try: |
| | 95 | parameter_list = ParameterListElement(text) |
| | 96 | text = parameter_list.remaining_text |
| | 97 | except NoMatch: |
| | 98 | pass |
| | 99 | self.names_and_calls.append((name, parameter_list)) |
| | 100 | return text |
| | 101 | |
| | 102 | def calculate(self, namespace): |
| | 103 | result = namespace |
| | 104 | for name, parameters in self.names_and_calls: |
| | 105 | try: result = getattr(result, name) |
| | 106 | except AttributeError: |
| | 107 | try: result = result[name] |
| | 108 | except KeyError: pass |
| | 109 | if result in (None, namespace): return None ## TODO: an explicit 'not found' exception? |
| | 110 | if parameters is not None: |
| | 111 | values = [value.calculate(namespace) for value in parameters.values] |
| | 112 | result = result(*values) |
| | 113 | return result |
| | 114 | |
| | 115 | |
| | 116 | class ParameterListElement: |
| | 117 | OPENING_PATTERN = re.compile(r'^\(\s*(.*)$', re.S) |
| | 118 | CLOSING_PATTERN = re.compile(r'^\s*\)(.*)$', re.S) |
| | 119 | COMMA_PATTERN = re.compile(r'^\s*,\s*(.*)$', re.S) |
| | 120 | |
| | 121 | def __init__(self, text): |
| | 122 | self.values = [] |
| | 123 | m = self.OPENING_PATTERN.match(text) |
| | 124 | if not m: raise NoMatch() |
| | 125 | text = m.group(1) |
| | 126 | while True: ## FIXME |
| | 127 | m = self.COMMA_PATTERN.match(text) |
| | 128 | if not m: break |
| | 129 | value = ValueElement(m.group(1)) |
| | 130 | text = value.remaining_text |
| | 131 | self.values.append(value) |
| | 132 | m = self.CLOSING_PATTERN.match(text) |
| | 133 | if not m: raise TemplateSyntaxError(self, ')', text) |
| | 134 | self.remaining_text = m.group(1) |
| | 135 | |
| | 136 | |
| | 137 | class PlaceholderElement: |
| | 138 | MY_PATTERN = re.compile(r'^\$(!?)(\{?)(.*)$', re.S) |
| | 139 | CLOSING_BRACE_PATTERN = re.compile(r'^\}(.*)$', re.S) |
| | 140 | def __init__(self, text): |
| | 141 | m = self.MY_PATTERN.match(text) |
| | 142 | if not m: raise NoMatch() |
| | 143 | self.silent = bool(m.group(1)) |
| | 144 | self.braces = bool(m.group(2)) |
| | 145 | text = m.group(3) |
| | 146 | try: |
| | 147 | self.expression = ExpressionElement(text) |
| | 148 | text = self.expression.remaining_text |
| | 149 | except NoMatch: |
| | 150 | raise TemplateSyntaxError(self, 'expression', text) |
| | 151 | if self.braces: |
| | 152 | m = self.CLOSING_BRACE_PATTERN.match(text) |
| 51 | | yield self.PLAIN, text |
| | 154 | raise TemplateSyntaxError(self, '}', text) |
| | 155 | text = m.group(1) |
| | 156 | self.remaining_text = text |
| | 157 | |
| | 158 | def evaluate(self, namespace, stream): |
| | 159 | value = self.expression.calculate(namespace) |
| | 160 | if value is None: |
| | 161 | if self.silent: value = '' |
| | 162 | else: |
| | 163 | value_as_str = '.'.join([name for name, params in self.expression.names_and_calls]) |
| | 164 | if self.braces: value = '${%s}' % value_as_str |
| | 165 | else: value = '$%s' % value_as_str |
| | 166 | stream.write(str(value)) |
| | 167 | |
| | 168 | |
| | 169 | class CommentElement: |
| | 170 | COMMENT_PATTERN = re.compile('^##.*?(?:\n|$)(.*)$', re.M + re.S) |
| | 171 | MULTI_LINE_COMMENT_PATTERN = re.compile('^#\*.*?\*#(?:[ \t]*\n)?(.*$)', re.M + re.S) |
| | 172 | def __init__(self, text): |
| | 173 | for pattern in (self.COMMENT_PATTERN, self.MULTI_LINE_COMMENT_PATTERN): |
| | 174 | m = pattern.match(text) |
| | 175 | if not m: continue |
| | 176 | self.remaining_text = m.group(1) |
| | 177 | return |
| | 178 | raise NoMatch() |
| | 179 | |
| | 180 | def evaluate(self, namespace, stream): |
| | 181 | pass |
| | 182 | |
| | 183 | |
| | 184 | class ConditionElement: |
| | 185 | OPENING_PATTERN = re.compile(r'^\(\s*(.*)$', re.S) |
| | 186 | CLOSING_PATTERN = re.compile(r'^\s*\)(.*)$', re.S) |
| | 187 | def __init__(self, text): |
| | 188 | m = self.OPENING_PATTERN.match(text) |
| | 189 | if not m: raise TemplateSyntaxError(self, '(', text) |
| | 190 | text = m.group(1) |
| | 191 | self.expression = ValueElement(text) |
| | 192 | text = self.expression.remaining_text |
| | 193 | m = self.CLOSING_PATTERN.match(text) |
| | 194 | if not m: raise TemplateSyntaxError(self, ')', text) |
| | 195 | self.remaining_text = m.group(1) |
| | 196 | |
| | 197 | def calculate(self, namespace): |
| | 198 | return self.expression.calculate(namespace) |
| | 199 | |
| | 200 | class EndElement: |
| | 201 | END = re.compile(r'^#end(.*)', re.I + re.S) |
| | 202 | def __init__(self, text): |
| | 203 | m = self.END.match(text) |
| | 204 | if not m: raise NoMatch() |
| | 205 | self.remaining_text = m.group(1) |
| | 206 | |
| | 207 | |
| | 208 | class IfElement: |
| | 209 | START = re.compile(r'^#if\b\s*(.*)', re.S + re.I) |
| | 210 | def __init__(self, text): |
| | 211 | m = self.START.match(text) |
| | 212 | if not m: raise NoMatch() |
| | 213 | text = m.group(1) |
| | 214 | self.condition = ConditionElement(text) |
| | 215 | text = self.condition.remaining_text |
| | 216 | self.block = BlockElement(text) |
| | 217 | text = self.block.remaining_text |
| | 218 | try: |
| | 219 | end = EndElement(text) |
| | 220 | self.remaining_text = end.remaining_text |
| | 221 | except NoMatch: |
| | 222 | raise TemplateSyntaxError(self, '#end', text) |
| | 223 | |
| | 224 | def evaluate(self, namespace, stream): |
| | 225 | if self.condition.calculate(namespace): |
| | 226 | self.block.evaluate(namespace, stream) |
| | 227 | |
| | 228 | |
| | 229 | class SetElement: |
| | 230 | START = re.compile(r'^#set\s*\(\s*\$([a-z_][a-z0-9_]*)\s*=\s*(.*)$', re.S + re.I) |
| | 231 | CLOSING_PATTERN = re.compile(r'^\s*\)(.*)$', re.S) |
| | 232 | def __init__(self, text): |
| | 233 | m = self.START.match(text) |
| | 234 | if not m: raise NoMatch() ## Could be cleaner b/c syntax error if no '(' |
| | 235 | self.var_name, text = m.groups() |
| | 236 | self.value = ValueElement(text) |
| | 237 | text = self.value.remaining_text |
| | 238 | m = self.CLOSING_PATTERN.match(text) |
| | 239 | if not m: |
| | 240 | raise TemplateSyntaxError(self, ')', text) |
| | 241 | self.remaining_text = m.group(1) |
| | 242 | |
| | 243 | def evaluate(self, namespace, stream): |
| | 244 | namespace[self.var_name] = self.value.calculate(namespace) |
| | 245 | |
| | 246 | |
| | 247 | class ForeachElement: |
| | 248 | START = re.compile(r'^#foreach\s*\(\s*\$([a-z_][a-z0-9_]*)\s*in\s*(.*)$', re.S + re.I) |
| | 249 | CLOSING_PATTERN = re.compile(r'^\s*\)(.*)$', re.S) |
| | 250 | def __init__(self, text): |
| | 251 | m = self.START.match(text) |
| | 252 | if not m: raise NoMatch() ## Could be cleaner b/c syntax error if no '(' |
| | 253 | self.loop_var_name, text = m.groups() |
| | 254 | self.value = ValueElement(text) |
| | 255 | text = self.value.remaining_text |
| | 256 | m = self.CLOSING_PATTERN.match(text) |
| | 257 | if not m: |
| | 258 | raise TemplateSyntaxError(self, ')', text) |
| | 259 | text = m.group(1) |
| | 260 | self.block = BlockElement(text) |
| | 261 | text = self.block.remaining_text |
| | 262 | try: end = EndElement(text) |
| | 263 | except NoMatch: raise TemplateSyntaxError(self, '#end', text) |
| | 264 | self.remaining_text = end.remaining_text |
| | 265 | |
| | 266 | |
| | 267 | def evaluate(self, namespace, stream): |
| | 268 | iterable = self.value.calculate(namespace) |
| | 269 | counter = 1 |
| | 270 | for item in iterable: |
| | 271 | namespace = LocalNamespace(namespace) |
| | 272 | namespace['velocityCount'] = counter |
| | 273 | namespace[self.loop_var_name] = item |
| | 274 | self.block.evaluate(namespace, stream) |
| | 275 | counter += 1 |
| | 276 | |
| | 277 | class TemplateElement: |
| | 278 | def __init__(self, text): |
| | 279 | self.block = BlockElement(text) |
| | 280 | if self.block.remaining_text: |
| | 281 | raise TemplateSyntaxError(self, 'block element', self.block.remaining_text) |
| | 282 | |
| | 283 | def evaluate(self, namespace, stream): |
| | 284 | namespace = LocalNamespace(namespace) |
| | 285 | self.block.evaluate(namespace, stream) |
| | 286 | |
| | 287 | |
| | 288 | class BlockElement: |
| | 289 | def __init__(self, text): |
| | 290 | self.children = [] |
| | 291 | while text: |
| | 292 | child_matched = False |
| | 293 | for child_type in (TextElement, PlaceholderElement, CommentElement, IfElement, SetElement, ForeachElement): |
| | 294 | try: |
| | 295 | child = child_type(text) |
| | 296 | text = child.remaining_text |
| | 297 | self.children.append(child) |
| | 298 | child_matched = True |
| | 299 | break |
| | 300 | except NoMatch: |
| | 301 | continue |
| | 302 | if not child_matched: |
| 53 | | plain, interesting = m.groups() |
| 54 | | yield self.PLAIN, plain |
| 55 | | m = self.PLACEHOLDER_PATTERN.match(interesting) |
| 56 | | if m: |
| 57 | | expression, silent, original_text, text = self.get_placeholder(m) |
| 58 | | yield self.PLACEHOLDER, (expression, silent, original_text) |
| 59 | | continue |
| 60 | | m = self.BEGIN_IF_PATTERN.match(interesting) |
| 61 | | if m: |
| 62 | | expression, text = m.groups() |
| 63 | | yield self.IF, expression |
| 64 | | continue |
| 65 | | m = self.BEGIN_FOREACH_PATTERN.match(interesting) |
| 66 | | if m: |
| 67 | | iter_var, expression, text = m.groups() |
| 68 | | yield self.FOREACH, (expression, iter_var) |
| 69 | | continue |
| 70 | | m = self.END_PATTERN.match(interesting) |
| 71 | | if m: |
| 72 | | yield self.END, None |
| 73 | | (text,) = m.groups() |
| 74 | | continue |
| 75 | | m = self.SET_PATTERN.match(interesting) |
| 76 | | if m: |
| 77 | | (var_name, rvalue, text) = m.groups() |
| 78 | | yield self.SET, (var_name, rvalue) |
| 79 | | continue |
| 80 | | m = self.ELSE_PATTERN.match(interesting) |
| 81 | | if m: |
| 82 | | (text,) = m.groups() |
| 83 | | yield self.ELSE, None |
| 84 | | continue |
| 85 | | m = self.COMMENT_PATTERN.match(interesting) |
| 86 | | if m: |
| 87 | | (text,) = m.groups() |
| 88 | | continue |
| 89 | | m = self.MULTI_LINE_COMMENT_PATTERN.match(interesting) |
| 90 | | if m: |
| 91 | | (text,) = m.groups() |
| 92 | | continue |
| 93 | | if interesting.startswith('$'): |
| 94 | | text = interesting[1:] |
| 95 | | yield self.PLAIN, '$' |
| 96 | | continue |
| 97 | | raise TemplateSyntaxError("invalid token: %s" % text[:40]) |
| 98 | | |
| 99 | | def get_placeholder(self, match): |
| 100 | | silent, open_brace, var_name, close_brace, rest = match.groups() |
| 101 | | if open_brace and not close_brace: |
| 102 | | raise TemplateSyntaxError("unmatched braces") |
| 103 | | if close_brace and not open_brace: |
| 104 | | rest = close_brace + rest |
| 105 | | original_text = ''.join(('$', silent, var_name)) |
| 106 | | else: |
| 107 | | original_text = ''.join(('$', open_brace, silent, var_name, close_brace)) |
| 108 | | return var_name, bool(silent), original_text, rest |
| 109 | | |
| 110 | | |
| 111 | | |
| 112 | | class Evaluator: |
| 113 | | def eval_expression(self, expression, namespace_dict): |
| 114 | | o = namespace_dict |
| 115 | | for part in expression.split('.'): |
| 116 | | if part.endswith('()'): ## FIXME |
| 117 | | part = part[:-2] |
| 118 | | try: o = getattr(o, part) |
| 119 | | except AttributeError: pass |
| 120 | | else: o = o() |
| 121 | | else: |
| 122 | | try: o = getattr(o, part) |
| 123 | | except AttributeError: |
| 124 | | try: o = o[part] |
| 125 | | except KeyError: pass |
| 126 | | if o in (None, namespace_dict): return None |
| 127 | | return o |
| 128 | | |
| 129 | | |
| 130 | | class BlockEvaluator(Evaluator): |
| 131 | | class LocalNamespace(dict): |
| 132 | | def __init__(self, parent_namespace): |
| 133 | | self.parent_namespace = parent_namespace |
| 134 | | def __getitem__(self, key): |
| 135 | | try: return dict.__getitem__(self, key) |
| 136 | | except KeyError: return self.parent_namespace[key] |
| 137 | | |
| 138 | | def __init__(self): |
| 139 | | self.children = [] |
| 140 | | self.delegate = None |
| 141 | | |
| 142 | | def evaluate(self, output_stream, namespace): |
| 143 | | self.evaluate_block(output_stream, BlockEvaluator.LocalNamespace(namespace)) |
| 144 | | |
| 145 | | def evaluate_block(self, output_stream, namespace): |
| | 304 | self.remaining_text = text |
| | 305 | |
| | 306 | def evaluate(self, namespace, stream): |
| 147 | | child.evaluate(output_stream, namespace) |
| 148 | | |
| 149 | | def add_evaluator(self, evaluator): |
| 150 | | self.children.append(evaluator) |
| 151 | | if hasattr(evaluator, 'add_evaluator'): |
| 152 | | self.delegate = evaluator |
| 153 | | |
| 154 | | def delegate_token(self, token_type, token_value): |
| 155 | | if self.delegate: |
| 156 | | if not self.delegate.feed(token_type, token_value): |
| 157 | | self.delegate = None |
| 158 | | return True |
| 159 | | return False |
| 160 | | |
| 161 | | def feed(self, token_type, token_value): |
| 162 | | if self.delegate_token(token_type, token_value): |
| 163 | | return True |
| 164 | | if token_type == Tokeniser.END: return False |
| 165 | | elif token_type == Tokeniser.PLAIN: self.add_evaluator(PlainTextEvaluator(token_value)) |
| 166 | | elif token_type == Tokeniser.PLACEHOLDER: self.add_evaluator(PlaceholderEvaluator(token_value)) |
| 167 | | elif token_type == Tokeniser.FOREACH: self.add_evaluator(ForeachEvaluator(token_value)) |
| 168 | | elif token_type == Tokeniser.IF: self.add_evaluator(IfEvaluator(token_value)) |
| 169 | | elif token_type == Tokeniser.SET: self.add_evaluator(SetEvaluator(token_value)) |
| 170 | | else: raise TemplateSyntaxError("illegal token in block: %s, %s" % (token_type, token_value)) |
| 171 | | return True |
| 172 | | |
| 173 | | |
| 174 | | class PlainTextEvaluator(Evaluator): |
| 175 | | def __init__(self, text): |
| 176 | | self.text = text |
| 177 | | |
| 178 | | def evaluate(self, output_stream, namespace): |
| 179 | | output_stream.write(self.text) |
| 180 | | |
| 181 | | |
| 182 | | class PlaceholderEvaluator(Evaluator): |
| 183 | | def __init__(self, token_value): |
| 184 | | self.expression, self.silent, self.original_text = token_value |
| 185 | | |
| 186 | | def evaluate(self, output_stream, namespace): |
| 187 | | value = self.eval_expression(self.expression, namespace) |
| 188 | | if value is None: |
| 189 | | if self.silent: expression_value = '' |
| 190 | | else: expression_value = self.original_text |
| 191 | | else: |
| 192 | | expression_value = str(value) |
| 193 | | output_stream.write(expression_value) |
| 194 | | |
| 195 | | |
| 196 | | class IfEvaluator(BlockEvaluator): |
| 197 | | def __init__(self, token_value): |
| 198 | | BlockEvaluator.__init__(self) |
| 199 | | self.condition_expression = token_value |
| 200 | | |
| 201 | | def evaluate_block(self, output_stream, namespace): |
| 202 | | value = self.eval_expression(self.condition_expression, namespace) |
| 203 | | if value: |
| 204 | | BlockEvaluator.evaluate_block(self, output_stream, namespace) |
| 205 | | |
| 206 | | |
| 207 | | class ForeachEvaluator(BlockEvaluator): |
| 208 | | def __init__(self, token_value): |
| 209 | | BlockEvaluator.__init__(self) |
| 210 | | self.expression, self.iter_var = token_value |
| 211 | | |
| 212 | | def evaluate_block(self, output_stream, namespace): |
| 213 | | values = self.eval_expression(self.expression, namespace) |
| 214 | | counter = 1 |
| 215 | | for value in values: |
| 216 | | namespace[self.iter_var] = value |
| 217 | | namespace['velocityCount'] = counter |
| 218 | | BlockEvaluator.evaluate_block(self, output_stream, namespace) |
| 219 | | counter += 1 |
| 220 | | |
| 221 | | |
| 222 | | class SetEvaluator(Evaluator): |
| 223 | | def __init__(self, token_value): |
| 224 | | self.var_name, self.rvalue = token_value |
| 225 | | |
| 226 | | def evaluate(self, output_stream, namespace): |
| 227 | | if self.rvalue.startswith('"'): |
| 228 | | value = self.rvalue[1:-1] |
| 229 | | else: |
| 230 | | value = int(self.rvalue) |
| 231 | | namespace[self.var_name] = value |
| | 308 | child.evaluate(namespace, stream) |
| | 309 | |