~ubuntu-branches/ubuntu/oneiric/monodevelop/oneiric-updates

« back to all changes in this revision

Viewing changes to src/addins/CSharpBinding/Parser/ExpressionFinder.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2009-02-18 08:40:51 UTC
  • mfrom: (1.2.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20090218084051-gh8m6ukvokbwj7cf
Tags: 1.9.2+dfsg-1ubuntu1
* Merge from Debian Experimental (LP: #330519), remaining Ubuntu changes:
  + debian/control:
    - Update for Gnome# 2.24
    - Add libmono-cairo1.0-cil to build-deps to fool pkg-config check

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
//  ExpressionFinder.cs
2
 
//
3
 
//  This file was derived from a file from #Develop. 
4
 
//
5
 
//  Copyright (C) 2001-2007 Daniel Grunwald <daniel@danielgrunwald.de>
6
 
// 
7
 
//  This program is free software; you can redistribute it and/or modify
8
 
//  it under the terms of the GNU General Public License as published by
9
 
//  the Free Software Foundation; either version 2 of the License, or
10
 
//  (at your option) any later version.
11
 
// 
12
 
//  This program is distributed in the hope that it will be useful,
13
 
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 
//  GNU General Public License for more details.
16
 
//  
17
 
//  You should have received a copy of the GNU General Public License
18
 
//  along with this program; if not, write to the Free Software
19
 
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
 
 
21
 
using System;
22
 
using System.Text;
23
 
using MonoDevelop.Projects.Parser;
24
 
 
25
 
namespace CSharpBinding.Parser
26
 
{
27
 
        /// <summary>
28
 
        /// Description of ExpressionFinder.
29
 
        /// </summary>
30
 
        public class ExpressionFinder : IExpressionFinder
31
 
        {
32
 
//              string fileName;
33
 
                
34
 
                public ExpressionFinder(string fileName)
35
 
                {
36
 
//                      this.fileName = fileName;
37
 
                }
38
 
                
39
 
                #region Capture Context
40
 
                ExpressionResult CreateResult(string expression, string inText, int offset)
41
 
                {
42
 
                        if (expression == null)
43
 
                                return new ExpressionResult(null);
44
 
                        if (expression.StartsWith("using "))
45
 
                                return new ExpressionResult(expression.Substring(6).TrimStart(), ExpressionContext.Namespace, null);
46
 
                        if (!hadParenthesis && expression.StartsWith("new ")) {
47
 
                                return new ExpressionResult(expression.Substring(4).TrimStart(), GetCreationContext(), null);
48
 
                        }
49
 
                        if (IsInAttribute(inText, offset))
50
 
                                return new ExpressionResult(expression, ExpressionContext.Attribute);
51
 
                        return new ExpressionResult(expression);
52
 
                }
53
 
                
54
 
                ExpressionContext GetCreationContext()
55
 
                {
56
 
                        return null;
57
 
/*                      UnGetToken();
58
 
                        if (GetNextNonWhiteSpace() == '=') { // was: "= new"
59
 
                                ReadNextToken();
60
 
                                if (curTokenType == Ident) {     // was: "ident = new"
61
 
                                        int typeEnd = offset;
62
 
                                        ReadNextToken();
63
 
                                        int typeStart = -1;
64
 
                                        while (curTokenType == Ident) {
65
 
                                                typeStart = offset + 1;
66
 
                                                ReadNextToken();
67
 
                                                if (curTokenType == Dot) {
68
 
                                                        ReadNextToken();
69
 
                                                } else {
70
 
                                                        break;
71
 
                                                }
72
 
                                        }
73
 
                                        if (typeStart >= 0) {
74
 
                                                string className = text.Substring(typeStart, typeEnd - typeStart);
75
 
                                                int pos = className.IndexOf('<');
76
 
                                                string nonGenericClassName, genericPart;
77
 
                                                int typeParameterCount = 0;
78
 
                                                if (pos > 0) {
79
 
                                                        nonGenericClassName = className.Substring(0, pos);
80
 
                                                        genericPart = className.Substring(pos);
81
 
                                                        pos = 0;
82
 
                                                        do {
83
 
                                                                typeParameterCount += 1;
84
 
                                                                pos = genericPart.IndexOf(',', pos + 1);
85
 
                                                        } while (pos > 0);
86
 
                                                } else {
87
 
                                                        nonGenericClassName = className;
88
 
                                                        genericPart = null;
89
 
                                                }
90
 
                                                ClassFinder finder = new ClassFinder(fileName, text, typeStart);
91
 
                                                IReturnType t = finder.SearchType(nonGenericClassName, typeParameterCount);
92
 
                                                IClass c = (t != null) ? t.GetUnderlyingClass() : null;
93
 
                                                if (c != null) {
94
 
                                                        ExpressionContext context = ExpressionContext.TypeDerivingFrom(c, true);
95
 
                                                        if (context.ShowEntry(c)) {
96
 
                                                                if (genericPart != null) {
97
 
                                                                        DefaultClass genericClass = new DefaultClass(c.CompilationUnit, c.ClassType, c.Modifiers, c.Region, c.DeclaringType);
98
 
                                                                        genericClass.FullyQualifiedName = c.FullyQualifiedName + genericPart;
99
 
                                                                        genericClass.Documentation = c.Documentation;
100
 
                                                                        context.SuggestedItem = genericClass;
101
 
                                                                } else {
102
 
                                                                        context.SuggestedItem = c;
103
 
                                                                }
104
 
                                                        }
105
 
                                                        return context;
106
 
                                                }
107
 
                                        }
108
 
                                }
109
 
                        } else {
110
 
                                UnGet();
111
 
                                ReadNextToken();
112
 
                                if (curTokenType == Ident && lastIdentifier == "throw") {
113
 
                                        return ExpressionContext.TypeDerivingFrom(ProjectContentRegistry.Mscorlib.GetClass("System.Exception"), true);
114
 
                                }
115
 
                        }
116
 
                        return ExpressionContext.ObjectCreation;
117
 
*/
118
 
                }
119
 
                
120
 
                bool IsInAttribute(string txt, int offset)
121
 
                {
122
 
                        // Get line start:
123
 
                        int lineStart = offset;
124
 
                        while (--lineStart > 0 && txt[lineStart] != '\n')
125
 
                        {}
126
 
                        
127
 
                        bool inAttribute = false;
128
 
                        int parens = 0;
129
 
                        for (int i = lineStart + 1; i < offset; i++) {
130
 
                                char ch = txt[i];
131
 
                                if (char.IsWhiteSpace(ch))
132
 
                                        continue;
133
 
                                if (!inAttribute) {
134
 
                                        // outside attribute
135
 
                                        if (ch == '[')
136
 
                                                inAttribute = true;
137
 
                                        else
138
 
                                                return false;
139
 
                                } else if (parens == 0) {
140
 
                                        // inside attribute, outside parameter list
141
 
                                        if (ch == ']')
142
 
                                                inAttribute = false;
143
 
                                        else if (ch == '(')
144
 
                                                parens = 1;
145
 
                                        else if (!char.IsLetterOrDigit(ch) && ch != ',')
146
 
                                                return false;
147
 
                                } else {
148
 
                                        // inside attribute, inside parameter list
149
 
                                        if (ch == '(')
150
 
                                                parens++;
151
 
                                        else if (ch == ')')
152
 
                                                parens--;
153
 
                                }
154
 
                        }
155
 
                        return inAttribute && parens == 0;
156
 
                }
157
 
                #endregion
158
 
                
159
 
                #region RemoveLastPart
160
 
                /// <summary>
161
 
                /// Removed the last part of the expression.
162
 
                /// </summary>
163
 
                /// <example>
164
 
                /// "arr[i]" => "arr"
165
 
                /// "obj.Field" => "obj"
166
 
                /// "obj.Method(args,...)" => "obj.Method"
167
 
                /// </example>
168
 
                public string RemoveLastPart(string expression)
169
 
                {
170
 
                        text = expression;
171
 
                        offset = text.Length - 1;
172
 
                        ReadNextToken();
173
 
                        if (curTokenType == Ident && Peek() == '.')
174
 
                                GetNext();
175
 
                        return text.Substring(0, offset + 1);
176
 
                }
177
 
                #endregion
178
 
                
179
 
                #region Find Expression
180
 
                public ExpressionResult FindExpression(string inText, int offset)
181
 
                {
182
 
                        inText = FilterComments(inText, ref offset);
183
 
                        return CreateResult(FindExpressionInternal(inText, offset), inText, offset);
184
 
                }
185
 
                
186
 
                public string FindExpressionInternal(string inText, int offset)
187
 
                {
188
 
                        // warning: Do not confuse this.offset and offset
189
 
                        this.text = inText;
190
 
                        this.offset = this.lastAccept = offset;
191
 
                        this.state  = START;
192
 
                        hadParenthesis = false;
193
 
                        if (this.text == null) {
194
 
                                return null;
195
 
                        }
196
 
                        
197
 
                        while (state != ERROR) {
198
 
                                ReadNextToken();
199
 
                                state = stateTable[state, curTokenType];
200
 
                                
201
 
                                if (state == ACCEPT || state == ACCEPT2) {
202
 
                                        lastAccept = this.offset;
203
 
                                }
204
 
                                if (state == ACCEPTNOMORE) {
205
 
                                        lastExpressionStartPosition = this.offset + 1;
206
 
                                        return this.text.Substring(this.offset + 1, offset - this.offset);
207
 
                                }
208
 
                        }
209
 
                        
210
 
                        if (lastAccept < 0)
211
 
                                return null;
212
 
                        
213
 
                        lastExpressionStartPosition = this.lastAccept + 1;
214
 
                        
215
 
                        return this.text.Substring(this.lastAccept + 1, offset - this.lastAccept);
216
 
                }
217
 
                
218
 
                int lastExpressionStartPosition;
219
 
                
220
 
                internal int LastExpressionStartPosition {
221
 
                        get {
222
 
                                return lastExpressionStartPosition;
223
 
                        }
224
 
                }
225
 
                #endregion
226
 
                
227
 
                #region FindFullExpression
228
 
                public ExpressionResult FindFullExpression(string inText, int offset)
229
 
                {
230
 
                        if (inText == null)
231
 
                                return new ExpressionResult (null);
232
 
                        int offsetWithoutComments = offset;
233
 
                        string textWithoutComments = FilterComments(inText, ref offsetWithoutComments);
234
 
                        string expressionBeforeOffset = FindExpressionInternal(textWithoutComments, offsetWithoutComments);
235
 
                        if (expressionBeforeOffset == null || expressionBeforeOffset.Length == 0)
236
 
                                return CreateResult(null, textWithoutComments, offsetWithoutComments);
237
 
                        StringBuilder b = new StringBuilder(expressionBeforeOffset);
238
 
                        // append characters after expression
239
 
                        bool wordFollowing = false;
240
 
                        int i;
241
 
                        for (i = offset + 1; i < inText.Length; ++i) {
242
 
                                char c = inText[i];
243
 
                                if (Char.IsLetterOrDigit(c) || c == '_') {
244
 
                                        if (Char.IsWhiteSpace(inText, i - 1)) {
245
 
                                                wordFollowing = true;
246
 
                                                break;
247
 
                                        }
248
 
                                        b.Append(c);
249
 
                                } else if (Char.IsWhiteSpace(c)) {
250
 
                                        // ignore whitespace
251
 
                                } else if (c == '(' || c == '[') {
252
 
                                        int otherBracket = SearchBracketForward(inText, i + 1, c, (c == '(') ? ')' : ']');
253
 
                                        if (otherBracket < 0)
254
 
                                                break;
255
 
                                        if (c == '[') {
256
 
                                                // do not include [] when it is an array declaration (versus indexer call)
257
 
                                                bool ok = false;
258
 
                                                for (int j = i + 1; j < otherBracket; j++) {
259
 
                                                        if (inText[j] != ',' && !char.IsWhiteSpace(inText, j)) {
260
 
                                                                ok = true;
261
 
                                                                break;
262
 
                                                        }
263
 
                                                }
264
 
                                                if (!ok) {
265
 
                                                        break;
266
 
                                                }
267
 
                                        }
268
 
                                        b.Append(inText, i, otherBracket - i + 1);
269
 
                                        break;
270
 
                                } else if (c == '<') {
271
 
                                        // accept only if this is a generic type reference
272
 
                                        int typeParameterEnd = FindEndOfTypeParameters(inText, i);
273
 
                                        if (typeParameterEnd < 0)
274
 
                                                break;
275
 
                                        b.Append(inText, i, typeParameterEnd - i + 1);
276
 
                                        i = typeParameterEnd;
277
 
                                } else {
278
 
                                        break;
279
 
                                }
280
 
                        }
281
 
                        ExpressionResult res = CreateResult(b.ToString(), textWithoutComments, offsetWithoutComments);
282
 
                        if (res.Context == ExpressionContext.Default && wordFollowing) {
283
 
                                b = new StringBuilder();
284
 
                                for (; i < inText.Length; ++i) {
285
 
                                        char c = inText[i];
286
 
                                        if (char.IsLetterOrDigit(c) || c == '_')
287
 
                                                b.Append(c);
288
 
                                        else
289
 
                                                break;
290
 
                                }
291
 
                                if (b.Length > 0) {
292
 
                                        if (ICSharpCode.NRefactory.Parser.CSharp.Keywords.GetToken(b.ToString()) < 0) {
293
 
                                                res.Context = ExpressionContext.Type;
294
 
                                        }
295
 
                                }
296
 
                        }
297
 
                        return res;
298
 
                }
299
 
                
300
 
                int FindEndOfTypeParameters(string inText, int offset)
301
 
                {
302
 
                        int level = 0;
303
 
                        for (int i = offset; i < inText.Length; ++i) {
304
 
                                char c = inText[i];
305
 
                                if (Char.IsLetterOrDigit(c) || Char.IsWhiteSpace(c)) {
306
 
                                        // ignore identifiers and whitespace
307
 
                                } else if (c == ',' || c == '?' || c == '[' || c == ']') {
308
 
                                        // ,  : seperating generic type parameters
309
 
                                        // ?  : nullable types
310
 
                                        // [] : arrays
311
 
                                } else if (c == '<') {
312
 
                                        ++level;
313
 
                                } else if (c == '>') {
314
 
                                        --level;
315
 
                                } else {
316
 
                                        return -1;
317
 
                                }
318
 
                                if (level == 0)
319
 
                                        return i;
320
 
                        }
321
 
                        return -1;
322
 
                }
323
 
                #endregion
324
 
                
325
 
                #region SearchBracketForward
326
 
                // like CSharpFormattingStrategy.SearchBracketForward, but operates on a string.
327
 
                private int SearchBracketForward(string text, int offset, char openBracket, char closingBracket)
328
 
                {
329
 
                        bool inString = false;
330
 
                        bool inChar   = false;
331
 
                        bool verbatim = false;
332
 
                        
333
 
                        bool lineComment  = false;
334
 
                        bool blockComment = false;
335
 
                        
336
 
                        if (offset < 0) return -1;
337
 
                        
338
 
                        int brackets = 1;
339
 
                        
340
 
                        for (; offset < text.Length; ++offset) {
341
 
                                char ch = text[offset];
342
 
                                switch (ch) {
343
 
                                        case '\r':
344
 
                                        case '\n':
345
 
                                                lineComment = false;
346
 
                                                inChar = false;
347
 
                                                if (!verbatim) inString = false;
348
 
                                                break;
349
 
                                        case '/':
350
 
                                                if (blockComment) {
351
 
                                                        if (offset > 0 && text[offset - 1] == '*') {
352
 
                                                                blockComment = false;
353
 
                                                        }
354
 
                                                }
355
 
                                                if (!inString && !inChar && offset + 1 < text.Length) {
356
 
                                                        if (!blockComment && text[offset + 1] == '/') {
357
 
                                                                lineComment = true;
358
 
                                                        }
359
 
                                                        if (!lineComment && text[offset + 1] == '*') {
360
 
                                                                blockComment = true;
361
 
                                                        }
362
 
                                                }
363
 
                                                break;
364
 
                                        case '"':
365
 
                                                if (!(inChar || lineComment || blockComment)) {
366
 
                                                        if (inString && verbatim) {
367
 
                                                                if (offset + 1 < text.Length && text[offset + 1] == '"') {
368
 
                                                                        ++offset; // skip escaped quote
369
 
                                                                        inString = false; // let the string go on
370
 
                                                                } else {
371
 
                                                                        verbatim = false;
372
 
                                                                }
373
 
                                                        } else if (!inString && offset > 0 && text[offset - 1] == '@') {
374
 
                                                                verbatim = true;
375
 
                                                        }
376
 
                                                        inString = !inString;
377
 
                                                }
378
 
                                                break;
379
 
                                        case '\'':
380
 
                                                if (!(inString || lineComment || blockComment)) {
381
 
                                                        inChar = !inChar;
382
 
                                                }
383
 
                                                break;
384
 
                                        case '\\':
385
 
                                                if ((inString && !verbatim) || inChar)
386
 
                                                        ++offset; // skip next character
387
 
                                                break;
388
 
                                        default:
389
 
                                                if (ch == openBracket) {
390
 
                                                        if (!(inString || inChar || lineComment || blockComment)) {
391
 
                                                                ++brackets;
392
 
                                                        }
393
 
                                                } else if (ch == closingBracket) {
394
 
                                                        if (!(inString || inChar || lineComment || blockComment)) {
395
 
                                                                --brackets;
396
 
                                                                if (brackets == 0) {
397
 
                                                                        return offset;
398
 
                                                                }
399
 
                                                        }
400
 
                                                }
401
 
                                                break;
402
 
                                }
403
 
                        }
404
 
                        return -1;
405
 
                }
406
 
                #endregion
407
 
                
408
 
                #region Comment Filter and 'inside string watcher'
409
 
                int initialOffset;
410
 
                public string FilterComments(string text, ref int offset)
411
 
                {
412
 
                        if (text == null || text.Length <= offset)
413
 
                                return null;
414
 
                        this.initialOffset = offset;
415
 
                        StringBuilder result = new StringBuilder();
416
 
                        int curOffset = 0;
417
 
                        
418
 
                        while (curOffset <= initialOffset) {
419
 
                                char ch = text[curOffset];
420
 
                                
421
 
                                switch (ch) {
422
 
                                        case '@':
423
 
                                                if (curOffset + 1 < text.Length && text[curOffset + 1] == '"') {
424
 
                                                        result.Append(text[curOffset++]); // @
425
 
                                                        result.Append(text[curOffset++]); // "
426
 
                                                        if (!ReadVerbatimString(result, text, ref curOffset)) {
427
 
                                                                return null;
428
 
                                                        }
429
 
                                                }else{
430
 
                                                        result.Append(ch);
431
 
                                                        ++curOffset;
432
 
                                                }
433
 
                                                break;
434
 
                                        case '\'':
435
 
                                                result.Append(ch);
436
 
                                                curOffset++;
437
 
                                                if(! ReadChar(result, text, ref curOffset)) {
438
 
                                                        return null;
439
 
                                                }
440
 
                                                break;
441
 
                                        case '"':
442
 
                                                result.Append(ch);
443
 
                                                curOffset++;
444
 
                                                if (!ReadString(result, text, ref curOffset)) {
445
 
                                                        return null;
446
 
                                                }
447
 
                                                break;
448
 
                                        case '/':
449
 
                                                if (curOffset + 1 < text.Length && text[curOffset + 1] == '/') {
450
 
                                                        offset    -= 2;
451
 
                                                        curOffset += 2;
452
 
                                                        if (!ReadToEOL(text, ref curOffset, ref offset)) {
453
 
                                                                return null;
454
 
                                                        }
455
 
                                                } else if (curOffset + 1 < text.Length && text[curOffset + 1] == '*') {
456
 
                                                        offset    -= 2;
457
 
                                                        curOffset += 2;
458
 
                                                        if (!ReadMultiLineComment(text, ref curOffset, ref offset)) {
459
 
                                                                return null;
460
 
                                                        }
461
 
                                                } else {
462
 
                                                        goto default;
463
 
                                                }
464
 
                                                break;
465
 
                                        case '#':
466
 
                                                if (!ReadToEOL(text, ref curOffset, ref offset)) {
467
 
                                                        return null;
468
 
                                                }
469
 
                                                break;
470
 
                                        default:
471
 
                                                result.Append(ch);
472
 
                                                ++curOffset;
473
 
                                                break;
474
 
                                }
475
 
                        }
476
 
                        
477
 
                        return result.ToString();
478
 
                }
479
 
                
480
 
                bool ReadToEOL(string text, ref int curOffset, ref int offset)
481
 
                {
482
 
                        while (curOffset <= initialOffset) {
483
 
                                char ch = text[curOffset++];
484
 
                                --offset;
485
 
                                if (ch == '\n') {
486
 
                                        return true;
487
 
                                }
488
 
                        }
489
 
                        return false;
490
 
                }
491
 
                
492
 
                bool ReadChar(StringBuilder outText, string text, ref int curOffset)
493
 
                {
494
 
                        if (curOffset > initialOffset)
495
 
                                return false;
496
 
                        char first = text[curOffset++];
497
 
                        outText.Append(first);
498
 
                        if (curOffset > initialOffset)
499
 
                                return false;
500
 
                        char second = text[curOffset++];
501
 
                        outText.Append(second);
502
 
                        if (first == '\\') {
503
 
                                // character is escape sequence, so read one char more
504
 
                                char next;
505
 
                                do {
506
 
                                        if (curOffset > initialOffset)
507
 
                                                return false;
508
 
                                        next = text[curOffset++];
509
 
                                        outText.Append(next);
510
 
                                        // unicode or hexadecimal character literals can have more content characters
511
 
                                } while((second == 'u' || second == 'x') && char.IsLetterOrDigit(next));
512
 
                        }
513
 
                        return text[curOffset - 1] == '\'';
514
 
                }
515
 
                
516
 
                bool ReadString(StringBuilder outText, string text, ref int curOffset)
517
 
                {
518
 
                        while (curOffset <= initialOffset) {
519
 
                                char ch = text[curOffset++];
520
 
                                outText.Append(ch);
521
 
                                if (ch == '"') {
522
 
                                        return true;
523
 
                                } else if (ch == '\\') {
524
 
                                        if (curOffset <= initialOffset)
525
 
                                                outText.Append(text[curOffset++]);
526
 
                                }
527
 
                        }
528
 
                        return false;
529
 
                }
530
 
                
531
 
                bool ReadVerbatimString(StringBuilder outText, string text, ref int curOffset)
532
 
                {
533
 
                        while (curOffset <= initialOffset) {
534
 
                                char ch = text[curOffset++];
535
 
                                outText.Append(ch);
536
 
                                if (ch == '"') {
537
 
                                        if (curOffset < text.Length && text[curOffset] == '"') {
538
 
                                                outText.Append(text[curOffset++]);
539
 
                                        } else {
540
 
                                                return true;
541
 
                                        }
542
 
                                }
543
 
                        }
544
 
                        return false;
545
 
                }
546
 
                
547
 
                bool ReadMultiLineComment(string text, ref int curOffset, ref int offset)
548
 
                {
549
 
                        while (curOffset <= initialOffset) {
550
 
                                char ch = text[curOffset++];
551
 
                                --offset;
552
 
                                if (ch == '*') {
553
 
                                        if (curOffset < text.Length && text[curOffset] == '/') {
554
 
                                                ++curOffset;
555
 
                                                --offset;
556
 
                                                return true;
557
 
                                        }
558
 
                                }
559
 
                        }
560
 
                        return false;
561
 
                }
562
 
                #endregion
563
 
                
564
 
                #region mini backward lexer
565
 
                string text;
566
 
                int    offset;
567
 
                
568
 
                char GetNext()
569
 
                {
570
 
                        if (offset >= 0) {
571
 
                                return text[offset--];
572
 
                        }
573
 
                        return '\0';
574
 
                }
575
 
                
576
 
                char GetNextNonWhiteSpace()
577
 
                {
578
 
                        char ch;
579
 
                        do {
580
 
                                ch = GetNext();
581
 
                        } while (char.IsWhiteSpace(ch));
582
 
                        return ch;
583
 
                }
584
 
                
585
 
                char Peek(int n)
586
 
                {
587
 
                        if (offset - n >= 0) {
588
 
                                return text[offset - n];
589
 
                        }
590
 
                        return '\0';
591
 
                }
592
 
                
593
 
                char Peek()
594
 
                {
595
 
                        if (offset >= 0) {
596
 
                                return text[offset];
597
 
                        }
598
 
                        return '\0';
599
 
                }
600
 
                
601
 
/*              void UnGet()
602
 
                {
603
 
                        ++offset;
604
 
                }
605
 
                
606
 
                void UnGetToken()
607
 
                {
608
 
                        do {
609
 
                                UnGet();
610
 
                        } while (char.IsLetterOrDigit(Peek()));
611
 
                }
612
 
*/              
613
 
                // tokens for our lexer
614
 
                static int Err     = 0;
615
 
                static int Dot     = 1;
616
 
                static int StrLit  = 2;
617
 
                static int Ident   = 3;
618
 
                static int New     = 4;
619
 
                static int Bracket = 5;
620
 
                static int Parent  = 6;
621
 
                static int Curly   = 7;
622
 
                static int Using   = 8;
623
 
                static int Digit   = 9;
624
 
                int curTokenType;
625
 
                
626
 
                readonly static string[] tokenStateName = new string[] {
627
 
                        "Err", "Dot", "StrLit", "Ident", "New", "Bracket", "Paren", "Curly", "Using", "Digit"
628
 
                };
629
 
                
630
 
                /// <summary>
631
 
                /// used to control whether an expression is in a ObjectCreation context (new *expr*),
632
 
                /// or is in the default context (e.g. "new MainForm().Show()", 'new ' is there part of the expression
633
 
                /// </summary>
634
 
                bool hadParenthesis;
635
 
                
636
 
                string lastIdentifier;
637
 
                
638
 
                void ReadNextToken()
639
 
                {
640
 
                        curTokenType = Err;
641
 
                        char ch = GetNextNonWhiteSpace();
642
 
                        if (ch == '\0') {
643
 
                                return;
644
 
                        }
645
 
                        
646
 
                        switch (ch) {
647
 
                                case '}':
648
 
                                        if (ReadBracket('{', '}')) {
649
 
                                                curTokenType = Curly;
650
 
                                        }
651
 
                                        break;
652
 
                                case ')':
653
 
                                        if (ReadBracket('(', ')')) {
654
 
                                                hadParenthesis = true;
655
 
                                                curTokenType = Parent;
656
 
                                        }
657
 
                                        break;
658
 
                                case ']':
659
 
                                        if (ReadBracket('[', ']')) {
660
 
                                                curTokenType = Bracket;
661
 
                                        }
662
 
                                        break;
663
 
                                case '>':
664
 
                                        if (ReadTypeParameters()) {
665
 
                                                // hack: ignore type parameters and continue reading without changing state
666
 
                                                ReadNextToken();
667
 
                                        }
668
 
                                        break;
669
 
                                case '.':
670
 
                                        curTokenType = Dot;
671
 
                                        break;
672
 
                                case ':':
673
 
                                        if (GetNext() == ':') {
674
 
                                                // treat :: like dot
675
 
                                                curTokenType = Dot;
676
 
                                        }
677
 
                                        break;
678
 
                                case '\'':
679
 
                                case '"':
680
 
                                        if (ReadStringLiteral(ch)) {
681
 
                                                curTokenType = StrLit;
682
 
                                        }
683
 
                                        break;
684
 
                                default:
685
 
                                        if (IsNumber(ch)) {
686
 
                                                ReadDigit(ch);
687
 
                                                curTokenType = Digit;
688
 
                                        } else if (IsIdentifierPart(ch)) {
689
 
                                                string ident = ReadIdentifier(ch);
690
 
                                                if (ident != null) {
691
 
                                                        switch (ident) {
692
 
                                                                case "new":
693
 
                                                                        curTokenType = New;
694
 
                                                                        break;
695
 
                                                                case "using":
696
 
                                                                        curTokenType = Using;
697
 
                                                                        break;
698
 
                                                                case "return":
699
 
                                                                case "throw":
700
 
                                                                case "in":
701
 
                                                                case "else":
702
 
                                                                        // treat as error / end of expression
703
 
                                                                        break;
704
 
                                                                default:
705
 
                                                                        curTokenType = Ident;
706
 
                                                                        lastIdentifier = ident;
707
 
                                                                        break;
708
 
                                                        }
709
 
                                                }
710
 
                                        }
711
 
                                        
712
 
                                        break;
713
 
                        }
714
 
                }
715
 
                bool IsNumber(char ch)
716
 
                {
717
 
                        if (!Char.IsDigit(ch))
718
 
                                return false;
719
 
                        int n = 0;
720
 
                        while (true) {
721
 
                                ch = Peek(n);
722
 
                                if (Char.IsDigit(ch)) {
723
 
                                        n++;
724
 
                                        continue;
725
 
                                }
726
 
                                return n > 0 && !Char.IsLetter(ch);
727
 
                        }
728
 
                }
729
 
                bool ReadStringLiteral(char litStart)
730
 
                {
731
 
                        while (true) {
732
 
                                char ch = GetNext();
733
 
                                if (ch == '\0') {
734
 
                                        return false;
735
 
                                }
736
 
                                if (ch == litStart) {
737
 
                                        if (Peek() == '@' && litStart == '"') {
738
 
                                                GetNext();
739
 
                                        }
740
 
                                        return true;
741
 
                                }
742
 
                        }
743
 
                }
744
 
                
745
 
                bool ReadTypeParameters()
746
 
                {
747
 
                        int level = 1;
748
 
                        while (level > 0) {
749
 
                                char ch = GetNext();
750
 
                                switch (ch) {
751
 
                                        case '?':
752
 
                                        case '[':
753
 
                                        case ',':
754
 
                                        case ']':
755
 
                                                break;
756
 
                                        case '<':
757
 
                                                --level;
758
 
                                                break;
759
 
                                        case '>':
760
 
                                                ++level;
761
 
                                                break;
762
 
                                        default:
763
 
                                                if (!char.IsWhiteSpace(ch) && !char.IsLetterOrDigit(ch))
764
 
                                                        return false;
765
 
                                                break;
766
 
                                }
767
 
                        }
768
 
                        return true;
769
 
                }
770
 
                
771
 
                bool ReadBracket(char openBracket, char closingBracket)
772
 
                {
773
 
                        int curlyBraceLevel    = 0;
774
 
                        int squareBracketLevel = 0;
775
 
                        int parenthesisLevel   = 0;
776
 
                        switch (openBracket) {
777
 
                                case '(':
778
 
                                        parenthesisLevel++;
779
 
                                        break;
780
 
                                case '[':
781
 
                                        squareBracketLevel++;
782
 
                                        break;
783
 
                                case '{':
784
 
                                        curlyBraceLevel++;
785
 
                                        break;
786
 
                        }
787
 
                        
788
 
                        while (parenthesisLevel != 0 || squareBracketLevel != 0 || curlyBraceLevel != 0) {
789
 
                                char ch = GetNext();
790
 
                                switch (ch) {
791
 
                                        case '\0':
792
 
                                                return false;
793
 
                                        case '(':
794
 
                                                parenthesisLevel--;
795
 
                                                break;
796
 
                                        case '[':
797
 
                                                squareBracketLevel--;
798
 
                                                break;
799
 
                                        case '{':
800
 
                                                curlyBraceLevel--;
801
 
                                                break;
802
 
                                        case ')':
803
 
                                                parenthesisLevel++;
804
 
                                                break;
805
 
                                        case ']':
806
 
                                                squareBracketLevel++;
807
 
                                                break;
808
 
                                        case '}':
809
 
                                                curlyBraceLevel++;
810
 
                                                break;
811
 
                                }
812
 
                        }
813
 
                        return true;
814
 
                }
815
 
                
816
 
                string ReadIdentifier(char ch)
817
 
                {
818
 
                        string identifier = ch.ToString();
819
 
                        while (IsIdentifierPart(Peek())) {
820
 
                                identifier = GetNext() + identifier;
821
 
                        }
822
 
                        return identifier;
823
 
                }
824
 
                
825
 
                void ReadDigit(char ch)
826
 
                {
827
 
                        //string digit = ch.ToString();
828
 
                        while (Char.IsDigit(Peek()) || Peek() == '.') {
829
 
                                GetNext();
830
 
                                //digit = GetNext() + digit;
831
 
                        }
832
 
                        //return digit;
833
 
                }
834
 
                
835
 
                bool IsIdentifierPart(char ch)
836
 
                {
837
 
                        return Char.IsLetterOrDigit(ch) || ch == '_' || ch == '@';
838
 
                }
839
 
                #endregion
840
 
                
841
 
                #region finite state machine
842
 
                readonly static int ERROR  = 0;
843
 
                readonly static int START  = 1;
844
 
                readonly static int DOT    = 2;
845
 
                readonly static int MORE   = 3;
846
 
                readonly static int CURLY  = 4;
847
 
                readonly static int CURLY2 = 5;
848
 
                readonly static int CURLY3 = 6;
849
 
                
850
 
                readonly static int ACCEPT = 7;
851
 
                readonly static int ACCEPTNOMORE = 8;
852
 
                readonly static int ACCEPT2 = 9;
853
 
                
854
 
                readonly static string[] stateName = new string[] {
855
 
                        "ERROR",
856
 
                        "START",
857
 
                        "DOT",
858
 
                        "MORE",
859
 
                        "CURLY",
860
 
                        "CURLY2",
861
 
                        "CURLY3",
862
 
                        "ACCEPT",
863
 
                        "ACCEPTNOMORE",
864
 
                        "ACCEPT2"
865
 
                };
866
 
                
867
 
                int state = 0;
868
 
                int lastAccept = 0;
869
 
                static int[,] stateTable = new int[,] {
870
 
                        //                   Err,     Dot,     Str,      ID,         New,     Brk,     Par,     Cur,   Using,       digit
871
 
                        /*ERROR*/        { ERROR,   ERROR,   ERROR,   ERROR,        ERROR,  ERROR,   ERROR,   ERROR,   ERROR,        ERROR},
872
 
                        /*START*/        { ERROR,     DOT,  ACCEPT,  ACCEPT,        ERROR,   MORE, ACCEPT2,   CURLY,   ACCEPTNOMORE, ERROR},
873
 
                        /*DOT*/          { ERROR,   ERROR,  ACCEPT,  ACCEPT,        ERROR,   MORE,  ACCEPT,   CURLY,   ERROR,        ACCEPT},
874
 
                        /*MORE*/         { ERROR,   ERROR,  ACCEPT,  ACCEPT,        ERROR,   MORE, ACCEPT2,   CURLY,   ERROR,        ACCEPT},
875
 
                        /*CURLY*/        { ERROR,   ERROR,   ERROR,   ERROR,        ERROR, CURLY2,   ERROR,   ERROR,   ERROR,        ERROR},
876
 
                        /*CURLY2*/       { ERROR,   ERROR,   ERROR,  CURLY3,        ERROR,  ERROR,   ERROR,   ERROR,   ERROR,        CURLY3},
877
 
                        /*CURLY3*/       { ERROR,   ERROR,   ERROR,   ERROR, ACCEPTNOMORE,  ERROR,   ERROR,   ERROR,   ERROR,        ERROR},
878
 
                        /*ACCEPT*/       { ERROR,    MORE,   ERROR,   ERROR,       ACCEPT,  ERROR,   ERROR,   ERROR,   ACCEPTNOMORE, ERROR},
879
 
                        /*ACCEPTNOMORE*/ { ERROR,   ERROR,   ERROR,   ERROR,        ERROR,  ERROR,   ERROR,   ERROR,   ERROR,        ERROR},
880
 
                        /*ACCEPT2*/      { ERROR,    MORE,   ERROR,  ACCEPT,       ACCEPT,  ERROR,   ERROR,   ERROR,   ERROR,        ACCEPT},
881
 
                };
882
 
                #endregion
883
 
        }
884
 
}