Clone of mesa.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

source_list.py 3.5KB

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