aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/YEngine/MMRScriptVarDict.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/YEngine/MMRScriptVarDict.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/YEngine/MMRScriptVarDict.cs401
1 files changed, 401 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptVarDict.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptVarDict.cs
new file mode 100644
index 0000000..a0bc7ba
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptVarDict.cs
@@ -0,0 +1,401 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31
32/**
33 * @brief Collection of variable/function/method definitions
34 */
35
36namespace OpenSim.Region.ScriptEngine.Yengine
37{
38 public class VarDict: IEnumerable
39 {
40 public VarDict outerVarDict; // next outer VarDict to search
41 public TokenDeclSDTypeClass thisClass; // this VarDict is for members of thisClass
42
43 private struct ArgTypes
44 {
45 public TokenType[] argTypes;
46
47 public bool CanBeCalledBy(TokenType[] calledBy)
48 {
49 if((argTypes == null) && (calledBy == null))
50 return true;
51 if((argTypes == null) || (calledBy == null))
52 return false;
53 if(argTypes.Length != calledBy.Length)
54 return false;
55 for(int i = argTypes.Length; --i >= 0;)
56 {
57 if(!TypeCast.IsAssignableFrom(argTypes[i], calledBy[i]))
58 return false;
59 }
60 return true;
61 }
62
63 public override bool Equals(Object that)
64 {
65 if(that == null)
66 return false;
67 if(that.GetType() != typeof(ArgTypes))
68 return false;
69 TokenType[] at = this.argTypes;
70 TokenType[] bt = ((ArgTypes)that).argTypes;
71 if((at == null) && (bt == null))
72 return true;
73 if((at == null) || (bt == null))
74 return false;
75 if(at.Length != bt.Length)
76 return false;
77 for(int i = at.Length; --i >= 0;)
78 {
79 if(at[i].ToString() != bt[i].ToString())
80 return false;
81 }
82 return true;
83 }
84
85 public override int GetHashCode()
86 {
87 TokenType[] at = this.argTypes;
88 if(at == null)
89 return -1;
90 int hc = 0;
91 for(int i = at.Length; --i >= 0;)
92 {
93 int c = (hc < 0) ? 1 : 0;
94 hc = hc * 2 + c;
95 hc ^= at[i].ToString().GetHashCode();
96 }
97 return hc;
98 }
99 }
100
101 private struct TDVEntry
102 {
103 public int count;
104 public TokenDeclVar var;
105 }
106
107 private bool isFrozen = false;
108 private bool locals;
109 private Dictionary<string, Dictionary<ArgTypes, TDVEntry>> master = new Dictionary<string, Dictionary<ArgTypes, TDVEntry>>();
110 private int count = 0;
111 private VarDict frozenLocals = null;
112
113 /**
114 * @brief Constructor.
115 * @param locals = false: cannot be frozen, allows forward references
116 * true: can be frozen, thus forbidding forward references
117 */
118 public VarDict(bool locals)
119 {
120 this.locals = locals;
121 }
122
123 /**
124 * @brief Add new variable to the dictionary.
125 */
126 public bool AddEntry(TokenDeclVar var)
127 {
128 if(isFrozen)
129 {
130 throw new Exception("var dict is frozen");
131 }
132
133 // Make sure we have a sub-dictionary based on the bare name (ie, no signature)
134 Dictionary<ArgTypes, TDVEntry> typedic;
135 if(!master.TryGetValue(var.name.val, out typedic))
136 {
137 typedic = new Dictionary<ArgTypes, TDVEntry>();
138 master.Add(var.name.val, typedic);
139 }
140
141 // See if there is an entry in the sub-dictionary that matches the argument signature.
142 // Note that fields have null argument lists.
143 // Methods always have a non-null argument list, even if only 0 entries long.
144 ArgTypes types;
145 types.argTypes = (var.argDecl == null) ? null : KeyTypesToStringTypes(var.argDecl.types);
146 if(typedic.ContainsKey(types))
147 return false;
148
149 // It is unique, add to its name-specific sub-dictionary.
150 TDVEntry entry;
151 entry.count = ++count;
152 entry.var = var;
153 typedic.Add(types, entry);
154 return true;
155 }
156
157 public int Count
158 {
159 get
160 {
161 return count;
162 }
163 }
164
165 /**
166 * @brief If this is not a local variable frame, just return the frame as is.
167 * If this is a local variable frame, return a version that is frozen,
168 * ie, one that does not contain any future additions.
169 */
170 public VarDict FreezeLocals()
171 {
172 // If not local var frame, return original frame as is.
173 // This will allow forward references as the future additions
174 // will be seen by lookups done in this dictionary.
175 if(!locals)
176 return this;
177
178 // If local var frame, return a copy frozen at this point.
179 // This disallows forward referenes as those future additions
180 // will not be seen by lookups done in the frozen dictionary.
181 if((frozenLocals == null) || (frozenLocals.count != this.count))
182 {
183 // Make a copy of the current var dictionary frame.
184 // We copy a reference to the dictionary, and though it may
185 // contain additions made after this point, those additions
186 // will have a count .gt. frozen count and will be ignored.
187 frozenLocals = new VarDict(true);
188
189 frozenLocals.outerVarDict = this.outerVarDict;
190 frozenLocals.thisClass = this.thisClass;
191 frozenLocals.master = this.master;
192 frozenLocals.count = this.count;
193 frozenLocals.frozenLocals = frozenLocals;
194
195 // Mark it as being frozen.
196 // - assert fail if any attempt is made to add to it
197 // - ignore any additions to the dictionary with greater count
198 frozenLocals.isFrozen = true;
199 }
200 return frozenLocals;
201 }
202
203 /**
204 * @brief Find all functions/variables that are callable
205 * @param name = name of function/variable to look for
206 * @param argTypes = the argument types the function is being called with
207 * null to look for a variable
208 * @returns null: no matching function/variable found
209 * else: list of matching functions/variables
210 * for variables, always of length 1
211 */
212 private List<TokenDeclVar> found = new List<TokenDeclVar>();
213 public TokenDeclVar[] FindCallables(string name, TokenType[] argTypes)
214 {
215 argTypes = KeyTypesToStringTypes(argTypes);
216 TokenDeclVar var = FindExact(name, argTypes);
217 if(var != null)
218 return new TokenDeclVar[] { var };
219
220 Dictionary<ArgTypes, TDVEntry> typedic;
221 if(!master.TryGetValue(name, out typedic))
222 return null;
223
224 found.Clear();
225 foreach(KeyValuePair<ArgTypes, TDVEntry> kvp in typedic)
226 {
227 if((kvp.Value.count <= this.count) && kvp.Key.CanBeCalledBy(argTypes))
228 {
229 found.Add(kvp.Value.var);
230 }
231 }
232 return (found.Count > 0) ? found.ToArray() : null;
233 }
234
235 /**
236 * @brief Find exact matching function/variable
237 * @param name = name of function to look for
238 * @param argTypes = argument types the function was declared with
239 * null to look for a variable
240 * @returns null: no matching function/variable found
241 * else: the matching function/variable
242 */
243 public TokenDeclVar FindExact(string name, TokenType[] argTypes)
244 {
245 // Look for list of stuff that matches the given name.
246 Dictionary<ArgTypes, TDVEntry> typedic;
247 if(!master.TryGetValue(name, out typedic))
248 return null;
249
250 // Loop through all fields/methods declared by that name, regardless of arg signature.
251 foreach(TDVEntry entry in typedic.Values)
252 {
253 if(entry.count > this.count)
254 continue;
255 TokenDeclVar var = entry.var;
256
257 // Get argument types of declaration.
258 // fields are always null
259 // methods are always non-null, though may be zero-length
260 TokenType[] declArgs = (var.argDecl == null) ? null : var.argDecl.types;
261
262 // Convert any key args to string args.
263 declArgs = KeyTypesToStringTypes(declArgs);
264
265 // If both are null, they are signature-less (ie, both are fields), and so match.
266 if((declArgs == null) && (argTypes == null))
267 return var;
268
269 // If calling a delegate, it is a match, regardless of delegate arg types.
270 // If it turns out the arg types do not match, the compiler will give an error
271 // trying to cast the arguments to the delegate arg types.
272 // We don't allow overloading same field name with different delegate types.
273 if((declArgs == null) && (argTypes != null))
274 {
275 TokenType fieldType = var.type;
276 if(fieldType is TokenTypeSDTypeDelegate)
277 return var;
278 }
279
280 // If not both null, no match, keep looking.
281 if((declArgs == null) || (argTypes == null))
282 continue;
283
284 // Both not null, match argument types to make sure we have correct overload.
285 int i = declArgs.Length;
286 if(i != argTypes.Length)
287 continue;
288 while(--i >= 0)
289 {
290 string da = declArgs[i].ToString();
291 string ga = argTypes[i].ToString();
292 if(da == "key")
293 da = "string";
294 if(ga == "key")
295 ga = "string";
296 if(da != ga)
297 break;
298 }
299 if(i < 0)
300 return var;
301 }
302
303 // No match.
304 return null;
305 }
306
307 /**
308 * @brief Replace any TokenTypeKey elements with TokenTypeStr so that
309 * it doesn't matter if functions are declared with key or string,
310 * they will accept either.
311 * @param argTypes = argument types as declared in source code
312 * @returns argTypes with any key replaced by string
313 */
314 private static TokenType[] KeyTypesToStringTypes(TokenType[] argTypes)
315 {
316 if(argTypes != null)
317 {
318 int i;
319 int nats = argTypes.Length;
320 for(i = nats; --i >= 0;)
321 {
322 if(argTypes[i] is TokenTypeKey)
323 break;
324 }
325 if(i >= 0)
326 {
327 TokenType[] at = new TokenType[nats];
328 for(i = nats; --i >= 0;)
329 {
330 at[i] = argTypes[i];
331 if(argTypes[i] is TokenTypeKey)
332 {
333 at[i] = new TokenTypeStr(argTypes[i]);
334 }
335 }
336 return at;
337 }
338 }
339 return argTypes;
340 }
341
342 // foreach goes through all the TokenDeclVars that were added
343
344 // IEnumerable
345 public IEnumerator GetEnumerator()
346 {
347 return new VarDictEnumerator(this.master, this.count);
348 }
349
350 private class VarDictEnumerator: IEnumerator
351 {
352 private IEnumerator masterEnum;
353 private IEnumerator typedicEnum;
354 private int count;
355
356 public VarDictEnumerator(Dictionary<string, Dictionary<ArgTypes, TDVEntry>> master, int count)
357 {
358 masterEnum = master.Values.GetEnumerator();
359 this.count = count;
360 }
361
362 // IEnumerator
363 public void Reset()
364 {
365 masterEnum.Reset();
366 typedicEnum = null;
367 }
368
369 // IEnumerator
370 public bool MoveNext()
371 {
372 while(true)
373 {
374 if(typedicEnum != null)
375 {
376 while(typedicEnum.MoveNext())
377 {
378 if(((TDVEntry)typedicEnum.Current).count <= this.count)
379 return true;
380 }
381 typedicEnum = null;
382 }
383 if(!masterEnum.MoveNext())
384 return false;
385 Dictionary<ArgTypes, TDVEntry> ctd;
386 ctd = (Dictionary<ArgTypes, TDVEntry>)masterEnum.Current;
387 typedicEnum = ctd.Values.GetEnumerator();
388 }
389 }
390
391 // IEnumerator
392 public object Current
393 {
394 get
395 {
396 return ((TDVEntry)typedicEnum.Current).var;
397 }
398 }
399 }
400 }
401}