Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / config / Expression.py
blob56a58db65fabe3dd226c344eeb272c74aa740518
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
12 # License.
14 # The Original Code is Mozilla build system.
16 # The Initial Developer of the Original Code is
17 # Mozilla Foundation.
18 # Portions created by the Initial Developer are Copyright (C) 2007
19 # the Initial Developer. All Rights Reserved.
21 # Contributor(s):
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 *****
38 """
39 Parses and evaluates simple statements for Preprocessor:
41 Expression currently supports the following grammar, whitespace is ignored:
43 expression :
44 unary ( ( '==' | '!=' ) unary ) ? ;
45 unary :
46 '!'? value ;
47 value :
48 [0-9]+ # integer
49 | \w+ # string identifier or value;
50 """
52 import re
54 class Expression:
55 def __init__(self, expression_string):
56 """
57 Create a new expression with this string.
58 The expression will already be parsed into an Abstract Syntax Tree.
59 """
60 self.content = expression_string
61 self.offset = 0
62 self.__ignore_whitespace()
63 self.e = self.__get_equality()
64 if self.content:
65 raise Expression.ParseError, self
67 def __get_equality(self):
68 """
69 Production: unary ( ( '==' | '!=' ) unary ) ?
70 """
71 if not len(self.content):
72 return None
73 rv = Expression.__AST("equality")
74 # unary
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
79 return rv[0]
80 # append operator
81 rv.append(Expression.__ASTLeaf('op', self.content[:2]))
82 self.__strip(2)
83 self.__ignore_whitespace()
84 rv.append(self.__get_unary())
85 self.__ignore_whitespace()
86 return rv
88 def __get_unary(self):
89 """
90 Production: '!'? value
91 """
92 # eat whitespace right away, too
93 not_ws = re.match('!\s*', self.content)
94 if not not_ws:
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()
100 return rv
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.
109 rv = None
110 word_len = re.match('[0-9]*', self.content).end()
111 if word_len:
112 value = int(self.content[:word_len])
113 rv = Expression.__ASTLeaf('int', value)
114 else:
115 word_len = re.match('\w*', self.content).end()
116 if word_len:
117 rv = Expression.__ASTLeaf('string', self.content[:word_len])
118 else:
119 raise Expression.ParseError, self
120 self.__strip(word_len)
121 self.__ignore_whitespace()
122 return rv
124 def __ignore_whitespace(self):
125 ws_len = re.match('\s*', self.content).end()
126 self.__strip(ws_len)
127 return
129 def __strip(self, length):
131 Remove a given amount of chars from the input and update
132 the offset.
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])
146 rv = left == right
147 if tok[1].value == '!=':
148 rv = not rv
149 return rv
150 # Mapping from token types to evaluator functions
151 # Apart from (non-)equality, all these can be simple lambda forms.
152 opmap = {
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);
160 class __AST(list):
162 Internal class implementing Abstract Syntax Tree nodes
164 def __init__(self, type):
165 self.type = type
166 super(self.__class__, self).__init__(self)
168 class __ASTLeaf:
170 Internal class implementing Abstract Syntax Tree leafs
172 def __init__(self, type, value):
173 self.value = value
174 self.type = type
175 def __str__(self):
176 return self.value.__str__()
177 def __repr__(self):
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]
189 def __str__(self):
190 return 'Unexpected content at offset %i, "%s"'%(self.offset, self.content)
192 class Context(dict):
194 This class holds variable values by subclassing dict, and while it
195 truthfully reports True and False on
197 name in context
199 it returns the variable name itself on
201 context["name"]
203 to reflect the ambiguity between string literals and preprocessor
204 variables.
206 def __getitem__(self, key):
207 if key in self:
208 return super(self.__class__, self).__getitem__(key)
209 return key