123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- """Source List Parser
-
- The syntax of a source list file is a very small subset of GNU Make. These
- features are supported
-
- operators: +=, :=
- line continuation
- non-nested variable expansion
- comment
-
- The goal is to allow Makefile's and SConscript's to share source listing.
- """
-
- class SourceListParser(object):
- def __init__(self):
- self._reset()
-
- def _reset(self, filename=None):
- self.filename = filename
-
- self.line_no = 1
- self.line_cont = ''
- self.symbol_table = {}
-
- def _error(self, msg):
- raise RuntimeError('%s:%d: %s' % (self.filename, self.line_no, msg))
-
- def _next_dereference(self, val, cur):
- """Locate the next $(...) in value."""
- deref_pos = val.find('$', cur)
- if deref_pos < 0:
- return (-1, -1)
- elif val[deref_pos + 1] != '(':
- self._error('non-variable dereference')
-
- deref_end = val.find(')', deref_pos + 2)
- if deref_end < 0:
- self._error('unterminated variable dereference')
-
- return (deref_pos, deref_end + 1)
-
- def _expand_value(self, val):
- """Perform variable expansion."""
- expanded = ''
- cur = 0
- while True:
- deref_pos, deref_end = self._next_dereference(val, cur)
- if deref_pos < 0:
- expanded += val[cur:]
- break
-
- sym = val[(deref_pos + 2):(deref_end - 1)]
- expanded += val[cur:deref_pos] + self.symbol_table[sym]
- cur = deref_end
-
- return expanded
-
- def _parse_definition(self, line):
- """Parse a variable definition line."""
- op_pos = line.find('=')
- op_end = op_pos + 1
- if op_pos < 0:
- self._error('not a variable definition')
-
- if op_pos > 0 and line[op_pos - 1] in [':', '+']:
- op_pos -= 1
- else:
- self._error('only := and += are supported')
-
- # set op, sym, and val
- op = line[op_pos:op_end]
- sym = line[:op_pos].strip()
- val = self._expand_value(line[op_end:].lstrip())
-
- if op == ':=':
- self.symbol_table[sym] = val
- elif op == '+=':
- self.symbol_table[sym] += ' ' + val
-
- def _parse_line(self, line):
- """Parse a source list line."""
- # more lines to come
- if line and line[-1] == '\\':
- # spaces around "\\\n" are replaced by a single space
- if self.line_cont:
- self.line_cont += line[:-1].strip() + ' '
- else:
- self.line_cont = line[:-1].rstrip() + ' '
- return 0
-
- # combine with previous lines
- if self.line_cont:
- line = self.line_cont + line.lstrip()
- self.line_cont = ''
-
- if line:
- begins_with_tab = (line[0] == '\t')
-
- line = line.lstrip()
- if line[0] != '#':
- if begins_with_tab:
- self._error('recipe line not supported')
- else:
- self._parse_definition(line)
-
- return 1
-
- def parse(self, filename):
- """Parse a source list file."""
- if self.filename != filename:
- fp = open(filename)
- lines = fp.read().splitlines()
- fp.close()
-
- try:
- self._reset(filename)
- for line in lines:
- self.line_no += self._parse_line(line)
- except:
- self._reset()
- raise
-
- return self.symbol_table
|