1 # ***** BEGIN LICENSE BLOCK *****
2 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 # The contents of this file are subject to the Mozilla Public License Version
5 # 1.1 (the "License"); you may not use this file except in compliance with
6 # the License. You may obtain a copy of the License at
7 # http://www.mozilla.org/MPL/
9 # Software distributed under the License is distributed on an "AS IS" basis,
10 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 # for the specific language governing rights and limitations under the
14 # The Original Code is Mozilla build system.
16 # The Initial Developer of the Original Code is
18 # Portions created by the Initial Developer are Copyright (C) 2007
19 # the Initial Developer. All Rights Reserved.
22 # Axel Hecht <axel@pike.org>
24 # Alternatively, the contents of this file may be used under the terms of
25 # either the GNU General Public License Version 2 or later (the "GPL"), or
26 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 # in which case the provisions of the GPL or the LGPL are applicable instead
28 # of those above. If you wish to allow use of your version of this file only
29 # under the terms of either the GPL or the LGPL, and not to allow others to
30 # use your version of this file under the terms of the MPL, indicate your
31 # decision by deleting the provisions above and replace them with the notice
32 # and other provisions required by the GPL or the LGPL. If you do not delete
33 # the provisions above, a recipient may use your version of this file under
34 # the terms of any one of the MPL, the GPL or the LGPL.
36 # ***** END LICENSE BLOCK *****
39 Parses and evaluates simple statements for Preprocessor:
41 Expression currently supports the following grammar, whitespace is ignored:
44 unary ( ( '==' | '!=' ) unary ) ? ;
49 | \w+ # string identifier or value;
55 def __init__(self
, expression_string
):
57 Create a new expression with this string.
58 The expression will already be parsed into an Abstract Syntax Tree.
60 self
.content
= expression_string
62 self
.__ignore
_whitespace
()
63 self
.e
= self
.__get
_equality
()
65 raise Expression
.ParseError
, self
67 def __get_equality(self
):
69 Production: unary ( ( '==' | '!=' ) unary ) ?
71 if not len(self
.content
):
73 rv
= Expression
.__AST
("equality")
75 rv
.append(self
.__get
_unary
())
76 self
.__ignore
_whitespace
()
77 if not re
.match('[=!]=', self
.content
):
78 # no equality needed, short cut to our prime unary
81 rv
.append(Expression
.__ASTLeaf
('op', self
.content
[:2]))
83 self
.__ignore
_whitespace
()
84 rv
.append(self
.__get
_unary
())
85 self
.__ignore
_whitespace
()
88 def __get_unary(self
):
90 Production: '!'? value
92 # eat whitespace right away, too
93 not_ws
= re
.match('!\s*', self
.content
)
95 return self
.__get
_value
()
96 rv
= Expression
.__AST
('not')
97 self
.__strip
(not_ws
.end())
98 rv
.append(self
.__get
_value
())
99 self
.__ignore
_whitespace
()
102 def __get_value(self
):
104 Production: ( [0-9]+ | \w+)
105 Note that the order is important, and the expression is kind-of
106 ambiguous as \w includes 0-9. One could make it unambiguous by
107 removing 0-9 from the first char of a string literal.
110 word_len
= re
.match('[0-9]*', self
.content
).end()
112 rv
= Expression
.__ASTLeaf
('int', int(self
.content
[:word_len
]))
114 word_len
= re
.match('\w*', self
.content
).end()
116 rv
= Expression
.__ASTLeaf
('string', self
.content
[:word_len
])
118 raise Expression
.ParseError
, self
119 self
.__strip
(word_len
)
120 self
.__ignore
_whitespace
()
123 def __ignore_whitespace(self
):
124 ws_len
= re
.match('\s*', self
.content
).end()
128 def __strip(self
, length
):
130 Remove a given amount of chars from the input and update
133 self
.content
= self
.content
[length
:]
134 self
.offset
+= length
136 def evaluate(self
, context
):
138 Evaluate the expression with the given context
141 # Helper function to evaluate __get_equality results
142 def eval_equality(tok
):
143 left
= opmap
[tok
[0].type](tok
[0])
144 right
= opmap
[tok
[2].type](tok
[2])
146 if tok
[1].value
== '!=':
149 # Mapping from token types to evaluator functions
150 # Apart from (non-)equality, all these can be simple lambda forms.
152 'equality': eval_equality
,
153 'not': lambda tok
: not opmap
[tok
[0].type](tok
[0]),
154 'string': lambda tok
: context
[tok
.value
],
155 'int': lambda tok
: tok
.value
}
157 return opmap
[self
.e
.type](self
.e
);
161 Internal class implementing Abstract Syntax Tree nodes
163 def __init__(self
, type):
165 super(self
.__class
__, self
).__init
__(self
)
169 Internal class implementing Abstract Syntax Tree leafs
171 def __init__(self
, type, value
):
175 return self
.value
.__str
__()
177 return self
.value
.__repr
__()
179 class ParseError(StandardError):
181 Error raised when parsing fails.
182 It has two members, offset and content, which give the offset of the
183 error and the offending content.
185 def __init__(self
, expression
):
186 self
.offset
= expression
.offset
187 self
.content
= expression
.content
[:3]
189 return 'Unexpected content at offset %i, "%s"'%(self
.offset
, self
.content
)
193 This class holds variable values by subclassing dict, and while it
194 truthfully reports True and False on
198 it returns the variable name itself on
202 to reflect the ambiguity between string literals and preprocessor
205 def __getitem__(self
, key
):
207 return super(self
.__class
__, self
).__getitem
__(key
)