00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174 import string
00175 import re
00176 from types import StringType, IntType, FloatType, DictType, ListType, ClassType
00177 import sys
00178
00179 class Template:
00180 """
00181 usage:
00182 tempalte_text = read template text from file
00183 dictionary = create dictionaly by using yaml
00184 t = Template(tempalte_text)
00185 generated_text = t.generate(dictionary)
00186
00187 """
00188
00189 def __init__(self, template, begin_mark="\[", end_mark="\]"):
00190 self.__procs = [self.__proc_text,
00191 self.__proc_cmd,
00192 self.__proc_bracket]
00193 self.template = template
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221 re_item = r'(?:"(?:[^\\"]|\\.)*"|[-\w.:]+)'
00222 re_command = r'%s(%s(?: +%s)*)%s' % \
00223 (begin_mark, re_item, re_item, end_mark)
00224 re_bracket = r'%s%s%s' % \
00225 (begin_mark, begin_mark, end_mark)
00226 re_comment = r'%s#[^%s]*%s' % \
00227 (begin_mark, end_mark, end_mark)
00228 self.begin_mark = begin_mark.replace("\\","")
00229 self.re_parse = re.compile(r'%s|(%s)|%s' % \
00230 (re_command, re_bracket, re_comment))
00231 self.re_args = re.compile(r'"(?:[^\\"]|\\.)*"|[-\w.:]+')
00232 self.re_number = re.compile(r'[0-9]+')
00233
00234
00235 self.token = self.re_parse.split(self.template)
00236 self.token_len = len(self.token)
00237
00238
00239 self.script = program
00240 self.indent = 4
00241 self.script_level = 2
00242 self.level = 0
00243 self.index = 0
00244 self.cmd_cxt = []
00245
00246
00247 self.__parse_template(self.token)
00248
00249 return
00250
00251 def generate(self, dict):
00252
00253 exec(self.script)
00254
00255 gen = Generator(self.token, dict)
00256
00257 return gen.generate()
00258
00259 def get_script(self):
00260 return self.script
00261
00262 def __push_level(self):
00263 self.level += 1
00264
00265 def __pop_level(self):
00266 self.level -= 1
00267
00268 def __write_cmd(self, cmd):
00269 tmp_cmd = self.__indent()
00270 tmp_cmd += "self.set_index(%s)\n" % (self.index)
00271 self.script += tmp_cmd
00272 self.__write_cmd_noindex(cmd)
00273
00274 def __write_cmd_noindex(self, cmd):
00275 tmp_cmd = self.__indent()
00276 tmp_cmd += cmd + "\n"
00277 self.script += tmp_cmd
00278
00279 def __parse_template(self, dict):
00280 try:
00281
00282 self.__parse()
00283 except YATException, e:
00284 self.__print_error(e)
00285 sys.exit(-1)
00286
00287 def __indent(self):
00288 indent = " " * ((self.script_level + self.level) * self.indent)
00289 return indent
00290
00291 def __parse(self):
00292 while self.index < self.token_len:
00293 self.__procs[self.index % 3]()
00294 self.index += 1
00295
00296 def __proc_text(self):
00297 if self.token[self.index] == None:
00298 return
00299 cmd_text = "self.write_token(%s)" % (self.index)
00300 self.__write_cmd(cmd_text)
00301 return True
00302
00303 def __proc_bracket(self):
00304 if self.token[self.index] == None:
00305 return
00306 cmd_text = "self.write(\"" + self.begin_mark + "\")"
00307 self.__write_cmd(cmd_text)
00308 return True
00309
00310 def __proc_cmd(self):
00311 cmd = self.token[self.index]
00312 try:
00313 args = self.re_args.findall(cmd)
00314 except:
00315 return
00316 self.del_nl_after_cmd()
00317 argc = len(args)
00318 if argc == 0:
00319 raise InvalidDirective(self.lineno(), "_an empty directive_ ")
00320
00321
00322 if argc == 1:
00323 if args[0] == "endfor":
00324 self.__endfor_cmd(args)
00325 return
00326 elif args[0] == "else":
00327 self.__else_cmd(args)
00328 return
00329 elif args[0] == "last":
00330 self.__last_cmd(args)
00331 return
00332 elif args[0] == "endif":
00333 self.__endif_cmd(args)
00334 return
00335 else:
00336 self.__cmd(args)
00337 return
00338 elif argc == 2:
00339 if args[0] == "if-any":
00340 self.__if_any_cmd(args)
00341 return
00342 elif argc == 4:
00343 if args[0] == "for" and args[2] == "in":
00344 self.__for_cmd(args)
00345 return True
00346 elif args[0] == "for-inv" and args[2] == "in":
00347 self.__for_inv_cmd(args)
00348 return True
00349 elif args[0] == "if" and args[2] == "is":
00350 self.__if_cmd(args)
00351 elif args[0] == "elif" and args[2] == "is":
00352 self.__elif_cmd(args)
00353 elif args[0] == "if-index" and args[2] == "is":
00354 self.__if_index_cmd(args)
00355 elif args[0] == "elif-index" and args[2] == "is":
00356 self.__elif_index_cmd(args)
00357 else:
00358 raise InvalidDirective(self.lineno(), cmd)
00359 else:
00360 raise InvalidDirective(self.lineno(), cmd)
00361 return True
00362
00363 def __cmd(self, args):
00364 cmd_text = "self.write_dict(\"%s\")" % (args[0])
00365 self.__write_cmd(cmd_text)
00366
00367
00368
00369
00370
00371
00372
00373 def __for_cmd(self, args):
00374 """
00375 The following [for] directive
00376 [for tmp_key in directive]
00377 is converted into the following python command.
00378 for i in len(directive):
00379 self.dicts.append({tmp_key: ditective[i])
00380 and, endfor directive terminate as the following,
00381 self.dicts.pop()
00382 """
00383 key = args[1]
00384 directive = args[3]
00385
00386
00387
00388 cmd_text = "%s_list = self.get_list(\"%s\")" % (key, directive)
00389 self.__write_cmd(cmd_text)
00390 cmd_text = "%s_len = len(%s_list)" % (key, key)
00391 self.__write_cmd(cmd_text)
00392 cmd_text = "for %s_index in range(len(%s_list)):" % (key, key)
00393 self.__write_cmd(cmd_text)
00394 self.__push_level()
00395 cmd_text = "self.push_dict({\"%s\": %s_list[%s_index]})" \
00396 % (key, key, key)
00397 self.__write_cmd(cmd_text)
00398 self.cmd_cxt.append("for")
00399
00400 def __for_inv_cmd(self, args):
00401 """
00402 The following [for] directive
00403 [for tmp_key in directive]
00404 is converted into the following python command.
00405 for i in len(directive):
00406 self.dicts.append({tmp_key: ditective[i])
00407 and, endfor directive terminate as the following,
00408 self.dicts.pop()
00409 """
00410 key = args[1]
00411 directive = args[3]
00412
00413
00414
00415 cmd_text = "%s_list = self.get_list(\"%s\")" % (key, directive)
00416 self.__write_cmd(cmd_text)
00417 cmd_text = "%s_len = len(%s_list)" % (key, key)
00418 self.__write_cmd(cmd_text)
00419 cmd_text = "for %s_index in range(len(%s_list))[::-1]:" % (key, key)
00420 self.__write_cmd(cmd_text)
00421 self.__push_level()
00422 cmd_text = "self.push_dict({\"%s\": %s_list[%s_index]})" \
00423 % (key, key, key)
00424 self.__write_cmd(cmd_text)
00425 self.cmd_cxt.append("for-inv")
00426
00427 def __endfor_cmd(self, args):
00428 try:
00429 cxt = self.cmd_cxt.pop()
00430 if cxt != "for" and cxt != "for-inv":
00431 raise UnmatchedBlock(self.lineno(), "endfor")
00432 self.__write_cmd("self.pop_dict()")
00433 self.__pop_level()
00434 except:
00435 print args, self.lineno()
00436 raise UnmatchedBlock(self.lineno(), "endfor")
00437 return
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448 def __if_cmd(self, args):
00449 """
00450 The following [if] directive
00451 [if directive is string]
00452 is converted into the following python command.
00453 if self.__get_string() == "string":
00454 """
00455 directive = args[1]
00456 string = args[3]
00457 if string[0] == '"' or string[-1] == "'" \
00458 or string[0] == '"' or string[-1] == "'":
00459 cmd_text = "if self.get_text(\"%s\") == %s:" % \
00460 (directive, string)
00461 else:
00462 cmd_text = "if self.get_text(\"%s\") == self.get_text(\"%s\"):" % \
00463 (directive, string)
00464 self.__write_cmd(cmd_text)
00465 self.__push_level()
00466 self.cmd_cxt.append("if")
00467 return
00468
00469 def __elif_cmd(self, args):
00470 if self.cmd_cxt[-1] != "if":
00471 raise UnmatchedBlock(self.lineno(), "elif")
00472 directive = args[1]
00473 string = args[3]
00474 if string[0] == '"' or string[-1] == "'" \
00475 or string[0] == '"' or string[-1] == "'":
00476 cmd_text = "elif self.get_text(\"%s\") == %s:" % \
00477 (directive, string)
00478 else:
00479 cmd_text = "elif self.get_text(\"%s\") == self.get_text(\"%s\"):" % \
00480 (directive, string)
00481 self.__pop_level()
00482 self.__write_cmd_noindex(cmd_text)
00483 self.__push_level()
00484 return
00485
00486
00487 def __if_index_cmd(self, args):
00488
00489
00490 cmdlist = {"first": "if %s_index == 0:",
00491 "even" : "if (%s_index %% 2) == 0:",
00492 "odd" : "if (%s_index %% 2) != 0:",
00493 "last" : "if %s_index == %s_len - 1:"}
00494 key = args[1]
00495 cmd = args[3]
00496 if len(self.re_number.findall(cmd)) == 1:
00497 cmd_text = "if %s_index == %s:" % (key, cmd)
00498 elif cmdlist.has_key(cmd):
00499 if cmd == "last":
00500 cmd_text = cmdlist[cmd] % (key,key)
00501 else:
00502 cmd_text = cmdlist[cmd] % (key)
00503 else:
00504 raise InvalidDirective(self.lineno(), ''.join(args))
00505 self.__write_cmd(cmd_text)
00506 self.__push_level()
00507 self.cmd_cxt.append("if-index")
00508
00509 def __elif_index_cmd(self, args):
00510 if self.cmd_cxt[-1] != "if-index":
00511 raise UnmatchedBlock(self.lineno(), "elif-index")
00512
00513
00514 cmdlist = {"first": "elif %s_index == 0:",
00515 "even" : "elif (%s_index %% 2) == 0:",
00516 "odd" : "elif (%s_index %% 2) != 0:",
00517 "last" : "elif %s_index == %s_len - 1:"}
00518 key = args[1]
00519 cmd = args[3]
00520 if len(self.re_number.findall(cmd)) == 1:
00521 cmd_text = "elif %s_index == %s:" % (key, cmd)
00522 elif cmdlist.has_key(cmd):
00523 if cmd == "last":
00524 cmd_text = cmdlist[cmd] % (key,key)
00525 else:
00526 cmd_text = cmdlist[cmd] % (key)
00527 else:
00528 raise InvalidDirective(self.lineno(), ' '.join(args))
00529 self.__pop_level()
00530 self.__write_cmd_noindex(cmd_text)
00531 self.__push_level()
00532
00533
00534 def __if_any_cmd(self, args):
00535 directive = args[1]
00536 cmd_text = "if self.has_key(\"%s\"):" % (directive)
00537 self.__write_cmd(cmd_text)
00538 self.__push_level()
00539 self.cmd_cxt.append("if-any")
00540 return
00541
00542 def __elif_any_cmd(self, args):
00543 if self.cmd_cxt[-1] != "if-any":
00544 raise UnmatchedBlock(self.lineno(), "elif-any")
00545 directive = args[1]
00546 cmd_text = "if self.has_key(\"%s\"):" % (directive)
00547 self.__pop_level()
00548 self.__write_cmd_noindex(cmd_text)
00549 self.__push_level()
00550 return
00551
00552
00553 def __else_cmd(self, args):
00554 if self.cmd_cxt[-1] != "if" and self.cmd_cxt[-1] != "if-index" \
00555 and self.cmd_cxt[-1] != "if-any":
00556 raise UnmatchedBlock(self.lineno(), "else")
00557 self.__pop_level()
00558 self.__write_cmd_noindex("else:")
00559 self.__push_level()
00560 return
00561
00562 def __endif_cmd(self, args):
00563 try:
00564 if self.cmd_cxt[-1] != "if" and self.cmd_cxt[-1] != "if-index" \
00565 and self.cmd_cxt[-1] != "if-any":
00566 raise UnmatchedBlock(self.lineno(), "endif")
00567 self.cmd_cxt.pop()
00568 self.__pop_level()
00569 except:
00570 raise UnmatchedBlock(self.lineno(), "endif")
00571 return
00572
00573
00574
00575 def __print_error(self, e):
00576 print "Parse Error: line", e.lineno, "in input data"
00577 print " " + ''.join(nesteditem(e.value))
00578 lines = self.template.split("\n")
00579 length = len(lines)
00580 print "------------------------------------------------------------"
00581 for i in range(1,10):
00582 l = e.lineno - 6 + i
00583 if l > 0 and l < length:
00584 print lines[l]
00585 if i == 5:
00586 uline = '~'*len(lines[l])
00587 print uline
00588 print "------------------------------------------------------------"
00589 if hasattr(e, 'context'):
00590 print "Current context:"
00591 print e.context
00592
00593
00594 def del_nl_after_cmd(self):
00595
00596 next = self.index + 2
00597 if next > self.token_len: return
00598 if self.token[next] == None: return
00599 text = self.token[next]
00600 tlen = len(text)
00601 if tlen > 0 and text[0] == '\n':
00602 self.token[next] = text[1:]
00603 return
00604 elif tlen > 0 and text[0] == '\r':
00605 self.token[next] = text[1:]
00606 return
00607 elif tlen > 1 and text[0:2] == '\r\n':
00608 self.token[next] = text[2:]
00609
00610 def lineno(self):
00611 l = 1
00612 for i in range(self.index):
00613 if isinstance(self.token[i], StringType):
00614 l += self.token[i].count('\n')
00615 for i in range(1, self.index, 3):
00616 l += 1
00617 return l
00618
00619
00620
00621
00622
00623 program = """
00624 class Generator(GeneratorBase):
00625 def __init__(self, token, dict):
00626 GeneratorBase.__init__(self, token, dict)
00627 def generate(self):
00628 try:
00629 self.process()
00630 except YATException, e:
00631 self.print_error(e)
00632 sys.exit(-1)
00633 return self.text
00634
00635 def process(self):
00636 """
00637
00638 class GeneratorBase:
00639 def __init__(self, token, dict):
00640 self.token = token
00641 self.dicts = [dict]
00642 self.index = 0
00643 self.text = ""
00644
00645 def print_error(self, e):
00646 print "\nTemplate Generation Error: line", e.lineno, "in input data"
00647 print " " + ''.join(nesteditem(e.value))
00648 temp = ""
00649 for i, s in enumerate(self.token):
00650 if s != None:
00651 if i % 3 == 1:
00652 temp += "[" + s + "]\n"
00653 else:
00654 temp += s
00655 lines = temp.split("\n")
00656 length = len(lines)
00657 print "Template text:"
00658 print "------------------------------------------------------------"
00659 for i in range(1,10):
00660 l = e.lineno - 6 + i
00661 if l > 0 and l < length:
00662 print lines[l]
00663 if i == 5:
00664 uline = '~'*len(lines[l])
00665 print uline
00666 if hasattr(e, 'context'):
00667 print "\nCurrent context:"
00668 print "------------------------------------------------------------"
00669 print e.context
00670
00671 def set_index(self, index):
00672 self.index = index
00673
00674 def push_dict(self, dict):
00675 self.dicts.append(dict)
00676
00677 def pop_dict(self):
00678 if len(self.dicts) < 2:
00679 raise UnmatchedBlock(self.lineno(), "")
00680 self.dicts.pop()
00681
00682 def write(self, text):
00683 self.text += text
00684
00685 def write_dict(self, keytext):
00686 self.write(self.get_text(keytext))
00687
00688 def write_token(self, index):
00689 self.write(self.token[index])
00690
00691 def lineno(self):
00692 cnt = 1
00693 for i in range(0, self.index, 3):
00694 if self.token[i] != None:
00695 cnt += self.token[i].count('\n')
00696
00697 for i in range(1, self.index, 3):
00698 if self.token[i] != None:
00699 cnt += 1
00700 return cnt
00701
00702 def get_text(self, keytext):
00703 val = self.get_value(keytext)
00704 if isinstance(val, StringType):
00705 return val
00706 if isinstance(val, IntType) or isinstance(val, FloatType):
00707 return str(val)
00708 raise UnexpectedData(self.lineno(), "\"" + keytext + \
00709 "\" should have string, int or float value.")
00710
00711 def get_list(self, keytext):
00712 val = self.get_value(keytext)
00713 if not isinstance(val, ListType):
00714 raise UnexpectedData(self.lineno(),
00715 "\"" + keytext + "\" should have list value.")
00716 return val
00717
00718 def has_key(self, keytext):
00719 try:
00720 self.get_value(keytext)
00721 return True
00722 except NotFound, e:
00723 return False
00724
00725 def get_value(self, keytext):
00726 keys = keytext.split('.')
00727 for i in range(len(self.dicts) - 1, -1, -1):
00728 dict_value = self.get_dict_value(keys, self.dicts[i])
00729 if dict_value != None:
00730 return dict_value
00731
00732
00733 keys.pop()
00734 if len(keys) != 0:
00735 raise NotFound(self.lineno(), keytext, self.get_value(keys[0]))
00736 raise NotFound(self.lineno(), keytext)
00737
00738 def get_dict_value(self, keys, dict):
00739 length = len(keys)
00740 d = dict
00741 for i in range(length):
00742 if isinstance(d, DictType) and d.has_key(keys[i]):
00743 d = d[keys[i]]
00744 else:
00745 return None
00746 return d
00747
00748
00749
00750
00751
00752 class YATException(Exception):
00753 pass
00754
00755 class UnknownError(YATException):
00756 def __init__(self, lineno):
00757 self.lineno = lineno
00758 self.value = "Unknown error."
00759
00760 class UnmatchedBlock(YATException):
00761 def __init__(self, lineno, msg):
00762 self.lineno = lineno
00763 self.value = "Unmatched block error: " + msg
00764
00765 class UnexpectedData(YATException):
00766 def __init__(self, lineno, msg):
00767 self.lineno = lineno
00768 self.value = msg
00769
00770 class NotFinalElement(YATException):
00771 def __init__(self, dictkey, dictvalue):
00772 self.value = "Specified key is not final element: ",\
00773 dictkey, "=>", dictvalue
00774
00775 class InvalidDirective(YATException):
00776 def __init__(self, lineno, directive):
00777 self.lineno = lineno
00778 self.value = "Invalid directive: \"[" + directive + "]\""
00779
00780 class UnmatchedData(YATException):
00781 def __init__(self, lineno, description):
00782 self.lineno = lineno
00783 self.value = "Unmatched data and input: ", description
00784
00785 class NotFound(YATException):
00786 def __init__(self, lineno, description, context = None):
00787 self.lineno = lineno
00788 self.value = "Value not found for: \"" + description + "\"\n"
00789 if context != None:
00790 try:
00791 import yaml
00792 self.context = yaml.dump(context, default_flow_style = False)
00793 except:
00794 pass
00795
00796
00797
00798
00799 def nesteditem(aList):
00800 for anItem in aList:
00801 if type(anItem)==list:
00802 for subitem in nesteditem(anItem):
00803 yield subitem
00804 else:
00805 yield anItem
00806
00807
00808
00809 if __name__ == "__main__":
00810 dict = []
00811 template = []
00812
00813
00814
00815 dict.append({"a": "This is a",
00816 "b": {"1": "This is b.1",
00817 "2": "This is b.2"}
00818 })
00819 template.append("""[a]
00820
00821 [b.1]
00822
00823 [b.2]""")
00824
00825
00826
00827
00828 dict.append({"list": [0, 1, 2],
00829 "listed_dict": [
00830 {"name": "x", "value": "1.0"},
00831 {"name": "y", "value": "0.2"},
00832 {"name": "z", "value": "0.1"}]})
00833 template.append("""[for lst in list]
00834 [lst],
00835 [endfor]
00836 [for lst in listed_dict]
00837 [lst.name]: [lst.value]
00838
00839 [endfor]""")
00840
00841
00842
00843
00844 dict.append({"list": [0,1,2,3,4,5,6,7,8,9,10]})
00845 template.append("""[for key in list]
00846 [if-index key is 3] [key] is hoge!!
00847 [elif-index key is 6] [key] is foo!!
00848 [elif-index key is 9] [key] is bar!!
00849 [elif-index key is first] [key] is first
00850 [elif-index key is last] Omoro-------!!!!
00851 [elif-index key is odd] [key] is odd number
00852 [elif-index key is even] [key] is even number
00853 [endif]
00854 [endfor]""")
00855
00856
00857
00858
00859 dict.append({"key1": "a", "key2": "b"})
00860 template.append("""[if key1 is a]
00861 The key1 is "a".
00862 [else]
00863 This key1 is not "a".
00864 [endif]""")
00865
00866
00867
00868
00869 dict.append({"key1": "a", "key2": "b"})
00870 template.append("""[if-any key1]
00871 key1 exists.
00872 [endif][if-any key3]
00873 key3 exists.
00874 [else]
00875 key3 does not exists.
00876 [endif]""")
00877
00878 dict.append({})
00879 template.append("""
00880 [[]bracket]
00881 [# comment]
00882 """)
00883
00884 import yaml
00885 if len(dict) == len(template):
00886 for i in range(len(dict)-1,len(dict)):
00887 t = Template(template[i])
00888 print "-" * 60
00889 print "Example:", i
00890 print "-" * 60
00891 print "Template:\n"
00892 print template[i]
00893 print "-" * 60
00894 print "Dictionary:\n"
00895 print yaml.dump(dict[i], default_flow_style=False)
00896 print "-" * 60
00897 print "Generated Script:\n"
00898 print t.get_script()
00899 print "-" * 60
00900 print "Generated Text:\n"
00901 print t.generate(dict[i])
00902 print ""