pymacro.py - text macros in python Using embedded python as a macro language. In a sense, this is a bit like php or asp - the text goes straight through, except for @ signalled stuff, which can be processed in a variety of ways. It's not the same in the sense that asp/php have an implied structure across boundaries; an example would be instructive.
13 __Id__ = "$Id: pymacro.py.html,v 1.1 2005/01/26 01:13:52 u37519820 Exp $" Table of Contents
Introductionmacro syntax:direct evaluation @(python expression) direct execution @{python code} indirect evaluation @function(parameters...) implied string parameter @function{a string without quotes} alternative string delimeters: @function/almost any delimeter/
to end of block/of text (depending on context)
comment to end of line:
single symbol - evaluated or executed (no () needed for 0 parm functions)
Was using re to find the brackets and parentheses, which isn't constructive
due to nesting issues. _find_matching implemented instead. ImportsAll standard python: sys, string, re, traceback, cStringIOGlobals94 _prefix = '@' # macro _prefix 95 96 # kind of a loose definition of symbol, so what 97 symbol_match = r"[A-Za-z_][A-Za-z0-9_\.]*" 98 integer_match = r"[0-9]+" 99 100 _re_white = re.compile("\s") 101 _re_symbol = re.compile(symbol_match) 102 _re_integer = re.compile(integer_match) 103 104 # be careful, macros execute in this name space 105 106 _g = globals() 107 _l = _g 108 109 write = sys.stdout.write pymax_process - public_process: primary internal function129 def _process(line, out): 136 while 1: 139 pos = string.find(line, _prefix) 166 if c == '{': # exec anything 167 ep = _find_matching(line, 0, '{', '}') # not necessary? 168 v = pymax_exec(line[1:ep-1]) 169 elif c == '(': # eval anything 170 ep = _find_matching(line, 0, '(', ')') # necessary! 171 v = pymax_eval(line[:ep]) 172 elif c == '#': # comment to end of line 173 ep = _must_find(line, '\n', 0) 174 v = '' 191 if c == '{': # single string 192 ep = _find_matching(line, p, '{', '}') 193 v = _do(s, line[p+1:ep-1]) 194 elif c == '(': # parameter list 195 ep = _find_matching(line, p, '(', ')') # necessary! 196 v = pymax_eval(line[:ep]) 197 elif c == ':': # to EOF 198 ep = 0 199 v = _do(s, line[p+1:]) 200 line = '' 201 elif c == '=': # assignment 202 ep, v = _assign(s, line, p+1) 203 elif c == '@': # standalone, abutted 204 ep = p+1 205 v = pymax_eval(s) 206 if callable(v): 207 v = v() 208 elif c in string.whitespace: # standalone 209 ep = p 210 v = pymax_eval(s) 211 if callable(v): 212 v = v() 213 else: # single string, arbitrary delimeter 214 ep = _must_find(line, c, p+1) 215 v = _do(s, line[p+1:ep-1]) _assign: handle assignment226 def _assign(s, line, p): 227 if line[p] == '"': 228 if line[p+1] == '"': 229 ep = _must_find(line, '"""', p+3) + 2 230 else: 231 ep = _must_find(line, '"', p+1) 232 v = pymax_exec(line[:ep]) #assuming s @ line[0] 233 234 elif line[p] == "'": 235 if line[p+1] == "'": 236 ep = _must_find(line, "'''", p+3) + 2 237 else: 238 ep = _must_find(line, "'", p+1) 239 v = pymax_exec(line[:ep]) #assuming s @ line[0] 240 241 elif line[p] == '(': 242 ep = _find_matching(line, p, '(', ')') # not necessary? 243 v = pymax_exec(line[:ep]) #assuming s @ line[0] 244 245 else: 246 m = _re_white.search(line, p) 247 if not m: 248 ep = len(line) 249 else: 250 ep = m.end() 251 if _re_integer.match(line[p:ep]): 252 v = pymax_exec(line[:ep]) #assuming s @ line[0] 253 else: # assume it's a string 254 v = pymax_exec( s + '=' + '"""' + line[p:ep] + '"""' ) 255 256 return ep, v Utility functions262 def pymax_excepted(f, l): 263 """ error wrapping """ 264 t = sys.exc_info() 265 s = string.join(traceback.format_exception(t[0], t[1], t[2])) + "\nFor: " + l 266 # report to stderr as well 267 sys.stderr.write(s) 268 return s 269 270 def pymax_eval(line): 271 """ eval with error wrapping """ 272 try: 273 return eval(line,_g,_l) 274 except: 275 return pymax_excepted('pymax_eval', line) 276 277 def pymax_exec(line): 278 """ exec with error wrapping """ 279 try: 280 exec line in _g, _l 281 return '' 282 except: 283 return pymax_excepted('pymax_exec', line) 284 285 def _must_find(s, e, p=0): 286 """ puke if not found """ 287 ep = string.find(s, e, p) 288 if ep < 0: 289 raise "NoMatching", e 290 return ep+1 291 292 def _find_matching(s, p, b, e): 293 """ handle nesting () and {} """ 294 nb = 1 295 ne = 0 296 p = p + 1 297 while ne < nb: 298 ep = string.find(s, e, p) 299 if ep < 0: 300 raise "NoMatching", e 301 nb = nb + string.count(s, b, p, ep) 302 ne = ne + 1 303 p = ep + 1 304 return ep+1 # next char 305 306 def _do(v, p): 307 """ one symbol with one string parameter """ 308 try: 309 v = pymax_eval(v) 310 if callable(v): 311 return v(p) 312 else: # auto format % string 313 return v % p 314 except: 315 return pymax_excepted('_do', p) macrosmain - for testing |