Subversion Repositories Kolibri OS

Rev

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