Merge branch 'xml-dsig-transforms-from-corefx' of git://github.com/vladimir-kazakov...
[mono.git] / mcs / tools / culevel / CompileUplevel.cs
1 //
2 // Authors:
3 //   Marek Habersack (mhabersack@novell.com)
4 //
5 // (C) 2007 Novell, Inc
6 //
7
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.CodeDom;
30 using System.CodeDom.Compiler;
31 using System.Collections;
32 using System.IO;
33 using System.Reflection;
34 using System.Text;
35 using System.Xml;
36 using Microsoft.CSharp;
37
38 namespace Mono.Tools
39 {
40         class Position
41         {
42                 int start = -1;
43                 int end = -1;
44     
45                 public int Start {
46                         get { return start; }
47                 }
48
49                 public int End {
50                         get { return end; }
51                 }
52
53                 public int RequiredUALength {
54                         get {
55                                 if (end == -1)
56                                         return start;
57                                 return end;
58                         }
59                 }
60     
61                 public override string ToString ()
62                 {
63                         StringBuilder sb = new StringBuilder ("Position {");
64                         sb.AppendFormat ("{0}", start);
65                         if (end != -1)
66                                 sb.AppendFormat (",{0}}}", end);
67                         else
68                                 sb.Append ("}");
69
70                         return sb.ToString ();
71                 }
72     
73                 public Position (string positions)
74                 {
75                         if (positions == null || positions.Length == 0)
76                                 throw new ArgumentException ("'positions' must not be null or empty");
77       
78                         string[] pa = positions.Split ('-');
79       
80                         if (pa.Length > 2)
81                                 throw new ApplicationException ("Syntax error in the positions attribute - only one dash can be present");
82
83                         try {
84                                 start = Int32.Parse (pa [0]);
85                         } catch (Exception) {
86                                 throw new ApplicationException ("The 'positions' attribute has invalid syntax");
87                         }
88
89                         if (start < 0)
90                                 throw new ApplicationException ("Start must be 0 or more.");
91       
92                         if (pa.Length == 2) {
93                                 try {
94                                         end = Int32.Parse (pa [1]);
95                                 } catch (Exception) {
96                                         throw new ApplicationException ("The 'positions' attribute has invalid syntax");
97                                 }
98
99                                 if (end < start)
100                                         throw new ApplicationException ("End of range must not be smaller than its start");
101
102                                 if (end == start)
103                                         end = -1;
104                         }
105                 }
106
107                 public CodeBinaryOperatorExpression GetExpression (string match)
108                 {
109                         int poslen;
110
111                         if (end == -1)
112                                 poslen = 0;
113                         else
114                                 poslen = end - start;
115                         poslen++;
116       
117                         if (match.Length != poslen)
118                                 throw new ApplicationException (
119                                         String.Format ("Match string '{0}' has incorrect length (expected {1})", match, poslen));
120       
121                         CodeBinaryOperatorExpression expr = new CodeBinaryOperatorExpression ();
122
123                         if (poslen == 1) {
124                                 expr.Left = new CodeArrayIndexerExpression (new CodeVariableReferenceExpression ("ua"),
125                                                                             new CodePrimitiveExpression (Start));
126                                 expr.Operator = CodeBinaryOperatorType.ValueEquality;
127                                 expr.Right = new CodePrimitiveExpression (match [0]);
128                         } else {
129                                 CodeBinaryOperatorExpression cur = expr, prev = expr, tmp;
130                                 int i, pos;
131         
132                                 for (i = 0, pos = Start; i < poslen; i++, pos++) {
133                                         tmp = new CodeBinaryOperatorExpression ();
134                                         tmp.Left = new CodeArrayIndexerExpression (new CodeVariableReferenceExpression ("ua"),
135                                                                                    new CodePrimitiveExpression (pos));
136                                         tmp.Operator = CodeBinaryOperatorType.ValueEquality;
137                                         tmp.Right = new CodePrimitiveExpression (match [i]);
138
139                                         if (i + 1 < poslen) {
140                                                 cur.Left = tmp;
141                                                 cur.Operator = CodeBinaryOperatorType.BooleanAnd;
142                                                 prev.Right = cur;
143                                                 prev = cur;
144                                                 cur = new CodeBinaryOperatorExpression ();
145                                         } else
146                                                 prev.Right = tmp;
147                                 }
148                         }
149       
150                         return expr;
151                 }
152         }
153   
154         class GroupDefinition
155         {
156                 readonly CodeMethodReturnStatement returnTrue = new CodeMethodReturnStatement (new CodePrimitiveExpression (true));
157                 readonly CodeMethodReturnStatement returnFalse = new CodeMethodReturnStatement (new CodePrimitiveExpression (false));
158     
159                 ArrayList positions = null;
160                 ArrayList matches = null;
161                 ArrayList childGroups;
162                 ArrayList exceptions;
163     
164                 bool defaultJS = true;
165                 int scanfrom = -1;
166                 int skip = -1;
167                 int level = 0;
168                 int groupId = 0;
169     
170                 public Position[] Positions {
171                         get {
172                                 if (positions == null)
173                                         return null;
174                                 return (Position[]) positions.ToArray (typeof (Position));
175                         }
176                 }
177
178                 public string[] Matches {
179                         get {
180                                 if (matches == null)
181                                         return null;
182                                 return (string[]) matches.ToArray (typeof (string));
183                         }
184                 }
185
186                 public ArrayList ChildGroups {
187                         get { return childGroups; }
188                 }
189     
190                 public bool DefaultJS {
191                         get { return defaultJS; }
192                 }
193
194                 public int ScanFrom {
195                         get { return scanfrom; }
196                 }
197
198                 public int Skip {
199                         get { return skip; }
200                 }
201
202                 public bool Positional {
203                         get { return positions != null; }
204                 }
205
206                 public bool GroupZero {
207                         get { return positions == null && matches == null && scanfrom == -1 && skip == -1; }
208                 }
209
210                 public int Level {
211                         get { return level; }
212                         set { level = value; }
213                 }
214
215                 public int GroupId {
216                         get { return groupId; }
217                         set { groupId = value; }
218                 }
219     
220                 public override string ToString ()
221                 {
222                         if (GroupZero)
223                                 return "GroupZero";
224       
225                         StringBuilder sb = new StringBuilder ("Group: ");
226                         if (Positional) {
227                                 sb.Append ("positions =");
228                                 foreach (Position p in positions)
229                                         sb.AppendFormat (" [{0}]", p.ToString ());
230                         } else {
231                                 sb.AppendFormat ("scanfrom {0}, skip {1}", scanfrom, skip);
232                         }
233
234                         sb.Append ("; matches =");
235                         foreach (string m in matches)
236                                 sb.AppendFormat (" [{0}]", m);
237       
238                         return sb.ToString ();
239                 }
240
241                 public GroupDefinition ()
242                 {
243                         childGroups = new ArrayList ();
244                 }
245       
246                 public GroupDefinition (XmlReader reader)
247                 {
248                         childGroups = new ArrayList ();
249
250                         string positions = reader.GetAttribute ("positions");
251                         string scanfrom = reader.GetAttribute ("scanfrom");
252       
253                         if (positions != null && scanfrom != null)
254                                 throw new ApplicationException ("The 'positions' and 'scanfrom' attributes are mutually exclusive");
255                         if ((positions == null || positions.Length == 0) && (scanfrom == null || scanfrom.Length == 0))
256                                 throw new ApplicationException ("Exactly one of the 'positions' or 'scanfrom' attributes must be present and have a value");
257       
258                         if (positions != null)
259                                 InitPositions (reader, positions);
260                         else
261                                 InitScanfrom (reader, scanfrom);
262
263                         string javascript = reader.GetAttribute ("javascript");
264                         if (javascript != null && javascript.Length > 0) {
265                                 try {
266                                         defaultJS = Boolean.Parse (javascript);
267                                 } catch (Exception) {
268                                         throw new ApplicationException ("Invalid value of the 'javascript' attribute. Must be a valid boolean value (true or false)");
269                                 }
270                         }
271
272                         string match = reader.GetAttribute ("match");
273                         if (match == null || match.Length == 0)
274                                 throw new ApplicationException ("Missing the 'match' attribute");
275
276                         InitMatches (match);
277                 }
278
279                 public void AddExcept (XmlReader reader)
280                 {
281                         if (exceptions == null)
282                                 exceptions = new ArrayList ();
283
284                         exceptions.Add (new GroupDefinition (reader));
285                 }
286     
287                 void InitMatches (string match)
288                 {
289                         if (positions != null) {
290                                 string[] ma = match.Split (',');
291       
292                                 if (ma.Length != positions.Count)
293                                         throw new ApplicationException ("Number of matches provided in the 'match' attribute is different that the number of positions.");
294
295                                 matches = new ArrayList (ma.Length);
296                                 foreach (string m in ma)
297                                         matches.Add (m);
298                         } else {
299                                 matches = new ArrayList (1);
300                                 matches.Add (match);
301                         }
302                 }
303     
304                 void InitPositions (XmlReader reader, string positions)
305                 {
306                         string[] pa = positions.Split (',');
307                         this.positions = new ArrayList (pa.Length);
308                         foreach (string p in pa)
309                                 this.positions.Add (new Position (p.Trim ()));
310                 }
311
312                 void InitScanfrom (XmlReader reader, string scanfrom)
313                 {
314                         string skip = reader.GetAttribute ("skip");
315       
316                         if (skip == null || skip.Length == 0)
317                                 this.skip = 0;
318                         else {
319                                 try {
320                                         this.skip = Int32.Parse (skip);
321                                 } catch (Exception) {
322                                         throw new ApplicationException ("Invalid value of the 'skip' attribute. Must be an integer.");
323                                 }
324                         }
325
326                         try {
327                                 this.scanfrom = Int32.Parse (scanfrom);
328                         } catch (Exception) {
329                                 throw new ApplicationException ("Invalid value of the 'scanfrom' attribute. Must be an integer.");
330                         }
331                 }
332
333                 public CodeCompileUnit GenerateCode ()
334                 {
335                         if (!GroupZero)
336                                 throw new ApplicationException ("Code can be generated only by GroupZero");
337       
338                         CodeCompileUnit unit = new CodeCompileUnit ();
339                         CodeNamespace ns = new CodeNamespace ("System.Web");
340                         unit.Namespaces.Add (ns);
341       
342                         ns.Imports.Add (new CodeNamespaceImport ("System"));
343
344                         CodeTypeDeclaration mainClass = new CodeTypeDeclaration ("UplevelHelper");
345                         mainClass.TypeAttributes = TypeAttributes.Class |
346                                 TypeAttributes.Sealed |
347                                 TypeAttributes.NotPublic |
348                                 TypeAttributes.NestedAssembly;
349                         ns.Types.Add (mainClass);
350
351                         GenerateMethod (mainClass);
352                         return unit;
353                 }
354
355                 CodeMemberMethod GetMainMethod ()
356                 {
357                         CodeMemberMethod mainMethod = new CodeMemberMethod ();
358                         mainMethod.Name = "IsUplevel";
359                         mainMethod.ReturnType = new CodeTypeReference (typeof (bool));
360                         mainMethod.Parameters.Add (new CodeParameterDeclarationExpression (typeof (string), "ua"));
361                         mainMethod.Attributes = MemberAttributes.Public | MemberAttributes.Static | MemberAttributes.Final;
362
363                         // if (ua == null)
364                         //    return false;
365                         CodeBinaryOperatorExpression uaNull = new CodeBinaryOperatorExpression ();
366                         uaNull.Left = new CodeArgumentReferenceExpression ("ua");
367                         uaNull.Operator = CodeBinaryOperatorType.ValueEquality;
368                         uaNull.Right = new CodePrimitiveExpression (null);
369                         mainMethod.Statements.Add (new CodeConditionStatement (uaNull, returnFalse));
370       
371                         // int ualength = ua.Length;
372                         mainMethod.Statements.Add (
373                                 new CodeVariableDeclarationStatement (
374                                         typeof (int),
375                                         "ualength",
376                                         new CodePropertyReferenceExpression (new CodeArgumentReferenceExpression ("ua"), "Length"))
377                         );
378
379                         // if (ualength == 0)
380                         //    return false;
381                         CodeBinaryOperatorExpression uaEmpty = new CodeBinaryOperatorExpression ();
382                         uaEmpty.Left = new CodeVariableReferenceExpression ("ualength");
383                         uaEmpty.Operator = CodeBinaryOperatorType.ValueEquality;
384                         uaEmpty.Right = new CodePrimitiveExpression (0);
385                         mainMethod.Statements.Add (new CodeConditionStatement (uaEmpty, returnFalse));
386
387                         // bool hasJavaScript = false;
388                         mainMethod.Statements.Add (
389                                 new CodeVariableDeclarationStatement (typeof (bool), "hasJavaScript",
390                                                                       new CodePrimitiveExpression (false)));
391       
392                         return mainMethod;
393                 }
394
395                 CodeMemberMethod GetGroupMethod ()
396                 {
397                         CodeMemberMethod groupMethod = new CodeMemberMethod ();
398                         groupMethod.Name = String.Format ("DetermineUplevel_{0}_{1}", level, groupId);
399                         groupMethod.ReturnType = new CodeTypeReference (typeof (bool));
400                         groupMethod.Parameters.Add (new CodeParameterDeclarationExpression (typeof (string), "ua"));
401                         CodeParameterDeclarationExpression hasJavaScript =
402                                 new CodeParameterDeclarationExpression (typeof (bool), "hasJavaScript");
403                         
404                         hasJavaScript.Direction = FieldDirection.Out;
405                         groupMethod.Parameters.Add (hasJavaScript);
406                         groupMethod.Parameters.Add (new CodeParameterDeclarationExpression (typeof (int), "ualength"));
407                         groupMethod.Attributes = MemberAttributes.Private | MemberAttributes.Static | MemberAttributes.Final;
408
409                         // hasJavaScript = <valueOf_DefaultJS>;
410                         CodeAssignStatement assign = new CodeAssignStatement (new CodeVariableReferenceExpression ("hasJavaScript"),
411                                                                               new CodePrimitiveExpression (DefaultJS));
412                         groupMethod.Statements.Add (assign);
413       
414                         return groupMethod;
415                 }
416
417                 ArrayList GenerateExceptions (CodeTypeDeclaration mainClass, bool assignHasJavaScript)
418                 {
419                         if (exceptions == null || exceptions.Count == 0)
420                                 return null;
421
422                         ArrayList matches = new ArrayList (exceptions.Count);
423                         CodeConditionStatement match;
424       
425                         foreach (GroupDefinition gd in exceptions) {
426                                 match = gd.GenerateConditionStatement (mainClass);
427                                 matches.Add (match);
428
429                                 if (assignHasJavaScript && gd.Positional)
430                                         match.TrueStatements.Add (new CodeAssignStatement (
431                                                                           new CodeVariableReferenceExpression ("hasJavaScript"),
432                                                                           new CodePrimitiveExpression (false)));
433                                 if (!assignHasJavaScript || GroupZero)
434                                         match.TrueStatements.Add (returnFalse);
435                                 else
436                                         match.TrueStatements.Add (returnTrue);
437                         }
438
439                         return matches;
440                 }
441     
442                 CodeMemberMethod GenerateMethod (CodeTypeDeclaration mainClass)
443                 {
444                         CodeMemberMethod method;
445
446                         if (GroupZero)
447                                 method = GetMainMethod ();
448                         else
449                                 method = GetGroupMethod ();
450
451                         mainClass.Members.Add (method);
452                         CodeConditionStatement matches, subMatches;
453                         ArrayList reverseMatches;
454                         CodeMemberMethod childMethod;
455                         CodeExpression hasJSRef = GroupZero ?
456                                 (CodeExpression) new CodeVariableReferenceExpression ("hasJavaScript") :
457                                 (CodeExpression) new CodeArgumentReferenceExpression ("hasJavaScript");
458
459                         reverseMatches = GenerateExceptions (mainClass, !GroupZero);
460                         if (reverseMatches != null && reverseMatches.Count > 0)
461                                 foreach (CodeConditionStatement ccs in reverseMatches)
462                                         method.Statements.Add (ccs);
463       
464                         if (childGroups.Count > 0) {
465                                 CodeDirectionExpression hasJavaScript = new CodeDirectionExpression (FieldDirection.Out, hasJSRef);
466                                 CodeExpression ualengthRef = GroupZero ?
467                                         (CodeExpression) new CodeVariableReferenceExpression ("ualength") :
468                                         (CodeExpression) new CodeArgumentReferenceExpression ("ualength");
469
470                                 int groupId = 0;
471         
472                                 CodeMethodReturnStatement returnHasJS = new CodeMethodReturnStatement (
473                                         new CodeVariableReferenceExpression ("hasJavaScript"));
474                                 
475                                 foreach (GroupDefinition gd in childGroups) {
476                                         matches = gd.GenerateConditionStatement (mainClass);
477
478                                         if (gd.ChildGroups.Count > 0) {
479                                                 childMethod = gd.GenerateMethod (mainClass);
480
481                                                 subMatches = new CodeConditionStatement ();
482                                                 subMatches.Condition = new CodeMethodInvokeExpression (
483                                                         new CodeMethodReferenceExpression (
484                                                                 new CodeTypeReferenceExpression ("UplevelHelper"), childMethod.Name),
485                                                         new CodeExpression[] {new CodeArgumentReferenceExpression ("ua"),
486                                                                               hasJavaScript, ualengthRef}
487                                                 );
488                                                 subMatches.TrueStatements.Add (returnHasJS);
489                                                 subMatches.FalseStatements.Add (new CodeMethodReturnStatement (
490                                                                                         new CodePrimitiveExpression (false))
491                                                 );
492                                                 
493                                                 matches.TrueStatements.Add (subMatches);
494                                         } else {
495                                                 reverseMatches = gd.GenerateExceptions (mainClass, !GroupZero);
496                                                 if (reverseMatches != null && reverseMatches.Count > 0)
497                                                         foreach (CodeConditionStatement ccs in reverseMatches)
498                                                                 matches.TrueStatements.Add (ccs);
499             
500                                                 if (!GroupZero && gd.Positional)
501                                                         matches.TrueStatements.Add (
502                                                                 new CodeAssignStatement (
503                                                                         new CodeVariableReferenceExpression ("hasJavaScript"),
504                                                                         new CodePrimitiveExpression (true))
505                                                         );
506                                                 matches.TrueStatements.Add (returnTrue);
507                                         }
508                                         method.Statements.Add (matches);
509                                         groupId++;
510                                 }
511
512                                 // return false;
513                                 method.Statements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (false)));
514                         } else 
515                                 // return <valueOf_DefaultJS>
516                                 method.Statements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (DefaultJS)));
517       
518                         return method;
519                 }
520
521                 CodeConditionStatement GenerateConditionStatement (CodeTypeDeclaration mainClass)
522                 {
523                         CodeConditionStatement ret = new CodeConditionStatement ();
524
525                         if (Positional)
526                                 ret.Condition = GeneratePositionalExpression ();
527                         else
528                                 ret.Condition = GenerateScanfromExpression (mainClass);
529
530                         return ret;
531                 }
532
533                 CodeExpression GeneratePositionalExpression ()
534                 {
535                         Position[] positions = Positions;
536                         string[] matches = Matches;
537                         ArrayList components = new ArrayList ();
538
539                         int i, reqLength = 0;
540                         Position p;
541       
542                         for (i = 0; i < positions.Length; i++) {
543                                 p = positions [i];
544                                 components.Add (p.GetExpression (matches [i]));
545                                 if (p.RequiredUALength > reqLength)
546                                         reqLength = p.RequiredUALength;
547                         }
548       
549                         CodeBinaryOperatorExpression expr = null;
550
551                         int complen = components.Count;
552                         i = 0;
553
554                         if (complen == 1)
555                                 expr = components [0] as CodeBinaryOperatorExpression;
556                         else {
557                                 expr = new CodeBinaryOperatorExpression ();
558                                 CodeBinaryOperatorExpression cur = expr, prev = expr;
559                                 foreach (CodeBinaryOperatorExpression op in components) {
560                                         if (i + 1 < complen) {
561                                                 cur.Left = op;
562                                                 cur.Operator = CodeBinaryOperatorType.BooleanAnd;
563                                                 prev.Right = cur;
564                                                 prev = cur;
565                                                 cur = new CodeBinaryOperatorExpression ();
566                                         } else {
567                                                 prev.Right = op;
568                                         }
569                                         i++;
570                                 }
571                         }
572
573                         CodeBinaryOperatorExpression sizeCheck = new CodeBinaryOperatorExpression ();
574                         sizeCheck.Left = GroupZero ?
575                                 (CodeExpression) new CodeVariableReferenceExpression ("ualength") :
576                                 (CodeExpression) new CodeArgumentReferenceExpression ("ualength");
577                         sizeCheck.Operator = CodeBinaryOperatorType.GreaterThan;
578                         sizeCheck.Right = new CodePrimitiveExpression (reqLength);
579
580                         CodeBinaryOperatorExpression ret = new CodeBinaryOperatorExpression ();
581                         ret.Left = sizeCheck;
582                         ret.Operator = CodeBinaryOperatorType.BooleanAnd;
583                         ret.Right = expr;
584       
585                         return ret;
586                 }
587
588                 CodeMemberMethod GenerateScanMethod ()
589                 {
590                         CodeMemberMethod method = new CodeMemberMethod ();
591                         method.Name = String.Format ("ScanForMatch_{0}_{1}", level, groupId);
592
593                         method.ReturnType = new CodeTypeReference (typeof (bool));
594                         method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (string), "ua"));
595                         CodeParameterDeclarationExpression hasJavaScript =
596                                 new CodeParameterDeclarationExpression (typeof (bool), "hasJavaScript");
597                         
598                         hasJavaScript.Direction = FieldDirection.Out;
599                         method.Parameters.Add (hasJavaScript);
600                         method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (int), "ualength"));
601                         method.Attributes = MemberAttributes.Private | MemberAttributes.Static | MemberAttributes.Final;
602
603                         // hasJavaScript = <valueOf_DefaultJS>;
604                         CodeAssignStatement assign = new CodeAssignStatement (new CodeVariableReferenceExpression ("hasJavaScript"),
605                                                                               new CodePrimitiveExpression (DefaultJS));
606
607                         method.Statements.Add (assign);
608                         return method;
609                 }
610
611                 CodeBinaryOperatorExpression GenerateScanCondition (string match, int matchLength, int startPosition)
612                 {
613                         CodeBinaryOperatorExpression ret = new CodeBinaryOperatorExpression ();
614
615                         int endPosition = startPosition + matchLength - 1;
616                         int matchStartPosition = 0;
617                         int matchEndPosition = matchLength - 1;
618                         CodeArgumentReferenceExpression uaRef = new CodeArgumentReferenceExpression ("ua");
619       
620                         if (matchLength == 1) {
621                                 ret.Left = new CodeArrayIndexerExpression (uaRef, new CodePrimitiveExpression (startPosition));
622                                 ret.Operator = CodeBinaryOperatorType.ValueEquality;
623                                 ret.Right = new CodePrimitiveExpression (match [matchStartPosition]);
624                                 return ret;
625                         }
626
627                         CodeBinaryOperatorExpression cur = ret, prev = null, lhs, rhs, tmp;
628       
629                         if (matchLength == 2) {
630                                 lhs = new CodeBinaryOperatorExpression ();
631                                 lhs.Left = new CodeArrayIndexerExpression (uaRef, new CodePrimitiveExpression (startPosition++));
632                                 lhs.Operator = CodeBinaryOperatorType.ValueEquality;
633                                 lhs.Right = new CodePrimitiveExpression (match [matchStartPosition]);
634
635                                 rhs = new CodeBinaryOperatorExpression ();
636                                 rhs.Left = new CodeArrayIndexerExpression (uaRef, new CodePrimitiveExpression (endPosition--));
637                                 rhs.Operator = CodeBinaryOperatorType.ValueEquality;
638                                 rhs.Right = new CodePrimitiveExpression (match [matchEndPosition]);
639
640                                 ret.Left = lhs;
641                                 ret.Operator = CodeBinaryOperatorType.BooleanAnd;
642                                 ret.Right = rhs;
643
644                                 return ret;
645                         }
646
647                         bool matchOdd = matchLength % 2 != 0;
648       
649                         while (matchLength >= 0) {
650                                 matchLength--;
651                                 if (!matchOdd || (matchOdd && (matchLength - 1) > 0)) {
652                                         lhs = new CodeBinaryOperatorExpression ();
653                                         lhs.Left = new CodeArrayIndexerExpression (
654                                                 uaRef,
655                                                 new CodeBinaryOperatorExpression (
656                                                         new CodeVariableReferenceExpression ("startPosition"),
657                                                         CodeBinaryOperatorType.Add,
658                                                         new CodePrimitiveExpression (matchStartPosition))
659                                         );
660                                         lhs.Operator = CodeBinaryOperatorType.ValueEquality;
661                                         lhs.Right = new CodePrimitiveExpression (match [matchStartPosition]);
662
663                                         rhs = new CodeBinaryOperatorExpression ();
664                                         rhs.Left = new CodeArrayIndexerExpression (
665                                                 uaRef,
666                                                 new CodeBinaryOperatorExpression (
667                                                         new CodeVariableReferenceExpression ("endPosition"),
668                                                         CodeBinaryOperatorType.Subtract,
669                                                         new CodePrimitiveExpression (matchEndPosition - matchLength))
670                                         );
671           
672                                         rhs.Operator = CodeBinaryOperatorType.ValueEquality;
673                                         rhs.Right = new CodePrimitiveExpression (match [matchEndPosition]);
674
675                                         tmp = new CodeBinaryOperatorExpression (lhs, CodeBinaryOperatorType.BooleanAnd, rhs);
676                                         matchLength--;
677                                 } else {
678                                         tmp = new CodeBinaryOperatorExpression ();
679                                         tmp.Left = new CodeArrayIndexerExpression (
680                                                 uaRef,
681                                                 new CodeBinaryOperatorExpression (
682                                                         new CodeVariableReferenceExpression ("startPosition"),
683                                                         CodeBinaryOperatorType.Add,
684                                                         new CodePrimitiveExpression (matchStartPosition - 1))
685                                         );
686                                         tmp.Operator = CodeBinaryOperatorType.ValueEquality;
687                                         tmp.Right = new CodePrimitiveExpression (match [matchStartPosition - 1]);
688                                 }
689         
690                                 if (matchLength - 1 >= 0) {
691                                         cur.Left = tmp;
692                                         cur.Operator = CodeBinaryOperatorType.BooleanAnd;
693                                         if (prev != null)
694                                                 prev.Right = cur;
695                                         prev = cur;
696                                         cur = new CodeBinaryOperatorExpression ();
697                                 } else
698                                         prev.Right = tmp;
699
700                                 matchStartPosition++;
701                                 matchEndPosition--;
702                         }
703       
704                         return ret;
705                 }
706     
707                 CodeExpression GenerateScanfromExpression (CodeTypeDeclaration mainClass)
708                 {
709                         CodeMemberMethod method = GenerateScanMethod ();
710
711                         int startPosition = scanfrom + skip;
712                         string match = matches [0] as string;
713                         int matchLength = match.Length;
714                         int minsize = startPosition + matchLength + 1;
715       
716                         // if (ualength < minsize)
717                         //    return false;
718                         CodeBinaryOperatorExpression uaSizeCheck = new CodeBinaryOperatorExpression ();
719                         uaSizeCheck.Left = new CodeArgumentReferenceExpression ("ualength");
720                         uaSizeCheck.Operator = CodeBinaryOperatorType.LessThan;
721                         uaSizeCheck.Right = new CodePrimitiveExpression (minsize);
722                         method.Statements.Add (
723                                 new CodeConditionStatement (uaSizeCheck,
724                                                             new CodeMethodReturnStatement (new CodePrimitiveExpression (false))));      
725
726                         // int startPosition = 0;
727                         method.Statements.Add (
728                                 new CodeVariableDeclarationStatement (typeof (int), "startPosition",
729                                                                       new CodePrimitiveExpression (0)));
730
731                         // int endPosition = startPosition + matchLength;
732                         method.Statements.Add (
733                                 new CodeVariableDeclarationStatement (
734                                         typeof (int), "endPosition",
735                                         new CodeBinaryOperatorExpression (
736                                                 new CodeVariableReferenceExpression ("startPosition"),
737                                                 CodeBinaryOperatorType.Add,
738                                                 new CodePrimitiveExpression (matchLength - 1))
739                                 )
740                         );
741
742                         // for (int ualeft = ualength; ualeft >= matchlen; ualeft--) {
743                         //   if (<condition>) {
744                         //      hasJavaScript = true;
745                         //      return true;
746                         //   }
747                         //   startPosition++;
748                         //   endPosition++;
749                         // }
750                         CodeIterationStatement iter = new CodeIterationStatement ();
751                         iter.InitStatement = new CodeVariableDeclarationStatement (
752                                 typeof (int), "ualeft", new CodeArgumentReferenceExpression ("ualength"));
753                         iter.IncrementStatement = new CodeAssignStatement (
754                                 new CodeVariableReferenceExpression ("ualeft"),
755                                 new CodeBinaryOperatorExpression (new CodeVariableReferenceExpression ("ualeft"),
756                                                                   CodeBinaryOperatorType.Subtract,
757                                                                   new CodePrimitiveExpression (1))
758                         );
759                         iter.TestExpression = new CodeBinaryOperatorExpression (
760                                 new CodeVariableReferenceExpression ("ualeft"),
761                                 CodeBinaryOperatorType.GreaterThanOrEqual,
762                                 new CodePrimitiveExpression (matchLength)
763                         );
764
765                         CodeConditionStatement cond = new CodeConditionStatement (
766                                 GenerateScanCondition (match, matchLength, startPosition));
767                         
768                         cond.TrueStatements.Add (
769                                 new CodeAssignStatement (new CodeArgumentReferenceExpression ("hasJavaScript"),
770                                                          new CodePrimitiveExpression (true)));
771                         cond.TrueStatements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (true)));
772                         iter.Statements.Add (cond);
773                         iter.Statements.Add (
774                                 new CodeAssignStatement (new CodeVariableReferenceExpression ("startPosition"),
775                                                          new CodeBinaryOperatorExpression (
776                                                                  new CodeVariableReferenceExpression ("startPosition"),
777                                                                  CodeBinaryOperatorType.Add,
778                                                                  new CodePrimitiveExpression (1)))
779                         );
780                         iter.Statements.Add (
781                                 new CodeAssignStatement (new CodeVariableReferenceExpression ("endPosition"),
782                                                          new CodeBinaryOperatorExpression (
783                                                                  new CodeVariableReferenceExpression ("endPosition"),
784                                                                  CodeBinaryOperatorType.Add,
785                                                                  new CodePrimitiveExpression (1)))
786                         );
787                         method.Statements.Add (iter);
788                         method.Statements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (false)));
789       
790                         mainClass.Members.Add (method);
791
792                         return new CodeMethodInvokeExpression (
793                                 new CodeMethodReferenceExpression (new CodeTypeReferenceExpression ("UplevelHelper"), method.Name),
794                                 new CodeExpression[] {new CodeArgumentReferenceExpression ("ua"),
795                                                       new CodeDirectionExpression (
796                                                               FieldDirection.Out,
797                                                               new CodeArgumentReferenceExpression ("hasJavaScript")),
798                                                       new CodeArgumentReferenceExpression ("ualength")}
799                         );
800                 }
801         }
802
803         public class CompileUplevel
804         {
805                 public static void Main (string[] args)
806                 {
807                         try {
808                                 CompileUplevel cu = new CompileUplevel ();
809                                 cu.Run (args);
810                         } catch (Exception ex) {
811                                 Console.Error.WriteLine ("Exception caught while generating UplevelHelper code:");
812                                 Console.Error.Write (ex);
813                                 Console.Error.WriteLine ();
814                         }
815                 }
816
817                 void Usage (string format, params object[] parms)
818                 {
819                         if (format != null && format.Length > 0) {
820                                 Console.Error.WriteLine (format, parms);
821                                 Environment.Exit (1);
822                         }
823
824                         Console.Error.WriteLine (@"Usage: culevel [OPTIONS] INPUT_FILE
825 Options:
826     -o|--o|-output|--output    file to write the generated code to.
827                                If not specified, output goes to the console
828     -h|--h|-help|--help        show this usage information.
829 ");
830                         Environment.Exit (0);
831                 }
832
833                 void DumpGroup (GroupDefinition gd, int indent)
834                 {
835                         Console.WriteLine ("{0}{1}", new String (' ', indent), gd.ToString ());
836                         foreach (GroupDefinition gd2 in gd.ChildGroups)
837                                 DumpGroup (gd2, indent + 1);
838                 }
839     
840                 void Run (string[] args)
841                 {
842                         if (args.Length < 1)
843                                 Usage ("Invalid number of parameters");
844       
845                         Stack context = new Stack ();
846                         GroupDefinition groupZero = new GroupDefinition ();
847                         GroupDefinition group, current;
848                         XmlReader reader = null;
849                         string outfile = null, infile = null;
850                         string a;
851       
852                         for (int i = 0; i < args.Length; i++) {
853                                 a = args [i];
854                                 if (a [0] == '-' && a.Length > 1) {
855                                         a = a.Substring (1).Trim ();
856           
857                                         switch (a.ToLower ()) {
858                                                 case "o":
859                                                 case "output":
860                                                 case "-output":
861                                                         i++;
862                                                         if (i > args.Length)
863                                                                 Usage ("Missing output file name");
864                                                         outfile = args [i];
865                                                         break;
866
867                                                 case "h":
868                                                 case "help":
869                                                 case "-help":
870                                                         Usage (null);
871                                                         break;
872               
873                                                 default:
874                                                         Usage ("Unknown command line option: '{0}'", a);
875                                                         break;
876                                         }
877                                 } else if (infile == null)
878                                         infile = args [i];
879                         }
880
881                         if (infile == null)
882                                 Usage ("Missing input file on the command line.");
883       
884                         try {
885                                 XmlNodeType nodeType;
886                                 int level = 1;
887                                 bool ingroup = false;
888         
889                                 reader = new XmlTextReader (infile);
890                                 while (reader.Read ()) {
891                                         nodeType = reader.NodeType;
892                                         if (nodeType != XmlNodeType.Element && nodeType != XmlNodeType.EndElement)
893                                                 continue;
894
895                                         current = context.Count > 0 ? context.Peek () as GroupDefinition : null;
896                                         if (ingroup && reader.LocalName == "except") {
897                                                 if (current == null)
898                                                         throw new ApplicationException ("Inside a group but there is no group on the stack");
899
900                                                 current.AddExcept (reader);
901                                                 continue;
902                                         }
903             
904                                         if (reader.LocalName != "group")
905                                                 continue;
906             
907                                         if (reader.NodeType == XmlNodeType.EndElement) {
908                                                 if (current == null)
909                                                         throw new ApplicationException ("Found group end, but no current group on stack");
910                                                 context.Pop ();
911                                                 if (context.Count == 0) {
912                                                         groupZero.ChildGroups.Add (current);
913                                                         current.GroupId = groupZero.ChildGroups.Count;
914                                                 }
915                                                 level--;
916                                                 ingroup = false;
917                                                 continue;
918                                         }
919             
920                                         group = new GroupDefinition (reader);
921                                         group.Level = level++;
922             
923                                         if (current != null) {
924                                                 current.ChildGroups.Add (group);
925                                                 group.GroupId = current.ChildGroups.Count;
926                                         }
927             
928                                         context.Push (group);
929                                         ingroup = true;
930                                 }
931                         } catch (Exception) {
932                                 throw;
933                         } finally {
934                                 if (reader != null)
935                                         reader.Close();
936                         }
937
938                         CodeCompileUnit unit = groupZero.GenerateCode ();
939                         if (unit == null)
940                                 Environment.Exit (1);
941       
942                         CodeDomProvider provider = new CSharpCodeProvider ();
943                         ICodeGenerator gen = provider.CreateGenerator ();
944
945                         TextWriter tw;
946                         if (outfile == null)
947                                 tw = Console.Out;
948                         else
949                                 tw = new IndentedTextWriter (new StreamWriter (outfile, false), "\t");
950                         gen.GenerateCodeFromCompileUnit (unit, tw, new CodeGeneratorOptions ());
951                         if (outfile != null)
952                                 tw.Close ();
953                 }
954         }
955 }