evaluate Condition on property to add Properties list.
[mono.git] / mcs / class / Microsoft.Build / Microsoft.Build.Internal / ExpressionParserManual.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using Microsoft.Build.Exceptions;
5
6 namespace Microsoft.Build.Internal
7 {
8         class ExpressionParserManual
9         {
10                 public ExpressionParserManual (string source, ExpressionValidationType validationType)
11                 {
12                         this.source = source;
13                         validation_type = validationType;
14                 }
15                 
16                 string source;
17                 ExpressionValidationType validation_type;
18                 
19                 public ExpressionList Parse ()
20                 {
21                         return Parse (0, source.Length);
22                 }
23                 
24                 static readonly char [] token_starters = "$@%('\"".ToCharArray ();
25                 
26                 ExpressionList Parse (int start, int end)
27                 {
28                         if (string.IsNullOrWhiteSpace (source))
29                                 return new ExpressionList ();
30
31                         var head = new List<Expression> ();
32                         var tail = new List<Expression> ();
33                         while (start < end) {
34                                 char token = source [start];
35                                 switch (token) {
36                                 case '$':
37                                 case '@':
38                                 case '%':
39                                         if (start == end || start + 1 == source.Length || source [start + 1] != '(') {
40                                                 if (validation_type == ExpressionValidationType.StrictBoolean)
41                                                         throw new InvalidProjectFileException (string.Format ("missing '(' after '{0}' at {1} in \"{2}\"", source [start], start, source));
42                                                 else
43                                                         goto default; // treat as raw literal to the section end
44                                         }
45                                         start++;
46                                         int last = source.LastIndexOf (')', end - 1, end - start - 1);
47                                         if (last < 0) {
48                                                 if (validation_type == ExpressionValidationType.StrictBoolean)
49                                                         throw new InvalidProjectFileException (string.Format ("expression did not have matching ')' since index {0} in \"{1}\"", start, source));
50                                                 else {
51                                                         if (token != '(')
52                                                                 start--;
53                                                         goto default; // treat as raw literal to the section end
54                                                 }
55                                         }
56                                         start++;
57                                         if (token == '$')
58                                                 head.Add (EvaluatePropertyExpression (start, last));
59                                         else if (token == '%')
60                                                 head.Add (EvaluateMetadataExpression (start, last));
61                                         else
62                                                 head.Add (EvaluateItemExpression (start, last));
63                                         start = last + 1;
64                                         break;
65                                         
66                                 // Below (until default) are important only for Condition evaluation
67                                 case '(':
68                                         if (validation_type == ExpressionValidationType.LaxString)
69                                                 goto default;
70                                         start++;
71                                         last = source.LastIndexOf (')', end - 1, end - start);
72                                         if (last < 0)
73                                                 throw new InvalidProjectFileException (string.Format ("expression did not have matching ')' since index {0} in \"{1}\"", start, source));
74                                         var contents = Parse (start, last).ToArray ();
75                                         if (contents.Length > 1)
76                                                 throw new InvalidProjectFileException (string.Format ("unexpected continuous expression within (){0} in \"{1}\"", contents [1].Column > 0 ? " at " + contents [1].Column : null, source));
77                                         head.Add (contents.First ());
78                                         break;
79
80                                 default:
81                                         int idx = source.IndexOfAny (token_starters, start + 1);
82                                         string name = idx < 0 ? source.Substring (start, end - start) : source.Substring (start, idx - start);
83                                         var val = new NameToken () { Name = name };
84                                         var literal = new RawStringLiteral () { Value = val };
85                                         head.Add (new StringLiteralExpression () { Contents = new ExpressionList () { literal } });
86                                         if (idx >= 0)
87                                                 start = idx;
88                                         else
89                                                 start = end;
90
91                                         break;
92                                 }
93                                 SkipSpaces (ref start);
94                         }
95                         var ret = new ExpressionList ();
96                         foreach (var e in head.Concat (((IEnumerable<Expression>) tail).Reverse ()))
97                                 ret.Add (e);
98                         return ret;
99                 }
100                 
101                 static readonly string spaces = " \t\r\n";
102                 
103                 void SkipSpaces (ref int start)
104                 {
105                         while (start < source.Length && spaces.Contains (source [start]))
106                                 start++;
107                 }
108                 
109                 PropertyAccessExpression EvaluatePropertyExpression (int start, int end)
110                 {
111                         // member access
112                         int idx = source.LastIndexOf ('.', start);
113                         if (idx >= 0) {
114                                 string name = (idx > 0) ? source.Substring (idx, end - idx) : source.Substring (start, end);
115                                 return new PropertyAccessExpression () {
116                                         Access = new PropertyAccess () {
117                                                 Name = new NameToken () { Name = name },
118                                                 TargetType = PropertyTargetType.Object,
119                                                 Target = idx < 0 ? null : Parse (start, idx).FirstOrDefault () 
120                                                 }
121                                         };
122                         } else {
123                                 // static type access
124                                 idx = source.IndexOf ("::", start, StringComparison.Ordinal);
125                                 if (idx >= 0) {
126                                         throw new NotImplementedException ();
127                                 
128                                         string type = source.Substring (start, idx - start);
129                                         if (type.Length < 2 || type [0] != '[' || type [type.Length - 1] != ']')
130                                                 throw new InvalidProjectFileException (string.Format ("Static function call misses appropriate type name surrounded by '[' and ']' at {0} in \"{1}\"", start, source));
131                                         int start2 = idx + 2;
132                                         int idx2 = source.IndexOf ('(', idx + 2, end - start2);
133                                         if (idx2 < 0) {
134                                                 // access to static property
135                                                 string member = source.Substring (start2, end - start2);
136                                         } else {
137                                                 // access to static method
138                                                 string member = source.Substring (start2, idx2 - start2);
139                                         }
140                                 } else {
141                                         // property access without member specification
142                                         return new PropertyAccessExpression () {
143                                                 Access = new PropertyAccess () {
144                                                         Name = new NameToken () { Name = source.Substring (start, end - start) },
145                                                         TargetType = PropertyTargetType.Object
146                                                         }
147                                                 };
148                                 }
149                         }
150                 }
151                 
152                 ItemAccessExpression EvaluateItemExpression (int start, int end)
153                 {
154                         // using property as context and evaluate
155                         int idx = source.IndexOf ("->", start, StringComparison.Ordinal);
156                         if (idx > 0) {
157                                 string name = source.Substring (start, idx - start);
158                                 return new ItemAccessExpression () {
159                                         Application = new ItemApplication () {
160                                                 Name = new NameToken () { Name = name },
161                                                 Expressions = Parse (idx, end - idx)
162                                                 }
163                                         };
164                         } else {
165                                 string name = source.Substring (start, end - start);
166                                 return new ItemAccessExpression () {
167                                         Application = new ItemApplication () { Name = new NameToken () { Name = name } }
168                                         };
169                         }
170                         
171                         throw new NotImplementedException ();
172                 }
173                 
174                 MetadataAccessExpression EvaluateMetadataExpression (int start, int end)
175                 {
176                         throw new NotImplementedException ();
177                 }
178         }
179 }
180