cmake_parser.py
Go to the documentation of this file.
00001 import re
00002 import sys
00003 from cmake import CMake, Command, Section, SectionStyle, CommandGroup
00004 
00005 ALL_CAPS = re.compile('^[A-Z_]+$')
00006 ALL_WHITESPACE = ['whitespace', 'newline']
00007 NOT_REAL = ALL_WHITESPACE + ['comment']
00008 
00009 
00010 def word_cb(scanner, token):
00011     if ALL_CAPS.match(token):
00012         return ('caps', token)
00013     else:
00014         return ('word', token)
00015 
00016 
00017 scanner = re.Scanner([
00018     (r'#.*\n', lambda scanner, token: ("comment", token)),
00019     (r'"[^"]*"', lambda scanner, token: ("string", token)),
00020     (r"\(", lambda scanner, token: ("left paren", token)),
00021     (r"\)", lambda scanner, token: ("right paren", token)),
00022     (r'[^ \t\r\n()#"]+', word_cb),
00023     (r'\n', lambda scanner, token: ("newline", token)),
00024     (r"[ \t]+", lambda scanner, token: ("whitespace", token)),
00025 ])
00026 
00027 
00028 def match_command_groups(contents, base_depth=0):
00029     revised_contents = []
00030 
00031     current = []
00032     group = None
00033     depth = base_depth
00034 
00035     for content in contents:
00036         if group is None:
00037             if content.__class__ == Command and content.command_name in ['if', 'foreach']:
00038                 group = content
00039                 depth = base_depth + 1
00040             else:
00041                 revised_contents.append(content)
00042         else:
00043             if content.__class__ == Command:
00044                 if content.command_name == group.command_name:
00045                     depth += 1
00046                 elif content.command_name == 'end' + group.command_name:
00047                     depth -= 1
00048                     if depth == base_depth:
00049                         recursive_contents = match_command_groups(current, base_depth + 1)
00050                         sub = CMake(initial_contents=recursive_contents, depth=base_depth + 1)
00051                         cg = CommandGroup(group, sub, content)
00052                         revised_contents.append(cg)
00053                         group = None
00054                         current = []
00055                         continue
00056             current.append(content)
00057 
00058     # Only will happen if the tags don't match. Shouldn't happen, but resolve leftovers
00059     if len(current) > 0:
00060         revised_contents += current
00061 
00062     return revised_contents
00063 
00064 
00065 class CMakeParseError(Exception):
00066     def __init__(self, msg):
00067         Exception.__init__(self, msg)
00068 
00069 
00070 class AwesomeParser:
00071     def __init__(self, s, debug=False):
00072         self.tokens, remainder = scanner.scan(s)
00073         if remainder != '':
00074             msg = 'Unrecognized tokens: %s' % (remainder)
00075             raise ValueError(msg)
00076 
00077         if debug:
00078             for typ, token in self.tokens:
00079                 print('[%s]%s' % (typ, repr(token)))
00080 
00081         self.contents = []
00082         while len(self.tokens) > 0:
00083             typ = self.get_type()
00084             if typ == 'comment':
00085                 self.contents.append(self.match(typ))
00086             elif typ == 'newline' or typ == 'whitespace':
00087                 s = self.match(typ)
00088                 self.contents.append(s)
00089             elif typ in ['word', 'caps']:
00090                 cmd = self.parse_command()
00091                 self.contents.append(cmd)
00092             else:
00093                 raise Exception('token ' + typ)
00094 
00095         # Match Command Groups
00096         self.contents = match_command_groups(self.contents)
00097 
00098         if debug:
00099             for chunk in self.contents:
00100                 print('[%s]' % chunk)
00101 
00102     def parse_command(self):
00103         command_name = self.match()
00104         original = command_name
00105         cmd = Command(command_name)
00106         while self.get_type() == 'whitespace':
00107             s = self.match('whitespace')
00108             cmd.pre_paren += s
00109             original += s
00110         original += self.match('left paren')
00111         paren_depth = 1
00112 
00113         while len(self.tokens) > 0:
00114             typ = self.next_real_type()
00115             if typ in ['word', 'caps', 'string']:
00116                 section, s = self.parse_section()
00117                 cmd.sections.append(section)
00118                 original += s
00119             else:
00120                 typ, tok_contents = self.tokens.pop(0)
00121                 original += tok_contents
00122                 if typ == 'right paren':
00123                     paren_depth -= 1
00124                     if paren_depth == 0:
00125                         cmd.original = original
00126                         return cmd
00127                 elif typ == 'left paren':
00128                     paren_depth += 1
00129                 else:
00130                     cmd.sections.append(tok_contents)
00131         raise CMakeParseError('File ended while processing command "%s"' % (command_name))
00132 
00133     def parse_section(self):
00134         original = ''
00135         style = SectionStyle()
00136         tokens = []
00137         cat = ''
00138         while self.get_type() in NOT_REAL:
00139             s = self.match()
00140             original += s
00141             style.prename += s
00142 
00143         if self.get_type() == 'caps':
00144             cat = self.match('caps')
00145             original += cat
00146             style.name_val_sep = ''
00147             while self.get_type() in ALL_WHITESPACE:
00148                 s = self.match()
00149                 original += s
00150                 style.name_val_sep += s
00151             if len(style.name_val_sep) == 0:
00152                 style.name_val_sep = ' '
00153 
00154         delims = set()
00155         current = ''
00156         while self.next_real_type() not in ['left paren', 'right paren', 'caps']:
00157             typ = self.get_type()
00158             if typ in ALL_WHITESPACE:
00159                 token = self.match()
00160                 original += token
00161                 current += token
00162             else:
00163                 if len(current) > 0:
00164                     delims.add(current)
00165                 current = ''
00166                 token = self.match()
00167                 original += token
00168                 tokens.append(token)
00169         if len(current) > 0:
00170             delims.add(current)
00171         if len(delims) > 0:
00172             if len(delims) == 1:
00173                 style.val_sep = list(delims)[0]
00174             else:
00175                 # TODO: Smarter multi delim parsing
00176                 # print(delims)
00177                 style.val_sep = list(delims)[0]
00178 
00179         # print(cat, tokens, style)
00180         return Section(cat, tokens, style), original
00181 
00182     def match(self, typ=None):
00183         if typ is None or self.get_type() == typ:
00184             typ, tok = self.tokens.pop(0)
00185             # print('[%s]%s'%(typ, repr(tok)))
00186             return tok
00187         else:
00188             sys.stderr.write('Token Dump:\n')
00189             for a in self.tokens:
00190                 sys.stderr.write(str(a) + '\n')
00191             raise CMakeParseError('Expected type "%s" but got "%s"' % (typ, self.get_type()))
00192 
00193     def get_type(self):
00194         if len(self.tokens) > 0:
00195             return self.tokens[0][0]
00196         else:
00197             return None
00198 
00199     def next_real_type(self):
00200         for x, y in self.tokens:
00201             if x not in NOT_REAL:
00202                 return x
00203 
00204 
00205 def parse_commands(s):
00206     parser = AwesomeParser(s)
00207     return parser.contents
00208 
00209 
00210 def parse_command(s):
00211     parser = AwesomeParser(s)
00212     assert len(parser.contents) == 1
00213     return parser.contents[0]
00214 
00215 
00216 def parse_file(filename):
00217     with open(filename) as f:
00218         s = f.read()
00219         return CMake(file_path=filename, initial_contents=parse_commands(s))


ros_introspection
Author(s):
autogenerated on Wed Jun 19 2019 19:21:34