Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. """Source List Parser
  2.  
  3. The syntax of a source list file is a very small subset of GNU Make.  These
  4. features are supported
  5.  
  6. operators: =, +=, :=
  7. line continuation
  8. non-nested variable expansion
  9. comment
  10.  
  11. The goal is to allow Makefile's and SConscript's to share source listing.
  12. """
  13.  
  14. class SourceListParser(object):
  15.     def __init__(self):
  16.         self.symbol_table = {}
  17.         self._reset()
  18.  
  19.     def _reset(self, filename=None):
  20.         self.filename = filename
  21.  
  22.         self.line_no = 1
  23.         self.line_cont = ''
  24.  
  25.     def _error(self, msg):
  26.         raise RuntimeError('%s:%d: %s' % (self.filename, self.line_no, msg))
  27.  
  28.     def _next_dereference(self, val, cur):
  29.         """Locate the next $(...) in value."""
  30.         deref_pos = val.find('$', cur)
  31.         if deref_pos < 0:
  32.             return (-1, -1)
  33.         elif val[deref_pos + 1] != '(':
  34.             self._error('non-variable dereference')
  35.  
  36.         deref_end = val.find(')', deref_pos + 2)
  37.         if deref_end < 0:
  38.             self._error('unterminated variable dereference')
  39.  
  40.         return (deref_pos, deref_end + 1)
  41.  
  42.     def _expand_value(self, val):
  43.         """Perform variable expansion."""
  44.         expanded = ''
  45.         cur = 0
  46.         while True:
  47.             deref_pos, deref_end = self._next_dereference(val, cur)
  48.             if deref_pos < 0:
  49.                 expanded += val[cur:]
  50.                 break
  51.  
  52.             sym = val[(deref_pos + 2):(deref_end - 1)]
  53.             expanded += val[cur:deref_pos] + self.symbol_table[sym]
  54.             cur = deref_end
  55.  
  56.         return expanded
  57.  
  58.     def _parse_definition(self, line):
  59.         """Parse a variable definition line."""
  60.         op_pos = line.find('=')
  61.         op_end = op_pos + 1
  62.         if op_pos < 0:
  63.             self._error('not a variable definition')
  64.  
  65.         if op_pos > 0:
  66.             if line[op_pos - 1] in [':', '+', '?']:
  67.                 op_pos -= 1
  68.         else:
  69.             self._error('only =, :=, and += are supported')
  70.  
  71.         # set op, sym, and val
  72.         op = line[op_pos:op_end]
  73.         sym = line[:op_pos].strip()
  74.         val = self._expand_value(line[op_end:].lstrip())
  75.  
  76.         if op in ('=', ':='):
  77.             self.symbol_table[sym] = val
  78.         elif op == '+=':
  79.             self.symbol_table[sym] += ' ' + val
  80.         elif op == '?=':
  81.             if sym not in self.symbol_table:
  82.                 self.symbol_table[sym] = val
  83.  
  84.     def _parse_line(self, line):
  85.         """Parse a source list line."""
  86.         # more lines to come
  87.         if line and line[-1] == '\\':
  88.             # spaces around "\\\n" are replaced by a single space
  89.             if self.line_cont:
  90.                 self.line_cont += line[:-1].strip() + ' '
  91.             else:
  92.                 self.line_cont = line[:-1].rstrip() + ' '
  93.             return 0
  94.  
  95.         # combine with previous lines
  96.         if self.line_cont:
  97.             line = self.line_cont + line.lstrip()
  98.             self.line_cont = ''
  99.  
  100.         if line:
  101.             begins_with_tab = (line[0] == '\t')
  102.  
  103.             line = line.lstrip()
  104.             if line[0] != '#':
  105.                 if begins_with_tab:
  106.                     self._error('recipe line not supported')
  107.                 else:
  108.                     self._parse_definition(line)
  109.  
  110.         return 1
  111.  
  112.     def parse(self, filename):
  113.         """Parse a source list file."""
  114.         if self.filename != filename:
  115.             fp = open(filename)
  116.             lines = fp.read().splitlines()
  117.             fp.close()
  118.  
  119.             try:
  120.                 self._reset(filename)
  121.                 for line in lines:
  122.                     self.line_no += self._parse_line(line)
  123.             except:
  124.                 self._reset()
  125.                 raise
  126.  
  127.         return self.symbol_table
  128.  
  129.     def add_symbol(self, name, value):
  130.         self.symbol_table[name] = value
  131.