cmake_parser.py
Go to the documentation of this file.
1 import os.path
2 import re
3 import sys
4 
5 from .cmake import CMake, Command, CommandGroup, Section, SectionStyle
6 
7 ALL_CAPS = re.compile('^[A-Z_]+$')
8 ALL_WHITESPACE = ['whitespace', 'newline']
9 NOT_REAL = ALL_WHITESPACE + ['comment']
10 
11 
12 def word_cb(scanner, token):
13  if ALL_CAPS.match(token):
14  return ('caps', token)
15  else:
16  return ('word', token)
17 
18 
19 scanner = re.Scanner([
20  (r'#.*\n', lambda scanner, token: ('comment', token)),
21  (r'"[^"]*"', lambda scanner, token: ('string', token)),
22  (r'\(', lambda scanner, token: ('left paren', token)),
23  (r'\)', lambda scanner, token: ('right paren', token)),
24  (r'[^ \t\r\n()#"]+', word_cb),
25  (r'\n', lambda scanner, token: ('newline', token)),
26  (r'[ \t]+', lambda scanner, token: ('whitespace', token)),
27 ])
28 
29 
30 def match_command_groups(contents, base_depth=0):
31  revised_contents = []
32 
33  current = []
34  group = None
35  depth = base_depth
36 
37  for content in contents:
38  if group is None:
39  if content.__class__ == Command and content.command_name in ['if', 'foreach']:
40  group = content
41  depth = base_depth + 1
42  else:
43  revised_contents.append(content)
44  else:
45  if content.__class__ == Command:
46  if content.command_name == group.command_name:
47  depth += 1
48  elif content.command_name == 'end' + group.command_name:
49  depth -= 1
50  if depth == base_depth:
51  recursive_contents = match_command_groups(current, base_depth + 1)
52  sub = CMake(initial_contents=recursive_contents, depth=base_depth + 1)
53  cg = CommandGroup(group, sub, content)
54  revised_contents.append(cg)
55  group = None
56  current = []
57  continue
58  current.append(content)
59 
60  # Only will happen if the tags don't match. Shouldn't happen, but resolve leftovers
61  if len(current) > 0:
62  revised_contents += current
63 
64  return revised_contents
65 
66 
67 class CMakeParseError(Exception):
68  def __init__(self, msg):
69  Exception.__init__(self, msg)
70 
71 
73  def __init__(self, s, debug=False):
74  self.tokens, remainder = scanner.scan(s)
75  if remainder != '':
76  msg = 'Unrecognized tokens: %s' % (remainder)
77  raise ValueError(msg)
78 
79  if debug:
80  for typ, token in self.tokens:
81  print('[%s]%s' % (typ, repr(token)))
82 
83  self.contents = []
84  while len(self.tokens) > 0:
85  typ = self.get_type()
86  if typ == 'comment':
87  self.contents.append(self.match(typ))
88  elif typ == 'newline' or typ == 'whitespace':
89  s = self.match(typ)
90  self.contents.append(s)
91  elif typ in ['word', 'caps']:
92  cmd = self.parse_command()
93  self.contents.append(cmd)
94  else:
95  raise Exception('token ' + typ)
96 
97  # Match Command Groups
99 
100  if debug:
101  for chunk in self.contents:
102  print('[%s]' % chunk)
103 
104  def parse_command(self):
105  command_name = self.match()
106  original = command_name
107  cmd = Command(command_name)
108  while self.get_type() == 'whitespace':
109  s = self.match('whitespace')
110  cmd.pre_paren += s
111  original += s
112  original += self.match('left paren')
113  paren_depth = 1
114 
115  while len(self.tokens) > 0:
116  typ = self.next_real_type()
117  if typ in ['word', 'caps', 'string']:
118  section, s = self.parse_section()
119  cmd.sections.append(section)
120  original += s
121  else:
122  typ, tok_contents = self.tokens.pop(0)
123  original += tok_contents
124  if typ == 'right paren':
125  paren_depth -= 1
126  if paren_depth == 0:
127  cmd.original = original
128  return cmd
129  elif typ == 'left paren':
130  paren_depth += 1
131  else:
132  cmd.sections.append(tok_contents)
133  raise CMakeParseError('File ended while processing command "%s"' % (command_name))
134 
135  def parse_section(self):
136  original = ''
137  style = SectionStyle()
138  tokens = []
139  cat = ''
140  while self.get_type() in NOT_REAL:
141  s = self.match()
142  original += s
143  style.prename += s
144 
145  if self.get_type() == 'caps':
146  cat = self.match('caps')
147  original += cat
148  style.name_val_sep = ''
149  while self.get_type() in ALL_WHITESPACE:
150  s = self.match()
151  original += s
152  style.name_val_sep += s
153  if len(style.name_val_sep) == 0:
154  style.name_val_sep = ' '
155 
156  delims = set()
157  current = ''
158  while self.next_real_type() not in ['left paren', 'right paren', 'caps']:
159  typ = self.get_type()
160  if typ in ALL_WHITESPACE:
161  token = self.match()
162  original += token
163  current += token
164  else:
165  if len(current) > 0:
166  delims.add(current)
167  current = ''
168  token = self.match()
169  original += token
170  tokens.append(token)
171  if len(current) > 0:
172  delims.add(current)
173  if len(delims) > 0:
174  if len(delims) == 1:
175  style.val_sep = list(delims)[0]
176  else:
177  # TODO: Smarter multi delim parsing
178  # print(delims)
179  style.val_sep = list(delims)[0]
180 
181  # print(cat, tokens, style)
182  return Section(cat, tokens, style), original
183 
184  def match(self, typ=None):
185  if typ is None or self.get_type() == typ:
186  typ, tok = self.tokens.pop(0)
187  # print('[%s]%s'%(typ, repr(tok)))
188  return tok
189  else:
190  sys.stderr.write('Token Dump:\n')
191  for a in self.tokens:
192  sys.stderr.write(str(a) + '\n')
193  raise CMakeParseError('Expected type "%s" but got "%s"' % (typ, self.get_type()))
194 
195  def get_type(self):
196  if len(self.tokens) > 0:
197  return self.tokens[0][0]
198  else:
199  return None
200 
201  def next_real_type(self):
202  for x, y in self.tokens:
203  if x not in NOT_REAL:
204  return x
205 
206 
208  parser = AwesomeParser(s)
209  return parser.contents
210 
211 
213  parser = AwesomeParser(s)
214  assert len(parser.contents) == 1
215  return parser.contents[0]
216 
217 
218 def parse_file(filename):
219  if not os.path.exists(filename):
220  return
221  with open(filename) as f:
222  s = f.read()
223  return CMake(file_path=filename, initial_contents=parse_commands(s))
def word_cb(scanner, token)
Definition: cmake_parser.py:12
def match_command_groups(contents, base_depth=0)
Definition: cmake_parser.py:30


ros_introspection
Author(s):
autogenerated on Wed Mar 3 2021 03:56:00