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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  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 and line[op_pos - 1] in [':', '+']:
  51. op_pos -= 1
  52. else:
  53. self._error('only := and += are supported')
  54. # set op, sym, and val
  55. op = line[op_pos:op_end]
  56. sym = line[:op_pos].strip()
  57. val = self._expand_value(line[op_end:].lstrip())
  58. if op == ':=':
  59. self.symbol_table[sym] = val
  60. elif op == '+=':
  61. self.symbol_table[sym] += ' ' + val
  62. def _parse_line(self, line):
  63. """Parse a source list line."""
  64. # more lines to come
  65. if line and line[-1] == '\\':
  66. # spaces around "\\\n" are replaced by a single space
  67. if self.line_cont:
  68. self.line_cont += line[:-1].strip() + ' '
  69. else:
  70. self.line_cont = line[:-1].rstrip() + ' '
  71. return 0
  72. # combine with previous lines
  73. if self.line_cont:
  74. line = self.line_cont + line.lstrip()
  75. self.line_cont = ''
  76. if line:
  77. begins_with_tab = (line[0] == '\t')
  78. line = line.lstrip()
  79. if line[0] != '#':
  80. if begins_with_tab:
  81. self._error('recipe line not supported')
  82. else:
  83. self._parse_definition(line)
  84. return 1
  85. def parse(self, filename):
  86. """Parse a source list file."""
  87. if self.filename != filename:
  88. fp = open(filename)
  89. lines = fp.read().splitlines()
  90. fp.close()
  91. try:
  92. self._reset(filename)
  93. for line in lines:
  94. self.line_no += self._parse_line(line)
  95. except:
  96. self._reset()
  97. raise
  98. return self.symbol_table