diff options
author | Jacek Antonelli | 2008-08-15 23:45:02 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:45:02 -0500 |
commit | d644fc64407dcd14ffcee6a0e9fbe28ee3a4e9bd (patch) | |
tree | 7ed0c2c27d717801238a2e6b5749cd5bf88c3059 /linden/indra/lib | |
parent | Second Life viewer sources 1.17.3.0 (diff) | |
download | meta-impy-d644fc64407dcd14ffcee6a0e9fbe28ee3a4e9bd.zip meta-impy-d644fc64407dcd14ffcee6a0e9fbe28ee3a4e9bd.tar.gz meta-impy-d644fc64407dcd14ffcee6a0e9fbe28ee3a4e9bd.tar.bz2 meta-impy-d644fc64407dcd14ffcee6a0e9fbe28ee3a4e9bd.tar.xz |
Second Life viewer sources 1.18.0.6
Diffstat (limited to 'linden/indra/lib')
-rw-r--r-- | linden/indra/lib/python/indra/__init__.py | 25 | ||||
-rw-r--r-- | linden/indra/lib/python/indra/compatibility.py | 121 | ||||
-rw-r--r-- | linden/indra/lib/python/indra/llmanifest.py | 2 | ||||
-rw-r--r-- | linden/indra/lib/python/indra/llmessage.py | 375 | ||||
-rw-r--r-- | linden/indra/lib/python/indra/tokenstream.py | 152 |
5 files changed, 674 insertions, 1 deletions
diff --git a/linden/indra/lib/python/indra/__init__.py b/linden/indra/lib/python/indra/__init__.py new file mode 100644 index 0000000..7548558 --- /dev/null +++ b/linden/indra/lib/python/indra/__init__.py | |||
@@ -0,0 +1,25 @@ | |||
1 | # @file __init__.py | ||
2 | # @brief Initialization file for the indra module. | ||
3 | # | ||
4 | # Copyright (c) 2006-2007, Linden Research, Inc. | ||
5 | # | ||
6 | # Second Life Viewer Source Code | ||
7 | # The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | # to you under the terms of the GNU General Public License, version 2.0 | ||
9 | # ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | # ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | # the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | # online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | # | ||
14 | # There are special exceptions to the terms and conditions of the GPL as | ||
15 | # it is applied to this Source Code. View the full text of the exception | ||
16 | # in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | # online at http://secondlife.com/developers/opensource/flossexception | ||
18 | # | ||
19 | # By copying, modifying or distributing this software, you acknowledge | ||
20 | # that you have read and understood your obligations described above, | ||
21 | # and agree to abide by those obligations. | ||
22 | # | ||
23 | # ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | # WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | # COMPLETENESS OR PERFORMANCE. | ||
diff --git a/linden/indra/lib/python/indra/compatibility.py b/linden/indra/lib/python/indra/compatibility.py new file mode 100644 index 0000000..abc1c6a --- /dev/null +++ b/linden/indra/lib/python/indra/compatibility.py | |||
@@ -0,0 +1,121 @@ | |||
1 | # @file compatibility.py | ||
2 | # @brief Classes that manage compatibility states. | ||
3 | # | ||
4 | # Copyright (c) 2007-2007, Linden Research, Inc. | ||
5 | # | ||
6 | # Second Life Viewer Source Code | ||
7 | # The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | # to you under the terms of the GNU General Public License, version 2.0 | ||
9 | # ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | # ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | # the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | # online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | # | ||
14 | # There are special exceptions to the terms and conditions of the GPL as | ||
15 | # it is applied to this Source Code. View the full text of the exception | ||
16 | # in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | # online at http://secondlife.com/developers/opensource/flossexception | ||
18 | # | ||
19 | # By copying, modifying or distributing this software, you acknowledge | ||
20 | # that you have read and understood your obligations described above, | ||
21 | # and agree to abide by those obligations. | ||
22 | # | ||
23 | # ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | # WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | # COMPLETENESS OR PERFORMANCE. | ||
26 | |||
27 | |||
28 | """Compatibility combination table: | ||
29 | |||
30 | I M O N S | ||
31 | -- -- -- -- -- | ||
32 | I: I I I I I | ||
33 | M: I M M M M | ||
34 | O: I M O M O | ||
35 | N: I M M N N | ||
36 | S: I M O N S | ||
37 | |||
38 | """ | ||
39 | |||
40 | class _Compatibility(object): | ||
41 | def __init__(self, reason): | ||
42 | self.reasons = [ ] | ||
43 | if reason: | ||
44 | self.reasons.append(reason) | ||
45 | |||
46 | def combine(self, other): | ||
47 | if self._level() <= other._level(): | ||
48 | return self._buildclone(other) | ||
49 | else: | ||
50 | return other._buildclone(self) | ||
51 | |||
52 | def prefix(self, leadin): | ||
53 | self.reasons = [ leadin + r for r in self.reasons ] | ||
54 | |||
55 | def same(self): return self._level() >= 1 | ||
56 | def deployable(self): return self._level() > 0 | ||
57 | def resolved(self): return self._level() > -1 | ||
58 | def compatible(self): return self._level() > -2 | ||
59 | |||
60 | def explain(self): | ||
61 | return self.__class__.__name__ + "\n" + "\n".join(self.reasons) + "\n" | ||
62 | |||
63 | def _buildclone(self, other=None): | ||
64 | c = self._buildinstance() | ||
65 | c.reasons = self.reasons | ||
66 | if other: | ||
67 | c.reasons = c.reasons + other.reasons | ||
68 | return c | ||
69 | |||
70 | def _buildinstance(self): | ||
71 | return self.__class__(None) | ||
72 | |||
73 | # def _level(self): | ||
74 | # raise RuntimeError('implement in subclass') | ||
75 | |||
76 | |||
77 | class Incompatible(_Compatibility): | ||
78 | def _level(self): | ||
79 | return -2 | ||
80 | |||
81 | class Mixed(_Compatibility): | ||
82 | def __init__(self, *inputs): | ||
83 | _Compatibility.__init__(self, None) | ||
84 | for i in inputs: | ||
85 | self.reasons += i.reasons | ||
86 | |||
87 | def _buildinstance(self): | ||
88 | return self.__class__() | ||
89 | |||
90 | def _level(self): | ||
91 | return -1 | ||
92 | |||
93 | class _Aged(_Compatibility): | ||
94 | def combine(self, other): | ||
95 | if self._level() == other._level(): | ||
96 | return self._buildclone(other) | ||
97 | if int(self._level()) == int(other._level()): | ||
98 | return Mixed(self, other) | ||
99 | return _Compatibility.combine(self, other) | ||
100 | |||
101 | class Older(_Aged): | ||
102 | def _level(self): | ||
103 | return -0.25 | ||
104 | |||
105 | class Newer(_Aged): | ||
106 | def _level(self): | ||
107 | return 0.25 | ||
108 | |||
109 | class Same(_Compatibility): | ||
110 | def __init__(self): | ||
111 | _Compatibility.__init__(self, None) | ||
112 | |||
113 | def _buildinstance(self): | ||
114 | return self.__class__() | ||
115 | |||
116 | def _level(self): | ||
117 | return 1 | ||
118 | |||
119 | |||
120 | |||
121 | |||
diff --git a/linden/indra/lib/python/indra/llmanifest.py b/linden/indra/lib/python/indra/llmanifest.py index e295cd7..22483b4 100644 --- a/linden/indra/lib/python/indra/llmanifest.py +++ b/linden/indra/lib/python/indra/llmanifest.py | |||
@@ -3,7 +3,7 @@ | |||
3 | # @author Ryan Williams | 3 | # @author Ryan Williams |
4 | # @brief Library for specifying operations on a set of files. | 4 | # @brief Library for specifying operations on a set of files. |
5 | # | 5 | # |
6 | # Copyright (c) 2006-2007, Linden Research, Inc. | 6 | # Copyright (c) 2007-2007, Linden Research, Inc. |
7 | # | 7 | # |
8 | # Second Life Viewer Source Code | 8 | # Second Life Viewer Source Code |
9 | # The source code in this file ("Source Code") is provided by Linden Lab | 9 | # The source code in this file ("Source Code") is provided by Linden Lab |
diff --git a/linden/indra/lib/python/indra/llmessage.py b/linden/indra/lib/python/indra/llmessage.py new file mode 100644 index 0000000..1d4995d --- /dev/null +++ b/linden/indra/lib/python/indra/llmessage.py | |||
@@ -0,0 +1,375 @@ | |||
1 | # @file llmessage.py | ||
2 | # @brief Message template parsing and compatiblity | ||
3 | # | ||
4 | # Copyright (c) 2007-2007, Linden Research, Inc. | ||
5 | # | ||
6 | # Second Life Viewer Source Code | ||
7 | # The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | # to you under the terms of the GNU General Public License, version 2.0 | ||
9 | # ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | # ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | # the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | # online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | # | ||
14 | # There are special exceptions to the terms and conditions of the GPL as | ||
15 | # it is applied to this Source Code. View the full text of the exception | ||
16 | # in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | # online at http://secondlife.com/developers/opensource/flossexception | ||
18 | # | ||
19 | # By copying, modifying or distributing this software, you acknowledge | ||
20 | # that you have read and understood your obligations described above, | ||
21 | # and agree to abide by those obligations. | ||
22 | # | ||
23 | # ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | # WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | # COMPLETENESS OR PERFORMANCE. | ||
26 | |||
27 | from sets import Set, ImmutableSet | ||
28 | |||
29 | from compatibility import Incompatible, Older, Newer, Same | ||
30 | from tokenstream import TokenStream | ||
31 | |||
32 | ### | ||
33 | ### Message Template | ||
34 | ### | ||
35 | |||
36 | class Template: | ||
37 | def __init__(self): | ||
38 | self.messages = { } | ||
39 | |||
40 | def addMessage(self, m): | ||
41 | self.messages[m.name] = m | ||
42 | |||
43 | def compatibleWithBase(self, base): | ||
44 | messagenames = ( | ||
45 | ImmutableSet(self.messages.keys()) | ||
46 | | ImmutableSet(base.messages.keys()) | ||
47 | ) | ||
48 | |||
49 | compatibility = Same() | ||
50 | for name in messagenames: | ||
51 | selfmessage = self.messages.get(name, None) | ||
52 | basemessage = base.messages.get(name, None) | ||
53 | |||
54 | if not selfmessage: | ||
55 | c = Older("missing message %s, did you mean to deprecate?" % name) | ||
56 | elif not basemessage: | ||
57 | c = Newer("added message %s" % name) | ||
58 | else: | ||
59 | c = selfmessage.compatibleWithBase(basemessage) | ||
60 | c.prefix("in message %s: " % name) | ||
61 | |||
62 | compatibility = compatibility.combine(c) | ||
63 | |||
64 | return compatibility | ||
65 | |||
66 | |||
67 | |||
68 | class Message: | ||
69 | HIGH = "High" | ||
70 | MEDIUM = "Medium" | ||
71 | LOW = "Low" | ||
72 | FIXED = "Fixed" | ||
73 | priorities = [ HIGH, MEDIUM, LOW, FIXED ] | ||
74 | prioritieswithnumber = [ FIXED ] | ||
75 | |||
76 | TRUSTED = "Trusted" | ||
77 | NOTTRUSTED = "NotTrusted" | ||
78 | trusts = [ TRUSTED, NOTTRUSTED ] | ||
79 | |||
80 | UNENCODED = "Unencoded" | ||
81 | ZEROCODED = "Zerocoded" | ||
82 | encodings = [ UNENCODED, ZEROCODED ] | ||
83 | |||
84 | NOTDEPRECATED = "NotDeprecated" | ||
85 | DEPRECATED = "Deprecated" | ||
86 | UDPDEPRECATED = "UDPDeprecated" | ||
87 | deprecations = [ NOTDEPRECATED, UDPDEPRECATED, DEPRECATED ] | ||
88 | # in order of increasing deprecation | ||
89 | |||
90 | def __init__(self, name, number, priority, trust, coding): | ||
91 | self.name = name | ||
92 | self.number = number | ||
93 | self.priority = priority | ||
94 | self.trust = trust | ||
95 | self.coding = coding | ||
96 | self.deprecateLevel = 0 | ||
97 | self.blocks = [ ] | ||
98 | |||
99 | def deprecated(self): | ||
100 | return self.deprecateLevel != 0 | ||
101 | |||
102 | def deprecate(self, deprecation): | ||
103 | self.deprecateLevel = self.deprecations.index(deprecation) | ||
104 | |||
105 | def addBlock(self, block): | ||
106 | self.blocks.append(block) | ||
107 | |||
108 | def compatibleWithBase(self, base): | ||
109 | if self.name != base.name: | ||
110 | # this should never happen in real life because of the | ||
111 | # way Template matches up messages by name | ||
112 | return Incompatible("has different name: %s vs. %s in base" | ||
113 | % (self.name, base.name)) | ||
114 | if self.priority != base.priority: | ||
115 | return Incompatible("has different priority: %s vs. %s in base" | ||
116 | % (self.priority, base.priority)) | ||
117 | if self.trust != base.trust: | ||
118 | return Incompatible("has different trust: %s vs. %s in base" | ||
119 | % (self.trust, base.trust)) | ||
120 | if self.coding != base.coding: | ||
121 | return Incompatible("has different coding: %s vs. %s in base" | ||
122 | % (self.coding, base.coding)) | ||
123 | if self.number != base.number: | ||
124 | return Incompatible("has different number: %s vs. %s in base" | ||
125 | % (self.number, base.number)) | ||
126 | |||
127 | compatibility = Same() | ||
128 | |||
129 | if self.deprecateLevel != base.deprecateLevel: | ||
130 | if self.deprecateLevel < base.deprecateLevel: | ||
131 | c = Older("is less deprecated: %s vs. %s in base" % ( | ||
132 | self.deprecations[self.deprecateLevel], | ||
133 | self.deprecations[base.deprecateLevel])) | ||
134 | else: | ||
135 | c = Newer("is more deprecated: %s vs. %s in base" % ( | ||
136 | self.deprecations[self.deprecateLevel], | ||
137 | self.deprecations[base.deprecateLevel])) | ||
138 | compatibility = compatibility.combine(c) | ||
139 | |||
140 | selflen = len(self.blocks) | ||
141 | baselen = len(base.blocks) | ||
142 | samelen = min(selflen, baselen) | ||
143 | |||
144 | for i in xrange(0, samelen): | ||
145 | selfblock = self.blocks[i] | ||
146 | baseblock = base.blocks[i] | ||
147 | |||
148 | c = selfblock.compatibleWithBase(baseblock) | ||
149 | if not c.same(): | ||
150 | c = Incompatible("block %d isn't identical" % i) | ||
151 | compatibility = compatibility.combine(c) | ||
152 | |||
153 | if selflen > baselen: | ||
154 | c = Newer("has %d extra blocks" % (selflen - baselen)) | ||
155 | elif selflen < baselen: | ||
156 | c = Older("missing %d extra blocks" % (baselen - selflen)) | ||
157 | else: | ||
158 | c = Same() | ||
159 | |||
160 | compatibility = compatibility.combine(c) | ||
161 | return compatibility | ||
162 | |||
163 | |||
164 | |||
165 | class Block(object): | ||
166 | SINGLE = "Single" | ||
167 | MULTIPLE = "Multiple" | ||
168 | VARIABLE = "Variable" | ||
169 | repeats = [ SINGLE, MULTIPLE, VARIABLE ] | ||
170 | repeatswithcount = [ MULTIPLE ] | ||
171 | |||
172 | def __init__(self, name, repeat, count=None): | ||
173 | self.name = name | ||
174 | self.repeat = repeat | ||
175 | self.count = count | ||
176 | self.variables = [ ] | ||
177 | |||
178 | def addVariable(self, variable): | ||
179 | self.variables.append(variable) | ||
180 | |||
181 | def compatibleWithBase(self, base): | ||
182 | if self.name != base.name: | ||
183 | return Incompatible("has different name: %s vs. %s in base" | ||
184 | % (self.name, base.name)) | ||
185 | if self.repeat != base.repeat: | ||
186 | return Incompatible("has different repeat: %s vs. %s in base" | ||
187 | % (self.repeat, base.repeat)) | ||
188 | if self.repeat in Block.repeatswithcount: | ||
189 | if self.count != base.count: | ||
190 | return Incompatible("has different count: %s vs. %s in base" | ||
191 | % (self.count, base.count)) | ||
192 | |||
193 | compatibility = Same() | ||
194 | |||
195 | selflen = len(self.variables) | ||
196 | baselen = len(base.variables) | ||
197 | |||
198 | for i in xrange(0, min(selflen, baselen)): | ||
199 | selfvar = self.variables[i] | ||
200 | basevar = base.variables[i] | ||
201 | |||
202 | c = selfvar.compatibleWithBase(basevar) | ||
203 | if not c.same(): | ||
204 | c = Incompatible("variable %d isn't identical" % i) | ||
205 | compatibility = compatibility.combine(c) | ||
206 | |||
207 | if selflen > baselen: | ||
208 | c = Newer("has %d extra variables" % (selflen - baselen)) | ||
209 | elif selflen < baselen: | ||
210 | c = Older("missing %d extra variables" % (baselen - selflen)) | ||
211 | else: | ||
212 | c = Same() | ||
213 | |||
214 | compatibility = compatibility.combine(c) | ||
215 | return compatibility | ||
216 | |||
217 | |||
218 | |||
219 | class Variable: | ||
220 | U8 = "U8"; U16 = "U16"; U32 = "U32"; U64 = "U64" | ||
221 | S8 = "S8"; S16 = "S16"; S32 = "S32"; S64 = "S64" | ||
222 | F32 = "F32"; F64 = "F64" | ||
223 | LLVECTOR3 = "LLVector3"; LLVECTOR3D = "LLVector3d"; LLVECTOR4 = "LLVector4" | ||
224 | LLQUATERNION = "LLQuaternion" | ||
225 | LLUUID = "LLUUID" | ||
226 | BOOL = "BOOL" | ||
227 | IPADDR = "IPADDR"; IPPORT = "IPPORT" | ||
228 | FIXED = "Fixed" | ||
229 | VARIABLE = "Variable" | ||
230 | types = [ U8, U16, U32, U64, S8, S16, S32, S64, F32, F64, | ||
231 | LLVECTOR3, LLVECTOR3D, LLVECTOR4, LLQUATERNION, | ||
232 | LLUUID, BOOL, IPADDR, IPPORT, FIXED, VARIABLE ] | ||
233 | typeswithsize = [ FIXED, VARIABLE ] | ||
234 | |||
235 | def __init__(self, name, type, size): | ||
236 | self.name = name | ||
237 | self.type = type | ||
238 | self.size = size | ||
239 | |||
240 | def compatibleWithBase(self, base): | ||
241 | if self.name != base.name: | ||
242 | return Incompatible("has different name: %s vs. %s in base" | ||
243 | % (self.name, base.name)) | ||
244 | if self.type != base.type: | ||
245 | return Incompatible("has different type: %s vs. %s in base" | ||
246 | % (self.type, base.type)) | ||
247 | if self.type in Variable.typeswithsize: | ||
248 | if self.size != base.size: | ||
249 | return Incompatible("has different size: %s vs. %s in base" | ||
250 | % (self.size, base.size)) | ||
251 | return Same() | ||
252 | |||
253 | |||
254 | |||
255 | ### | ||
256 | ### Parsing Message Templates | ||
257 | ### | ||
258 | |||
259 | class TemplateParser: | ||
260 | def __init__(self, tokens): | ||
261 | self._tokens = tokens | ||
262 | self._version = 0 | ||
263 | self._numbers = { } | ||
264 | for p in Message.priorities: | ||
265 | self._numbers[p] = 0 | ||
266 | |||
267 | def parseTemplate(self): | ||
268 | tokens = self._tokens | ||
269 | t = Template() | ||
270 | while True: | ||
271 | if tokens.want("version"): | ||
272 | v = float(tokens.require(tokens.wantFloat())) | ||
273 | self._version = v | ||
274 | t.version = v | ||
275 | continue | ||
276 | |||
277 | m = self.parseMessage() | ||
278 | if m: | ||
279 | t.addMessage(m) | ||
280 | continue | ||
281 | |||
282 | if self._version >= 2.0: | ||
283 | tokens.require(tokens.wantEOF()) | ||
284 | break | ||
285 | else: | ||
286 | if tokens.wantEOF(): | ||
287 | break | ||
288 | |||
289 | tokens.consume() | ||
290 | # just assume (gulp) that this is a comment | ||
291 | # line 468: "sim -> dataserver" | ||
292 | return t | ||
293 | |||
294 | |||
295 | def parseMessage(self): | ||
296 | tokens = self._tokens | ||
297 | if not tokens.want("{"): | ||
298 | return None | ||
299 | |||
300 | name = tokens.require(tokens.wantSymbol()) | ||
301 | priority = tokens.require(tokens.wantOneOf(Message.priorities)) | ||
302 | |||
303 | if self._version >= 2.0 or priority in Message.prioritieswithnumber: | ||
304 | number = int("+" + tokens.require(tokens.wantInteger()), 0) | ||
305 | else: | ||
306 | self._numbers[priority] += 1 | ||
307 | number = self._numbers[priority] | ||
308 | |||
309 | trust = tokens.require(tokens.wantOneOf(Message.trusts)) | ||
310 | coding = tokens.require(tokens.wantOneOf(Message.encodings)) | ||
311 | |||
312 | m = Message(name, number, priority, trust, coding) | ||
313 | |||
314 | if self._version >= 2.0: | ||
315 | d = tokens.wantOneOf(Message.deprecations) | ||
316 | if d: | ||
317 | m.deprecate(d) | ||
318 | |||
319 | while True: | ||
320 | b = self.parseBlock() | ||
321 | if not b: | ||
322 | break | ||
323 | m.addBlock(b) | ||
324 | |||
325 | tokens.require(tokens.want("}")) | ||
326 | |||
327 | return m | ||
328 | |||
329 | |||
330 | def parseBlock(self): | ||
331 | tokens = self._tokens | ||
332 | if not tokens.want("{"): | ||
333 | return None | ||
334 | name = tokens.require(tokens.wantSymbol()) | ||
335 | repeat = tokens.require(tokens.wantOneOf(Block.repeats)) | ||
336 | if repeat in Block.repeatswithcount: | ||
337 | count = int(tokens.require(tokens.wantInteger())) | ||
338 | else: | ||
339 | count = None | ||
340 | |||
341 | b = Block(name, repeat, count) | ||
342 | |||
343 | while True: | ||
344 | v = self.parseVariable() | ||
345 | if not v: | ||
346 | break | ||
347 | b.addVariable(v) | ||
348 | |||
349 | tokens.require(tokens.want("}")) | ||
350 | return b | ||
351 | |||
352 | |||
353 | def parseVariable(self): | ||
354 | tokens = self._tokens | ||
355 | if not tokens.want("{"): | ||
356 | return None | ||
357 | name = tokens.require(tokens.wantSymbol()) | ||
358 | type = tokens.require(tokens.wantOneOf(Variable.types)) | ||
359 | if type in Variable.typeswithsize: | ||
360 | size = tokens.require(tokens.wantInteger()) | ||
361 | else: | ||
362 | tokens.wantInteger() # in LandStatRequest: "{ ParcelLocalID S32 1 }" | ||
363 | size = None | ||
364 | tokens.require(tokens.want("}")) | ||
365 | return Variable(name, type, size) | ||
366 | |||
367 | def parseTemplateString(s): | ||
368 | return TemplateParser(TokenStream().fromString(s)).parseTemplate() | ||
369 | |||
370 | def parseTemplateFile(f): | ||
371 | return TemplateParser(TokenStream().fromFile(f)).parseTemplate() | ||
372 | |||
373 | |||
374 | |||
375 | |||
diff --git a/linden/indra/lib/python/indra/tokenstream.py b/linden/indra/lib/python/indra/tokenstream.py new file mode 100644 index 0000000..7dab11f --- /dev/null +++ b/linden/indra/lib/python/indra/tokenstream.py | |||
@@ -0,0 +1,152 @@ | |||
1 | # @file tokenstream.py | ||
2 | # @brief Message template parsing utility class | ||
3 | # | ||
4 | # Copyright (c) 2007-2007, Linden Research, Inc. | ||
5 | # | ||
6 | # Second Life Viewer Source Code | ||
7 | # The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | # to you under the terms of the GNU General Public License, version 2.0 | ||
9 | # ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | # ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | # the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | # online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | # | ||
14 | # There are special exceptions to the terms and conditions of the GPL as | ||
15 | # it is applied to this Source Code. View the full text of the exception | ||
16 | # in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | # online at http://secondlife.com/developers/opensource/flossexception | ||
18 | # | ||
19 | # By copying, modifying or distributing this software, you acknowledge | ||
20 | # that you have read and understood your obligations described above, | ||
21 | # and agree to abide by those obligations. | ||
22 | # | ||
23 | # ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | # WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | # COMPLETENESS OR PERFORMANCE. | ||
26 | |||
27 | import re | ||
28 | |||
29 | class _EOF(object): | ||
30 | pass | ||
31 | |||
32 | EOF = _EOF() | ||
33 | |||
34 | class _LineMarker(int): | ||
35 | pass | ||
36 | |||
37 | _commentRE = re.compile(r'//.*') | ||
38 | _symbolRE = re.compile(r'[a-zA-Z_][a-zA-Z_0-9]*') | ||
39 | _integerRE = re.compile(r'(0x[0-9A-Fa-f]+|0\d*|[1-9]\d*)') | ||
40 | _floatRE = re.compile(r'\d+(\.\d*)?') | ||
41 | |||
42 | |||
43 | class ParseError(Exception): | ||
44 | def __init__(self, stream, reason): | ||
45 | self.line = stream.line | ||
46 | self.context = stream._context() | ||
47 | self.reason = reason | ||
48 | |||
49 | def _contextString(self): | ||
50 | c = [ ] | ||
51 | for t in self.context: | ||
52 | if isinstance(t, _LineMarker): | ||
53 | break | ||
54 | c.append(t) | ||
55 | return " ".join(c) | ||
56 | |||
57 | def __str__(self): | ||
58 | return "line %d: %s @ ... %s" % ( | ||
59 | self.line, self.reason, self._contextString()) | ||
60 | |||
61 | def __nonzero__(self): | ||
62 | return False | ||
63 | |||
64 | |||
65 | def _optionText(options): | ||
66 | n = len(options) | ||
67 | if n == 1: | ||
68 | return '"%s"' % options[0] | ||
69 | return '"' + '", "'.join(options[0:(n-1)]) + '" or "' + options[-1] + '"' | ||
70 | |||
71 | |||
72 | class TokenStream(object): | ||
73 | def __init__(self): | ||
74 | self.line = 0 | ||
75 | self.tokens = [ ] | ||
76 | |||
77 | def fromString(self, string): | ||
78 | return self.fromLines(string.split('\n')) | ||
79 | |||
80 | def fromFile(self, file): | ||
81 | return self.fromLines(file) | ||
82 | |||
83 | def fromLines(self, lines): | ||
84 | i = 0 | ||
85 | for line in lines: | ||
86 | i += 1 | ||
87 | self.tokens.append(_LineMarker(i)) | ||
88 | self.tokens.extend(_commentRE.sub(" ", line).split()) | ||
89 | self._consumeLines() | ||
90 | return self | ||
91 | |||
92 | def consume(self): | ||
93 | if not self.tokens: | ||
94 | return EOF | ||
95 | t = self.tokens.pop(0) | ||
96 | self._consumeLines() | ||
97 | return t | ||
98 | |||
99 | def _consumeLines(self): | ||
100 | while self.tokens and isinstance(self.tokens[0], _LineMarker): | ||
101 | self.line = self.tokens.pop(0) | ||
102 | |||
103 | def peek(self): | ||
104 | if not self.tokens: | ||
105 | return EOF | ||
106 | return self.tokens[0] | ||
107 | |||
108 | def want(self, t): | ||
109 | if t == self.peek(): | ||
110 | return self.consume() | ||
111 | return ParseError(self, 'expected "%s"' % t) | ||
112 | |||
113 | def wantOneOf(self, options): | ||
114 | assert len(options) | ||
115 | if self.peek() in options: | ||
116 | return self.consume() | ||
117 | return ParseError(self, 'expected one of %s' % _optionText(options)) | ||
118 | |||
119 | def wantEOF(self): | ||
120 | return self.want(EOF) | ||
121 | |||
122 | def wantRE(self, re, message=None): | ||
123 | t = self.peek() | ||
124 | if t != EOF: | ||
125 | m = re.match(t) | ||
126 | if m and m.end() == len(t): | ||
127 | return self.consume() | ||
128 | if not message: | ||
129 | message = "expected match for r'%s'" % re.pattern | ||
130 | return ParseError(self, message) | ||
131 | |||
132 | def wantSymbol(self): | ||
133 | return self.wantRE(_symbolRE, "expected symbol") | ||
134 | |||
135 | def wantInteger(self): | ||
136 | return self.wantRE(_integerRE, "expected integer") | ||
137 | |||
138 | def wantFloat(self): | ||
139 | return self.wantRE(_floatRE, "expected float") | ||
140 | |||
141 | def _context(self): | ||
142 | n = min(5, len(self.tokens)) | ||
143 | return self.tokens[0:n] | ||
144 | |||
145 | def require(self, t): | ||
146 | if t: | ||
147 | return t | ||
148 | if isinstance(t, ParseError): | ||
149 | raise t | ||
150 | else: | ||
151 | raise ParseError(self, "unmet requirement") | ||
152 | |||