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 value
= int(self
.content
[:word_len
])
113 rv
= Expression
.__ASTLeaf
('int', value
)
115 word_len
= re
.match('\w*', self
.content
).end()
117 rv
= Expression
.__ASTLeaf
('string', self
.content
[:word_len
])
119 raise Expression
.ParseError
, self
120 self
.__strip
(word_len
)
121 self
.__ignore
_whitespace
()
124 def __ignore_whitespace(self
):
125 ws_len
= re
.match('\s*', self
.content
).end()
129 def __strip(self
, length
):
131 Remove a given amount of chars from the input and update
134 self
.content
= self
.content
[length
:]
135 self
.offset
+= length
137 def evaluate(self
, context
):
139 Evaluate the expression with the given context
142 # Helper function to evaluate __get_equality results
143 def eval_equality(tok
):
144 left
= opmap
[tok
[0].type](tok
[0])
145 right
= opmap
[tok
[2].type](tok
[2])
147 if tok
[1].value
== '!=':
150 # Mapping from token types to evaluator functions
151 # Apart from (non-)equality, all these can be simple lambda forms.
153 'equality': eval_equality
,
154 'not': lambda tok
: not opmap
[tok
[0].type](tok
[0]),
155 'string': lambda tok
: context
[tok
.value
],
156 'int': lambda tok
: tok
.value
}
158 return opmap
[self
.e
.type](self
.e
);
162 Internal class implementing Abstract Syntax Tree nodes
164 def __init__(self
, type):
166 super(self
.__class
__, self
).__init
__(self
)
170 Internal class implementing Abstract Syntax Tree leafs
172 def __init__(self
, type, value
):
176 return self
.value
.__str
__()
178 return self
.value
.__repr
__()
180 class ParseError(StandardError):
182 Error raised when parsing fails.
183 It has two members, offset and content, which give the offset of the
184 error and the offending content.
186 def __init__(self
, expression
):
187 self
.offset
= expression
.offset
188 self
.content
= expression
.content
[:3]
190 return 'Unexpected content at offset %i, "%s"'%(self
.offset
, self
.content
)
194 This class holds variable values by subclassing dict, and while it
195 truthfully reports True and False on
199 it returns the variable name itself on
203 to reflect the ambiguity between string literals and preprocessor
206 def __getitem__(self
, key
):
208 return super(self
.__class
__, self
).__getitem
__(key
)