summaryrefslogtreecommitdiff
path: root/waflib/Tools/c_preproc.py
diff options
context:
space:
mode:
Diffstat (limited to 'waflib/Tools/c_preproc.py')
-rw-r--r--waflib/Tools/c_preproc.py672
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)