5 * Moonlight List (moonlight-list@lists.ximian.com)
7 * Copyright 2008 Novell, Inc. (http://www.novell.com)
9 * See the LICENSE file included with the distribution for details.
15 using System
.Collections
.Generic
;
18 public enum Token2Type
{
26 public Token2Type type
;
29 public Token2 (Token2Type type
, string value)
31 //Console.WriteLine ("Token2: {0}, '{1}'", type, value);
35 public Token2 (Token2Type type
, char value)
37 //Console.WriteLine ("Token2: {0}, '{1}'", type, value);
39 this.value = value.ToString ();
42 public override string ToString ()
44 return string.Format ("{0} '{1}'", type
, value);
49 public class Tokenizer
{
50 private Queue
<string> files
;
51 private StreamReader current_stream
;
52 private Token2 current_token
;
53 private string current_file
;
54 private List
<char> chars
= new List
<char> ();
55 private int current_line
;
57 public int CurrentLine
{
58 get { return current_line; }
61 public Token2 CurrentToken
{
62 get { return current_token; }
65 public string CurrentFile
{
66 get { return current_file; }
69 public Tokenizer (string [] files
)
71 this.files
= new Queue
<string> (files
);
74 public char ReadNextChar ()
76 StringBuilder line
= new StringBuilder ();
81 while (current_stream
== null || current_stream
.EndOfStream
) {
82 if (current_stream
!= null) {
83 current_stream
.Close ();
84 current_stream
= null;
87 return char.MinValue
;;
88 current_file
= files
.Dequeue ();
89 current_stream
= new StreamReader (current_file
);
91 //Console.WriteLine ("Parsing {0}...", current_file);
95 line
.Append (current_stream
.ReadLine ());
98 if (line
== null || line
.Length
== 0)
101 if (line
[line
.Length
- 1] != '\\')
107 //Console.WriteLine ("ReadNextChar: Read line: '{0}'", line);
109 if (line
.Length
== 0) {
110 //Console.WriteLine ("ReadNextChar: Empty line");
114 if (line
[0] == '#') {
115 //Console.WriteLine ("ReadNextChar: Skipped preprocessor line: '{0}'", line);
121 for (int i
= 0; i
< line
.Length
; i
++)
122 chars
.Add (line
[i
]);
127 public char GetNextChar ()
131 if (chars
.Count
!= 0) {
134 //Console.WriteLine ("GetNextChar (): popped '{0}'", result);
138 result
= ReadNextChar ();
139 //Console.WriteLine ("GetNextChar (): read '{0}'", result);
141 // All newlines are passed as only one '\n' to the rest of the code
142 if (result
== '\n') {
143 if (PeekChar (1) == '\r')
145 } else if (result
== '\r') {
146 if (PeekChar (1) == '\n')
147 result
= ReadNextChar ();
154 public void PutBackChar (char v
)
159 public char PeekChar (int positions
)
161 while (chars
.Count
< positions
) {
162 char c
= ReadNextChar ();
163 if (c
== char.MinValue
)
168 //Console.WriteLine ("PeekChar ({0}): peeked '{1}'", positions, chars [positions - 1]);
169 return chars
[positions
- 1];
172 public bool Advance (bool throw_on_end
)
177 result
= AdvanceInternal (throw_on_end
);
178 if (CurrentToken
.value == "G_BEGIN_DECLS") {
180 } else if (CurrentToken
.value == "G_END_DECLS") {
186 } catch (Exception ex
) {
187 throw new Exception (string.Format ("{0}({1}): {2}", current_file
, current_line
, ex
.Message
), ex
);
191 private bool AdvanceInternal (bool throw_on_end
)
194 StringBuilder builder
= new StringBuilder ();
201 current
= GetNextChar ();
202 } while (IsWhiteSpace (current
));
204 if (current
== '/') {
205 current
= GetNextChar ();
206 if (current
== '*') { // Found a comment
207 // Skip any whitespace
209 current
= GetNextChar ();
210 } while (IsWhiteSpace (current
));
212 // Check for a comment property
213 if (current
== '@') {
214 current
= GetNextChar ();
216 if (current
== char.MinValue
)
217 throw new Exception ("Unexpected end of code.");
218 if (current
== '*' && PeekChar (1) == '/')
220 builder
.Append (current
);
221 current
= GetNextChar ();
223 while (builder
.Length
> 0 && builder
[builder
.Length
- 1] == ' ')
226 if (builder
.Length
== 0)
227 throw new Exception ("Empty comment property.");
231 if (current
== char.MinValue
)
232 throw new Exception ("Unexpected end of code.");
233 if (current
== '*' && PeekChar (1) == '/')
236 current
= GetNextChar ();
240 throw new Exception (string.Format ("Expected '*', got '{0}'", current
));
242 current
= GetNextChar ();
244 throw new Exception (string.Format ("Expected '/', got '{0}'", current
));
246 if (builder
.Length
!= 0) {
247 current_token
= new Token2 (Token2Type
.CommentProperty
, builder
.ToString ());
250 // We've skipped the comment, start again
252 } else if (current
== '/') { // Found a comment
254 current
= GetNextChar ();
255 } while (current
!= '\r' && current
!= '\n' && current
!= char.MinValue
);
256 if (current
== '\r') {
257 } else if (current
== '\n') {
259 throw new Exception ("Expected end of line.");
261 // We've skipped the comment, start again
264 PutBackChar (current
);
265 current_token
= new Token2 (Token2Type
.Punctuation
, "/");
270 if (IsPunctuation (current
)) {
271 current_token
= new Token2 (Token2Type
.Punctuation
, current
);
275 if (current
== '\'') {
276 current_token
= new Token2 (Token2Type
.Punctuation
, current
);
280 if (current
== '"') {
282 current
= GetNextChar ();
283 if (current
== '\\') {
284 current
= GetNextChar ();
285 builder
.Append (current
); // We don't care much about special characters like \n, \t, etc.
286 } else if (current
!= '"') {
287 builder
.Append (current
);
288 } else if (current
== char.MinValue
) {
289 throw new Exception ("Unexpected end of code in string literal.");
290 } else if (current
== '"') {
291 GetNextChar (); // Skip the "
294 throw new Exception (string.Format ("Got unexpected character: {0}", current
));
297 current_token
= new Token2 (Token2Type
.Literal
, builder
.ToString ());
301 if (IsPunctuation (current
)) {
302 current_token
= new Token2 (Token2Type
.Literal
, current
);
306 if (IsIdentifier (current
)) {
307 builder
.Append (current
);
308 while (IsIdentifier (PeekChar (1))) {
309 builder
.Append (GetNextChar ());
311 current_token
= new Token2 (Token2Type
.Identifier
, builder
.ToString ());
315 if (current
== char.MinValue
) {
317 throw new Exception ("Unexpected end of code");
321 throw new Exception (string.Format ("Unexpected character: {0}", current
));
324 public void FindStartBrace ()
326 while (CurrentToken
.value != "{") {
331 public void SyncWithEndBrace ()
335 AcceptOrThrow (Token2Type
.Punctuation
, "{");
338 if (Accept (Token2Type
.Punctuation
, "{")) {
340 } else if (Accept (Token2Type
.Punctuation
, "}")) {
350 public string GetIdentifier ()
354 result
= CurrentToken
.value;
359 public void VerifyIdentifier ()
361 VerifyType (Token2Type
.Identifier
);
364 public void VerifyType (Token2Type type
)
366 if (CurrentToken
.type
!= type
)
367 throw new Exception (string.Format ("Expected {0}, got {1}", type
, CurrentToken
));
370 public bool Accept (Token2Type type
, string value)
372 if (CurrentToken
.type
!= type
)
375 if (CurrentToken
.value != value)
382 public void AcceptOrThrow (Token2Type type
, string value)
384 if (CurrentToken
.type
!= type
)
385 throw new Exception (string.Format ("Expected {0} '{2}', got {1}", type
, CurrentToken
, value));
387 if (CurrentToken
.value != value)
388 throw new Exception (string.Format ("Expected '{0}', not '{1}'", value, CurrentToken
.value));
393 public static bool IsIdentifier(string str
)
395 return IsIdentifier (str
[0]);
398 public static bool IsIdentifier (char c
)
400 return char.IsLetterOrDigit (c
) || c
== '_';
403 public static bool IsPunctuation (char c
)
436 public static bool IsWhiteSpace (char c
)
438 return char.IsWhiteSpace (c
);