#!/usr/bin/env python ############################################################################ # # Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) # # See accompanying file COPYING.TXT file for licensing details. # ############################################################################ import os import re import sys import StringIO str_match=r'"([^"\\]|\\[^"]|\\")*"' single_var_param_match=r'(?:-?\d+|"(?:[^"\\]|\\[^"]|\\")*")' call_param_match=r'(?:\(\)|\((?:' + single_var_param_match + r')(?:,' + single_var_param_match + r')*\))' variable_match=r"\*?([a-zA-Z][a-zA-Z0-9_]*"+ call_param_match +r"?)(((\.|->)([a-zA-Z][a-zA-Z0-9_]*" + call_param_match + r"?))*)" def interleave(*args): for idx in range(0, max(map(len,args))): for arg in args: try: yield arg[idx] except IndexError: continue def output_declaration(s): global stack global file_name global line_number global declarations declarations.write('\t'*len(stack) + '#line %d "%s"' % (line_number,file_name)+'\n') declarations.write('\t'*len(stack) + s + '\n'); def output_definition(s): global stack global file_name global line_number global definitions definitions.write('\t'*(len(stack)-1) + '#line %d "%s"' % (line_number,file_name)+'\n') definitions.write('\t'*(len(stack)-1) + s + '\n'); def output_all(s): output_definition(s) output_declaration(s) class tmpl_descr: def __init__(self,start,size): self.start_id=start self.param_num=size class skin_block: basic_pattern='skin' basic_name='skin' pattern=r'^<%\s*skin\s+(\w+)?\s*%>$' type='skin' def use(self,m): global inline_cpp_to global namespace_name inline_cpp_to = output_declaration name = m.group(1) if namespace_name == '': if name == '': error_ext("Skin name is not defined implicitly or explicitly") else: namespace_name = name elif namespace_name != name and name: error_exit("Can't use more then one skin name for same skin: %s, %s" % ( namespace_name,name)) output_all( "namespace %s {" % namespace_name) global stack stack.append(self) def on_end(self): global namespace_name output_all( "} // end of namespace %s" % namespace_name) def write_class_loader(unsafe = False): global class_list global namespace_name output_definition("namespace {") output_definition(" cppcms::views::generator my_generator; ") output_definition(" struct loader { ") output_definition(" loader() { ") output_definition(' my_generator.name("%s");' % namespace_name) if unsafe: safe = 'false' else: safe = 'true' for class_def in class_list: output_definition( ' my_generator.add_view<%s::%s,%s>("%s",%s);' \ % (class_def.namespace,class_def.name,class_def.content_name,class_def.name,safe)) output_definition(' cppcms::views::pool::instance().add(my_generator);') output_definition(' }') output_definition(' ~loader() { cppcms::views::pool::instance().remove(my_generator); }') output_definition('} a_loader;') output_definition('} // anon ') class html_type: basic_pattern='x?html' basic_name='xhtml' pattern=r'^<%\s*(x)?html\s*%>$' def use(self,m): global html_type_code if m.group(1): html_type_code='as_xhtml' else: html_type_code='as_html' class view_block: pattern=r'^<%\s*view\s+(\w+)\s+uses\s+(:?:?\w+(::\w+)*)(\s+extends\s+(:?:?\w+(::\w+)?))?(?P\s+abstract)?(?P\s+inline)?\s*%>$' basic_pattern='view' basic_name='view' type='view' topmost = 0 def declare(self): if self.extends=='' : constructor='cppcms::base_view(_s)' self.extends='cppcms::base_view' self.topmost = 1 else: constructor='%s(_s,_content)' % self.extends; global dll_api if self.inline: api_ref = '' else: api_ref = dll_api output_declaration( "struct %s %s :public %s" % (api_ref, self.class_name , self.extends )) output_declaration( "{") if self.uses!='' : output_declaration( "\t%s &content;" % self.uses) output_declaration( "\t%s(std::ostream &_s,%s &_content): %s,content(_content),_domain_id(0)" % ( self.class_name,self.uses,constructor )) else: output_declaration( "\t%s(std::ostream &_s): %s,_domain_id(0)" % ( self.class_name,constructor )) output_declaration("\t{") global spec_gettext if spec_gettext: self.gettext_domain = spec_gettext; output_declaration( '\t\t_domain_id=cppcms::translation_domain_scope::domain_id(_s,"%s");' % self.gettext_domain) else: output_declaration( '\t\t_domain_id=booster::locale::ios_info::get(_s).domain_id();') self.gettext_domain = None; output_declaration("\t}") def use(self,m): global view_created view_created = True self.abstract = m.group('abs')!=None self.inline = m.group('inline')!=None global view_name global output_template self.class_name=m.group(1) view_name = self.class_name self.uses=m.group(2) if m.group(4): self.extends=m.group(5) else: self.extends='' self.declare(); global stack if len(stack)!=1 or stack[-1].type!='skin': error_exit("You must define view inside skin block only") stack.append(self) global namespace_name class information: content_name=self.uses name=self.class_name namespace=namespace_name global class_list if not self.abstract: class_list.append(information()) def on_end(self): output_declaration( "private:") output_declaration( "\tint _domain_id;") output_declaration( "}; // end of class %s" % self.class_name) class template_block: pattern=r'^<%\s*template\s+([a-zA-Z]\w*)\s*\(([\w\s,:\&]*)\)\s*(?P=\s*0\s*)?%>$' basic_pattern = 'template' basic_name='template' type='template' plist=[] def create_parameters(self,lst): pattern=r'^\s*((:?:?\w+(::\w+)*)\s*(const)?\s*(\&)?\s*(\w+))\s*(,(.*))?$' m=re.match(pattern,lst) res=[] while m: global tmpl_seq id=m.group(6) if id in tmpl_seq: error_exit("Duplicate definition of patameter %s" % id) for v in self.plist: del tmpl_seq[v] return "" tmpl_seq[id]='' res.append(m.group(1)) self.plist.append(id) if m.group(8): lst=m.group(8) m=re.match(pattern,lst) else: return ','.join(res) for v in self.plist: del tmpl_seq[v] error_exit("Wrong expression %s" % lst) def use(self,m): global view_name global inline_templates self.name=m.group(1) params="" abstract = m.group('abs') != None if m.group(2) and not re.match('^\s*$',m.group(2)): params=self.create_parameters(m.group(2)) if abstract: output_declaration( "virtual void %s(%s) = 0;" % (self.name,params) ) else: if inline_templates: output_declaration( "virtual void %s(%s) {" % (self.name,params) ) output_declaration( "\tcppcms::translation_domain_scope _trs(out(),_domain_id);\n") else: output_declaration( "virtual void %s(%s);" % (self.name,params) ) output_definition( "void %s::%s(%s) {" % (view_name,self.name,params) ) output_definition( "\tcppcms::translation_domain_scope _trs(out(),_domain_id);\n") global stack if len(stack)==0 or stack[-1].type!='view': error_exit("You must define template inside view block only") if abstract: return stack.append(self) global current_template current_template=self.name global ignore_inline global inline_cpp_to ignore_inline=0 inline_cpp_to = output_template def on_end(self): global output_template output_template( "} // end of template %s" % self.name) global ignore_inline ignore_inline=1 global tmpl_seq tmpl_seq={} global inline_cpp_to inline_cpp_to = output_declaration def inline_content(s): global ignore_inline global output_template if not ignore_inline: output_template( 'out()<<"%s";' % to_string(s)) def warning(x): global file_name global line_number sys.stderr.write("Warning: %s in file %s, line %d\n" % (x,file_name,line_number)) def error_exit(x): global exit_flag global file_name global line_number sys.stderr.write("Error: %s in file %s, line %d\n" % (x,file_name,line_number)) exit_flag=1 def to_string(s): res='' for c in s: global stack if c=='\n': res+="\\n\""+"\n"+"\t"*len(stack)+"\t\"" elif c=="\t": res+="\\t" elif c=="\v": res+="\\v" elif c=="\b": res+="\\b" elif c=="\r": res+="\\r" elif c=="\f": res+="\\f" elif c=="\a": res+="\\a" elif c=="\\": res+="\\\\" elif c=="\"": res+="\\\"" elif ord(c)>0 and ord(c)<32: res+="%03o" % ord(c) else: res+=c return res def make_ident(val): m=re.match('^'+variable_match+'$',val) global tmpl_seq if m.group(1) in tmpl_seq: return val m2=re.match('^\*(.*)$',val) if m2: return "*content." + m2.group(1) else: return "content." + val def print_using_block_start(class_name,variable_name,content_name,temp_and_view=None): global output_template if content_name: content=make_ident(content_name) guard=True else: content ='content' guard=False output_template(r'{') if guard: output_template(r' cppcms::base_content::app_guard _g(%s,content);' % content); if temp_and_view: output_template(r' cppcms::views::view_lock _vl(%s,out(),%s); %s &%s = _vl.use_view<%s>();' % ( temp_and_view, content, class_name, variable_name, class_name)); else: output_template(r' %s %s(out(),%s);' % ( class_name, variable_name, content)); def print_using_block_end(): global output_template output_template('}') class using_block: pattern=r'^<%\s*using\s+(?P(?:\w+::)*\w+)(?:\s+with\s+(?P' + variable_match + r'))?\s+as\s+(?P[a-zA-Z_]\w*)' \ + r'(?P\s+from' \ + r'(\s*(?P'+ str_match +r')|(?:\s+(?P' + variable_match + r')))' \ + r'(\s*,\s*((?P'+ str_match +r')|(?P' + variable_match + r')))?' \ + r')?' \ + '\s*%>$' basic_pattern = 'using' basic_name = 'using' type='using' def use(self,m): if m.group('from'): temp_and_view = get_render_names(m); else: temp_and_view = None print_using_block_start(m.group('class'),m.group('name'),m.group('content'),temp_and_view) global stack stack.append(self) def on_end(self): print_using_block_end(); class foreach_block: pattern=r'^<%\s*foreach\s+([a-zA-Z]\w*)(\s+as\s+((:?:?\w+)(::\w+)*))?' \ + r'(?:\s+rowid\s+([a-zA-Z]\w*)(?:\s+from\s+(\d+))?)?' \ + r'(?:\s+(reverse))?' \ + '\s+in\s+(' + variable_match +')\s*%>$' basic_pattern = 'foreach' basic_name = 'foreach' type='foreach' has_item=0 has_separator=0 separator_label='' on_first_label='' type_name=0 def use(self,m): global output_template self.ident=m.group(1) self.seq_name=make_ident(m.group(9)) self.rowid = m.group(6) if m.group(7): self.rowid_begin = int(m.group(7)) else: self.rowid_begin = 0 if m.group(8): self.reverse = 'r' else: self.reverse = '' self.type_name = m.group(3) global tmpl_seq if self.ident in tmpl_seq: error_exit("Nested sequences with same name %s" % self.ident) if self.rowid == self.ident: error_exit("Nested sequence and rowid has same name %s" % self.ident) if self.rowid and (self.rowid in tmpl_seq): error_exit("Nested sequences with same rowid name %s" % self.rowid ) tmpl_seq[self.ident]=''; output_template( "if((%s).%sbegin()!=(%s).%send()) {" % (self.seq_name,self.reverse,self.seq_name,self.reverse) ) if self.rowid: tmpl_seq[self.rowid]=''; output_template(" int %s = %s;" % (self.rowid,self.rowid_begin)) global stack stack.append(self) def on_end(self): global output_template if not self.has_item: error_exit("foreach without item") global tmpl_seq del tmpl_seq[self.ident] if self.rowid: del tmpl_seq[self.rowid] output_template( "}" ) def prepare_foreach(self): global output_template if not self.type_name: ptr_type = 'CPPCMS_TYPEOF((%(s)s).%(r)sbegin())' else: ptr_type = self.type_name incr = '' if self.rowid: incr = ',++%s' % self.rowid; fmt = "for("+ptr_type+" %(i)s_ptr=(%(s)s).%(r)sbegin(),%(i)s_ptr_end=(%(s)s).%(r)send();%(i)s_ptr!=%(i)s_ptr_end;++%(i)s_ptr%(u)s) {"; fmt = fmt % { 's' : self.seq_name, 'i' : self.ident , 'r' : self.reverse, 'u' : incr }; output_template(fmt) if not self.type_name: output_template( "CPPCMS_TYPEOF(*%s_ptr) &%s=*%s_ptr;" % (self.ident,self.ident,self.ident)) else: output_template( "std::iterator_traits< %s >::value_type &%s=*%s_ptr;" % (self.type_name,self.ident,self.ident)) if self.has_separator: output_template( "if(%s_ptr!=(%s).%sbegin()) {" % (self.ident,self.seq_name,self.reverse)) class separator_block: pattern=r'^<%\s*separator\s*%>' basic_pattern = 'separator' basic_name = 'separator' type='separator' def use(self,m): global stack if len(stack)==0 or stack[len(stack)-1].type!='foreach': error_exit("separator without foreach") return foreachb=stack[len(stack)-1] if foreachb.has_separator: error_exit("two separators for one foreach") foreachb.has_separator=1 foreachb.prepare_foreach() class item_block: pattern=r'^<%\s*item\s*%>' basic_pattern = 'item' basic_name = 'item' type='item' def use(self,m): global stack global output_template if not stack or stack[-1].type!='foreach': error_exit("item without foreach") return foreachb=stack[-1] if foreachb.has_item: error_exit("Two items for one foreach"); if foreachb.has_separator: output_template( "} // end of separator") else: foreachb.prepare_foreach() foreachb.has_item=1 stack.append(self) def on_end(self): global output_template output_template( "} // end of item" ) class empty_block: pattern=r'^<%\s*empty\s*%>' basic_pattern = 'empty' basic_name = 'empty' type='empty' def use(self,m): global stack global output_template if not stack or stack[-1].type!='foreach': error_exit("empty without foreach") return forb=stack.pop() if not forb.has_item: error_exit("Unexpected empty - item missed?") output_template( " } else {") self.ident=forb.ident self.rowid=forb.rowid stack.append(self) def on_end(self): global output_template output_template( "} // end of empty") global tmpl_seq del tmpl_seq[self.ident] if self.rowid: del tmpl_seq[self.rowid] class else_block: pattern=r'^<%\s*else\s*%>$' basic_pattern = 'else' basic_name = 'else' type='else' def on_end(self): global output_template output_template("}") def use(self,m): global output_template prev=stack.pop() if prev.type!='if' and prev.type!='elif': error_exit("elif without if"); output_template( "}else{") stack.append(self) class if_block: pattern=r'^<%\s*(if|elif)\s+((not\s+|not\s+empty\s+|empty\s+)?('+variable_match+')|\((.+)\)|)\s*%>$' basic_pattern = '(if|elif)' basic_name = 'if/elif' type='if' def prepare(self): global output_template output_template( "if(%s) {" % self.ident) def on_end(self): global output_template output_template( "} // endif") def use(self,m): global stack global output_template self.type=m.group(1) if m.group(4): if m.group(4)=='rtl': self.ident='(cppcms::locale::translate("LTR").str(out().getloc())=="RTL")' else: self.ident=make_ident(m.group(4)) if m.group(3): if re.match('.*empty',m.group(3)): self.ident=self.ident + '.empty()' if re.match('not.*',m.group(3)): self.ident="!("+self.ident+")" else: self.ident=m.group(10) if self.type == 'if' : self.prepare() stack.append(self) else: # type == elif if stack : prev=stack.pop() if prev.type!='if' and prev.type!='elif': error_exit("elif without if"); output_template( "}") output_template( "else") self.prepare() stack.append(self) else: error_exit("Unexpeced elif"); # END ifop class end_block: pattern=r'^<%\s*end(\s+(\w+))?\s*%>$'; basic_pattern = 'end' basic_name = 'end' def use(self,m): global stack if not stack: error_exit("Unexpeced 'end'"); else: obj=stack.pop(); if m.group(1): if obj.type!=m.group(2): error_exit("End of %s does not match block %s" % (m.group(2) , obj.type)); obj.on_end() class error_com: pattern=r'^<%(.*)%>$' basic_pattern = '' basic_name = '' def use(self,m): error_exit("Invalid statement `%s'" % m.group(1)) class domain_block: pattern=r'^<%\s*domain\s+(\w+)\s*%>$' basic_pattern = 'domain' basic_name = 'domain' type = 'domain' def use(self,m): gt = m.group(1) global spec_gettext global view_created if not spec_gettext: if view_created: error_exit("Can't use domain command after a view was created") else: spec_gettext = gt return if spec_gettext != gt: error_exit("Gettext domain is already defined as `%s' and differs from given `%s'" % (spec_gettext , gt )) class cpp_include_block: pattern=r'^<%\s*c\+\+(src)?\s+(.*)%>$' basic_pattern = 'c\+\+(src)?' basic_name = 'c++' def use(self,m): global inline_cpp_to if m.group(1): output_definition(m.group(2)); else: inline_cpp_to( m.group(2)); def get_filter(cur): if not cur: global scope_filter return scope_filter return cur class base_show: mark='('+variable_match+r')\s*(\|(.*))?' base_pattern='^\s*'+mark + '$' def __init__(self,default_filter=None): self.default_filter=default_filter def get_params(self,s): pattern='^\s*(('+variable_match+')|('+str_match+')|(\-?\d+(\.\d*)?))\s*(,(.*))?$' res=[] m=re.match(pattern,s) while m: if m.group(2): res.append(make_ident(m.group(2))) elif m.group(8): res.append(m.group(8)) elif m.group(10): res.append(m.group(10)) if m.group(13): s=m.group(13) m=re.match(pattern,s) else: return res error_exit("Invalid parameters: `%s'" % s ) return [] def prepare(self,s): m=re.match(self.base_pattern,s) if not m: error_exit("No variable") return []; var=make_ident(m.group(1)) if not m.group(8): return "%s(%s)" % (get_filter(self.default_filter), var) filters=m.group(8) expr='^\s*(ext\s+)?(\w+)\s*(\((([^"\)]|'+str_match + ')*)\))?\s*(\|(.*))?$' m=re.match(expr,filters) while m: if m.group(1): func="content."+m.group(2) else: func="cppcms::filters::" + m.group(2) if m.group(3): params=','.join([var]+self.get_params(m.group(4))) else: params=var var=func+"("+params+")" if m.group(8): filters=m.group(8) m=re.match(expr,filters) else: return var error_exit("Seems to be a problem in expression %s" % filters) return ""; class form_block: pattern=r'^<%\s*form\s+(as_p|as_table|as_ul|as_dl|as_space|input|block|begin|end)\s+('\ + variable_match +')\s*%>$' basic_pattern = 'form' basic_name = 'form' type = 'form' def format_input(self,command_type,ident): global html_type_code global output_template flags = 'cppcms::form_flags::' + html_type_code; output_template('{ cppcms::form_context _form_context(out(),%s);' % flags) render_command = ' (%s).render_input(_form_context);' % ident; if command_type=='begin': output_template(' _form_context.widget_part(cppcms::form_context::first_part);') output_template(render_command) elif command_type=='end': output_template(' _form_context.widget_part(cppcms::form_context::second_part);') output_template(render_command) else: output_template(' _form_context.widget_part(cppcms::form_context::first_part);') output_template(render_command) output_template(' out() << (%s).attributes_string();' % ident) output_template(' _form_context.widget_part(cppcms::form_context::second_part);') output_template(render_command) output_template('}') def use(self,m): global output_template ident=make_ident(m.group(2)) command_type = m.group(1) global html_type_code if command_type=='input' or command_type=='begin' or command_type=='end' or command_type=='block': if command_type != 'block': self.format_input(command_type,ident) else: self.format_input('begin',ident) self.ident = ident self.command_type = 'end' global stack stack.append(self) else: flags = 'cppcms::form_flags::%s,cppcms::form_flags::%s' % ( html_type_code, m.group(1)); output_template('{ cppcms::form_context _form_context(out(),%s); (%s).render(_form_context); }' % (flags , ident)) def on_end(self): self.format_input(self.command_type,self.ident) def get_render_names(m): first_str = m.group('fst_str') first_var = m.group('fst_var') if first_var: first = make_ident(first_var) else: first = first_str second_str = m.group('snd_str') second_var = m.group('snd_var') if second_var: second = make_ident(second_var) else: second = second_str if first and second: template_name = first view_name = second else: global namespace_name template_name = '"' + namespace_name + '"' view_name = first; return template_name + ', ' + view_name class render_block: pattern=r'^<%\s*render\s+' \ + r'((?P'+ str_match +r')|(?P' + variable_match + r'))' \ + r'(\s*,\s*((?P'+ str_match +r')|(?P' + variable_match + r')))?' \ + r'(\s+with\s+(?P' + variable_match + r'))?\s*%>$' basic_pattern = 'render' basic_name = 'render' def use(self,m): global output_template if m.group('content'): content = make_ident(m.group('content')) guard=True else: content = 'content'; guard=False temp_and_view = get_render_names(m); output_template('{') if guard: output_template(r'cppcms::base_content::app_guard _g(%s,content);' % content) output_template(r'cppcms::views::pool::instance().render(%s,out(),%s);' % (temp_and_view,content)) output_template('}') class filters_show_block(base_show): pattern=r'^<%(=)?\s*('+ variable_match + r'\s*(\|.*)?)%>$' basic_pattern = '=?' basic_name = 'Inline Variable' def use(self,m): global output_template if not m.group(1): warning("Variables syntax like <% foo %> is deprecated, use <%= foo %> syntax"); expr=self.prepare(m.group(2)); if expr!="": output_template('out()<<%s;' % expr) def make_format_params(s,default_filter = None): pattern=r'^(([^,\("]|'+str_match+'|\(([^"\)]|'+str_match+')*\))+)(,(.*))?$' params=[] m=re.match(pattern,s) s_orig=s while m.group(1): res=base_show(default_filter).prepare(m.group(1)) if res: params.append(res) if not m.group(6): return params s=m.group(7) m=re.match(pattern,s) error_exit("Seems to be wrong parameters list [%s]" % s_orig) return [] class filter_block: pattern=r'^<%\s*filter\s+(ext\s+)?(\w+)\s*%>' basic_pattern = 'filter' basic_name = 'filter' type = 'filter' def use(self,m): global scope_filter self.save_filter = scope_filter if m.group(1): scope_filter='content.' + m.group(2) else: scope_filter='cppcms::filters::' + m.group(2) global stack stack.append(self) def on_end(self): global scope_filter scope_filter=self.save_filter class cache_block: pattern=r'^<%\s*cache\s+((?P'+ \ str_match +')|(?P'+ variable_match +r'))' + \ r'(\s+for\s+(?P