Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5564 | serge | 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>>>> |