~ricotz/valide/valide-svgicons

« back to all changes in this revision

Viewing changes to plugins/completion/valencia-provider/valencia/scanner.vala

  • Committer: gege2061
  • Date: 2010-09-03 21:40:48 UTC
  • Revision ID: svn-v4:35bcdfa6-b98f-11dd-bba1-afcbec1a1e1f:trunk:687
Use Afrodite for completion

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2009-2010 Yorba Foundation
 
2
 *
 
3
 * This software is licensed under the GNU Lesser General Public License
 
4
 * (version 2.1 or later).  See the COPYING file in this distribution. 
 
5
 */
 
6
 
 
7
namespace Valencia {
 
8
 
 
9
enum Token {
 
10
    NONE,
 
11
    EOF,
 
12
    CHAR,    // an unrecognized punctuation character
 
13
    CHAR_LITERAL,    // a literal such as 'x'
 
14
    STRING_LITERAL,
 
15
    ID,
 
16
    
 
17
    // punctuation
 
18
    ASTERISK, LEFT_BRACE, RIGHT_BRACE, LEFT_BRACKET, RIGHT_BRACKET, COLON, COMMA, EQUALS, ELLIPSIS,
 
19
    HASH, LEFT_PAREN, RIGHT_PAREN, PERIOD, QUESTION_MARK, SEMICOLON, LESS_THAN, GREATER_THAN,
 
20
 
 
21
    // keywords
 
22
    ABSTRACT, ASYNC, BASE, CLASS, CONST, CONSTRUCT, DELEGATE, ELSE, ENUM, EXTERN, FOR, FOREACH, IF,
 
23
    INLINE, INTERFACE, INTERNAL, NAMESPACE, NEW, OUT, OVERRIDE, OWNED, PRIVATE, PROTECTED, PUBLIC,
 
24
    REF, RETURN, SIGNAL, STATIC, STRUCT, THIS, UNOWNED, USING, VIRTUAL, WEAK, WHILE
 
25
}
 
26
 
 
27
struct Keyword {
 
28
    public string name;
 
29
    public Token token;
 
30
}
 
31
 
 
32
const Keyword[] keywords = {
 
33
    { "abstract", Token.ABSTRACT },
 
34
    { "async", Token.ASYNC },
 
35
    { "base", Token.BASE },
 
36
    { "class", Token.CLASS },
 
37
    { "const", Token.CONST },
 
38
    { "construct", Token.CONSTRUCT },
 
39
    { "delegate", Token.DELEGATE },
 
40
    { "else", Token.ELSE }, 
 
41
    { "enum", Token.ENUM },
 
42
    { "extern", Token.EXTERN },
 
43
    { "for", Token.FOR },
 
44
    { "foreach", Token.FOREACH },
 
45
    { "if", Token.IF },
 
46
    { "inline", Token.INLINE },
 
47
    { "interface", Token.INTERFACE },
 
48
    { "internal", Token.INTERNAL },
 
49
    { "namespace", Token.NAMESPACE },
 
50
    { "new", Token.NEW },
 
51
    { "out", Token.OUT },
 
52
    { "override", Token.OVERRIDE },
 
53
    { "owned", Token.OWNED },
 
54
    { "private", Token.PRIVATE },
 
55
    { "protected", Token.PROTECTED },
 
56
    { "public", Token.PUBLIC },
 
57
    { "ref", Token.REF },
 
58
    { "return", Token.RETURN },
 
59
    { "signal", Token.SIGNAL },
 
60
    { "static", Token.STATIC },
 
61
    { "struct", Token.STRUCT },
 
62
    { "this", Token.THIS },
 
63
    { "unowned", Token.UNOWNED },
 
64
    { "using", Token.USING },
 
65
    { "virtual", Token.VIRTUAL },
 
66
    { "weak", Token.WEAK },
 
67
    { "while", Token.WHILE }
 
68
};
 
69
 
 
70
class Scanner : Object {
 
71
    // The lookahead token.  If not NONE, it extends from characters (token_start_char) to (input),
 
72
    // and from positions (token_start) to (input_pos).
 
73
    Token token = Token.NONE;
 
74
    
 
75
    weak string token_start_char;
 
76
    weak string input_begin;
 
77
    weak string input;
 
78
    
 
79
    int token_start;
 
80
    int input_pos;
 
81
    
 
82
    // The last token retrieved with next_token() extends from characters (start_char) to
 
83
    // (end_char), and from positions (start) to (end).
 
84
    weak string start_char;
 
85
    weak string end_char;
 
86
    public int start;    // starting character position
 
87
    public int end;        // ending character position
 
88
    
 
89
    public Scanner(string input) {
 
90
        this.input = input;
 
91
        input_begin = input;
 
92
    }
 
93
 
 
94
    void advance() {
 
95
        input = input.next_char();
 
96
        ++input_pos;
 
97
    }
 
98
    
 
99
    unichar peek_char() { return input.get_char(); }
 
100
    
 
101
    // Peek two characters ahead.
 
102
    unichar peek_char2() {
 
103
        return input == "" ? '\0' : input.next_char().get_char();
 
104
    }
 
105
 
 
106
    unichar next_char() {
 
107
        unichar c = peek_char();
 
108
        advance();
 
109
        return c;
 
110
    }
 
111
    
 
112
    bool accept(unichar c) {
 
113
        if (peek_char() == c) {
 
114
            advance();
 
115
            return true;
 
116
        }
 
117
        return false;
 
118
    }
 
119
 
 
120
    // Return true if the current token equals s.    
 
121
    bool match(string s) {
 
122
        char *p = token_start_char;
 
123
        char *q = s;
 
124
        while (*p != 0 && *q != 0 && *p == *q) {
 
125
            p = p + 1;
 
126
            q = q + 1;
 
127
        }
 
128
        return p == input && *q == 0;
 
129
    }
 
130
 
 
131
    // Read characters until we reach a triple quote (""") string terminator.
 
132
    void read_triple_string() {
 
133
        while (input != "")
 
134
            if (next_char() == '"' && accept('"') && accept('"'))
 
135
                return;
 
136
    }
 
137
    
 
138
    void skip_line() {
 
139
      while (input != "") {
 
140
          unichar c = next_char();
 
141
          if (c == '\n')
 
142
              break;
 
143
      }
 
144
    }
 
145
    
 
146
    bool is_first_token_on_line() {
 
147
        weak string line = input;
 
148
        // Go back to the '#' character
 
149
        line = line.prev_char();
 
150
        if (direct_equal(line, input_begin))
 
151
            return true;
 
152
 
 
153
        while (true) {
 
154
            line = line.prev_char();
 
155
            unichar c = line.get_char();
 
156
            if (direct_equal(line, input_begin) && c.isspace())
 
157
                return true;
 
158
            else if (c == '\n')
 
159
                return true;
 
160
            else if (!c.isspace())
 
161
                return false;
 
162
        }
 
163
    }
 
164
 
 
165
    Token read_token() {
 
166
        while (input != "") {
 
167
            token_start_char = input;
 
168
            token_start = input_pos;
 
169
            unichar c = next_char();
 
170
 
 
171
            if (c.isspace())
 
172
                continue;
 
173
            
 
174
            bool accept_all_chars_as_id = false;
 
175
            if (c == '@') {
 
176
                accept_all_chars_as_id = true;
 
177
                // Don't include the '@' in ID's
 
178
                token_start_char = input;
 
179
                token_start = input_pos;
 
180
                c = next_char();
 
181
            }
 
182
 
 
183
            // identifier start
 
184
            if (c.isalpha() || c == '_' || (accept_all_chars_as_id && c.isalnum())) { 
 
185
                while (true) {
 
186
                    c = peek_char();
 
187
                    if (!c.isalnum() && c != '_')
 
188
                        break;
 
189
                    advance();
 
190
                }
 
191
                // We don't use the foreach statement to iterate over the keywords array;
 
192
                // that would copy the Keyword structure (and the string it contains) on
 
193
                // each iteration, which would be slow.
 
194
                if (!accept_all_chars_as_id) {
 
195
                    for (int i = 0 ; i < keywords.length ; ++i)
 
196
                        if (match(keywords[i].name))
 
197
                            return keywords[i].token;
 
198
                }
 
199
                return Token.ID;
 
200
            }
 
201
            switch (c) {
 
202
                case '/':
 
203
                    unichar d = peek_char();
 
204
                    if (d == '/') {    // single-line comment
 
205
                        while (input != "" && next_char() != '\n')
 
206
                            ;
 
207
                        token_start_char = input;
 
208
                        token_start = input_pos;
 
209
                        continue;
 
210
                    }
 
211
                    if (d == '*') {       // multi-line comment
 
212
                        advance();    // move past '*'
 
213
                        while (input != "") { 
 
214
                            if (next_char() == '*' && peek_char() == '/') {
 
215
                                advance();    // move past '/'
 
216
                                break;
 
217
                            }
 
218
                        }
 
219
                        token_start_char = input;
 
220
                        token_start = input_pos;
 
221
                        continue;
 
222
                    }
 
223
                    return Token.CHAR;
 
224
                case '"':
 
225
                    if (accept('"')) {        // ""
 
226
                        if (accept('"'))    // """
 
227
                            read_triple_string();
 
228
                    } else {
 
229
                        while (input != "") {
 
230
                            unichar d = next_char();
 
231
                            if (d == '"' || d == '\n')
 
232
                                break;
 
233
                            else if (d == '\'')    // escape sequence
 
234
                                advance();
 
235
                        }
 
236
                    }
 
237
                    return Token.STRING_LITERAL;
 
238
                case '\'':
 
239
                    accept('\\');    // optional backslash beginning escape sequence
 
240
                    advance();
 
241
                    accept('\'');    // closing single quote
 
242
                    return Token.CHAR_LITERAL;
 
243
                case '*': return Token.ASTERISK;
 
244
                case '{': return Token.LEFT_BRACE;
 
245
                case '}': return Token.RIGHT_BRACE;
 
246
                case '[': return Token.LEFT_BRACKET;
 
247
                case ']': return Token.RIGHT_BRACKET;
 
248
                case ':': return Token.COLON;
 
249
                case ',': return Token.COMMA;
 
250
                case '=': return Token.EQUALS;
 
251
                case '#': 
 
252
                    if (is_first_token_on_line()) {
 
253
                        skip_line();
 
254
                        continue;
 
255
                    } else return Token.HASH;
 
256
                case '(': return Token.LEFT_PAREN;
 
257
                case ')': return Token.RIGHT_PAREN;
 
258
                case '.':
 
259
                    if (peek_char() == '.' && peek_char2() == '.') {
 
260
                        advance();
 
261
                        advance();
 
262
                        return Token.ELLIPSIS;
 
263
                    }
 
264
                    return Token.PERIOD;
 
265
                case '?': return Token.QUESTION_MARK;
 
266
                case ';': return Token.SEMICOLON;
 
267
                case '<': return Token.LESS_THAN;
 
268
                case '>': return Token.GREATER_THAN;
 
269
                default:  return Token.CHAR;
 
270
            }
 
271
        }
 
272
        return Token.EOF;
 
273
    }
 
274
    
 
275
    public Token peek_token() {
 
276
        if (token == Token.NONE)
 
277
            token = read_token();
 
278
        return token;
 
279
    }
 
280
    
 
281
    public Token next_token() { 
 
282
        Token t = peek_token();
 
283
        token = Token.NONE;
 
284
        start_char = token_start_char;
 
285
        end_char = input;
 
286
        start = token_start;
 
287
        end = input_pos;
 
288
        return t;
 
289
    }
 
290
    
 
291
    public bool accept_token(Token t) {
 
292
        if (peek_token() == t) {
 
293
            next_token();
 
294
            return true;
 
295
        }
 
296
        return false;
 
297
    }
 
298
 
 
299
    public bool eof() { return peek_token() == Token.EOF; }
 
300
 
 
301
    // Return the source text of the last token retrieved.
 
302
    public string val() {
 
303
        size_t bytes = (char *) end_char - (char *) start_char;
 
304
        return start_char.ndup(bytes);
 
305
    }
 
306
 
 
307
    public unowned string get_start() {
 
308
        return start_char;
 
309
    }
 
310
 
 
311
    public unowned string get_start_after_comments() {
 
312
        // Skip any comments after the end character and take the first character after them
 
313
        peek_token();
 
314
        return token_start_char;
 
315
    }
 
316
    
 
317
}
 
318
 
 
319
}