diff options
Diffstat (limited to 'waflib/Tools/c_preproc.py')
-rw-r--r-- | waflib/Tools/c_preproc.py | 672 |
1 files changed, 672 insertions, 0 deletions
diff --git a/waflib/Tools/c_preproc.py b/waflib/Tools/c_preproc.py new file mode 100644 index 0000000..8781b73 --- /dev/null +++ b/waflib/Tools/c_preproc.py @@ -0,0 +1,672 @@ +#! /usr/bin/env python +# encoding: utf-8 +# WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file + +import re,string,traceback +from waflib import Logs,Utils,Errors +class PreprocError(Errors.WafError): + pass +FILE_CACHE_SIZE=100000 +LINE_CACHE_SIZE=100000 +POPFILE='-' +recursion_limit=150 +go_absolute=False +standard_includes=['/usr/local/include','/usr/include'] +if Utils.is_win32: + standard_includes=[] +use_trigraphs=0 +strict_quotes=0 +g_optrans={'not':'!','not_eq':'!','and':'&&','and_eq':'&=','or':'||','or_eq':'|=','xor':'^','xor_eq':'^=','bitand':'&','bitor':'|','compl':'~',} +re_lines=re.compile('^[ \t]*(?:#|%:)[ \t]*(ifdef|ifndef|if|else|elif|endif|include|import|define|undef|pragma)[ \t]*(.*)\r*$',re.IGNORECASE|re.MULTILINE) +re_mac=re.compile("^[a-zA-Z_]\w*") +re_fun=re.compile('^[a-zA-Z_][a-zA-Z0-9_]*[(]') +re_pragma_once=re.compile('^\s*once\s*',re.IGNORECASE) +re_nl=re.compile('\\\\\r*\n',re.MULTILINE) +re_cpp=re.compile(r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',re.DOTALL|re.MULTILINE) +trig_def=[('??'+a,b)for a,b in zip("=-/!'()<>",r'#~\|^[]{}')] +chr_esc={'0':0,'a':7,'b':8,'t':9,'n':10,'f':11,'v':12,'r':13,'\\':92,"'":39} +NUM='i' +OP='O' +IDENT='T' +STR='s' +CHAR='c' +tok_types=[NUM,STR,IDENT,OP] +exp_types=[r"""0[xX](?P<hex>[a-fA-F0-9]+)(?P<qual1>[uUlL]*)|L*?'(?P<char>(\\.|[^\\'])+)'|(?P<n1>\d+)[Ee](?P<exp0>[+-]*?\d+)(?P<float0>[fFlL]*)|(?P<n2>\d*\.\d+)([Ee](?P<exp1>[+-]*?\d+))?(?P<float1>[fFlL]*)|(?P<n4>\d+\.\d*)([Ee](?P<exp2>[+-]*?\d+))?(?P<float2>[fFlL]*)|(?P<oct>0*)(?P<n0>\d+)(?P<qual2>[uUlL]*)""",r'L?"([^"\\]|\\.)*"',r'[a-zA-Z_]\w*',r'%:%:|<<=|>>=|\.\.\.|<<|<%|<:|<=|>>|>=|\+\+|\+=|--|->|-=|\*=|/=|%:|%=|%>|==|&&|&=|\|\||\|=|\^=|:>|!=|##|[\(\)\{\}\[\]<>\?\|\^\*\+&=:!#;,%/\-\?\~\.]',] +re_clexer=re.compile('|'.join(["(?P<%s>%s)"%(name,part)for name,part in zip(tok_types,exp_types)]),re.M) +accepted='a' +ignored='i' +undefined='u' +skipped='s' +def repl(m): + s=m.group() + if s[0]=='/': + return' ' + return s +prec={} +ops=['* / %','+ -','<< >>','< <= >= >','== !=','& | ^','&& ||',','] +for x,syms in enumerate(ops): + for u in syms.split(): + prec[u]=x +def reduce_nums(val_1,val_2,val_op): + try: + a=0+val_1 + except TypeError: + a=int(val_1) + try: + b=0+val_2 + except TypeError: + b=int(val_2) + d=val_op + if d=='%': + c=a%b + elif d=='+': + c=a+b + elif d=='-': + c=a-b + elif d=='*': + c=a*b + elif d=='/': + c=a/b + elif d=='^': + c=a^b + elif d=='==': + c=int(a==b) + elif d=='|'or d=='bitor': + c=a|b + elif d=='||'or d=='or': + c=int(a or b) + elif d=='&'or d=='bitand': + c=a&b + elif d=='&&'or d=='and': + c=int(a and b) + elif d=='!='or d=='not_eq': + c=int(a!=b) + elif d=='^'or d=='xor': + c=int(a^b) + elif d=='<=': + c=int(a<=b) + elif d=='<': + c=int(a<b) + elif d=='>': + c=int(a>b) + elif d=='>=': + c=int(a>=b) + elif d=='<<': + c=a<<b + elif d=='>>': + c=a>>b + else: + c=0 + return c +def get_num(lst): + if not lst: + raise PreprocError('empty list for get_num') + (p,v)=lst[0] + if p==OP: + if v=='(': + count_par=1 + i=1 + while i<len(lst): + (p,v)=lst[i] + if p==OP: + if v==')': + count_par-=1 + if count_par==0: + break + elif v=='(': + count_par+=1 + i+=1 + else: + raise PreprocError('rparen expected %r'%lst) + (num,_)=get_term(lst[1:i]) + return(num,lst[i+1:]) + elif v=='+': + return get_num(lst[1:]) + elif v=='-': + num,lst=get_num(lst[1:]) + return(reduce_nums('-1',num,'*'),lst) + elif v=='!': + num,lst=get_num(lst[1:]) + return(int(not int(num)),lst) + elif v=='~': + num,lst=get_num(lst[1:]) + return(~int(num),lst) + else: + raise PreprocError('Invalid op token %r for get_num'%lst) + elif p==NUM: + return v,lst[1:] + elif p==IDENT: + return 0,lst[1:] + else: + raise PreprocError('Invalid token %r for get_num'%lst) +def get_term(lst): + if not lst: + raise PreprocError('empty list for get_term') + num,lst=get_num(lst) + if not lst: + return(num,[]) + (p,v)=lst[0] + if p==OP: + if v==',': + return get_term(lst[1:]) + elif v=='?': + count_par=0 + i=1 + while i<len(lst): + (p,v)=lst[i] + if p==OP: + if v==')': + count_par-=1 + elif v=='(': + count_par+=1 + elif v==':': + if count_par==0: + break + i+=1 + else: + raise PreprocError('rparen expected %r'%lst) + if int(num): + return get_term(lst[1:i]) + else: + return get_term(lst[i+1:]) + else: + num2,lst=get_num(lst[1:]) + if not lst: + num2=reduce_nums(num,num2,v) + return get_term([(NUM,num2)]+lst) + p2,v2=lst[0] + if p2!=OP: + raise PreprocError('op expected %r'%lst) + if prec[v2]>=prec[v]: + num2=reduce_nums(num,num2,v) + return get_term([(NUM,num2)]+lst) + else: + num3,lst=get_num(lst[1:]) + num3=reduce_nums(num2,num3,v2) + return get_term([(NUM,num),(p,v),(NUM,num3)]+lst) + raise PreprocError('cannot reduce %r'%lst) +def reduce_eval(lst): + num,lst=get_term(lst) + return(NUM,num) +def stringize(lst): + lst=[str(v2)for(p2,v2)in lst] + return"".join(lst) +def paste_tokens(t1,t2): + p1=None + if t1[0]==OP and t2[0]==OP: + p1=OP + elif t1[0]==IDENT and(t2[0]==IDENT or t2[0]==NUM): + p1=IDENT + elif t1[0]==NUM and t2[0]==NUM: + p1=NUM + if not p1: + raise PreprocError('tokens do not make a valid paste %r and %r'%(t1,t2)) + return(p1,t1[1]+t2[1]) +def reduce_tokens(lst,defs,ban=[]): + i=0 + while i<len(lst): + (p,v)=lst[i] + if p==IDENT and v=="defined": + del lst[i] + if i<len(lst): + (p2,v2)=lst[i] + if p2==IDENT: + if v2 in defs: + lst[i]=(NUM,1) + else: + lst[i]=(NUM,0) + elif p2==OP and v2=='(': + del lst[i] + (p2,v2)=lst[i] + del lst[i] + if v2 in defs: + lst[i]=(NUM,1) + else: + lst[i]=(NUM,0) + else: + raise PreprocError('Invalid define expression %r'%lst) + elif p==IDENT and v in defs: + if isinstance(defs[v],str): + a,b=extract_macro(defs[v]) + defs[v]=b + macro_def=defs[v] + to_add=macro_def[1] + if isinstance(macro_def[0],list): + del lst[i] + accu=to_add[:] + reduce_tokens(accu,defs,ban+[v]) + for tmp in accu: + lst.insert(i,tmp) + i+=1 + else: + args=[] + del lst[i] + if i>=len(lst): + raise PreprocError('expected ( after %r (got nothing)'%v) + (p2,v2)=lst[i] + if p2!=OP or v2!='(': + raise PreprocError('expected ( after %r'%v) + del lst[i] + one_param=[] + count_paren=0 + while i<len(lst): + p2,v2=lst[i] + del lst[i] + if p2==OP and count_paren==0: + if v2=='(': + one_param.append((p2,v2)) + count_paren+=1 + elif v2==')': + if one_param: + args.append(one_param) + break + elif v2==',': + if not one_param: + raise PreprocError('empty param in funcall %r'%v) + args.append(one_param) + one_param=[] + else: + one_param.append((p2,v2)) + else: + one_param.append((p2,v2)) + if v2=='(': + count_paren+=1 + elif v2==')': + count_paren-=1 + else: + raise PreprocError('malformed macro') + accu=[] + arg_table=macro_def[0] + j=0 + while j<len(to_add): + (p2,v2)=to_add[j] + if p2==OP and v2=='#': + if j+1<len(to_add)and to_add[j+1][0]==IDENT and to_add[j+1][1]in arg_table: + toks=args[arg_table[to_add[j+1][1]]] + accu.append((STR,stringize(toks))) + j+=1 + else: + accu.append((p2,v2)) + elif p2==OP and v2=='##': + if accu and j+1<len(to_add): + t1=accu[-1] + if to_add[j+1][0]==IDENT and to_add[j+1][1]in arg_table: + toks=args[arg_table[to_add[j+1][1]]] + if toks: + accu[-1]=paste_tokens(t1,toks[0]) + accu.extend(toks[1:]) + else: + accu.append((p2,v2)) + accu.extend(toks) + elif to_add[j+1][0]==IDENT and to_add[j+1][1]=='__VA_ARGS__': + va_toks=[] + st=len(macro_def[0]) + pt=len(args) + for x in args[pt-st+1:]: + va_toks.extend(x) + va_toks.append((OP,',')) + if va_toks: + va_toks.pop() + if len(accu)>1: + (p3,v3)=accu[-1] + (p4,v4)=accu[-2] + if v3=='##': + accu.pop() + if v4==','and pt<st: + accu.pop() + accu+=va_toks + else: + accu[-1]=paste_tokens(t1,to_add[j+1]) + j+=1 + else: + accu.append((p2,v2)) + elif p2==IDENT and v2 in arg_table: + toks=args[arg_table[v2]] + reduce_tokens(toks,defs,ban+[v]) + accu.extend(toks) + else: + accu.append((p2,v2)) + j+=1 + reduce_tokens(accu,defs,ban+[v]) + for x in range(len(accu)-1,-1,-1): + lst.insert(i,accu[x]) + i+=1 +def eval_macro(lst,defs): + reduce_tokens(lst,defs,[]) + if not lst: + raise PreprocError('missing tokens to evaluate') + if lst: + p,v=lst[0] + if p==IDENT and v not in defs: + raise PreprocError('missing macro %r'%lst) + p,v=reduce_eval(lst) + return int(v)!=0 +def extract_macro(txt): + t=tokenize(txt) + if re_fun.search(txt): + p,name=t[0] + p,v=t[1] + if p!=OP: + raise PreprocError('expected (') + i=1 + pindex=0 + params={} + prev='(' + while 1: + i+=1 + p,v=t[i] + if prev=='(': + if p==IDENT: + params[v]=pindex + pindex+=1 + prev=p + elif p==OP and v==')': + break + else: + raise PreprocError('unexpected token (3)') + elif prev==IDENT: + if p==OP and v==',': + prev=v + elif p==OP and v==')': + break + else: + raise PreprocError('comma or ... expected') + elif prev==',': + if p==IDENT: + params[v]=pindex + pindex+=1 + prev=p + elif p==OP and v=='...': + raise PreprocError('not implemented (1)') + else: + raise PreprocError('comma or ... expected (2)') + elif prev=='...': + raise PreprocError('not implemented (2)') + else: + raise PreprocError('unexpected else') + return(name,[params,t[i+1:]]) + else: + (p,v)=t[0] + if len(t)>1: + return(v,[[],t[1:]]) + else: + return(v,[[],[('T','')]]) +re_include=re.compile('^\s*(<(?:.*)>|"(?:.*)")') +def extract_include(txt,defs): + m=re_include.search(txt) + if m: + txt=m.group(1) + return txt[0],txt[1:-1] + toks=tokenize(txt) + reduce_tokens(toks,defs,['waf_include']) + if not toks: + raise PreprocError('could not parse include %r'%txt) + if len(toks)==1: + if toks[0][0]==STR: + return'"',toks[0][1] + else: + if toks[0][1]=='<'and toks[-1][1]=='>': + ret='<',stringize(toks).lstrip('<').rstrip('>') + return ret + raise PreprocError('could not parse include %r'%txt) +def parse_char(txt): + if not txt: + raise PreprocError('attempted to parse a null char') + if txt[0]!='\\': + return ord(txt) + c=txt[1] + if c=='x': + if len(txt)==4 and txt[3]in string.hexdigits: + return int(txt[2:],16) + return int(txt[2:],16) + elif c.isdigit(): + if c=='0'and len(txt)==2: + return 0 + for i in 3,2,1: + if len(txt)>i and txt[1:1+i].isdigit(): + return(1+i,int(txt[1:1+i],8)) + else: + try: + return chr_esc[c] + except KeyError: + raise PreprocError('could not parse char literal %r'%txt) +def tokenize(s): + return tokenize_private(s)[:] +def tokenize_private(s): + ret=[] + for match in re_clexer.finditer(s): + m=match.group + for name in tok_types: + v=m(name) + if v: + if name==IDENT: + if v in g_optrans: + name=OP + elif v.lower()=="true": + v=1 + name=NUM + elif v.lower()=="false": + v=0 + name=NUM + elif name==NUM: + if m('oct'): + v=int(v,8) + elif m('hex'): + v=int(m('hex'),16) + elif m('n0'): + v=m('n0') + else: + v=m('char') + if v: + v=parse_char(v) + else: + v=m('n2')or m('n4') + elif name==OP: + if v=='%:': + v='#' + elif v=='%:%:': + v='##' + elif name==STR: + v=v[1:-1] + ret.append((name,v)) + break + return ret +def format_defines(lst): + ret=[] + for y in lst: + if y: + pos=y.find('=') + if pos==-1: + ret.append(y) + elif pos>0: + ret.append('%s %s'%(y[:pos],y[pos+1:])) + else: + raise ValueError('Invalid define expression %r'%y) + return ret +class c_parser(object): + def __init__(self,nodepaths=None,defines=None): + self.lines=[] + if defines is None: + self.defs={} + else: + self.defs=dict(defines) + self.state=[] + self.count_files=0 + self.currentnode_stack=[] + self.nodepaths=nodepaths or[] + self.nodes=[] + self.names=[] + self.curfile='' + self.ban_includes=set() + self.listed=set() + def cached_find_resource(self,node,filename): + try: + cache=node.ctx.preproc_cache_node + except AttributeError: + cache=node.ctx.preproc_cache_node=Utils.lru_cache(FILE_CACHE_SIZE) + key=(node,filename) + try: + return cache[key] + except KeyError: + ret=node.find_resource(filename) + if ret: + if getattr(ret,'children',None): + ret=None + elif ret.is_child_of(node.ctx.bldnode): + tmp=node.ctx.srcnode.search_node(ret.path_from(node.ctx.bldnode)) + if tmp and getattr(tmp,'children',None): + ret=None + cache[key]=ret + return ret + def tryfind(self,filename,kind='"',env=None): + if filename.endswith('.moc'): + self.names.append(filename) + return None + self.curfile=filename + found=None + if kind=='"': + if env.MSVC_VERSION: + for n in reversed(self.currentnode_stack): + found=self.cached_find_resource(n,filename) + if found: + break + else: + found=self.cached_find_resource(self.currentnode_stack[-1],filename) + if not found: + for n in self.nodepaths: + found=self.cached_find_resource(n,filename) + if found: + break + listed=self.listed + if found and not found in self.ban_includes: + if found not in listed: + listed.add(found) + self.nodes.append(found) + self.addlines(found) + else: + if filename not in listed: + listed.add(filename) + self.names.append(filename) + return found + def filter_comments(self,node): + code=node.read() + if use_trigraphs: + for(a,b)in trig_def: + code=code.split(a).join(b) + code=re_nl.sub('',code) + code=re_cpp.sub(repl,code) + return re_lines.findall(code) + def parse_lines(self,node): + try: + cache=node.ctx.preproc_cache_lines + except AttributeError: + cache=node.ctx.preproc_cache_lines=Utils.lru_cache(LINE_CACHE_SIZE) + try: + return cache[node] + except KeyError: + cache[node]=lines=self.filter_comments(node) + lines.append((POPFILE,'')) + lines.reverse() + return lines + def addlines(self,node): + self.currentnode_stack.append(node.parent) + self.count_files+=1 + if self.count_files>recursion_limit: + raise PreprocError('recursion limit exceeded') + if Logs.verbose: + Logs.debug('preproc: reading file %r',node) + try: + lines=self.parse_lines(node) + except EnvironmentError: + raise PreprocError('could not read the file %r'%node) + except Exception: + if Logs.verbose>0: + Logs.error('parsing %r failed %s',node,traceback.format_exc()) + else: + self.lines.extend(lines) + def start(self,node,env): + Logs.debug('preproc: scanning %s (in %s)',node.name,node.parent.name) + self.current_file=node + self.addlines(node) + if env.DEFINES: + lst=format_defines(env.DEFINES) + lst.reverse() + self.lines.extend([('define',x)for x in lst]) + while self.lines: + (token,line)=self.lines.pop() + if token==POPFILE: + self.count_files-=1 + self.currentnode_stack.pop() + continue + try: + state=self.state + if token[:2]=='if': + state.append(undefined) + elif token=='endif': + state.pop() + if token[0]!='e': + if skipped in self.state or ignored in self.state: + continue + if token=='if': + ret=eval_macro(tokenize(line),self.defs) + if ret: + state[-1]=accepted + else: + state[-1]=ignored + elif token=='ifdef': + m=re_mac.match(line) + if m and m.group()in self.defs: + state[-1]=accepted + else: + state[-1]=ignored + elif token=='ifndef': + m=re_mac.match(line) + if m and m.group()in self.defs: + state[-1]=ignored + else: + state[-1]=accepted + elif token=='include'or token=='import': + (kind,inc)=extract_include(line,self.defs) + self.current_file=self.tryfind(inc,kind,env) + if token=='import': + self.ban_includes.add(self.current_file) + elif token=='elif': + if state[-1]==accepted: + state[-1]=skipped + elif state[-1]==ignored: + if eval_macro(tokenize(line),self.defs): + state[-1]=accepted + elif token=='else': + if state[-1]==accepted: + state[-1]=skipped + elif state[-1]==ignored: + state[-1]=accepted + elif token=='define': + try: + self.defs[self.define_name(line)]=line + except AttributeError: + raise PreprocError('Invalid define line %r'%line) + elif token=='undef': + m=re_mac.match(line) + if m and m.group()in self.defs: + self.defs.__delitem__(m.group()) + elif token=='pragma': + if re_pragma_once.match(line.lower()): + self.ban_includes.add(self.current_file) + except Exception as e: + if Logs.verbose: + Logs.debug('preproc: line parsing failed (%s): %s %s',e,line,traceback.format_exc()) + def define_name(self,line): + return re_mac.match(line).group() +def scan(task): + try: + incn=task.generator.includes_nodes + except AttributeError: + raise Errors.WafError('%r is missing a feature such as "c", "cxx" or "includes": '%task.generator) + if go_absolute: + nodepaths=incn+[task.generator.bld.root.find_dir(x)for x in standard_includes] + else: + nodepaths=[x for x in incn if x.is_child_of(x.ctx.srcnode)or x.is_child_of(x.ctx.bldnode)] + tmp=c_parser(nodepaths) + tmp.start(task.inputs[0],task.env) + return(tmp.nodes,tmp.names) |