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
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
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
00176
00177 style.val_sep = list(delims)[0]
00178
00179
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
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))