Mercurial > silverbladetech
comparison MetroWpf/MetroWpf.Framework/Extensions/Dynamic.cs @ 15:060f02cd4591
Initial commit, pre airport work
author | stevenh7776 stevenhollidge@hotmail.com |
---|---|
date | Mon, 12 Mar 2012 23:05:21 +0800 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
14:741981715d94 | 15:060f02cd4591 |
---|---|
1 //Copyright (C) Microsoft Corporation. All rights reserved. | |
2 | |
3 using System.Collections.Generic; | |
4 using System.Linq.Expressions; | |
5 using System.Reflection; | |
6 using System.Reflection.Emit; | |
7 using System.Text; | |
8 using System.Threading; | |
9 | |
10 namespace System.Linq.Dynamic | |
11 { | |
12 public static class DynamicQueryable | |
13 { | |
14 public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values) | |
15 { | |
16 return (IQueryable<T>)Where((IQueryable)source, predicate, values); | |
17 } | |
18 | |
19 public static IQueryable Where(this IQueryable source, string predicate, params object[] values) | |
20 { | |
21 if (source == null) throw new ArgumentNullException("source"); | |
22 if (predicate == null) throw new ArgumentNullException("predicate"); | |
23 LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values); | |
24 return source.Provider.CreateQuery( | |
25 Expression.Call( | |
26 typeof(Queryable), "Where", | |
27 new Type[] { source.ElementType }, | |
28 source.Expression, Expression.Quote(lambda))); | |
29 } | |
30 | |
31 public static IQueryable Select(this IQueryable source, string selector, params object[] values) | |
32 { | |
33 if (source == null) throw new ArgumentNullException("source"); | |
34 if (selector == null) throw new ArgumentNullException("selector"); | |
35 LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, null, selector, values); | |
36 return source.Provider.CreateQuery( | |
37 Expression.Call( | |
38 typeof(Queryable), "Select", | |
39 new Type[] { source.ElementType, lambda.Body.Type }, | |
40 source.Expression, Expression.Quote(lambda))); | |
41 } | |
42 | |
43 public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) | |
44 { | |
45 return (IQueryable<T>)OrderBy((IQueryable)source, ordering, values); | |
46 } | |
47 | |
48 public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) | |
49 { | |
50 if (source == null) throw new ArgumentNullException("source"); | |
51 if (ordering == null) throw new ArgumentNullException("ordering"); | |
52 ParameterExpression[] parameters = new ParameterExpression[] { | |
53 Expression.Parameter(source.ElementType, "") }; | |
54 ExpressionParser parser = new ExpressionParser(parameters, ordering, values); | |
55 IEnumerable<DynamicOrdering> orderings = parser.ParseOrdering(); | |
56 Expression queryExpr = source.Expression; | |
57 string methodAsc = "OrderBy"; | |
58 string methodDesc = "OrderByDescending"; | |
59 foreach (DynamicOrdering o in orderings) | |
60 { | |
61 queryExpr = Expression.Call( | |
62 typeof(Queryable), o.Ascending ? methodAsc : methodDesc, | |
63 new Type[] { source.ElementType, o.Selector.Type }, | |
64 queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters))); | |
65 methodAsc = "ThenBy"; | |
66 methodDesc = "ThenByDescending"; | |
67 } | |
68 return source.Provider.CreateQuery(queryExpr); | |
69 } | |
70 | |
71 public static IQueryable Take(this IQueryable source, int count) | |
72 { | |
73 if (source == null) throw new ArgumentNullException("source"); | |
74 return source.Provider.CreateQuery( | |
75 Expression.Call( | |
76 typeof(Queryable), "Take", | |
77 new Type[] { source.ElementType }, | |
78 source.Expression, Expression.Constant(count))); | |
79 } | |
80 | |
81 public static IQueryable Skip(this IQueryable source, int count) | |
82 { | |
83 if (source == null) throw new ArgumentNullException("source"); | |
84 return source.Provider.CreateQuery( | |
85 Expression.Call( | |
86 typeof(Queryable), "Skip", | |
87 new Type[] { source.ElementType }, | |
88 source.Expression, Expression.Constant(count))); | |
89 } | |
90 | |
91 public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values) | |
92 { | |
93 if (source == null) throw new ArgumentNullException("source"); | |
94 if (keySelector == null) throw new ArgumentNullException("keySelector"); | |
95 if (elementSelector == null) throw new ArgumentNullException("elementSelector"); | |
96 LambdaExpression keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, values); | |
97 LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, values); | |
98 return source.Provider.CreateQuery( | |
99 Expression.Call( | |
100 typeof(Queryable), "GroupBy", | |
101 new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type }, | |
102 source.Expression, Expression.Quote(keyLambda), Expression.Quote(elementLambda))); | |
103 } | |
104 | |
105 public static bool Any(this IQueryable source) | |
106 { | |
107 if (source == null) throw new ArgumentNullException("source"); | |
108 return (bool)source.Provider.Execute( | |
109 Expression.Call( | |
110 typeof(Queryable), "Any", | |
111 new Type[] { source.ElementType }, source.Expression)); | |
112 } | |
113 | |
114 public static int Count(this IQueryable source) | |
115 { | |
116 if (source == null) throw new ArgumentNullException("source"); | |
117 return (int)source.Provider.Execute( | |
118 Expression.Call( | |
119 typeof(Queryable), "Count", | |
120 new Type[] { source.ElementType }, source.Expression)); | |
121 } | |
122 } | |
123 | |
124 public abstract class DynamicClass | |
125 { | |
126 public override string ToString() | |
127 { | |
128 PropertyInfo[] props = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); | |
129 StringBuilder sb = new StringBuilder(); | |
130 sb.Append("{"); | |
131 for (int i = 0; i < props.Length; i++) | |
132 { | |
133 if (i > 0) sb.Append(", "); | |
134 sb.Append(props[i].Name); | |
135 sb.Append("="); | |
136 sb.Append(props[i].GetValue(this, null)); | |
137 } | |
138 sb.Append("}"); | |
139 return sb.ToString(); | |
140 } | |
141 } | |
142 | |
143 public class DynamicProperty | |
144 { | |
145 string name; | |
146 Type type; | |
147 | |
148 public DynamicProperty(string name, Type type) | |
149 { | |
150 if (name == null) throw new ArgumentNullException("name"); | |
151 if (type == null) throw new ArgumentNullException("type"); | |
152 this.name = name; | |
153 this.type = type; | |
154 } | |
155 | |
156 public string Name | |
157 { | |
158 get { return name; } | |
159 } | |
160 | |
161 public Type Type | |
162 { | |
163 get { return type; } | |
164 } | |
165 } | |
166 | |
167 public static class DynamicExpression | |
168 { | |
169 public static Expression Parse(Type resultType, string expression, params object[] values) | |
170 { | |
171 ExpressionParser parser = new ExpressionParser(null, expression, values); | |
172 return parser.Parse(resultType); | |
173 } | |
174 | |
175 public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, params object[] values) | |
176 { | |
177 return ParseLambda(new ParameterExpression[] { Expression.Parameter(itType, "") }, resultType, expression, values); | |
178 } | |
179 | |
180 public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, params object[] values) | |
181 { | |
182 ExpressionParser parser = new ExpressionParser(parameters, expression, values); | |
183 return Expression.Lambda(parser.Parse(resultType), parameters); | |
184 } | |
185 | |
186 public static Expression<Func<T, S>> ParseLambda<T, S>(string expression, params object[] values) | |
187 { | |
188 return (Expression<Func<T, S>>)ParseLambda(typeof(T), typeof(S), expression, values); | |
189 } | |
190 | |
191 public static Type CreateClass(params DynamicProperty[] properties) | |
192 { | |
193 return ClassFactory.Instance.GetDynamicClass(properties); | |
194 } | |
195 | |
196 public static Type CreateClass(IEnumerable<DynamicProperty> properties) | |
197 { | |
198 return ClassFactory.Instance.GetDynamicClass(properties); | |
199 } | |
200 } | |
201 | |
202 internal class DynamicOrdering | |
203 { | |
204 public Expression Selector; | |
205 public bool Ascending; | |
206 } | |
207 | |
208 internal class Signature : IEquatable<Signature> | |
209 { | |
210 public DynamicProperty[] properties; | |
211 public int hashCode; | |
212 | |
213 public Signature(IEnumerable<DynamicProperty> properties) | |
214 { | |
215 this.properties = properties.ToArray(); | |
216 hashCode = 0; | |
217 foreach (DynamicProperty p in properties) | |
218 { | |
219 hashCode ^= p.Name.GetHashCode() ^ p.Type.GetHashCode(); | |
220 } | |
221 } | |
222 | |
223 public override int GetHashCode() | |
224 { | |
225 return hashCode; | |
226 } | |
227 | |
228 public override bool Equals(object obj) | |
229 { | |
230 return obj is Signature ? Equals((Signature)obj) : false; | |
231 } | |
232 | |
233 public bool Equals(Signature other) | |
234 { | |
235 if (properties.Length != other.properties.Length) return false; | |
236 for (int i = 0; i < properties.Length; i++) | |
237 { | |
238 if (properties[i].Name != other.properties[i].Name || | |
239 properties[i].Type != other.properties[i].Type) return false; | |
240 } | |
241 return true; | |
242 } | |
243 } | |
244 | |
245 internal class ClassFactory | |
246 { | |
247 public static readonly ClassFactory Instance = new ClassFactory(); | |
248 | |
249 static ClassFactory() { } // Trigger lazy initialization of static fields | |
250 | |
251 ModuleBuilder module; | |
252 Dictionary<Signature, Type> classes; | |
253 int classCount; | |
254 ReaderWriterLock rwLock; | |
255 | |
256 private ClassFactory() | |
257 { | |
258 AssemblyName name = new AssemblyName("DynamicClasses"); | |
259 AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); | |
260 #if ENABLE_LINQ_PARTIAL_TRUST | |
261 new ReflectionPermission(PermissionState.Unrestricted).Assert(); | |
262 #endif | |
263 try | |
264 { | |
265 module = assembly.DefineDynamicModule("Module"); | |
266 } | |
267 finally | |
268 { | |
269 #if ENABLE_LINQ_PARTIAL_TRUST | |
270 PermissionSet.RevertAssert(); | |
271 #endif | |
272 } | |
273 classes = new Dictionary<Signature, Type>(); | |
274 rwLock = new ReaderWriterLock(); | |
275 } | |
276 | |
277 public Type GetDynamicClass(IEnumerable<DynamicProperty> properties) | |
278 { | |
279 rwLock.AcquireReaderLock(Timeout.Infinite); | |
280 try | |
281 { | |
282 Signature signature = new Signature(properties); | |
283 Type type; | |
284 if (!classes.TryGetValue(signature, out type)) | |
285 { | |
286 type = CreateDynamicClass(signature.properties); | |
287 classes.Add(signature, type); | |
288 } | |
289 return type; | |
290 } | |
291 finally | |
292 { | |
293 rwLock.ReleaseReaderLock(); | |
294 } | |
295 } | |
296 | |
297 Type CreateDynamicClass(DynamicProperty[] properties) | |
298 { | |
299 LockCookie cookie = rwLock.UpgradeToWriterLock(Timeout.Infinite); | |
300 try | |
301 { | |
302 string typeName = "DynamicClass" + (classCount + 1); | |
303 #if ENABLE_LINQ_PARTIAL_TRUST | |
304 new ReflectionPermission(PermissionState.Unrestricted).Assert(); | |
305 #endif | |
306 try | |
307 { | |
308 TypeBuilder tb = this.module.DefineType(typeName, TypeAttributes.Class | | |
309 TypeAttributes.Public, typeof(DynamicClass)); | |
310 FieldInfo[] fields = GenerateProperties(tb, properties); | |
311 GenerateEquals(tb, fields); | |
312 GenerateGetHashCode(tb, fields); | |
313 Type result = tb.CreateType(); | |
314 classCount++; | |
315 return result; | |
316 } | |
317 finally | |
318 { | |
319 #if ENABLE_LINQ_PARTIAL_TRUST | |
320 PermissionSet.RevertAssert(); | |
321 #endif | |
322 } | |
323 } | |
324 finally | |
325 { | |
326 rwLock.DowngradeFromWriterLock(ref cookie); | |
327 } | |
328 } | |
329 | |
330 FieldInfo[] GenerateProperties(TypeBuilder tb, DynamicProperty[] properties) | |
331 { | |
332 FieldInfo[] fields = new FieldBuilder[properties.Length]; | |
333 for (int i = 0; i < properties.Length; i++) | |
334 { | |
335 DynamicProperty dp = properties[i]; | |
336 FieldBuilder fb = tb.DefineField("_" + dp.Name, dp.Type, FieldAttributes.Private); | |
337 PropertyBuilder pb = tb.DefineProperty(dp.Name, PropertyAttributes.HasDefault, dp.Type, null); | |
338 MethodBuilder mbGet = tb.DefineMethod("get_" + dp.Name, | |
339 MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, | |
340 dp.Type, Type.EmptyTypes); | |
341 ILGenerator genGet = mbGet.GetILGenerator(); | |
342 genGet.Emit(OpCodes.Ldarg_0); | |
343 genGet.Emit(OpCodes.Ldfld, fb); | |
344 genGet.Emit(OpCodes.Ret); | |
345 MethodBuilder mbSet = tb.DefineMethod("set_" + dp.Name, | |
346 MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, | |
347 null, new Type[] { dp.Type }); | |
348 ILGenerator genSet = mbSet.GetILGenerator(); | |
349 genSet.Emit(OpCodes.Ldarg_0); | |
350 genSet.Emit(OpCodes.Ldarg_1); | |
351 genSet.Emit(OpCodes.Stfld, fb); | |
352 genSet.Emit(OpCodes.Ret); | |
353 pb.SetGetMethod(mbGet); | |
354 pb.SetSetMethod(mbSet); | |
355 fields[i] = fb; | |
356 } | |
357 return fields; | |
358 } | |
359 | |
360 void GenerateEquals(TypeBuilder tb, FieldInfo[] fields) | |
361 { | |
362 MethodBuilder mb = tb.DefineMethod("Equals", | |
363 MethodAttributes.Public | MethodAttributes.ReuseSlot | | |
364 MethodAttributes.Virtual | MethodAttributes.HideBySig, | |
365 typeof(bool), new Type[] { typeof(object) }); | |
366 ILGenerator gen = mb.GetILGenerator(); | |
367 LocalBuilder other = gen.DeclareLocal(tb); | |
368 Label next = gen.DefineLabel(); | |
369 gen.Emit(OpCodes.Ldarg_1); | |
370 gen.Emit(OpCodes.Isinst, tb); | |
371 gen.Emit(OpCodes.Stloc, other); | |
372 gen.Emit(OpCodes.Ldloc, other); | |
373 gen.Emit(OpCodes.Brtrue_S, next); | |
374 gen.Emit(OpCodes.Ldc_I4_0); | |
375 gen.Emit(OpCodes.Ret); | |
376 gen.MarkLabel(next); | |
377 foreach (FieldInfo field in fields) | |
378 { | |
379 Type ft = field.FieldType; | |
380 Type ct = typeof(EqualityComparer<>).MakeGenericType(ft); | |
381 next = gen.DefineLabel(); | |
382 gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null); | |
383 gen.Emit(OpCodes.Ldarg_0); | |
384 gen.Emit(OpCodes.Ldfld, field); | |
385 gen.Emit(OpCodes.Ldloc, other); | |
386 gen.Emit(OpCodes.Ldfld, field); | |
387 gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("Equals", new Type[] { ft, ft }), null); | |
388 gen.Emit(OpCodes.Brtrue_S, next); | |
389 gen.Emit(OpCodes.Ldc_I4_0); | |
390 gen.Emit(OpCodes.Ret); | |
391 gen.MarkLabel(next); | |
392 } | |
393 gen.Emit(OpCodes.Ldc_I4_1); | |
394 gen.Emit(OpCodes.Ret); | |
395 } | |
396 | |
397 void GenerateGetHashCode(TypeBuilder tb, FieldInfo[] fields) | |
398 { | |
399 MethodBuilder mb = tb.DefineMethod("GetHashCode", | |
400 MethodAttributes.Public | MethodAttributes.ReuseSlot | | |
401 MethodAttributes.Virtual | MethodAttributes.HideBySig, | |
402 typeof(int), Type.EmptyTypes); | |
403 ILGenerator gen = mb.GetILGenerator(); | |
404 gen.Emit(OpCodes.Ldc_I4_0); | |
405 foreach (FieldInfo field in fields) | |
406 { | |
407 Type ft = field.FieldType; | |
408 Type ct = typeof(EqualityComparer<>).MakeGenericType(ft); | |
409 gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null); | |
410 gen.Emit(OpCodes.Ldarg_0); | |
411 gen.Emit(OpCodes.Ldfld, field); | |
412 gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("GetHashCode", new Type[] { ft }), null); | |
413 gen.Emit(OpCodes.Xor); | |
414 } | |
415 gen.Emit(OpCodes.Ret); | |
416 } | |
417 } | |
418 | |
419 public sealed class ParseException : Exception | |
420 { | |
421 int position; | |
422 | |
423 public ParseException(string message, int position) | |
424 : base(message) | |
425 { | |
426 this.position = position; | |
427 } | |
428 | |
429 public int Position | |
430 { | |
431 get { return position; } | |
432 } | |
433 | |
434 public override string ToString() | |
435 { | |
436 return string.Format(Res.ParseExceptionFormat, Message, position); | |
437 } | |
438 } | |
439 | |
440 internal class ExpressionParser | |
441 { | |
442 struct Token | |
443 { | |
444 public TokenId id; | |
445 public string text; | |
446 public int pos; | |
447 } | |
448 | |
449 enum TokenId | |
450 { | |
451 Unknown, | |
452 End, | |
453 Identifier, | |
454 StringLiteral, | |
455 IntegerLiteral, | |
456 RealLiteral, | |
457 Exclamation, | |
458 Percent, | |
459 Amphersand, | |
460 OpenParen, | |
461 CloseParen, | |
462 Asterisk, | |
463 Plus, | |
464 Comma, | |
465 Minus, | |
466 Dot, | |
467 Slash, | |
468 Colon, | |
469 LessThan, | |
470 Equal, | |
471 GreaterThan, | |
472 Question, | |
473 OpenBracket, | |
474 CloseBracket, | |
475 Bar, | |
476 ExclamationEqual, | |
477 DoubleAmphersand, | |
478 LessThanEqual, | |
479 LessGreater, | |
480 DoubleEqual, | |
481 GreaterThanEqual, | |
482 DoubleBar | |
483 } | |
484 | |
485 interface ILogicalSignatures | |
486 { | |
487 void F(bool x, bool y); | |
488 void F(bool? x, bool? y); | |
489 } | |
490 | |
491 interface IArithmeticSignatures | |
492 { | |
493 void F(int x, int y); | |
494 void F(uint x, uint y); | |
495 void F(long x, long y); | |
496 void F(ulong x, ulong y); | |
497 void F(float x, float y); | |
498 void F(double x, double y); | |
499 void F(decimal x, decimal y); | |
500 void F(int? x, int? y); | |
501 void F(uint? x, uint? y); | |
502 void F(long? x, long? y); | |
503 void F(ulong? x, ulong? y); | |
504 void F(float? x, float? y); | |
505 void F(double? x, double? y); | |
506 void F(decimal? x, decimal? y); | |
507 } | |
508 | |
509 interface IRelationalSignatures : IArithmeticSignatures | |
510 { | |
511 void F(string x, string y); | |
512 void F(char x, char y); | |
513 void F(DateTime x, DateTime y); | |
514 void F(TimeSpan x, TimeSpan y); | |
515 void F(char? x, char? y); | |
516 void F(DateTime? x, DateTime? y); | |
517 void F(TimeSpan? x, TimeSpan? y); | |
518 } | |
519 | |
520 interface IEqualitySignatures : IRelationalSignatures | |
521 { | |
522 void F(bool x, bool y); | |
523 void F(bool? x, bool? y); | |
524 } | |
525 | |
526 interface IAddSignatures : IArithmeticSignatures | |
527 { | |
528 void F(DateTime x, TimeSpan y); | |
529 void F(TimeSpan x, TimeSpan y); | |
530 void F(DateTime? x, TimeSpan? y); | |
531 void F(TimeSpan? x, TimeSpan? y); | |
532 } | |
533 | |
534 interface ISubtractSignatures : IAddSignatures | |
535 { | |
536 void F(DateTime x, DateTime y); | |
537 void F(DateTime? x, DateTime? y); | |
538 } | |
539 | |
540 interface INegationSignatures | |
541 { | |
542 void F(int x); | |
543 void F(long x); | |
544 void F(float x); | |
545 void F(double x); | |
546 void F(decimal x); | |
547 void F(int? x); | |
548 void F(long? x); | |
549 void F(float? x); | |
550 void F(double? x); | |
551 void F(decimal? x); | |
552 } | |
553 | |
554 interface INotSignatures | |
555 { | |
556 void F(bool x); | |
557 void F(bool? x); | |
558 } | |
559 | |
560 interface IEnumerableSignatures | |
561 { | |
562 void Where(bool predicate); | |
563 void Any(); | |
564 void Any(bool predicate); | |
565 void All(bool predicate); | |
566 void Count(); | |
567 void Count(bool predicate); | |
568 void Min(object selector); | |
569 void Max(object selector); | |
570 void Sum(int selector); | |
571 void Sum(int? selector); | |
572 void Sum(long selector); | |
573 void Sum(long? selector); | |
574 void Sum(float selector); | |
575 void Sum(float? selector); | |
576 void Sum(double selector); | |
577 void Sum(double? selector); | |
578 void Sum(decimal selector); | |
579 void Sum(decimal? selector); | |
580 void Average(int selector); | |
581 void Average(int? selector); | |
582 void Average(long selector); | |
583 void Average(long? selector); | |
584 void Average(float selector); | |
585 void Average(float? selector); | |
586 void Average(double selector); | |
587 void Average(double? selector); | |
588 void Average(decimal selector); | |
589 void Average(decimal? selector); | |
590 } | |
591 | |
592 static readonly Type[] predefinedTypes = { | |
593 typeof(Object), | |
594 typeof(Boolean), | |
595 typeof(Char), | |
596 typeof(String), | |
597 typeof(SByte), | |
598 typeof(Byte), | |
599 typeof(Int16), | |
600 typeof(UInt16), | |
601 typeof(Int32), | |
602 typeof(UInt32), | |
603 typeof(Int64), | |
604 typeof(UInt64), | |
605 typeof(Single), | |
606 typeof(Double), | |
607 typeof(Decimal), | |
608 typeof(DateTime), | |
609 typeof(TimeSpan), | |
610 typeof(Guid), | |
611 typeof(Math), | |
612 typeof(Convert) | |
613 }; | |
614 | |
615 static readonly Expression trueLiteral = Expression.Constant(true); | |
616 static readonly Expression falseLiteral = Expression.Constant(false); | |
617 static readonly Expression nullLiteral = Expression.Constant(null); | |
618 | |
619 static readonly string keywordIt = "it"; | |
620 static readonly string keywordIif = "iif"; | |
621 static readonly string keywordNew = "new"; | |
622 | |
623 static Dictionary<string, object> keywords; | |
624 | |
625 Dictionary<string, object> symbols; | |
626 IDictionary<string, object> externals; | |
627 Dictionary<Expression, string> literals; | |
628 ParameterExpression it; | |
629 string text; | |
630 int textPos; | |
631 int textLen; | |
632 char ch; | |
633 Token token; | |
634 | |
635 public ExpressionParser(ParameterExpression[] parameters, string expression, object[] values) | |
636 { | |
637 if (expression == null) throw new ArgumentNullException("expression"); | |
638 if (keywords == null) keywords = CreateKeywords(); | |
639 symbols = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); | |
640 literals = new Dictionary<Expression, string>(); | |
641 if (parameters != null) ProcessParameters(parameters); | |
642 if (values != null) ProcessValues(values); | |
643 text = expression; | |
644 textLen = text.Length; | |
645 SetTextPos(0); | |
646 NextToken(); | |
647 } | |
648 | |
649 void ProcessParameters(ParameterExpression[] parameters) | |
650 { | |
651 foreach (ParameterExpression pe in parameters) | |
652 if (!String.IsNullOrEmpty(pe.Name)) | |
653 AddSymbol(pe.Name, pe); | |
654 if (parameters.Length == 1 && String.IsNullOrEmpty(parameters[0].Name)) | |
655 it = parameters[0]; | |
656 } | |
657 | |
658 void ProcessValues(object[] values) | |
659 { | |
660 for (int i = 0; i < values.Length; i++) | |
661 { | |
662 object value = values[i]; | |
663 if (i == values.Length - 1 && value is IDictionary<string, object>) | |
664 { | |
665 externals = (IDictionary<string, object>)value; | |
666 } | |
667 else | |
668 { | |
669 AddSymbol("@" + i.ToString(System.Globalization.CultureInfo.InvariantCulture), value); | |
670 } | |
671 } | |
672 } | |
673 | |
674 void AddSymbol(string name, object value) | |
675 { | |
676 if (symbols.ContainsKey(name)) | |
677 throw ParseError(Res.DuplicateIdentifier, name); | |
678 symbols.Add(name, value); | |
679 } | |
680 | |
681 public Expression Parse(Type resultType) | |
682 { | |
683 int exprPos = token.pos; | |
684 Expression expr = ParseExpression(); | |
685 if (resultType != null) | |
686 if ((expr = PromoteExpression(expr, resultType, true)) == null) | |
687 throw ParseError(exprPos, Res.ExpressionTypeMismatch, GetTypeName(resultType)); | |
688 ValidateToken(TokenId.End, Res.SyntaxError); | |
689 return expr; | |
690 } | |
691 | |
692 #pragma warning disable 0219 | |
693 public IEnumerable<DynamicOrdering> ParseOrdering() | |
694 { | |
695 List<DynamicOrdering> orderings = new List<DynamicOrdering>(); | |
696 while (true) | |
697 { | |
698 Expression expr = ParseExpression(); | |
699 bool ascending = true; | |
700 if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending")) | |
701 { | |
702 NextToken(); | |
703 } | |
704 else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending")) | |
705 { | |
706 NextToken(); | |
707 ascending = false; | |
708 } | |
709 orderings.Add(new DynamicOrdering { Selector = expr, Ascending = ascending }); | |
710 if (token.id != TokenId.Comma) break; | |
711 NextToken(); | |
712 } | |
713 ValidateToken(TokenId.End, Res.SyntaxError); | |
714 return orderings; | |
715 } | |
716 #pragma warning restore 0219 | |
717 | |
718 // ?: operator | |
719 Expression ParseExpression() | |
720 { | |
721 int errorPos = token.pos; | |
722 Expression expr = ParseLogicalOr(); | |
723 if (token.id == TokenId.Question) | |
724 { | |
725 NextToken(); | |
726 Expression expr1 = ParseExpression(); | |
727 ValidateToken(TokenId.Colon, Res.ColonExpected); | |
728 NextToken(); | |
729 Expression expr2 = ParseExpression(); | |
730 expr = GenerateConditional(expr, expr1, expr2, errorPos); | |
731 } | |
732 return expr; | |
733 } | |
734 | |
735 // ||, or operator | |
736 Expression ParseLogicalOr() | |
737 { | |
738 Expression left = ParseLogicalAnd(); | |
739 while (token.id == TokenId.DoubleBar || TokenIdentifierIs("or")) | |
740 { | |
741 Token op = token; | |
742 NextToken(); | |
743 Expression right = ParseLogicalAnd(); | |
744 CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); | |
745 left = Expression.OrElse(left, right); | |
746 } | |
747 return left; | |
748 } | |
749 | |
750 // &&, and operator | |
751 Expression ParseLogicalAnd() | |
752 { | |
753 Expression left = ParseComparison(); | |
754 while (token.id == TokenId.DoubleAmphersand || TokenIdentifierIs("and")) | |
755 { | |
756 Token op = token; | |
757 NextToken(); | |
758 Expression right = ParseComparison(); | |
759 CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); | |
760 left = Expression.AndAlso(left, right); | |
761 } | |
762 return left; | |
763 } | |
764 | |
765 // =, ==, !=, <>, >, >=, <, <= operators | |
766 Expression ParseComparison() | |
767 { | |
768 Expression left = ParseAdditive(); | |
769 while (token.id == TokenId.Equal || token.id == TokenId.DoubleEqual || | |
770 token.id == TokenId.ExclamationEqual || token.id == TokenId.LessGreater || | |
771 token.id == TokenId.GreaterThan || token.id == TokenId.GreaterThanEqual || | |
772 token.id == TokenId.LessThan || token.id == TokenId.LessThanEqual) | |
773 { | |
774 Token op = token; | |
775 NextToken(); | |
776 Expression right = ParseAdditive(); | |
777 bool isEquality = op.id == TokenId.Equal || op.id == TokenId.DoubleEqual || | |
778 op.id == TokenId.ExclamationEqual || op.id == TokenId.LessGreater; | |
779 if (isEquality && !left.Type.IsValueType && !right.Type.IsValueType) | |
780 { | |
781 if (left.Type != right.Type) | |
782 { | |
783 if (left.Type.IsAssignableFrom(right.Type)) | |
784 { | |
785 right = Expression.Convert(right, left.Type); | |
786 } | |
787 else if (right.Type.IsAssignableFrom(left.Type)) | |
788 { | |
789 left = Expression.Convert(left, right.Type); | |
790 } | |
791 else | |
792 { | |
793 throw IncompatibleOperandsError(op.text, left, right, op.pos); | |
794 } | |
795 } | |
796 } | |
797 else if (IsEnumType(left.Type) || IsEnumType(right.Type)) | |
798 { | |
799 if (left.Type != right.Type) | |
800 { | |
801 Expression e; | |
802 if ((e = PromoteExpression(right, left.Type, true)) != null) | |
803 { | |
804 right = e; | |
805 } | |
806 else if ((e = PromoteExpression(left, right.Type, true)) != null) | |
807 { | |
808 left = e; | |
809 } | |
810 else | |
811 { | |
812 throw IncompatibleOperandsError(op.text, left, right, op.pos); | |
813 } | |
814 } | |
815 } | |
816 else | |
817 { | |
818 CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), | |
819 op.text, ref left, ref right, op.pos); | |
820 } | |
821 switch (op.id) | |
822 { | |
823 case TokenId.Equal: | |
824 case TokenId.DoubleEqual: | |
825 left = GenerateEqual(left, right); | |
826 break; | |
827 case TokenId.ExclamationEqual: | |
828 case TokenId.LessGreater: | |
829 left = GenerateNotEqual(left, right); | |
830 break; | |
831 case TokenId.GreaterThan: | |
832 left = GenerateGreaterThan(left, right); | |
833 break; | |
834 case TokenId.GreaterThanEqual: | |
835 left = GenerateGreaterThanEqual(left, right); | |
836 break; | |
837 case TokenId.LessThan: | |
838 left = GenerateLessThan(left, right); | |
839 break; | |
840 case TokenId.LessThanEqual: | |
841 left = GenerateLessThanEqual(left, right); | |
842 break; | |
843 } | |
844 } | |
845 return left; | |
846 } | |
847 | |
848 // +, -, & operators | |
849 Expression ParseAdditive() | |
850 { | |
851 Expression left = ParseMultiplicative(); | |
852 while (token.id == TokenId.Plus || token.id == TokenId.Minus || | |
853 token.id == TokenId.Amphersand) | |
854 { | |
855 Token op = token; | |
856 NextToken(); | |
857 Expression right = ParseMultiplicative(); | |
858 switch (op.id) | |
859 { | |
860 case TokenId.Plus: | |
861 if (left.Type == typeof(string) || right.Type == typeof(string)) | |
862 goto case TokenId.Amphersand; | |
863 CheckAndPromoteOperands(typeof(IAddSignatures), op.text, ref left, ref right, op.pos); | |
864 left = GenerateAdd(left, right); | |
865 break; | |
866 case TokenId.Minus: | |
867 CheckAndPromoteOperands(typeof(ISubtractSignatures), op.text, ref left, ref right, op.pos); | |
868 left = GenerateSubtract(left, right); | |
869 break; | |
870 case TokenId.Amphersand: | |
871 left = GenerateStringConcat(left, right); | |
872 break; | |
873 } | |
874 } | |
875 return left; | |
876 } | |
877 | |
878 // *, /, %, mod operators | |
879 Expression ParseMultiplicative() | |
880 { | |
881 Expression left = ParseUnary(); | |
882 while (token.id == TokenId.Asterisk || token.id == TokenId.Slash || | |
883 token.id == TokenId.Percent || TokenIdentifierIs("mod")) | |
884 { | |
885 Token op = token; | |
886 NextToken(); | |
887 Expression right = ParseUnary(); | |
888 CheckAndPromoteOperands(typeof(IArithmeticSignatures), op.text, ref left, ref right, op.pos); | |
889 switch (op.id) | |
890 { | |
891 case TokenId.Asterisk: | |
892 left = Expression.Multiply(left, right); | |
893 break; | |
894 case TokenId.Slash: | |
895 left = Expression.Divide(left, right); | |
896 break; | |
897 case TokenId.Percent: | |
898 case TokenId.Identifier: | |
899 left = Expression.Modulo(left, right); | |
900 break; | |
901 } | |
902 } | |
903 return left; | |
904 } | |
905 | |
906 // -, !, not unary operators | |
907 Expression ParseUnary() | |
908 { | |
909 if (token.id == TokenId.Minus || token.id == TokenId.Exclamation || | |
910 TokenIdentifierIs("not")) | |
911 { | |
912 Token op = token; | |
913 NextToken(); | |
914 if (op.id == TokenId.Minus && (token.id == TokenId.IntegerLiteral || | |
915 token.id == TokenId.RealLiteral)) | |
916 { | |
917 token.text = "-" + token.text; | |
918 token.pos = op.pos; | |
919 return ParsePrimary(); | |
920 } | |
921 Expression expr = ParseUnary(); | |
922 if (op.id == TokenId.Minus) | |
923 { | |
924 CheckAndPromoteOperand(typeof(INegationSignatures), op.text, ref expr, op.pos); | |
925 expr = Expression.Negate(expr); | |
926 } | |
927 else | |
928 { | |
929 CheckAndPromoteOperand(typeof(INotSignatures), op.text, ref expr, op.pos); | |
930 expr = Expression.Not(expr); | |
931 } | |
932 return expr; | |
933 } | |
934 return ParsePrimary(); | |
935 } | |
936 | |
937 Expression ParsePrimary() | |
938 { | |
939 Expression expr = ParsePrimaryStart(); | |
940 while (true) | |
941 { | |
942 if (token.id == TokenId.Dot) | |
943 { | |
944 NextToken(); | |
945 expr = ParseMemberAccess(null, expr); | |
946 } | |
947 else if (token.id == TokenId.OpenBracket) | |
948 { | |
949 expr = ParseElementAccess(expr); | |
950 } | |
951 else | |
952 { | |
953 break; | |
954 } | |
955 } | |
956 return expr; | |
957 } | |
958 | |
959 Expression ParsePrimaryStart() | |
960 { | |
961 switch (token.id) | |
962 { | |
963 case TokenId.Identifier: | |
964 return ParseIdentifier(); | |
965 case TokenId.StringLiteral: | |
966 return ParseStringLiteral(); | |
967 case TokenId.IntegerLiteral: | |
968 return ParseIntegerLiteral(); | |
969 case TokenId.RealLiteral: | |
970 return ParseRealLiteral(); | |
971 case TokenId.OpenParen: | |
972 return ParseParenExpression(); | |
973 default: | |
974 throw ParseError(Res.ExpressionExpected); | |
975 } | |
976 } | |
977 | |
978 Expression ParseStringLiteral() | |
979 { | |
980 ValidateToken(TokenId.StringLiteral); | |
981 char quote = token.text[0]; | |
982 string s = token.text.Substring(1, token.text.Length - 2); | |
983 int start = 0; | |
984 while (true) | |
985 { | |
986 int i = s.IndexOf(quote, start); | |
987 if (i < 0) break; | |
988 s = s.Remove(i, 1); | |
989 start = i + 1; | |
990 } | |
991 if (quote == '\'') | |
992 { | |
993 if (s.Length != 1) | |
994 throw ParseError(Res.InvalidCharacterLiteral); | |
995 NextToken(); | |
996 return CreateLiteral(s[0], s); | |
997 } | |
998 NextToken(); | |
999 return CreateLiteral(s, s); | |
1000 } | |
1001 | |
1002 Expression ParseIntegerLiteral() | |
1003 { | |
1004 ValidateToken(TokenId.IntegerLiteral); | |
1005 string text = token.text; | |
1006 if (text[0] != '-') | |
1007 { | |
1008 ulong value; | |
1009 if (!UInt64.TryParse(text, out value)) | |
1010 throw ParseError(Res.InvalidIntegerLiteral, text); | |
1011 NextToken(); | |
1012 if (value <= (ulong)Int32.MaxValue) return CreateLiteral((int)value, text); | |
1013 if (value <= (ulong)UInt32.MaxValue) return CreateLiteral((uint)value, text); | |
1014 if (value <= (ulong)Int64.MaxValue) return CreateLiteral((long)value, text); | |
1015 return CreateLiteral(value, text); | |
1016 } | |
1017 else | |
1018 { | |
1019 long value; | |
1020 if (!Int64.TryParse(text, out value)) | |
1021 throw ParseError(Res.InvalidIntegerLiteral, text); | |
1022 NextToken(); | |
1023 if (value >= Int32.MinValue && value <= Int32.MaxValue) | |
1024 return CreateLiteral((int)value, text); | |
1025 return CreateLiteral(value, text); | |
1026 } | |
1027 } | |
1028 | |
1029 Expression ParseRealLiteral() | |
1030 { | |
1031 ValidateToken(TokenId.RealLiteral); | |
1032 string text = token.text; | |
1033 object value = null; | |
1034 char last = text[text.Length - 1]; | |
1035 if (last == 'F' || last == 'f') | |
1036 { | |
1037 float f; | |
1038 if (Single.TryParse(text.Substring(0, text.Length - 1), out f)) value = f; | |
1039 } | |
1040 else | |
1041 { | |
1042 double d; | |
1043 if (Double.TryParse(text, out d)) value = d; | |
1044 } | |
1045 if (value == null) throw ParseError(Res.InvalidRealLiteral, text); | |
1046 NextToken(); | |
1047 return CreateLiteral(value, text); | |
1048 } | |
1049 | |
1050 Expression CreateLiteral(object value, string text) | |
1051 { | |
1052 ConstantExpression expr = Expression.Constant(value); | |
1053 literals.Add(expr, text); | |
1054 return expr; | |
1055 } | |
1056 | |
1057 Expression ParseParenExpression() | |
1058 { | |
1059 ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); | |
1060 NextToken(); | |
1061 Expression e = ParseExpression(); | |
1062 ValidateToken(TokenId.CloseParen, Res.CloseParenOrOperatorExpected); | |
1063 NextToken(); | |
1064 return e; | |
1065 } | |
1066 | |
1067 Expression ParseIdentifier() | |
1068 { | |
1069 ValidateToken(TokenId.Identifier); | |
1070 object value; | |
1071 if (keywords.TryGetValue(token.text, out value)) | |
1072 { | |
1073 if (value is Type) return ParseTypeAccess((Type)value); | |
1074 if (value == (object)keywordIt) return ParseIt(); | |
1075 if (value == (object)keywordIif) return ParseIif(); | |
1076 if (value == (object)keywordNew) return ParseNew(); | |
1077 NextToken(); | |
1078 return (Expression)value; | |
1079 } | |
1080 if (symbols.TryGetValue(token.text, out value) || | |
1081 externals != null && externals.TryGetValue(token.text, out value)) | |
1082 { | |
1083 Expression expr = value as Expression; | |
1084 if (expr == null) | |
1085 { | |
1086 expr = Expression.Constant(value); | |
1087 } | |
1088 else | |
1089 { | |
1090 LambdaExpression lambda = expr as LambdaExpression; | |
1091 if (lambda != null) return ParseLambdaInvocation(lambda); | |
1092 } | |
1093 NextToken(); | |
1094 return expr; | |
1095 } | |
1096 if (it != null) return ParseMemberAccess(null, it); | |
1097 throw ParseError(Res.UnknownIdentifier, token.text); | |
1098 } | |
1099 | |
1100 Expression ParseIt() | |
1101 { | |
1102 if (it == null) | |
1103 throw ParseError(Res.NoItInScope); | |
1104 NextToken(); | |
1105 return it; | |
1106 } | |
1107 | |
1108 Expression ParseIif() | |
1109 { | |
1110 int errorPos = token.pos; | |
1111 NextToken(); | |
1112 Expression[] args = ParseArgumentList(); | |
1113 if (args.Length != 3) | |
1114 throw ParseError(errorPos, Res.IifRequiresThreeArgs); | |
1115 return GenerateConditional(args[0], args[1], args[2], errorPos); | |
1116 } | |
1117 | |
1118 Expression GenerateConditional(Expression test, Expression expr1, Expression expr2, int errorPos) | |
1119 { | |
1120 if (test.Type != typeof(bool)) | |
1121 throw ParseError(errorPos, Res.FirstExprMustBeBool); | |
1122 if (expr1.Type != expr2.Type) | |
1123 { | |
1124 Expression expr1as2 = expr2 != nullLiteral ? PromoteExpression(expr1, expr2.Type, true) : null; | |
1125 Expression expr2as1 = expr1 != nullLiteral ? PromoteExpression(expr2, expr1.Type, true) : null; | |
1126 if (expr1as2 != null && expr2as1 == null) | |
1127 { | |
1128 expr1 = expr1as2; | |
1129 } | |
1130 else if (expr2as1 != null && expr1as2 == null) | |
1131 { | |
1132 expr2 = expr2as1; | |
1133 } | |
1134 else | |
1135 { | |
1136 string type1 = expr1 != nullLiteral ? expr1.Type.Name : "null"; | |
1137 string type2 = expr2 != nullLiteral ? expr2.Type.Name : "null"; | |
1138 if (expr1as2 != null && expr2as1 != null) | |
1139 throw ParseError(errorPos, Res.BothTypesConvertToOther, type1, type2); | |
1140 throw ParseError(errorPos, Res.NeitherTypeConvertsToOther, type1, type2); | |
1141 } | |
1142 } | |
1143 return Expression.Condition(test, expr1, expr2); | |
1144 } | |
1145 | |
1146 Expression ParseNew() | |
1147 { | |
1148 NextToken(); | |
1149 ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); | |
1150 NextToken(); | |
1151 List<DynamicProperty> properties = new List<DynamicProperty>(); | |
1152 List<Expression> expressions = new List<Expression>(); | |
1153 while (true) | |
1154 { | |
1155 int exprPos = token.pos; | |
1156 Expression expr = ParseExpression(); | |
1157 string propName; | |
1158 if (TokenIdentifierIs("as")) | |
1159 { | |
1160 NextToken(); | |
1161 propName = GetIdentifier(); | |
1162 NextToken(); | |
1163 } | |
1164 else | |
1165 { | |
1166 MemberExpression me = expr as MemberExpression; | |
1167 if (me == null) throw ParseError(exprPos, Res.MissingAsClause); | |
1168 propName = me.Member.Name; | |
1169 } | |
1170 expressions.Add(expr); | |
1171 properties.Add(new DynamicProperty(propName, expr.Type)); | |
1172 if (token.id != TokenId.Comma) break; | |
1173 NextToken(); | |
1174 } | |
1175 ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected); | |
1176 NextToken(); | |
1177 Type type = DynamicExpression.CreateClass(properties); | |
1178 MemberBinding[] bindings = new MemberBinding[properties.Count]; | |
1179 for (int i = 0; i < bindings.Length; i++) | |
1180 bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]); | |
1181 return Expression.MemberInit(Expression.New(type), bindings); | |
1182 } | |
1183 | |
1184 Expression ParseLambdaInvocation(LambdaExpression lambda) | |
1185 { | |
1186 int errorPos = token.pos; | |
1187 NextToken(); | |
1188 Expression[] args = ParseArgumentList(); | |
1189 MethodBase method; | |
1190 if (FindMethod(lambda.Type, "Invoke", false, args, out method) != 1) | |
1191 throw ParseError(errorPos, Res.ArgsIncompatibleWithLambda); | |
1192 return Expression.Invoke(lambda, args); | |
1193 } | |
1194 | |
1195 Expression ParseTypeAccess(Type type) | |
1196 { | |
1197 int errorPos = token.pos; | |
1198 NextToken(); | |
1199 if (token.id == TokenId.Question) | |
1200 { | |
1201 if (!type.IsValueType || IsNullableType(type)) | |
1202 throw ParseError(errorPos, Res.TypeHasNoNullableForm, GetTypeName(type)); | |
1203 type = typeof(Nullable<>).MakeGenericType(type); | |
1204 NextToken(); | |
1205 } | |
1206 if (token.id == TokenId.OpenParen) | |
1207 { | |
1208 Expression[] args = ParseArgumentList(); | |
1209 MethodBase method; | |
1210 switch (FindBestMethod(type.GetConstructors(), args, out method)) | |
1211 { | |
1212 case 0: | |
1213 if (args.Length == 1) | |
1214 return GenerateConversion(args[0], type, errorPos); | |
1215 throw ParseError(errorPos, Res.NoMatchingConstructor, GetTypeName(type)); | |
1216 case 1: | |
1217 return Expression.New((ConstructorInfo)method, args); | |
1218 default: | |
1219 throw ParseError(errorPos, Res.AmbiguousConstructorInvocation, GetTypeName(type)); | |
1220 } | |
1221 } | |
1222 ValidateToken(TokenId.Dot, Res.DotOrOpenParenExpected); | |
1223 NextToken(); | |
1224 return ParseMemberAccess(type, null); | |
1225 } | |
1226 | |
1227 Expression GenerateConversion(Expression expr, Type type, int errorPos) | |
1228 { | |
1229 Type exprType = expr.Type; | |
1230 if (exprType == type) return expr; | |
1231 if (exprType.IsValueType && type.IsValueType) | |
1232 { | |
1233 if ((IsNullableType(exprType) || IsNullableType(type)) && | |
1234 GetNonNullableType(exprType) == GetNonNullableType(type)) | |
1235 return Expression.Convert(expr, type); | |
1236 if ((IsNumericType(exprType) || IsEnumType(exprType)) && | |
1237 (IsNumericType(type)) || IsEnumType(type)) | |
1238 return Expression.ConvertChecked(expr, type); | |
1239 } | |
1240 if (exprType.IsAssignableFrom(type) || type.IsAssignableFrom(exprType) || | |
1241 exprType.IsInterface || type.IsInterface) | |
1242 return Expression.Convert(expr, type); | |
1243 throw ParseError(errorPos, Res.CannotConvertValue, | |
1244 GetTypeName(exprType), GetTypeName(type)); | |
1245 } | |
1246 | |
1247 Expression ParseMemberAccess(Type type, Expression instance) | |
1248 { | |
1249 if (instance != null) type = instance.Type; | |
1250 int errorPos = token.pos; | |
1251 string id = GetIdentifier(); | |
1252 NextToken(); | |
1253 if (token.id == TokenId.OpenParen) | |
1254 { | |
1255 if (instance != null && type != typeof(string)) | |
1256 { | |
1257 Type enumerableType = FindGenericType(typeof(IEnumerable<>), type); | |
1258 if (enumerableType != null) | |
1259 { | |
1260 Type elementType = enumerableType.GetGenericArguments()[0]; | |
1261 return ParseAggregate(instance, elementType, id, errorPos); | |
1262 } | |
1263 } | |
1264 Expression[] args = ParseArgumentList(); | |
1265 MethodBase mb; | |
1266 switch (FindMethod(type, id, instance == null, args, out mb)) | |
1267 { | |
1268 case 0: | |
1269 throw ParseError(errorPos, Res.NoApplicableMethod, | |
1270 id, GetTypeName(type)); | |
1271 case 1: | |
1272 MethodInfo method = (MethodInfo)mb; | |
1273 if (!IsPredefinedType(method.DeclaringType)) | |
1274 throw ParseError(errorPos, Res.MethodsAreInaccessible, GetTypeName(method.DeclaringType)); | |
1275 if (method.ReturnType == typeof(void)) | |
1276 throw ParseError(errorPos, Res.MethodIsVoid, | |
1277 id, GetTypeName(method.DeclaringType)); | |
1278 return Expression.Call(instance, (MethodInfo)method, args); | |
1279 default: | |
1280 throw ParseError(errorPos, Res.AmbiguousMethodInvocation, | |
1281 id, GetTypeName(type)); | |
1282 } | |
1283 } | |
1284 else | |
1285 { | |
1286 MemberInfo member = FindPropertyOrField(type, id, instance == null); | |
1287 if (member == null) | |
1288 throw ParseError(errorPos, Res.UnknownPropertyOrField, | |
1289 id, GetTypeName(type)); | |
1290 return member is PropertyInfo ? | |
1291 Expression.Property(instance, (PropertyInfo)member) : | |
1292 Expression.Field(instance, (FieldInfo)member); | |
1293 } | |
1294 } | |
1295 | |
1296 static Type FindGenericType(Type generic, Type type) | |
1297 { | |
1298 while (type != null && type != typeof(object)) | |
1299 { | |
1300 if (type.IsGenericType && type.GetGenericTypeDefinition() == generic) return type; | |
1301 if (generic.IsInterface) | |
1302 { | |
1303 foreach (Type intfType in type.GetInterfaces()) | |
1304 { | |
1305 Type found = FindGenericType(generic, intfType); | |
1306 if (found != null) return found; | |
1307 } | |
1308 } | |
1309 type = type.BaseType; | |
1310 } | |
1311 return null; | |
1312 } | |
1313 | |
1314 Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos) | |
1315 { | |
1316 ParameterExpression outerIt = it; | |
1317 ParameterExpression innerIt = Expression.Parameter(elementType, ""); | |
1318 it = innerIt; | |
1319 Expression[] args = ParseArgumentList(); | |
1320 it = outerIt; | |
1321 MethodBase signature; | |
1322 if (FindMethod(typeof(IEnumerableSignatures), methodName, false, args, out signature) != 1) | |
1323 throw ParseError(errorPos, Res.NoApplicableAggregate, methodName); | |
1324 Type[] typeArgs; | |
1325 if (signature.Name == "Min" || signature.Name == "Max") | |
1326 { | |
1327 typeArgs = new Type[] { elementType, args[0].Type }; | |
1328 } | |
1329 else | |
1330 { | |
1331 typeArgs = new Type[] { elementType }; | |
1332 } | |
1333 if (args.Length == 0) | |
1334 { | |
1335 args = new Expression[] { instance }; | |
1336 } | |
1337 else | |
1338 { | |
1339 args = new Expression[] { instance, Expression.Lambda(args[0], innerIt) }; | |
1340 } | |
1341 return Expression.Call(typeof(Enumerable), signature.Name, typeArgs, args); | |
1342 } | |
1343 | |
1344 Expression[] ParseArgumentList() | |
1345 { | |
1346 ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); | |
1347 NextToken(); | |
1348 Expression[] args = token.id != TokenId.CloseParen ? ParseArguments() : new Expression[0]; | |
1349 ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected); | |
1350 NextToken(); | |
1351 return args; | |
1352 } | |
1353 | |
1354 Expression[] ParseArguments() | |
1355 { | |
1356 List<Expression> argList = new List<Expression>(); | |
1357 while (true) | |
1358 { | |
1359 argList.Add(ParseExpression()); | |
1360 if (token.id != TokenId.Comma) break; | |
1361 NextToken(); | |
1362 } | |
1363 return argList.ToArray(); | |
1364 } | |
1365 | |
1366 Expression ParseElementAccess(Expression expr) | |
1367 { | |
1368 int errorPos = token.pos; | |
1369 ValidateToken(TokenId.OpenBracket, Res.OpenParenExpected); | |
1370 NextToken(); | |
1371 Expression[] args = ParseArguments(); | |
1372 ValidateToken(TokenId.CloseBracket, Res.CloseBracketOrCommaExpected); | |
1373 NextToken(); | |
1374 if (expr.Type.IsArray) | |
1375 { | |
1376 if (expr.Type.GetArrayRank() != 1 || args.Length != 1) | |
1377 throw ParseError(errorPos, Res.CannotIndexMultiDimArray); | |
1378 Expression index = PromoteExpression(args[0], typeof(int), true); | |
1379 if (index == null) | |
1380 throw ParseError(errorPos, Res.InvalidIndex); | |
1381 return Expression.ArrayIndex(expr, index); | |
1382 } | |
1383 else | |
1384 { | |
1385 MethodBase mb; | |
1386 switch (FindIndexer(expr.Type, args, out mb)) | |
1387 { | |
1388 case 0: | |
1389 throw ParseError(errorPos, Res.NoApplicableIndexer, | |
1390 GetTypeName(expr.Type)); | |
1391 case 1: | |
1392 return Expression.Call(expr, (MethodInfo)mb, args); | |
1393 default: | |
1394 throw ParseError(errorPos, Res.AmbiguousIndexerInvocation, | |
1395 GetTypeName(expr.Type)); | |
1396 } | |
1397 } | |
1398 } | |
1399 | |
1400 static bool IsPredefinedType(Type type) | |
1401 { | |
1402 foreach (Type t in predefinedTypes) if (t == type) return true; | |
1403 return false; | |
1404 } | |
1405 | |
1406 static bool IsNullableType(Type type) | |
1407 { | |
1408 return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); | |
1409 } | |
1410 | |
1411 static Type GetNonNullableType(Type type) | |
1412 { | |
1413 return IsNullableType(type) ? type.GetGenericArguments()[0] : type; | |
1414 } | |
1415 | |
1416 static string GetTypeName(Type type) | |
1417 { | |
1418 Type baseType = GetNonNullableType(type); | |
1419 string s = baseType.Name; | |
1420 if (type != baseType) s += '?'; | |
1421 return s; | |
1422 } | |
1423 | |
1424 static bool IsNumericType(Type type) | |
1425 { | |
1426 return GetNumericTypeKind(type) != 0; | |
1427 } | |
1428 | |
1429 static bool IsSignedIntegralType(Type type) | |
1430 { | |
1431 return GetNumericTypeKind(type) == 2; | |
1432 } | |
1433 | |
1434 static bool IsUnsignedIntegralType(Type type) | |
1435 { | |
1436 return GetNumericTypeKind(type) == 3; | |
1437 } | |
1438 | |
1439 static int GetNumericTypeKind(Type type) | |
1440 { | |
1441 type = GetNonNullableType(type); | |
1442 if (type.IsEnum) return 0; | |
1443 switch (Type.GetTypeCode(type)) | |
1444 { | |
1445 case TypeCode.Char: | |
1446 case TypeCode.Single: | |
1447 case TypeCode.Double: | |
1448 case TypeCode.Decimal: | |
1449 return 1; | |
1450 case TypeCode.SByte: | |
1451 case TypeCode.Int16: | |
1452 case TypeCode.Int32: | |
1453 case TypeCode.Int64: | |
1454 return 2; | |
1455 case TypeCode.Byte: | |
1456 case TypeCode.UInt16: | |
1457 case TypeCode.UInt32: | |
1458 case TypeCode.UInt64: | |
1459 return 3; | |
1460 default: | |
1461 return 0; | |
1462 } | |
1463 } | |
1464 | |
1465 static bool IsEnumType(Type type) | |
1466 { | |
1467 return GetNonNullableType(type).IsEnum; | |
1468 } | |
1469 | |
1470 void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos) | |
1471 { | |
1472 Expression[] args = new Expression[] { expr }; | |
1473 MethodBase method; | |
1474 if (FindMethod(signatures, "F", false, args, out method) != 1) | |
1475 throw ParseError(errorPos, Res.IncompatibleOperand, | |
1476 opName, GetTypeName(args[0].Type)); | |
1477 expr = args[0]; | |
1478 } | |
1479 | |
1480 void CheckAndPromoteOperands(Type signatures, string opName, ref Expression left, ref Expression right, int errorPos) | |
1481 { | |
1482 Expression[] args = new Expression[] { left, right }; | |
1483 MethodBase method; | |
1484 if (FindMethod(signatures, "F", false, args, out method) != 1) | |
1485 throw IncompatibleOperandsError(opName, left, right, errorPos); | |
1486 left = args[0]; | |
1487 right = args[1]; | |
1488 } | |
1489 | |
1490 Exception IncompatibleOperandsError(string opName, Expression left, Expression right, int pos) | |
1491 { | |
1492 return ParseError(pos, Res.IncompatibleOperands, | |
1493 opName, GetTypeName(left.Type), GetTypeName(right.Type)); | |
1494 } | |
1495 | |
1496 MemberInfo FindPropertyOrField(Type type, string memberName, bool staticAccess) | |
1497 { | |
1498 BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | | |
1499 (staticAccess ? BindingFlags.Static : BindingFlags.Instance); | |
1500 foreach (Type t in SelfAndBaseTypes(type)) | |
1501 { | |
1502 MemberInfo[] members = t.FindMembers(MemberTypes.Property | MemberTypes.Field, | |
1503 flags, Type.FilterNameIgnoreCase, memberName); | |
1504 if (members.Length != 0) return members[0]; | |
1505 } | |
1506 return null; | |
1507 } | |
1508 | |
1509 int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method) | |
1510 { | |
1511 BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | | |
1512 (staticAccess ? BindingFlags.Static : BindingFlags.Instance); | |
1513 foreach (Type t in SelfAndBaseTypes(type)) | |
1514 { | |
1515 MemberInfo[] members = t.FindMembers(MemberTypes.Method, | |
1516 flags, Type.FilterNameIgnoreCase, methodName); | |
1517 int count = FindBestMethod(members.Cast<MethodBase>(), args, out method); | |
1518 if (count != 0) return count; | |
1519 } | |
1520 method = null; | |
1521 return 0; | |
1522 } | |
1523 | |
1524 int FindIndexer(Type type, Expression[] args, out MethodBase method) | |
1525 { | |
1526 foreach (Type t in SelfAndBaseTypes(type)) | |
1527 { | |
1528 MemberInfo[] members = t.GetDefaultMembers(); | |
1529 if (members.Length != 0) | |
1530 { | |
1531 IEnumerable<MethodBase> methods = members. | |
1532 OfType<PropertyInfo>(). | |
1533 Select(p => (MethodBase)p.GetGetMethod()). | |
1534 Where(m => m != null); | |
1535 int count = FindBestMethod(methods, args, out method); | |
1536 if (count != 0) return count; | |
1537 } | |
1538 } | |
1539 method = null; | |
1540 return 0; | |
1541 } | |
1542 | |
1543 static IEnumerable<Type> SelfAndBaseTypes(Type type) | |
1544 { | |
1545 if (type.IsInterface) | |
1546 { | |
1547 List<Type> types = new List<Type>(); | |
1548 AddInterface(types, type); | |
1549 return types; | |
1550 } | |
1551 return SelfAndBaseClasses(type); | |
1552 } | |
1553 | |
1554 static IEnumerable<Type> SelfAndBaseClasses(Type type) | |
1555 { | |
1556 while (type != null) | |
1557 { | |
1558 yield return type; | |
1559 type = type.BaseType; | |
1560 } | |
1561 } | |
1562 | |
1563 static void AddInterface(List<Type> types, Type type) | |
1564 { | |
1565 if (!types.Contains(type)) | |
1566 { | |
1567 types.Add(type); | |
1568 foreach (Type t in type.GetInterfaces()) AddInterface(types, t); | |
1569 } | |
1570 } | |
1571 | |
1572 class MethodData | |
1573 { | |
1574 public MethodBase MethodBase; | |
1575 public ParameterInfo[] Parameters; | |
1576 public Expression[] Args; | |
1577 } | |
1578 | |
1579 int FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args, out MethodBase method) | |
1580 { | |
1581 MethodData[] applicable = methods. | |
1582 Select(m => new MethodData { MethodBase = m, Parameters = m.GetParameters() }). | |
1583 Where(m => IsApplicable(m, args)). | |
1584 ToArray(); | |
1585 if (applicable.Length > 1) | |
1586 { | |
1587 applicable = applicable. | |
1588 Where(m => applicable.All(n => m == n || IsBetterThan(args, m, n))). | |
1589 ToArray(); | |
1590 } | |
1591 if (applicable.Length == 1) | |
1592 { | |
1593 MethodData md = applicable[0]; | |
1594 for (int i = 0; i < args.Length; i++) args[i] = md.Args[i]; | |
1595 method = md.MethodBase; | |
1596 } | |
1597 else | |
1598 { | |
1599 method = null; | |
1600 } | |
1601 return applicable.Length; | |
1602 } | |
1603 | |
1604 bool IsApplicable(MethodData method, Expression[] args) | |
1605 { | |
1606 if (method.Parameters.Length != args.Length) return false; | |
1607 Expression[] promotedArgs = new Expression[args.Length]; | |
1608 for (int i = 0; i < args.Length; i++) | |
1609 { | |
1610 ParameterInfo pi = method.Parameters[i]; | |
1611 if (pi.IsOut) return false; | |
1612 Expression promoted = PromoteExpression(args[i], pi.ParameterType, false); | |
1613 if (promoted == null) return false; | |
1614 promotedArgs[i] = promoted; | |
1615 } | |
1616 method.Args = promotedArgs; | |
1617 return true; | |
1618 } | |
1619 | |
1620 Expression PromoteExpression(Expression expr, Type type, bool exact) | |
1621 { | |
1622 if (expr.Type == type) return expr; | |
1623 if (expr is ConstantExpression) | |
1624 { | |
1625 ConstantExpression ce = (ConstantExpression)expr; | |
1626 if (ce == nullLiteral) | |
1627 { | |
1628 if (!type.IsValueType || IsNullableType(type)) | |
1629 return Expression.Constant(null, type); | |
1630 } | |
1631 else | |
1632 { | |
1633 string text; | |
1634 if (literals.TryGetValue(ce, out text)) | |
1635 { | |
1636 Type target = GetNonNullableType(type); | |
1637 Object value = null; | |
1638 switch (Type.GetTypeCode(ce.Type)) | |
1639 { | |
1640 case TypeCode.Int32: | |
1641 case TypeCode.UInt32: | |
1642 case TypeCode.Int64: | |
1643 case TypeCode.UInt64: | |
1644 value = ParseNumber(text, target); | |
1645 break; | |
1646 case TypeCode.Double: | |
1647 if (target == typeof(decimal)) value = ParseNumber(text, target); | |
1648 break; | |
1649 case TypeCode.String: | |
1650 value = ParseEnum(text, target); | |
1651 break; | |
1652 } | |
1653 if (value != null) | |
1654 return Expression.Constant(value, type); | |
1655 } | |
1656 } | |
1657 } | |
1658 if (IsCompatibleWith(expr.Type, type)) | |
1659 { | |
1660 if (type.IsValueType || exact) return Expression.Convert(expr, type); | |
1661 return expr; | |
1662 } | |
1663 return null; | |
1664 } | |
1665 | |
1666 static object ParseNumber(string text, Type type) | |
1667 { | |
1668 switch (Type.GetTypeCode(GetNonNullableType(type))) | |
1669 { | |
1670 case TypeCode.SByte: | |
1671 sbyte sb; | |
1672 if (sbyte.TryParse(text, out sb)) return sb; | |
1673 break; | |
1674 case TypeCode.Byte: | |
1675 byte b; | |
1676 if (byte.TryParse(text, out b)) return b; | |
1677 break; | |
1678 case TypeCode.Int16: | |
1679 short s; | |
1680 if (short.TryParse(text, out s)) return s; | |
1681 break; | |
1682 case TypeCode.UInt16: | |
1683 ushort us; | |
1684 if (ushort.TryParse(text, out us)) return us; | |
1685 break; | |
1686 case TypeCode.Int32: | |
1687 int i; | |
1688 if (int.TryParse(text, out i)) return i; | |
1689 break; | |
1690 case TypeCode.UInt32: | |
1691 uint ui; | |
1692 if (uint.TryParse(text, out ui)) return ui; | |
1693 break; | |
1694 case TypeCode.Int64: | |
1695 long l; | |
1696 if (long.TryParse(text, out l)) return l; | |
1697 break; | |
1698 case TypeCode.UInt64: | |
1699 ulong ul; | |
1700 if (ulong.TryParse(text, out ul)) return ul; | |
1701 break; | |
1702 case TypeCode.Single: | |
1703 float f; | |
1704 if (float.TryParse(text, out f)) return f; | |
1705 break; | |
1706 case TypeCode.Double: | |
1707 double d; | |
1708 if (double.TryParse(text, out d)) return d; | |
1709 break; | |
1710 case TypeCode.Decimal: | |
1711 decimal e; | |
1712 if (decimal.TryParse(text, out e)) return e; | |
1713 break; | |
1714 } | |
1715 return null; | |
1716 } | |
1717 | |
1718 static object ParseEnum(string name, Type type) | |
1719 { | |
1720 if (type.IsEnum) | |
1721 { | |
1722 MemberInfo[] memberInfos = type.FindMembers(MemberTypes.Field, | |
1723 BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static, | |
1724 Type.FilterNameIgnoreCase, name); | |
1725 if (memberInfos.Length != 0) return ((FieldInfo)memberInfos[0]).GetValue(null); | |
1726 } | |
1727 return null; | |
1728 } | |
1729 | |
1730 static bool IsCompatibleWith(Type source, Type target) | |
1731 { | |
1732 if (source == target) return true; | |
1733 if (!target.IsValueType) return target.IsAssignableFrom(source); | |
1734 Type st = GetNonNullableType(source); | |
1735 Type tt = GetNonNullableType(target); | |
1736 if (st != source && tt == target) return false; | |
1737 TypeCode sc = st.IsEnum ? TypeCode.Object : Type.GetTypeCode(st); | |
1738 TypeCode tc = tt.IsEnum ? TypeCode.Object : Type.GetTypeCode(tt); | |
1739 switch (sc) | |
1740 { | |
1741 case TypeCode.SByte: | |
1742 switch (tc) | |
1743 { | |
1744 case TypeCode.SByte: | |
1745 case TypeCode.Int16: | |
1746 case TypeCode.Int32: | |
1747 case TypeCode.Int64: | |
1748 case TypeCode.Single: | |
1749 case TypeCode.Double: | |
1750 case TypeCode.Decimal: | |
1751 return true; | |
1752 } | |
1753 break; | |
1754 case TypeCode.Byte: | |
1755 switch (tc) | |
1756 { | |
1757 case TypeCode.Byte: | |
1758 case TypeCode.Int16: | |
1759 case TypeCode.UInt16: | |
1760 case TypeCode.Int32: | |
1761 case TypeCode.UInt32: | |
1762 case TypeCode.Int64: | |
1763 case TypeCode.UInt64: | |
1764 case TypeCode.Single: | |
1765 case TypeCode.Double: | |
1766 case TypeCode.Decimal: | |
1767 return true; | |
1768 } | |
1769 break; | |
1770 case TypeCode.Int16: | |
1771 switch (tc) | |
1772 { | |
1773 case TypeCode.Int16: | |
1774 case TypeCode.Int32: | |
1775 case TypeCode.Int64: | |
1776 case TypeCode.Single: | |
1777 case TypeCode.Double: | |
1778 case TypeCode.Decimal: | |
1779 return true; | |
1780 } | |
1781 break; | |
1782 case TypeCode.UInt16: | |
1783 switch (tc) | |
1784 { | |
1785 case TypeCode.UInt16: | |
1786 case TypeCode.Int32: | |
1787 case TypeCode.UInt32: | |
1788 case TypeCode.Int64: | |
1789 case TypeCode.UInt64: | |
1790 case TypeCode.Single: | |
1791 case TypeCode.Double: | |
1792 case TypeCode.Decimal: | |
1793 return true; | |
1794 } | |
1795 break; | |
1796 case TypeCode.Int32: | |
1797 switch (tc) | |
1798 { | |
1799 case TypeCode.Int32: | |
1800 case TypeCode.Int64: | |
1801 case TypeCode.Single: | |
1802 case TypeCode.Double: | |
1803 case TypeCode.Decimal: | |
1804 return true; | |
1805 } | |
1806 break; | |
1807 case TypeCode.UInt32: | |
1808 switch (tc) | |
1809 { | |
1810 case TypeCode.UInt32: | |
1811 case TypeCode.Int64: | |
1812 case TypeCode.UInt64: | |
1813 case TypeCode.Single: | |
1814 case TypeCode.Double: | |
1815 case TypeCode.Decimal: | |
1816 return true; | |
1817 } | |
1818 break; | |
1819 case TypeCode.Int64: | |
1820 switch (tc) | |
1821 { | |
1822 case TypeCode.Int64: | |
1823 case TypeCode.Single: | |
1824 case TypeCode.Double: | |
1825 case TypeCode.Decimal: | |
1826 return true; | |
1827 } | |
1828 break; | |
1829 case TypeCode.UInt64: | |
1830 switch (tc) | |
1831 { | |
1832 case TypeCode.UInt64: | |
1833 case TypeCode.Single: | |
1834 case TypeCode.Double: | |
1835 case TypeCode.Decimal: | |
1836 return true; | |
1837 } | |
1838 break; | |
1839 case TypeCode.Single: | |
1840 switch (tc) | |
1841 { | |
1842 case TypeCode.Single: | |
1843 case TypeCode.Double: | |
1844 return true; | |
1845 } | |
1846 break; | |
1847 default: | |
1848 if (st == tt) return true; | |
1849 break; | |
1850 } | |
1851 return false; | |
1852 } | |
1853 | |
1854 static bool IsBetterThan(Expression[] args, MethodData m1, MethodData m2) | |
1855 { | |
1856 bool better = false; | |
1857 for (int i = 0; i < args.Length; i++) | |
1858 { | |
1859 int c = CompareConversions(args[i].Type, | |
1860 m1.Parameters[i].ParameterType, | |
1861 m2.Parameters[i].ParameterType); | |
1862 if (c < 0) return false; | |
1863 if (c > 0) better = true; | |
1864 } | |
1865 return better; | |
1866 } | |
1867 | |
1868 // Return 1 if s -> t1 is a better conversion than s -> t2 | |
1869 // Return -1 if s -> t2 is a better conversion than s -> t1 | |
1870 // Return 0 if neither conversion is better | |
1871 static int CompareConversions(Type s, Type t1, Type t2) | |
1872 { | |
1873 if (t1 == t2) return 0; | |
1874 if (s == t1) return 1; | |
1875 if (s == t2) return -1; | |
1876 bool t1t2 = IsCompatibleWith(t1, t2); | |
1877 bool t2t1 = IsCompatibleWith(t2, t1); | |
1878 if (t1t2 && !t2t1) return 1; | |
1879 if (t2t1 && !t1t2) return -1; | |
1880 if (IsSignedIntegralType(t1) && IsUnsignedIntegralType(t2)) return 1; | |
1881 if (IsSignedIntegralType(t2) && IsUnsignedIntegralType(t1)) return -1; | |
1882 return 0; | |
1883 } | |
1884 | |
1885 Expression GenerateEqual(Expression left, Expression right) | |
1886 { | |
1887 return Expression.Equal(left, right); | |
1888 } | |
1889 | |
1890 Expression GenerateNotEqual(Expression left, Expression right) | |
1891 { | |
1892 return Expression.NotEqual(left, right); | |
1893 } | |
1894 | |
1895 Expression GenerateGreaterThan(Expression left, Expression right) | |
1896 { | |
1897 if (left.Type == typeof(string)) | |
1898 { | |
1899 return Expression.GreaterThan( | |
1900 GenerateStaticMethodCall("Compare", left, right), | |
1901 Expression.Constant(0) | |
1902 ); | |
1903 } | |
1904 return Expression.GreaterThan(left, right); | |
1905 } | |
1906 | |
1907 Expression GenerateGreaterThanEqual(Expression left, Expression right) | |
1908 { | |
1909 if (left.Type == typeof(string)) | |
1910 { | |
1911 return Expression.GreaterThanOrEqual( | |
1912 GenerateStaticMethodCall("Compare", left, right), | |
1913 Expression.Constant(0) | |
1914 ); | |
1915 } | |
1916 return Expression.GreaterThanOrEqual(left, right); | |
1917 } | |
1918 | |
1919 Expression GenerateLessThan(Expression left, Expression right) | |
1920 { | |
1921 if (left.Type == typeof(string)) | |
1922 { | |
1923 return Expression.LessThan( | |
1924 GenerateStaticMethodCall("Compare", left, right), | |
1925 Expression.Constant(0) | |
1926 ); | |
1927 } | |
1928 return Expression.LessThan(left, right); | |
1929 } | |
1930 | |
1931 Expression GenerateLessThanEqual(Expression left, Expression right) | |
1932 { | |
1933 if (left.Type == typeof(string)) | |
1934 { | |
1935 return Expression.LessThanOrEqual( | |
1936 GenerateStaticMethodCall("Compare", left, right), | |
1937 Expression.Constant(0) | |
1938 ); | |
1939 } | |
1940 return Expression.LessThanOrEqual(left, right); | |
1941 } | |
1942 | |
1943 Expression GenerateAdd(Expression left, Expression right) | |
1944 { | |
1945 if (left.Type == typeof(string) && right.Type == typeof(string)) | |
1946 { | |
1947 return GenerateStaticMethodCall("Concat", left, right); | |
1948 } | |
1949 return Expression.Add(left, right); | |
1950 } | |
1951 | |
1952 Expression GenerateSubtract(Expression left, Expression right) | |
1953 { | |
1954 return Expression.Subtract(left, right); | |
1955 } | |
1956 | |
1957 Expression GenerateStringConcat(Expression left, Expression right) | |
1958 { | |
1959 return Expression.Call( | |
1960 null, | |
1961 typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }), | |
1962 new[] { left, right }); | |
1963 } | |
1964 | |
1965 MethodInfo GetStaticMethod(string methodName, Expression left, Expression right) | |
1966 { | |
1967 return left.Type.GetMethod(methodName, new[] { left.Type, right.Type }); | |
1968 } | |
1969 | |
1970 Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right) | |
1971 { | |
1972 return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right }); | |
1973 } | |
1974 | |
1975 void SetTextPos(int pos) | |
1976 { | |
1977 textPos = pos; | |
1978 ch = textPos < textLen ? text[textPos] : '\0'; | |
1979 } | |
1980 | |
1981 void NextChar() | |
1982 { | |
1983 if (textPos < textLen) textPos++; | |
1984 ch = textPos < textLen ? text[textPos] : '\0'; | |
1985 } | |
1986 | |
1987 void NextToken() | |
1988 { | |
1989 while (Char.IsWhiteSpace(ch)) NextChar(); | |
1990 TokenId t; | |
1991 int tokenPos = textPos; | |
1992 switch (ch) | |
1993 { | |
1994 case '!': | |
1995 NextChar(); | |
1996 if (ch == '=') | |
1997 { | |
1998 NextChar(); | |
1999 t = TokenId.ExclamationEqual; | |
2000 } | |
2001 else | |
2002 { | |
2003 t = TokenId.Exclamation; | |
2004 } | |
2005 break; | |
2006 case '%': | |
2007 NextChar(); | |
2008 t = TokenId.Percent; | |
2009 break; | |
2010 case '&': | |
2011 NextChar(); | |
2012 if (ch == '&') | |
2013 { | |
2014 NextChar(); | |
2015 t = TokenId.DoubleAmphersand; | |
2016 } | |
2017 else | |
2018 { | |
2019 t = TokenId.Amphersand; | |
2020 } | |
2021 break; | |
2022 case '(': | |
2023 NextChar(); | |
2024 t = TokenId.OpenParen; | |
2025 break; | |
2026 case ')': | |
2027 NextChar(); | |
2028 t = TokenId.CloseParen; | |
2029 break; | |
2030 case '*': | |
2031 NextChar(); | |
2032 t = TokenId.Asterisk; | |
2033 break; | |
2034 case '+': | |
2035 NextChar(); | |
2036 t = TokenId.Plus; | |
2037 break; | |
2038 case ',': | |
2039 NextChar(); | |
2040 t = TokenId.Comma; | |
2041 break; | |
2042 case '-': | |
2043 NextChar(); | |
2044 t = TokenId.Minus; | |
2045 break; | |
2046 case '.': | |
2047 NextChar(); | |
2048 t = TokenId.Dot; | |
2049 break; | |
2050 case '/': | |
2051 NextChar(); | |
2052 t = TokenId.Slash; | |
2053 break; | |
2054 case ':': | |
2055 NextChar(); | |
2056 t = TokenId.Colon; | |
2057 break; | |
2058 case '<': | |
2059 NextChar(); | |
2060 if (ch == '=') | |
2061 { | |
2062 NextChar(); | |
2063 t = TokenId.LessThanEqual; | |
2064 } | |
2065 else if (ch == '>') | |
2066 { | |
2067 NextChar(); | |
2068 t = TokenId.LessGreater; | |
2069 } | |
2070 else | |
2071 { | |
2072 t = TokenId.LessThan; | |
2073 } | |
2074 break; | |
2075 case '=': | |
2076 NextChar(); | |
2077 if (ch == '=') | |
2078 { | |
2079 NextChar(); | |
2080 t = TokenId.DoubleEqual; | |
2081 } | |
2082 else | |
2083 { | |
2084 t = TokenId.Equal; | |
2085 } | |
2086 break; | |
2087 case '>': | |
2088 NextChar(); | |
2089 if (ch == '=') | |
2090 { | |
2091 NextChar(); | |
2092 t = TokenId.GreaterThanEqual; | |
2093 } | |
2094 else | |
2095 { | |
2096 t = TokenId.GreaterThan; | |
2097 } | |
2098 break; | |
2099 case '?': | |
2100 NextChar(); | |
2101 t = TokenId.Question; | |
2102 break; | |
2103 case '[': | |
2104 NextChar(); | |
2105 t = TokenId.OpenBracket; | |
2106 break; | |
2107 case ']': | |
2108 NextChar(); | |
2109 t = TokenId.CloseBracket; | |
2110 break; | |
2111 case '|': | |
2112 NextChar(); | |
2113 if (ch == '|') | |
2114 { | |
2115 NextChar(); | |
2116 t = TokenId.DoubleBar; | |
2117 } | |
2118 else | |
2119 { | |
2120 t = TokenId.Bar; | |
2121 } | |
2122 break; | |
2123 case '"': | |
2124 case '\'': | |
2125 char quote = ch; | |
2126 do | |
2127 { | |
2128 NextChar(); | |
2129 while (textPos < textLen && ch != quote) NextChar(); | |
2130 if (textPos == textLen) | |
2131 throw ParseError(textPos, Res.UnterminatedStringLiteral); | |
2132 NextChar(); | |
2133 } while (ch == quote); | |
2134 t = TokenId.StringLiteral; | |
2135 break; | |
2136 default: | |
2137 if (Char.IsLetter(ch) || ch == '@' || ch == '_') | |
2138 { | |
2139 do | |
2140 { | |
2141 NextChar(); | |
2142 } while (Char.IsLetterOrDigit(ch) || ch == '_'); | |
2143 t = TokenId.Identifier; | |
2144 break; | |
2145 } | |
2146 if (Char.IsDigit(ch)) | |
2147 { | |
2148 t = TokenId.IntegerLiteral; | |
2149 do | |
2150 { | |
2151 NextChar(); | |
2152 } while (Char.IsDigit(ch)); | |
2153 if (ch == '.') | |
2154 { | |
2155 t = TokenId.RealLiteral; | |
2156 NextChar(); | |
2157 ValidateDigit(); | |
2158 do | |
2159 { | |
2160 NextChar(); | |
2161 } while (Char.IsDigit(ch)); | |
2162 } | |
2163 if (ch == 'E' || ch == 'e') | |
2164 { | |
2165 t = TokenId.RealLiteral; | |
2166 NextChar(); | |
2167 if (ch == '+' || ch == '-') NextChar(); | |
2168 ValidateDigit(); | |
2169 do | |
2170 { | |
2171 NextChar(); | |
2172 } while (Char.IsDigit(ch)); | |
2173 } | |
2174 if (ch == 'F' || ch == 'f') NextChar(); | |
2175 break; | |
2176 } | |
2177 if (textPos == textLen) | |
2178 { | |
2179 t = TokenId.End; | |
2180 break; | |
2181 } | |
2182 throw ParseError(textPos, Res.InvalidCharacter, ch); | |
2183 } | |
2184 token.id = t; | |
2185 token.text = text.Substring(tokenPos, textPos - tokenPos); | |
2186 token.pos = tokenPos; | |
2187 } | |
2188 | |
2189 bool TokenIdentifierIs(string id) | |
2190 { | |
2191 return token.id == TokenId.Identifier && String.Equals(id, token.text, StringComparison.OrdinalIgnoreCase); | |
2192 } | |
2193 | |
2194 string GetIdentifier() | |
2195 { | |
2196 ValidateToken(TokenId.Identifier, Res.IdentifierExpected); | |
2197 string id = token.text; | |
2198 if (id.Length > 1 && id[0] == '@') id = id.Substring(1); | |
2199 return id; | |
2200 } | |
2201 | |
2202 void ValidateDigit() | |
2203 { | |
2204 if (!Char.IsDigit(ch)) throw ParseError(textPos, Res.DigitExpected); | |
2205 } | |
2206 | |
2207 void ValidateToken(TokenId t, string errorMessage) | |
2208 { | |
2209 if (token.id != t) throw ParseError(errorMessage); | |
2210 } | |
2211 | |
2212 void ValidateToken(TokenId t) | |
2213 { | |
2214 if (token.id != t) throw ParseError(Res.SyntaxError); | |
2215 } | |
2216 | |
2217 Exception ParseError(string format, params object[] args) | |
2218 { | |
2219 return ParseError(token.pos, format, args); | |
2220 } | |
2221 | |
2222 Exception ParseError(int pos, string format, params object[] args) | |
2223 { | |
2224 return new ParseException(string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args), pos); | |
2225 } | |
2226 | |
2227 static Dictionary<string, object> CreateKeywords() | |
2228 { | |
2229 Dictionary<string, object> d = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); | |
2230 d.Add("true", trueLiteral); | |
2231 d.Add("false", falseLiteral); | |
2232 d.Add("null", nullLiteral); | |
2233 d.Add(keywordIt, keywordIt); | |
2234 d.Add(keywordIif, keywordIif); | |
2235 d.Add(keywordNew, keywordNew); | |
2236 foreach (Type type in predefinedTypes) d.Add(type.Name, type); | |
2237 return d; | |
2238 } | |
2239 } | |
2240 | |
2241 static class Res | |
2242 { | |
2243 public const string DuplicateIdentifier = "The identifier '{0}' was defined more than once"; | |
2244 public const string ExpressionTypeMismatch = "Expression of type '{0}' expected"; | |
2245 public const string ExpressionExpected = "Expression expected"; | |
2246 public const string InvalidCharacterLiteral = "Character literal must contain exactly one character"; | |
2247 public const string InvalidIntegerLiteral = "Invalid integer literal '{0}'"; | |
2248 public const string InvalidRealLiteral = "Invalid real literal '{0}'"; | |
2249 public const string UnknownIdentifier = "Unknown identifier '{0}'"; | |
2250 public const string NoItInScope = "No 'it' is in scope"; | |
2251 public const string IifRequiresThreeArgs = "The 'iif' function requires three arguments"; | |
2252 public const string FirstExprMustBeBool = "The first expression must be of type 'Boolean'"; | |
2253 public const string BothTypesConvertToOther = "Both of the types '{0}' and '{1}' convert to the other"; | |
2254 public const string NeitherTypeConvertsToOther = "Neither of the types '{0}' and '{1}' converts to the other"; | |
2255 public const string MissingAsClause = "Expression is missing an 'as' clause"; | |
2256 public const string ArgsIncompatibleWithLambda = "Argument list incompatible with lambda expression"; | |
2257 public const string TypeHasNoNullableForm = "Type '{0}' has no nullable form"; | |
2258 public const string NoMatchingConstructor = "No matching constructor in type '{0}'"; | |
2259 public const string AmbiguousConstructorInvocation = "Ambiguous invocation of '{0}' constructor"; | |
2260 public const string CannotConvertValue = "A value of type '{0}' cannot be converted to type '{1}'"; | |
2261 public const string NoApplicableMethod = "No applicable method '{0}' exists in type '{1}'"; | |
2262 public const string MethodsAreInaccessible = "Methods on type '{0}' are not accessible"; | |
2263 public const string MethodIsVoid = "Method '{0}' in type '{1}' does not return a value"; | |
2264 public const string AmbiguousMethodInvocation = "Ambiguous invocation of method '{0}' in type '{1}'"; | |
2265 public const string UnknownPropertyOrField = "No property or field '{0}' exists in type '{1}'"; | |
2266 public const string NoApplicableAggregate = "No applicable aggregate method '{0}' exists"; | |
2267 public const string CannotIndexMultiDimArray = "Indexing of multi-dimensional arrays is not supported"; | |
2268 public const string InvalidIndex = "Array index must be an integer expression"; | |
2269 public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}'"; | |
2270 public const string AmbiguousIndexerInvocation = "Ambiguous invocation of indexer in type '{0}'"; | |
2271 public const string IncompatibleOperand = "Operator '{0}' incompatible with operand type '{1}'"; | |
2272 public const string IncompatibleOperands = "Operator '{0}' incompatible with operand types '{1}' and '{2}'"; | |
2273 public const string UnterminatedStringLiteral = "Unterminated string literal"; | |
2274 public const string InvalidCharacter = "Syntax error '{0}'"; | |
2275 public const string DigitExpected = "Digit expected"; | |
2276 public const string SyntaxError = "Syntax error"; | |
2277 public const string TokenExpected = "{0} expected"; | |
2278 public const string ParseExceptionFormat = "{0} (at index {1})"; | |
2279 public const string ColonExpected = "':' expected"; | |
2280 public const string OpenParenExpected = "'(' expected"; | |
2281 public const string CloseParenOrOperatorExpected = "')' or operator expected"; | |
2282 public const string CloseParenOrCommaExpected = "')' or ',' expected"; | |
2283 public const string DotOrOpenParenExpected = "'.' or '(' expected"; | |
2284 public const string OpenBracketExpected = "'[' expected"; | |
2285 public const string CloseBracketOrCommaExpected = "']' or ',' expected"; | |
2286 public const string IdentifierExpected = "Identifier expected"; | |
2287 } | |
2288 } |