2 // Mono.MonoBASIC.Tokenizer.cs: The Tokenizer for the MonoBASIC compiler
4 // Author: A Rafael D Teixeira (rafaelteixeirabr@hotmail.com)
6 // Based on cs-tokenizer.cs by Miguel de Icaza (miguel@gnu.org)
8 // Licensed under the terms of the GNU GPL
10 // Copyright (C) 2001 A Rafael D Teixeira
13 namespace Mono.MonoBASIC
17 using System.Collections;
19 using System.Globalization;
24 /// Tokenizer for MonoBASIC source code.
27 public class Tokenizer : yyParser.yyInput
30 // TODO: public SourceFile file_name;
31 public string file_name;
32 public string ref_name;
33 public int ref_line = 1;
36 public int current_token = Token.EOL;
37 bool handle_get_set = false;
38 bool cant_have_a_type_character = false;
40 public int ExpandedTabsSize = 4;
42 public string location {
46 if (current_token == Token.ERROR)
47 det = "detail: " + error_details;
51 return "Line: "+line+" Col: "+col + "\n" +
52 "VirtLine: "+ref_line +
53 " Token: "+current_token + " " + det;
57 public bool properties {
59 return handle_get_set;
63 handle_get_set = value;
70 static Hashtable keywords;
71 static NumberStyles styles;
72 static NumberFormatInfo csharp_format_info;
80 const int TAKEN_BEFORE = 2;
81 const int ELSE_SEEN = 4;
82 const int PARENT_TAKING = 8;
83 const int REGION = 16;
86 // pre-processor if stack state:
91 // Values for the associated token returned
98 // Details about the error encoutered by the tokenizer
100 string error_details;
102 public string error {
104 return error_details;
120 static void initTokens ()
122 keywords = new Hashtable ();
124 keywords.Add ("addhandler", Token.ADDHANDLER);
125 keywords.Add ("addressof", Token.ADDRESSOF);
126 keywords.Add ("alias", Token.ALIAS);
127 keywords.Add ("and", Token.AND);
128 keywords.Add ("andalso", Token.ANDALSO);
129 keywords.Add ("ansi", Token.ANSI);
130 keywords.Add ("as", Token.AS);
131 keywords.Add ("assembly", Token.ASSEMBLY);
132 keywords.Add ("auto", Token.AUTO);
133 keywords.Add ("binary", Token.BINARY);
134 keywords.Add ("boolean", Token.BOOLEAN);
135 keywords.Add ("byref", Token.BYREF);
136 keywords.Add ("byte", Token.BYTE);
137 keywords.Add ("byval", Token.BYVAL);
138 keywords.Add ("call", Token.CALL);
139 keywords.Add ("case", Token.CASE);
140 keywords.Add ("catch", Token.CATCH);
141 keywords.Add ("cbool", Token.CBOOL);
142 keywords.Add ("cbyte", Token.CBYTE);
143 keywords.Add ("cchar", Token.CCHAR);
144 keywords.Add ("cdate", Token.CDATE);
145 keywords.Add ("cdec", Token.CDEC);
146 keywords.Add ("cdbl", Token.CDBL);
147 keywords.Add ("char", Token.CHAR);
148 keywords.Add ("cint", Token.CINT);
149 keywords.Add ("class", Token.CLASS);
150 keywords.Add ("clng", Token.CLNG);
151 keywords.Add ("cobj", Token.COBJ);
152 keywords.Add ("compare", Token.COMPARE);
153 keywords.Add ("const", Token.CONST);
154 keywords.Add ("cshort", Token.CSHORT);
155 keywords.Add ("csng", Token.CSNG);
156 keywords.Add ("cstr", Token.CSTR);
157 keywords.Add ("ctype", Token.CTYPE);
158 keywords.Add ("date", Token.DATE);
159 keywords.Add ("decimal", Token.DECIMAL);
160 keywords.Add ("declare", Token.DECLARE);
161 keywords.Add ("default", Token.DEFAULT);
162 keywords.Add ("delegate", Token.DELEGATE);
163 keywords.Add ("dim", Token.DIM);
164 keywords.Add ("do", Token.DO);
165 keywords.Add ("double", Token.DOUBLE);
166 keywords.Add ("each", Token.EACH);
167 keywords.Add ("else", Token.ELSE);
168 keywords.Add ("elseif", Token.ELSEIF);
169 keywords.Add ("end", Token.END);
170 keywords.Add ("enum", Token.ENUM);
171 keywords.Add ("erase", Token.ERASE);
172 keywords.Add ("error", Token.ERROR);
173 keywords.Add ("event", Token.EVENT);
174 keywords.Add ("exit", Token.EXIT);
175 keywords.Add ("explicit", Token.EXPLICIT);
176 keywords.Add ("false", Token.FALSE);
177 keywords.Add ("finally", Token.FINALLY);
178 keywords.Add ("for", Token.FOR);
179 keywords.Add ("friend", Token.FRIEND);
180 keywords.Add ("function", Token.FUNCTION);
181 keywords.Add ("get", Token.GET);
182 //keywords.Add ("gettype", Token.GETTYPE);
183 keywords.Add ("goto", Token.GOTO);
184 keywords.Add ("handles", Token.HANDLES);
185 keywords.Add ("if", Token.IF);
186 keywords.Add ("implements", Token.IMPLEMENTS);
187 keywords.Add ("imports", Token.IMPORTS);
188 keywords.Add ("in", Token.IN);
189 keywords.Add ("inherits", Token.INHERITS);
190 keywords.Add ("integer", Token.INTEGER);
191 keywords.Add ("interface", Token.INTERFACE);
192 keywords.Add ("is", Token.IS);
193 keywords.Add ("let ", Token.LET );
194 keywords.Add ("lib ", Token.LIB );
195 keywords.Add ("like ", Token.LIKE );
196 keywords.Add ("long", Token.LONG);
197 keywords.Add ("loop", Token.LOOP);
198 keywords.Add ("me", Token.ME);
199 keywords.Add ("mod", Token.MOD);
200 keywords.Add ("module", Token.MODULE);
201 keywords.Add ("mustinherit", Token.MUSTINHERIT);
202 keywords.Add ("mustoverride", Token.MUSTOVERRIDE);
203 keywords.Add ("mybase", Token.MYBASE);
204 keywords.Add ("myclass", Token.MYCLASS);
205 keywords.Add ("namespace", Token.NAMESPACE);
206 keywords.Add ("new", Token.NEW);
207 keywords.Add ("next", Token.NEXT);
208 keywords.Add ("not", Token.NOT);
209 keywords.Add ("nothing", Token.NOTHING);
210 keywords.Add ("notinheritable", Token.NOTINHERITABLE);
211 keywords.Add ("notoverridable", Token.NOTOVERRIDABLE);
212 keywords.Add ("object", Token.OBJECT);
213 keywords.Add ("off", Token.OFF);
214 keywords.Add ("on", Token.ON);
215 keywords.Add ("option", Token.OPTION);
216 keywords.Add ("optional", Token.OPTIONAL);
217 keywords.Add ("or", Token.OR);
218 keywords.Add ("orelse", Token.ORELSE);
219 keywords.Add ("overloads", Token.OVERLOADS);
220 keywords.Add ("overridable", Token.OVERRIDABLE);
221 keywords.Add ("overrides", Token.OVERRIDES);
222 keywords.Add ("paramarray", Token.PARAM_ARRAY);
223 keywords.Add ("preserve", Token.PRESERVE);
224 keywords.Add ("private", Token.PRIVATE);
225 keywords.Add ("property", Token.PROPERTY);
226 keywords.Add ("protected", Token.PROTECTED);
227 keywords.Add ("public", Token.PUBLIC);
228 keywords.Add ("raiseevent", Token.RAISEEVENT);
229 keywords.Add ("readonly", Token.READONLY);
230 keywords.Add ("redim", Token.REDIM);
231 keywords.Add ("rem", Token.REM);
232 keywords.Add ("removehandler", Token.REMOVEHANDLER);
233 keywords.Add ("resume", Token.RESUME);
234 keywords.Add ("return", Token.RETURN);
235 keywords.Add ("select", Token.SELECT);
236 keywords.Add ("set", Token.SET);
237 keywords.Add ("shadows", Token.SHADOWS);
238 keywords.Add ("shared", Token.SHARED);
239 keywords.Add ("short", Token.SHORT);
240 keywords.Add ("single", Token.SINGLE);
241 keywords.Add ("sizeof", Token.SIZEOF);
242 keywords.Add ("static", Token.STATIC);
243 keywords.Add ("step", Token.STEP);
244 keywords.Add ("stop", Token.STOP);
245 keywords.Add ("strict", Token.STRICT);
246 keywords.Add ("string", Token.STRING);
247 keywords.Add ("structure", Token.STRUCTURE);
248 keywords.Add ("sub", Token.SUB);
249 keywords.Add ("synclock", Token.SYNCLOCK);
250 keywords.Add ("text", Token.TEXT);
251 keywords.Add ("then", Token.THEN);
252 keywords.Add ("throw", Token.THROW);
253 keywords.Add ("to", Token.TO);
254 keywords.Add ("true", Token.TRUE);
255 keywords.Add ("try", Token.TRY);
256 keywords.Add ("typeof", Token.TYPEOF);
257 keywords.Add ("unicode", Token.UNICODE);
258 keywords.Add ("until", Token.UNTIL);
259 keywords.Add ("variant", Token.VARIANT);
260 keywords.Add ("when", Token.WHEN);
261 keywords.Add ("while", Token.WHILE);
262 keywords.Add ("with", Token.WITH);
263 keywords.Add ("withevents", Token.WITHEVENTS);
264 keywords.Add ("writeonly", Token.WRITEONLY);
265 keywords.Add ("xor", Token.XOR);
267 if (Parser.UseExtendedSyntax){
268 keywords.Add ("yield", Token.YIELD);
279 csharp_format_info = new NumberFormatInfo ();
280 csharp_format_info.CurrencyDecimalSeparator = ".";
281 styles = NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint;
284 public Tokenizer (System.IO.TextReader input, string fname, ArrayList defines)
286 this.ref_name = fname;
290 Location.Push (fname);
293 bool is_keyword (string name)
296 name = name.ToLower();
298 res = keywords.Contains(name);
299 if ((name == "GET" || name == "SET") && handle_get_set == false)
304 int getKeyword (string name)
306 return (int) (keywords [name.ToLower()]);
309 public Location Location {
311 return new Location (ref_line, col);
315 void define (string def)
317 if (!RootContext.AllDefines.Contains(def)){
318 RootContext.AllDefines [def] = true;
320 if (defines.Contains (def))
322 defines [def] = true;
325 public bool PropertyParsing {
327 return handle_get_set;
331 handle_get_set = value;
335 bool is_identifier_start_character (char c)
337 return Char.IsLetter (c) || c == '_' ;
340 bool is_identifier_part_character (char c)
342 return (Char.IsLetter (c) || Char.IsDigit (c) || c == '_');
345 int is_punct (char c, ref bool doread)
352 error_details = c.ToString();
358 return Token.OPEN_BRACKET;
360 return Token.CLOSE_BRACKET;
362 return Token.OPEN_BRACE;
364 return Token.CLOSE_BRACE;
366 return Token.OPEN_PARENS;
368 return Token.CLOSE_PARENS;
374 if (is_identifier_start_character((char)d) || cant_have_a_type_character)
375 return Token.EXCLAMATION;
376 return Token.SINGLETYPECHAR;
378 if (cant_have_a_type_character)
380 return Token.DOLAR_SIGN;
382 if (cant_have_a_type_character)
384 return Token.AT_SIGN;
386 if (cant_have_a_type_character)
388 return Token.PERCENT;
392 workout_preprocessing_directive();
395 if (cant_have_a_type_character)
396 return ExtractDateTimeLiteral();
397 return Token.NUMBER_SIGN;
399 if (!cant_have_a_type_character)
400 return Token.LONGTYPECHAR;
401 t = handle_integer_literal_in_other_bases(d);
402 if (t == Token.NONE) {
405 t = Token.OP_CONCAT_ASSIGN;
416 t = Token.OP_ADD_ASSIGN;
424 t = Token.OP_SUB_ASSIGN;
438 return Token.OP_MULT_ASSIGN;
446 return Token.OP_DIV_ASSIGN;
454 return Token.OP_IDIV_ASSIGN;
456 return Token.OP_IDIV;
462 return Token.OP_EXP_ASSIGN;
480 return Token.OP_SHIFT_LEFT;
493 return Token.OP_SHIFT_RIGHT;
501 return Token.ATTR_ASSIGN;
509 bool decimal_digits (int c)
512 bool seen_digits = false;
515 number.Append ((char) c);
517 while ((d = peekChar ()) != -1){
518 if (Char.IsDigit ((char)d)){
519 number.Append ((char) d);
529 int real_type_suffix (int c)
535 t = Token.LITERAL_SINGLE;
538 t = Token.LITERAL_DOUBLE;
541 t= Token.LITERAL_DECIMAL;
550 int integer_type_suffix (int c)
558 t = Token.LITERAL_INTEGER; // SHORT ?
559 val = ((IConvertible)val).ToInt16(null);
562 t = Token.LITERAL_INTEGER;
563 val = ((IConvertible)val).ToInt32(null);
566 t= Token.LITERAL_INTEGER; // LONG ?
567 val = ((IConvertible)val).ToInt64(null);
570 if ((long)val <= System.Int32.MaxValue &&
571 (long)val >= System.Int32.MinValue) {
572 val = ((IConvertible)val).ToInt32(null);
573 return Token.LITERAL_INTEGER;
575 val = ((IConvertible)val).ToInt64(null);
576 return Token.LITERAL_INTEGER; // LONG ?
581 } catch (Exception e) {
587 int adjust_real (int t)
589 string s = number.ToString ();
592 case Token.LITERAL_DECIMAL:
593 val = new System.Decimal ();
594 val = System.Decimal.Parse (
595 s, styles, csharp_format_info);
597 case Token.LITERAL_DOUBLE:
598 val = new System.Double ();
599 val = System.Double.Parse (
600 s, styles, csharp_format_info);
602 case Token.LITERAL_SINGLE:
603 val = new System.Double ();
604 val = (float) System.Double.Parse (
605 s, styles, csharp_format_info);
609 val = new System.Double ();
610 val = System.Double.Parse (
611 s, styles, csharp_format_info);
612 t = Token.LITERAL_DOUBLE;
620 StringBuilder hexNumber = new StringBuilder ();
624 while ((d = peekChar ()) != -1){
625 char e = Char.ToUpper ((char) d);
627 if (Char.IsDigit (e) || (e >= 'A' && e <= 'F')){
628 hexNumber.Append (e);
633 return System.Int64.Parse (hexNumber.ToString(), NumberStyles.HexNumber);
638 long valueToReturn = 0;
642 while ((d = peekChar ()) != -1){
644 if (Char.IsDigit (e) && (e < '8')){
646 valueToReturn += (d - (int)'0');
652 return valueToReturn;
655 int handle_integer_literal_in_other_bases(int peek)
657 if (peek == 'h' || peek == 'H'){
660 return integer_type_suffix (peekChar ());
663 if (peek == 'o' || peek == 'O'){
665 val = octal_digits ();
666 return integer_type_suffix (peekChar ());
673 // Invoked if we know we have .digits or digits
675 int is_number (int c)
677 bool is_real = false;
678 number = new StringBuilder ();
683 if (Char.IsDigit ((char)c)){
689 // We need to handle the case of
690 // "1.1" vs "1.ToString()" (LITERAL_SINGLE vs NUMBER DOT IDENTIFIER)
693 if (decimal_digits (getChar())){
699 val = System.Int64.Parse(number.ToString());
700 return integer_type_suffix('.');
704 if (c == 'e' || c == 'E'){
711 number.Append ((char) c);
714 } else if (c == '-'){
715 number.Append ((char) c);
723 type = real_type_suffix (c);
724 if (type == Token.NONE && !is_real){
725 val = System.Int64.Parse(number.ToString());
726 return integer_type_suffix(c);
729 return adjust_real (type);
734 if (putback_char != -1){
735 int x = putback_char;
740 return reader.Read ();
745 if (putback_char != -1)
747 return reader.Peek ();
752 if (putback_char != -1)
753 throw new Exception ("This should not happen putback on putback");
757 public bool advance ()
759 return current_token != Token.EOF ;
762 public Object Value {
768 public Object value ()
773 private bool IsEOL(int currentChar)
775 if (currentChar == 0x0D)
777 if (peekChar() == 0x0A) // if it is a CR-LF pair consume LF also
782 return (currentChar == -1 || currentChar == 0x0A || currentChar == 0x2028 || currentChar == 0x2029);
785 private int DropComments()
788 while (!IsEOL(d = getChar ()))
799 int lastToken = current_token;
802 current_token = xtoken ();
803 if (current_token == 0)
805 if (current_token == Token.REM)
806 current_token = DropComments();
807 } while (lastToken == Token.EOL && current_token == Token.EOL);
809 return current_token;
812 private string GetIdentifier()
815 if (is_identifier_start_character ((char) c))
816 return GetIdentifier(c);
821 private string GetIdentifier(int c)
823 StringBuilder id = new StringBuilder ();
825 id.Append ((char) c);
827 while ((c = peekChar ()) != -1)
829 if (is_identifier_part_character ((char) c))
831 id.Append ((char)getChar ());
838 cant_have_a_type_character = false;
840 return id.ToString ();
843 private bool is_doublequote(int currentChar)
845 return (currentChar == '"' ||
846 currentChar == 0x201C || // unicode left double-quote character
847 currentChar == 0x201D); // unicode right double-quote character
850 private bool is_whitespace(int c)
852 return (c == ' ' || c == '\t' || c == '\v' || c == '\r' || c == 0xa0);
855 private bool tokens_seen = false;
857 private void nextLine()
859 cant_have_a_type_character = true;
873 for (;(c = getChar ()) != -1; col++) {
875 // Handle line continuation character
879 if (!is_identifier_part_character((char)d)) {
880 while ((c = getChar ()) != -1 && !IsEOL(c)) {}
886 if (is_whitespace(c)) {
887 // expand tabs for location
889 col = (((col + ExpandedTabsSize) / ExpandedTabsSize) * ExpandedTabsSize) - 1;
890 cant_have_a_type_character = true;
894 // Handle line comments.
902 if (current_token == Token.EOL) // if last token was also EOL keep skipping
907 // Handle escaped identifiers
910 if ((val = GetIdentifier()) == null)
912 if ((c = getChar()) != ']')
915 return Token.IDENTIFIER;
918 // Handle unescaped identifiers
919 if (is_identifier_start_character ((char) c))
922 if ((id = GetIdentifier(c)) == null)
926 if (is_keyword(id) && (current_token != Token.DOT))
927 return getKeyword(id);
928 return Token.IDENTIFIER;
931 // Treat string literals
932 if (is_doublequote(c)) {
933 cant_have_a_type_character = true;
934 return ExtractStringOrCharLiteral(c);
937 // handle numeric literals
940 cant_have_a_type_character = true;
942 if (Char.IsDigit ((char) peekChar ()))
943 return is_number (c);
947 if (Char.IsDigit ((char) c))
949 cant_have_a_type_character = true;
951 return is_number (c);
954 if ((t = is_punct ((char)c, ref doread)) != Token.ERROR) {
955 cant_have_a_type_character = true;
968 error_details = ((char)c).ToString ();
972 if (current_token != Token.EOL) // if last token wasn't EOL send it before EOF
978 private int ExtractDateTimeLiteral()
982 StringBuilder sb = new StringBuilder();
983 for (;(c = getChar ()) != -1; col++)
986 val = ParseDateLiteral(sb);
987 return Token.LITERAL_DATE;
1000 private int ExtractStringOrCharLiteral(int c)
1002 StringBuilder s = new StringBuilder ();
1006 while ((c = getChar ()) != -1){
1007 if (is_doublequote(c)){
1008 if (is_doublequote(peekChar()))
1011 //handle Char Literals
1012 if (peekChar() == 'C' || peekChar() == 'c') {
1014 if (s.Length == 1) {
1016 return Token.LITERAL_CHARACTER;
1018 val = "Incorrect length for a character literal";
1022 val = s.ToString ();
1023 return Token.LITERAL_STRING;
1034 s.Append ((char) c);
1040 private void workout_preprocessing_directive()
1047 cont = handle_preprocessing_directive (cont);
1055 bool skipping = false;
1056 for (;(c = getChar ()) != -1; col++) {
1057 if (is_whitespace(c))
1070 if (c == '#' && !skipping)
1073 tokens_seen = false;
1075 Report.Error (1027, Location, "#endif/#endregion expected");
1078 static IFormatProvider enUSculture = new CultureInfo("en-US", true);
1080 private DateTime ParseDateLiteral(StringBuilder value)
1084 return DateTime.Parse(value.ToString(),
1086 DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AllowWhiteSpaces);
1088 catch (FormatException ex)
1090 //TODO: What is the correct error number and message?
1091 Report.Error (1, Location, string.Format("Invalid date literal '{0}'", value.ToString())
1092 + Environment.NewLine + ex.ToString());
1096 Report.Error (1, Location, "Error parsing date literal"); //TODO: What is the correct error number and message?
1098 return new DateTime();
1102 public void cleanup ()
1104 /* borrowed from mcs - have to work it to have preprocessing in mbas
1106 if (ifstack != null && ifstack.Count >= 1) {
1107 int state = (int) ifstack.Pop ();
1108 if ((state & REGION) != 0)
1109 Report.Error (1038, "#endregion directive expected");
1111 Report.Error (1027, "#endif directive expected");
1116 static StringBuilder static_cmd_arg = new StringBuilder ();
1118 void get_cmd_arg (out string cmd, out string arg)
1122 tokens_seen = false;
1126 // skip over white space
1127 while ((c = getChar ()) != -1 && (c != '\n') && (c != '\r') && ((c == ' ') || (c == '\t')))
1134 } else if (c == '\r')
1137 static_cmd_arg.Length = 0;
1138 static_cmd_arg.Append ((char) c);
1141 while ((c = getChar ()) != -1 && (c != '\n') && (c != ' ') && (c != '\t') && (c != '\r')){
1142 static_cmd_arg.Append ((char) c);
1145 cmd = static_cmd_arg.ToString().ToLower();
1151 } else if (c == '\r')
1154 // skip over white space
1155 while ((c = getChar ()) != -1 && (c != '\n') && (c != '\r') && ((c == ' ') || (c == '\t')))
1162 } else if (c == '\r'){
1167 static_cmd_arg.Length = 0;
1168 static_cmd_arg.Append ((char) c);
1170 while ((c = getChar ()) != -1 && (c != '\n') && (c != '\r')){
1171 static_cmd_arg.Append ((char) c);
1177 } else if (c == '\r')
1179 arg = static_cmd_arg.ToString ().Trim ();
1181 if (cmd == "end" && arg.ToLower() == "region") {
1185 if (cmd == "end" && arg.ToLower() == "if") {
1193 // Handles the #line directive
1195 bool PreProcessLine (string arg)
1200 if (arg == "default"){
1202 ref_name = file_name;
1203 Location.Push (ref_name);
1210 if ((pos = arg.IndexOf (' ')) != -1 && pos != 0){
1211 ref_line = System.Int32.Parse (arg.Substring (0, pos));
1214 char [] quotes = { '\"' };
1216 string name = arg.Substring (pos). Trim (quotes);
1217 ref_name = name; // TODO: Synchronize with mcs: Location.LookupFile (name);
1218 Location.Push (ref_name);
1220 ref_line = System.Int32.Parse (arg);
1230 // Handles #define and #undef
1232 void PreProcessDefinition (bool is_define, string arg)
1234 if (arg == "" || arg == "true" || arg == "false"){
1235 Report.Error (1001, Location, "Missing identifer to pre-processor directive");
1239 char[] whitespace = { ' ', '\t' };
1240 if (arg.IndexOfAny (whitespace) != -1){
1241 Report.Error (1025, Location, "Single-line comment or end-of-line expected");
1245 foreach (char c in arg){
1246 if (!Char.IsLetter (c) && (c != '_')){
1247 Report.Error (1001, Location, "Identifier expected");
1253 if (defines == null)
1254 defines = new Hashtable ();
1257 if (defines == null)
1259 if (defines.Contains (arg))
1260 defines.Remove (arg);
1264 bool eval_val (string s)
1271 if (defines == null)
1273 if (defines.Contains (s))
1279 bool pp_primary (ref string s)
1288 s = s.Substring (1);
1289 bool val = pp_expr (ref s);
1290 if (s.Length > 0 && s [0] == ')'){
1291 s = s.Substring (1);
1294 Error_InvalidDirective ();
1298 if (is_identifier_start_character (c)){
1304 if (is_identifier_part_character (c)){
1308 bool v = eval_val (s.Substring (0, j));
1309 s = s.Substring (j);
1312 bool vv = eval_val (s);
1317 Error_InvalidDirective ();
1321 bool pp_unary (ref string s)
1328 if (len > 1 && s [1] == '='){
1329 Error_InvalidDirective ();
1332 s = s.Substring (1);
1333 return ! pp_primary (ref s);
1335 return pp_primary (ref s);
1337 Error_InvalidDirective ();
1342 bool pp_eq (ref string s)
1344 bool va = pp_unary (ref s);
1350 if (len > 2 && s [1] == '='){
1351 s = s.Substring (2);
1352 return va == pp_unary (ref s);
1354 Error_InvalidDirective ();
1357 } else if (s [0] == '!' && len > 1 && s [1] == '='){
1358 s = s.Substring (2);
1360 return va != pp_unary (ref s);
1369 bool pp_and (ref string s)
1371 bool va = pp_eq (ref s);
1377 if (len > 2 && s [1] == '&'){
1378 s = s.Substring (2);
1379 return (va & pp_eq (ref s));
1381 Error_InvalidDirective ();
1390 // Evaluates an expression for `#if' or `#elif'
1392 bool pp_expr (ref string s)
1394 bool va = pp_and (ref s);
1401 if (len > 2 && s [1] == '|'){
1402 s = s.Substring (2);
1403 return va | pp_expr (ref s);
1405 Error_InvalidDirective ();
1414 bool eval (string s)
1416 bool v = pp_expr (ref s);
1419 Error_InvalidDirective ();
1426 void Error_InvalidDirective ()
1428 Report.Error (1517, Location, "Invalid pre-processor directive");
1431 void Error_UnexpectedDirective (string extra)
1435 "Unexpected processor directive (" + extra + ")");
1438 void Error_TokensSeen ()
1442 "Cannot define or undefine pre-processor symbols after a token in the file");
1446 // if true, then the code continues processing the code
1447 // if false, the code stays in a loop until another directive is
1450 bool handle_preprocessing_directive (bool caller_is_taking)
1452 //char [] blank = { ' ', '\t' };
1454 bool region_directive = false;
1456 get_cmd_arg (out cmd, out arg);
1457 // Eat any trailing whitespaces and single-line comments
1458 if (arg.IndexOf ("'") != -1)
1459 arg = arg.Substring (0, arg.IndexOf ("//"));
1460 arg = arg.TrimEnd (' ', '\t');
1463 // The first group of pre-processing instructions is always processed
1465 switch (cmd.ToLower()){
1467 if (!PreProcessLine (arg))
1470 "Argument to #line directive is missing or invalid");
1474 region_directive = true;
1479 region_directive = true;
1484 Error_InvalidDirective ();
1487 bool taking = false;
1488 if (ifstack == null)
1489 ifstack = new Stack ();
1491 if (ifstack.Count == 0){
1494 int state = (int) ifstack.Peek ();
1495 if ((state & TAKING) != 0)
1499 if (eval (arg) && taking){
1500 int push = TAKING | TAKEN_BEFORE | PARENT_TAKING;
1501 if (region_directive)
1503 ifstack.Push (push);
1506 int push = (taking ? PARENT_TAKING : 0);
1507 if (region_directive)
1509 ifstack.Push (push);
1514 if (ifstack == null || ifstack.Count == 0){
1515 Error_UnexpectedDirective ("no #if for this #end if");
1518 int pop = (int) ifstack.Pop ();
1520 if (region_directive && ((pop & REGION) == 0))
1521 Report.Error (1027, Location, "#end if directive expected");
1522 else if (!region_directive && ((pop & REGION) != 0))
1523 Report.Error (1038, Location, "#end region directive expected");
1525 if (ifstack.Count == 0)
1528 int state = (int) ifstack.Peek ();
1530 if ((state & TAKING) != 0)
1538 if (ifstack == null || ifstack.Count == 0){
1539 Error_UnexpectedDirective ("no #if for this #elif");
1542 int state = (int) ifstack.Peek ();
1544 if ((state & REGION) != 0) {
1545 Report.Error (1038, Location, "#end region directive expected");
1549 if ((state & ELSE_SEEN) != 0){
1550 Error_UnexpectedDirective ("#elif not valid after #else");
1554 if ((state & (TAKEN_BEFORE | TAKING)) != 0)
1557 if (eval (arg) && ((state & PARENT_TAKING) != 0)){
1558 state = (int) ifstack.Pop ();
1559 ifstack.Push (state | TAKING | TAKEN_BEFORE);
1566 if (ifstack == null || ifstack.Count == 0){
1569 "Unexpected processor directive (no #if for this #else)");
1572 int state = (int) ifstack.Peek ();
1574 if ((state & REGION) != 0) {
1575 Report.Error (1038, Location, "#end region directive expected");
1579 if ((state & ELSE_SEEN) != 0){
1580 Error_UnexpectedDirective ("#else within #else");
1587 if ((state & TAKEN_BEFORE) == 0){
1588 ret = ((state & PARENT_TAKING) != 0);
1597 ifstack.Push (state | ELSE_SEEN);
1604 // These are only processed if we are in a `taking' block
1606 if (!caller_is_taking)
1609 switch (cmd.ToLower()){
1611 /* if (any_token_seen){
1612 Error_TokensSeen ();
1615 PreProcessDefinition (true, arg);
1619 /* if (any_token_seen){
1620 Error_TokensSeen ();
1623 PreProcessDefinition (false, arg);
1627 Report.Error (1029, Location, "#error: '" + arg + "'");
1631 Report.Warning (1030, Location, "#warning: '" + arg + "'");
1635 Report.Error (1024, Location, "Preprocessor directive expected (got: '" + cmd + "')");