1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/tools/incparser/IncParser.cs Sun Jul 06 04:49:55 2008 +0000
1.3 @@ -0,0 +1,1342 @@
1.4 +using System;
1.5 +using System.Collections.Generic;
1.6 +using System.IO;
1.7 +using System.Text;
1.8 +
1.9 +namespace incparser
1.10 +{
1.11 + enum LexToken
1.12 + {
1.13 + TOKEN_NONE,
1.14 + TOKEN_INCLUDE,
1.15 + TOKEN_DEFINE,
1.16 + TOKEN_CONST,
1.17 + TOKEN_STRUCT,
1.18 + TOKEN_FUNCTAG,
1.19 + TOKEN_FUNCENUM,
1.20 + TOKEN_ENUM,
1.21 + TOKEN_NATIVE,
1.22 + TOKEN_STOCK,
1.23 + TOKEN_PUBLIC,
1.24 + TOKEN_FORWARD,
1.25 + TOKEN_QUOTCHAR,
1.26 + TOKEN_OPERATOR,
1.27 + TOKEN_CHARACTER,
1.28 + /*TOKEN_STRING,*/
1.29 + TOKEN_LABEL,
1.30 + TOKEN_SYMBOL,
1.31 + TOKEN_DOCBLOCK,
1.32 + TOKEN_EOF,
1.33 + }
1.34 +
1.35 + class ParseException : System.Exception
1.36 + {
1.37 + public ParseException(string message) : base(message)
1.38 + {
1.39 + }
1.40 + };
1.41 +
1.42 + class KeywordToken
1.43 + {
1.44 + public KeywordToken(string k, LexToken t)
1.45 + {
1.46 + keyword = k;
1.47 + token = t;
1.48 + }
1.49 + public string keyword;
1.50 + public LexToken token;
1.51 + };
1.52 +
1.53 + class Tokenizer
1.54 + {
1.55 + private static bool s_initialized = false;
1.56 + private static int s_position = 0;
1.57 + private static KeywordToken [] s_tokens = null;
1.58 + public static KeywordToken[] Tokens = null;
1.59 +
1.60 + public static void Initialize()
1.61 + {
1.62 + if (s_initialized)
1.63 + {
1.64 + return;
1.65 + }
1.66 + s_tokens = new KeywordToken[12];
1.67 + s_initialized = true;
1.68 + s_position = 0;
1.69 +
1.70 + AddToken("#include", LexToken.TOKEN_INCLUDE);
1.71 + AddToken("#define", LexToken.TOKEN_DEFINE);
1.72 + AddToken("native", LexToken.TOKEN_NATIVE);
1.73 + AddToken("functag", LexToken.TOKEN_FUNCTAG);
1.74 + AddToken("funcenum", LexToken.TOKEN_FUNCENUM);
1.75 + AddToken("enum", LexToken.TOKEN_ENUM);
1.76 + AddToken("stock", LexToken.TOKEN_STOCK);
1.77 + AddToken("public", LexToken.TOKEN_PUBLIC);
1.78 + AddToken("forward", LexToken.TOKEN_FORWARD);
1.79 + AddToken("const", LexToken.TOKEN_CONST);
1.80 + AddToken("struct", LexToken.TOKEN_STRUCT);
1.81 + AddToken("operator", LexToken.TOKEN_OPERATOR);
1.82 +
1.83 + Tokens = s_tokens;
1.84 + }
1.85 +
1.86 + private static void AddToken(string lex, LexToken tok)
1.87 + {
1.88 + s_tokens[s_position] = new KeywordToken(lex, tok);
1.89 + s_position++;
1.90 + }
1.91 + };
1.92 +
1.93 + class IncParser
1.94 + {
1.95 + private string FileName;
1.96 + private StringBuilder Contents;
1.97 + private bool LexPushed;
1.98 + private LexToken _LastToken;
1.99 + private string _LexString;
1.100 + private char _LexChar;
1.101 + private uint LineNo;
1.102 +
1.103 + public string LexString
1.104 + {
1.105 + get
1.106 + {
1.107 + return _LexString;
1.108 + }
1.109 + }
1.110 +
1.111 + public char LexChar
1.112 + {
1.113 + get
1.114 + {
1.115 + return _LexChar;
1.116 + }
1.117 + }
1.118 +
1.119 + public uint GetLineNumber()
1.120 + {
1.121 + return LineNo;
1.122 + }
1.123 +
1.124 + public IncParser(string file)
1.125 + {
1.126 + FileName = file;
1.127 +
1.128 + /* Clear out lexer stuff */
1.129 + LexPushed = false;
1.130 + _LastToken = LexToken.TOKEN_NONE;
1.131 + _LexString = null;
1.132 + _LexChar = '\0';
1.133 + LineNo = 1;
1.134 +
1.135 + /* Initialize; this can throw an exception */
1.136 + Initialize();
1.137 + }
1.138 +
1.139 + public void Parse(ParseWriter w)
1.140 + {
1.141 + w.BeginSection(System.IO.Path.GetFileName(FileName));
1.142 + LexToken tok = LexToken.TOKEN_NONE;
1.143 + while ((tok = lex()) != LexToken.TOKEN_EOF)
1.144 + {
1.145 + switch (tok)
1.146 + {
1.147 + case LexToken.TOKEN_DOCBLOCK:
1.148 + {
1.149 + w.WritePair("doc", LexString);
1.150 + break;
1.151 + }
1.152 + case LexToken.TOKEN_DEFINE:
1.153 + {
1.154 + PARSE_Define(w);
1.155 + break;
1.156 + }
1.157 + case LexToken.TOKEN_ENUM:
1.158 + {
1.159 + PARSE_Enum(w);
1.160 + break;
1.161 + }
1.162 + case LexToken.TOKEN_FORWARD:
1.163 + {
1.164 + PARSE_Function(tok, w);
1.165 + break;
1.166 + }
1.167 + case LexToken.TOKEN_NATIVE:
1.168 + {
1.169 + PARSE_Function(tok, w);
1.170 + break;
1.171 + }
1.172 + case LexToken.TOKEN_STOCK:
1.173 + {
1.174 + PARSE_Function(tok, w);
1.175 + break;
1.176 + }
1.177 + case LexToken.TOKEN_FUNCTAG:
1.178 + {
1.179 + PARSE_FuncTag(w);
1.180 + break;
1.181 + }
1.182 + case LexToken.TOKEN_FUNCENUM:
1.183 + {
1.184 + PARSE_FuncEnum(w);
1.185 + break;
1.186 + }
1.187 + case LexToken.TOKEN_INCLUDE:
1.188 + {
1.189 + PARSE_Include(w);
1.190 + break;
1.191 + }
1.192 + case LexToken.TOKEN_STRUCT:
1.193 + {
1.194 + PARSE_Struct(w);
1.195 + break;
1.196 + }
1.197 + case LexToken.TOKEN_PUBLIC:
1.198 + {
1.199 + PARSE_Public(w);
1.200 + break;
1.201 + }
1.202 + default:
1.203 + {
1.204 + if (_LexString == "static" || _LexString == "new")
1.205 + {
1.206 + PARSE_Public(w);
1.207 + break;
1.208 + }
1.209 + throw new ParseException("Unrecognized token: " + tok + " " + _LexString);
1.210 + }
1.211 + }
1.212 + }
1.213 + w.EndSection();
1.214 + }
1.215 +
1.216 + private void PARSE_Public(ParseWriter w)
1.217 + {
1.218 + MatchToken(LexToken.TOKEN_CONST);
1.219 +
1.220 + /* Eat up an optional tag */
1.221 + MatchToken(LexToken.TOKEN_LABEL);
1.222 +
1.223 + /* Eat up a name */
1.224 + NeedToken(LexToken.TOKEN_SYMBOL);
1.225 +
1.226 + while (MatchChar('['))
1.227 + {
1.228 + DiscardUntilChar(']');
1.229 + NeedChar(']');
1.230 + }
1.231 +
1.232 + if (MatchChar('='))
1.233 + {
1.234 + ignore_block();
1.235 + }
1.236 + else if (MatchChar('(') && MatchChar(')'))
1.237 + {
1.238 + ignore_block();
1.239 + return;
1.240 + }
1.241 +
1.242 + NeedChar(';');
1.243 + }
1.244 +
1.245 + private void PARSE_Struct(ParseWriter w)
1.246 + {
1.247 + /* For now, we completely ignore these (they're not written to the output) */
1.248 + NeedToken(LexToken.TOKEN_SYMBOL);
1.249 + w.structList.Add(LexString);
1.250 + NeedChar('{');
1.251 +
1.252 + bool need_closebrace = true;
1.253 + bool more_entries = true;
1.254 + do
1.255 + {
1.256 + if (MatchChar('}'))
1.257 + {
1.258 + need_closebrace = false;
1.259 + break;
1.260 + }
1.261 + MatchToken(LexToken.TOKEN_CONST);
1.262 + MatchToken(LexToken.TOKEN_LABEL);
1.263 + NeedToken(LexToken.TOKEN_SYMBOL);
1.264 + while (MatchChar('['))
1.265 + {
1.266 + DiscardUntilChar(']');
1.267 + NeedChar(']');
1.268 + }
1.269 + more_entries = MatchChar(',');
1.270 + MatchToken(LexToken.TOKEN_DOCBLOCK);
1.271 + if (!more_entries)
1.272 + {
1.273 + break;
1.274 + }
1.275 + } while (true);
1.276 + if (need_closebrace)
1.277 + {
1.278 + NeedChar('}');
1.279 + }
1.280 + NeedChar(';');
1.281 + }
1.282 +
1.283 + private void PARSE_Include(ParseWriter w)
1.284 + {
1.285 + /* read until the end of the line */
1.286 + int index = Contents.ToString().IndexOf('\n');
1.287 + string value;
1.288 + if (index == -1)
1.289 + {
1.290 + value = Contents.ToString();
1.291 + Contents.Remove(0, value.Length);
1.292 + }
1.293 + else if (index == 0)
1.294 + {
1.295 + value = "";
1.296 + }
1.297 + else
1.298 + {
1.299 + value = Contents.ToString().Substring(0, index);
1.300 + Contents.Remove(0, index);
1.301 + }
1.302 +
1.303 + /* Strip whitespace */
1.304 + value = value.Trim('\r', '\t', ' ');
1.305 +
1.306 + /* Write */
1.307 + w.BeginSection("include");
1.308 + w.WritePair("name", value);
1.309 + w.EndSection();
1.310 + }
1.311 +
1.312 + private void PARSE_FuncEnum(ParseWriter w)
1.313 + {
1.314 + w.BeginSection("funcenum");
1.315 +
1.316 + /* Get the functag name */
1.317 + NeedToken(LexToken.TOKEN_SYMBOL);
1.318 + w.WritePair("name", LexString);
1.319 + w.funcenumList.Add(LexString);
1.320 +
1.321 + NeedChar('{');
1.322 +
1.323 + bool need_closebrace = true;
1.324 + do
1.325 + {
1.326 + /* Shortcut out? */
1.327 + if (MatchChar('}'))
1.328 + {
1.329 + need_closebrace = false;
1.330 + break;
1.331 + }
1.332 +
1.333 + /* Start function section */
1.334 + w.BeginSection("function");
1.335 +
1.336 + if (MatchToken(LexToken.TOKEN_DOCBLOCK))
1.337 + {
1.338 + w.WritePair("doc", LexString);
1.339 + }
1.340 +
1.341 + /* Get the return tag */
1.342 + if (MatchToken(LexToken.TOKEN_LABEL))
1.343 + {
1.344 + w.WritePair("return", LexString);
1.345 + }
1.346 +
1.347 + /* Get function type */
1.348 + if (MatchToken(LexToken.TOKEN_PUBLIC))
1.349 + {
1.350 + w.WritePair("type", "public");
1.351 + }
1.352 + else if (MatchToken(LexToken.TOKEN_STOCK))
1.353 + {
1.354 + w.WritePair("type", "stock");
1.355 + }
1.356 +
1.357 + /* Parse the parameters */
1.358 + ParseParameters(w);
1.359 +
1.360 + /* End the section */
1.361 + w.EndSection();
1.362 +
1.363 + if (!MatchChar(','))
1.364 + {
1.365 + break;
1.366 + }
1.367 + } while (true);
1.368 +
1.369 + if (need_closebrace)
1.370 + {
1.371 + NeedChar('}');
1.372 + }
1.373 + NeedChar(';');
1.374 +
1.375 + w.EndSection();
1.376 + }
1.377 +
1.378 + private void PARSE_FuncTag(ParseWriter w)
1.379 + {
1.380 + w.BeginSection("functag");
1.381 +
1.382 + /* Get the functag name */
1.383 + NeedToken(LexToken.TOKEN_SYMBOL);
1.384 + w.WritePair("name", LexString);
1.385 + w.functagList.Add(LexString);
1.386 +
1.387 + if (MatchToken(LexToken.TOKEN_LABEL))
1.388 + {
1.389 + w.WritePair("return", LexString);
1.390 + }
1.391 +
1.392 + if (MatchToken(LexToken.TOKEN_PUBLIC))
1.393 + {
1.394 + w.WritePair("type", "public");
1.395 + }
1.396 + else if (MatchToken(LexToken.TOKEN_STOCK))
1.397 + {
1.398 + w.WritePair("type", "stock");
1.399 + }
1.400 +
1.401 + ParseParameters(w);
1.402 + NeedChar(';');
1.403 +
1.404 + w.EndSection();
1.405 + }
1.406 +
1.407 + private void PARSE_Function(LexToken tok, ParseWriter w)
1.408 + {
1.409 + string tag="", name;
1.410 +
1.411 + /* Get the return value */
1.412 + if (MatchToken(LexToken.TOKEN_LABEL))
1.413 + {
1.414 + tag = LexString;
1.415 + }
1.416 +
1.417 + if (MatchToken(LexToken.TOKEN_OPERATOR))
1.418 + {
1.419 + /* Check if we're some sort of invalid function */
1.420 + DiscardUntilCharOrComment('(', false);
1.421 + ParseParameters(null);
1.422 + if (tok == LexToken.TOKEN_STOCK)
1.423 + {
1.424 + ignore_block();
1.425 + }
1.426 + else if (tok == LexToken.TOKEN_NATIVE || tok == LexToken.TOKEN_FORWARD)
1.427 + {
1.428 + if (MatchChar('='))
1.429 + {
1.430 + DiscardUntilCharOrComment(';', false);
1.431 + }
1.432 + NeedChar(';');
1.433 + }
1.434 + return;
1.435 + }
1.436 +
1.437 + /* Get the name */
1.438 + NeedToken(LexToken.TOKEN_SYMBOL);
1.439 + name = LexString;
1.440 +
1.441 + PARSE_Function(tok, w, tag, name);
1.442 + }
1.443 +
1.444 + private void PARSE_Function(LexToken tok, ParseWriter w, string tag, string name)
1.445 + {
1.446 + if (tok == LexToken.TOKEN_FORWARD)
1.447 + {
1.448 + w.BeginSection("forward");
1.449 + w.forwardList.Add(name);
1.450 + }
1.451 + else if (tok == LexToken.TOKEN_NATIVE)
1.452 + {
1.453 + w.BeginSection("native");
1.454 + w.nativeList.Add(name);
1.455 + }
1.456 + else if (tok == LexToken.TOKEN_STOCK)
1.457 + {
1.458 + w.BeginSection("stock");
1.459 + w.stockList.Add(name);
1.460 + }
1.461 +
1.462 + w.WritePair("name", name);
1.463 +
1.464 + if (tag.Length > 0)
1.465 + {
1.466 + w.WritePair("return", tag);
1.467 + }
1.468 +
1.469 + ParseParameters(w);
1.470 +
1.471 + if (tok == LexToken.TOKEN_STOCK)
1.472 + {
1.473 + ignore_block();
1.474 + }
1.475 + else
1.476 + {
1.477 + /* Make sure there's a semicolon */
1.478 + if (MatchChar('='))
1.479 + {
1.480 + DiscardUntilCharOrComment(';', false);
1.481 + }
1.482 + NeedChar(';');
1.483 + }
1.484 +
1.485 + w.EndSection();
1.486 + }
1.487 +
1.488 + private void ParseParameters(ParseWriter w)
1.489 + {
1.490 + NeedChar('(');
1.491 + string extra;
1.492 + do
1.493 + {
1.494 + if (MatchChar(')'))
1.495 + {
1.496 + return;
1.497 + }
1.498 +
1.499 + /* Start the parameter */
1.500 + if (w != null)
1.501 + {
1.502 + w.BeginSection("parameter");
1.503 + }
1.504 +
1.505 + /* Check for a 'const' token */
1.506 + if (MatchToken(LexToken.TOKEN_CONST) && w != null)
1.507 + {
1.508 + w.WritePair("const", "true");
1.509 + }
1.510 +
1.511 + /* Check if it's by reference */
1.512 + if (MatchChar('&') && w != null)
1.513 + {
1.514 + w.WritePair("byref", "true");
1.515 + }
1.516 +
1.517 + /* Check for a tag */
1.518 + if (MatchToken(LexToken.TOKEN_LABEL) && w != null)
1.519 + {
1.520 + w.WritePair("tag", LexString);
1.521 + }
1.522 +
1.523 + /* Get the symbol and write it */
1.524 + if (MatchString(0, "...") && w != null)
1.525 + {
1.526 + w.WritePair("name", "...");
1.527 + }
1.528 + else
1.529 + {
1.530 + NeedToken(LexToken.TOKEN_SYMBOL);
1.531 + if (w != null)
1.532 + {
1.533 + w.WritePair("name", LexString);
1.534 + }
1.535 + }
1.536 +
1.537 + /* Check if we have a default value yet */
1.538 + bool default_value = false;
1.539 + if (MatchChar('='))
1.540 + {
1.541 + default_value = true;
1.542 + }
1.543 + else if (MatchChar('['))
1.544 + {
1.545 + extra = "";
1.546 + string temp;
1.547 + do
1.548 + {
1.549 + extra += "[";
1.550 + temp = ConsumeUntilChar(']');
1.551 + if (temp != null)
1.552 + {
1.553 + extra += temp;
1.554 + }
1.555 + extra += "]";
1.556 + NeedChar(']');
1.557 + } while (MatchChar('['));
1.558 + w.WritePair("decoration", extra);
1.559 + }
1.560 +
1.561 + /* If we have a default value, get it */
1.562 + if (default_value || MatchChar('='))
1.563 + {
1.564 + if ((extra = ConsumeParamDefValue()) == null)
1.565 + {
1.566 + throw new ParseException("Expected a default value; found none");
1.567 + }
1.568 + if (w != null)
1.569 + {
1.570 + w.WritePair("defval", extra);
1.571 + }
1.572 + }
1.573 +
1.574 + /* End the parameter */
1.575 + if (w != null)
1.576 + {
1.577 + w.EndSection();
1.578 + }
1.579 +
1.580 + if (!MatchChar(','))
1.581 + {
1.582 + break;
1.583 + }
1.584 + } while (true);
1.585 +
1.586 + NeedChar(')');
1.587 + }
1.588 +
1.589 + private void PARSE_Enum(ParseWriter w)
1.590 + {
1.591 + string name = "";
1.592 + if (MatchToken(LexToken.TOKEN_SYMBOL))
1.593 + {
1.594 + name = LexString;
1.595 + }
1.596 +
1.597 + w.BeginSection("enum");
1.598 + w.WritePair("name", name);
1.599 + w.BeginSection("properties");
1.600 +
1.601 + w.enumList.Add(name);
1.602 +
1.603 + NeedChar('{');
1.604 + bool more_entries = true;
1.605 + bool need_closebrace = true;
1.606 +
1.607 + if (MatchToken(LexToken.TOKEN_DOCBLOCK))
1.608 + {
1.609 + w.WritePair("doc", LexString);
1.610 + }
1.611 +
1.612 + do
1.613 + {
1.614 + if (MatchChar('}'))
1.615 + {
1.616 + need_closebrace = false;
1.617 + break;
1.618 + }
1.619 + NeedToken(LexToken.TOKEN_SYMBOL);
1.620 + name = LexString;
1.621 +
1.622 + if (MatchChar('='))
1.623 + {
1.624 + DiscardUntilCharOrComment(",\n}", true);
1.625 + }
1.626 + more_entries = MatchChar(',');
1.627 + w.WritePair("name", name);
1.628 + w.enumTypeList.Add(name);
1.629 + if (MatchToken(LexToken.TOKEN_DOCBLOCK))
1.630 + {
1.631 + w.WritePair("doc", LexString);
1.632 + }
1.633 + } while (more_entries);
1.634 + if (need_closebrace)
1.635 + {
1.636 + NeedChar('}');
1.637 + }
1.638 + NeedChar(';');
1.639 + w.EndSection();
1.640 + w.EndSection();
1.641 + }
1.642 +
1.643 + private void PARSE_Define(ParseWriter w)
1.644 + {
1.645 + /* first, get the symbol name */
1.646 + NeedToken(LexToken.TOKEN_SYMBOL);
1.647 + string name = LexString;
1.648 + string value;
1.649 +
1.650 + /* read until we hit the end of a line OR a comment */
1.651 + int endpoint = -1;
1.652 + for (int i = 0; i < Contents.Length; i++)
1.653 + {
1.654 + if (Contents[i] == '\n')
1.655 + {
1.656 + endpoint = i;
1.657 + break;
1.658 + }
1.659 + else if (CheckString(i, "/**"))
1.660 + {
1.661 + endpoint = i;
1.662 + break;
1.663 + }
1.664 + }
1.665 +
1.666 + if (endpoint < 1)
1.667 + {
1.668 + value = "";
1.669 + }
1.670 + else
1.671 + {
1.672 + value = Contents.ToString().Substring(0, endpoint);
1.673 + Contents.Remove(0, endpoint);
1.674 + }
1.675 +
1.676 + /* Write */
1.677 + w.defineList.Add(name);
1.678 +
1.679 + w.BeginSection("define");
1.680 + w.WritePair("name", name);
1.681 + w.WritePair("linetext", value);
1.682 + w.EndSection();
1.683 + }
1.684 +
1.685 + /* Reads up to a character and discards all text before it.
1.686 + */
1.687 + private void DiscardUntilChar(char c)
1.688 + {
1.689 + int eol = Contents.ToString().IndexOf(c);
1.690 +
1.691 + if (eol < 1)
1.692 + {
1.693 + return;
1.694 + }
1.695 +
1.696 + Contents.Remove(0, eol);
1.697 + }
1.698 +
1.699 + /* Same as DiscardUntilChar, except the discarded
1.700 + * text is returned first.
1.701 + */
1.702 + private string ConsumeUntilChar(char c)
1.703 + {
1.704 + int eol = Contents.ToString().IndexOf(c);
1.705 +
1.706 + if (eol < 1)
1.707 + {
1.708 + return null;
1.709 + }
1.710 +
1.711 + string rest = Contents.ToString().Substring(0, eol);
1.712 + Contents.Remove(0, eol);
1.713 + return rest;
1.714 + }
1.715 +
1.716 + /**
1.717 + * Returns a parameter's default value.
1.718 + */
1.719 + private string ConsumeParamDefValue()
1.720 + {
1.721 + int eol = FindEndOfExprLine(0);
1.722 + if (eol < 1)
1.723 + {
1.724 + return null;
1.725 + }
1.726 +
1.727 + string rest = Contents.ToString().Substring(0, eol);
1.728 + Contents.Remove(0, eol);
1.729 + return rest;
1.730 + }
1.731 +
1.732 + /**
1.733 + * Discards all data up to a certain character, ignoring
1.734 + * the contents, unless specified to stop at a doc block comment.
1.735 + */
1.736 + private void DiscardUntilCharOrComment(char c, bool stop_at_comment)
1.737 + {
1.738 + for (int i = 0; i < Contents.Length; i++)
1.739 + {
1.740 + if (Contents[i] == '\n')
1.741 + {
1.742 + LineNo++;
1.743 + }
1.744 + else if (Contents[i] == c)
1.745 + {
1.746 + if (i > 0)
1.747 + {
1.748 + Contents.Remove(0, i);
1.749 + }
1.750 + return;
1.751 + }
1.752 + else if (stop_at_comment && CheckString(i, "/**"))
1.753 + {
1.754 + if (i > 0)
1.755 + {
1.756 + Contents.Remove(0, i);
1.757 + }
1.758 + return;
1.759 + }
1.760 + }
1.761 + }
1.762 +
1.763 + /**
1.764 + * Discards all data up to a certain character, ignoring
1.765 + * the contents, unless specified to stop at a doc block comment.
1.766 + */
1.767 + private void DiscardUntilCharOrComment(string s, bool stop_at_comment)
1.768 + {
1.769 + for (int i = 0; i < Contents.Length; i++)
1.770 + {
1.771 + if (Contents[i] == '\n')
1.772 + {
1.773 + LineNo++;
1.774 + }
1.775 + else if (s.Contains(Contents[i].ToString()))
1.776 + {
1.777 + if (i > 0)
1.778 + {
1.779 + Contents.Remove(0, i);
1.780 + }
1.781 + return;
1.782 + }
1.783 + else if (stop_at_comment && CheckString(i, "/**"))
1.784 + {
1.785 + if (i > 0)
1.786 + {
1.787 + Contents.Remove(0, i);
1.788 + }
1.789 + return;
1.790 + }
1.791 + }
1.792 + }
1.793 +
1.794 + /**
1.795 + * Finds the end of an expression
1.796 + */
1.797 + private int FindEndOfExprLine(int start)
1.798 + {
1.799 + for (int i = start; i < Contents.Length; i++)
1.800 + {
1.801 + if (Contents[i] == '{')
1.802 + {
1.803 + if ((i = FindEndOfConsumptionLine('}', start+1)) == -1)
1.804 + {
1.805 + throw new ParseException("Expected token: } (found none)");
1.806 + }
1.807 + }
1.808 + else if (Contents[i] == ')' || Contents[i] == ',')
1.809 + {
1.810 + return i;
1.811 + }
1.812 + }
1.813 +
1.814 + return -1;
1.815 + }
1.816 +
1.817 + private int FindEndOfConsumptionLine(char c, int start)
1.818 + {
1.819 + for (int i = start; i < Contents.Length; i++)
1.820 + {
1.821 + if (Contents[i] == '{')
1.822 + {
1.823 + if ((i = FindEndOfConsumptionLine('}', i + 1)) == -1)
1.824 + {
1.825 + throw new ParseException("Expected token: } (found none)");
1.826 + }
1.827 + }
1.828 + else if (Contents[i] == c)
1.829 + {
1.830 + return i;
1.831 + }
1.832 + }
1.833 +
1.834 + return -1;
1.835 + }
1.836 +
1.837 + private void Initialize()
1.838 + {
1.839 + /* Open file, read it, and close it */
1.840 + StreamReader sr = File.OpenText(FileName);
1.841 + Contents = new StringBuilder(sr.ReadToEnd());
1.842 + sr.Close();
1.843 +
1.844 + /* Strip comments (may throw an exception) */
1.845 + preprocess();
1.846 +
1.847 + /* Lastly, initialize the tokenizer */
1.848 + Tokenizer.Initialize();
1.849 + }
1.850 +
1.851 + /* Consumes a token iff it matches */
1.852 + private bool MatchToken(LexToken token)
1.853 + {
1.854 + LexToken tok = lex();
1.855 + if (tok != token)
1.856 + {
1.857 + lexpush();
1.858 + return false;
1.859 + }
1.860 + return true;
1.861 + }
1.862 +
1.863 + /* Consumes a token and aborts the parser if there is no match */
1.864 + private void NeedToken(LexToken token)
1.865 + {
1.866 + if (!MatchToken(token))
1.867 + {
1.868 + throw new ParseException("Expected token: " + token);
1.869 + }
1.870 + }
1.871 +
1.872 + /* Consumes a character iff it matches */
1.873 + private bool MatchChar(char c)
1.874 + {
1.875 + if (lex() != LexToken.TOKEN_CHARACTER)
1.876 + {
1.877 + lexpush();
1.878 + return false;
1.879 + }
1.880 + if (_LexChar != c)
1.881 + {
1.882 + lexpush();
1.883 + return false;
1.884 + }
1.885 + return true;
1.886 + }
1.887 +
1.888 + /* Consumes a character or aborts the parser if there is no match */
1.889 + private void NeedChar(char c)
1.890 + {
1.891 + if (!MatchChar(c))
1.892 + {
1.893 + throw new ParseException("Expected character: " + c);
1.894 + }
1.895 + }
1.896 +
1.897 + /* Saves the lexer state so we don't reparse unnecessarily */
1.898 + private void lexpush()
1.899 + {
1.900 + if (LexPushed)
1.901 + {
1.902 + throw new ParseException("Lexer cannot be pushed twice");
1.903 + }
1.904 + LexPushed = true;
1.905 + }
1.906 +
1.907 + /* Attempts to match a string against the next stream tokens.
1.908 + * On success, the token stream is advanced.
1.909 + */
1.910 + private bool MatchString(int startPos, string str)
1.911 + {
1.912 + if (str.Length > Contents.Length - startPos)
1.913 + {
1.914 + return false;
1.915 + }
1.916 + for (int i = 0; i < str.Length; i++)
1.917 + {
1.918 + if (str[i] != Contents[startPos + i])
1.919 + {
1.920 + return false;
1.921 + }
1.922 + }
1.923 + Contents.Remove(startPos, str.Length);
1.924 + return true;
1.925 + }
1.926 +
1.927 + /* Attempts to match a string against the next stream tokens */
1.928 + private bool CheckString(int startPos, string str)
1.929 + {
1.930 + if (str.Length > Contents.Length - startPos)
1.931 + {
1.932 + return false;
1.933 + }
1.934 + for (int i = 0; i < str.Length; i++)
1.935 + {
1.936 + if (str[i] != Contents[startPos + i])
1.937 + {
1.938 + return false;
1.939 + }
1.940 + }
1.941 + return true;
1.942 + }
1.943 +
1.944 + /* Ignores a block starting and ending with { and } respectively */
1.945 + private void ignore_block()
1.946 + {
1.947 + /* Hrm, this is tricky. First, expect an open brace. */
1.948 + NeedChar('{');
1.949 + /* Consume the entire function by keeping track of braces */
1.950 + int numBraces = 1;
1.951 + bool in_string = false;
1.952 + bool found_end = false;
1.953 + for (int i = 0; i < Contents.Length; i++)
1.954 + {
1.955 + if (Contents[i] == '\n')
1.956 + {
1.957 + LineNo++;
1.958 + }
1.959 + if (!in_string)
1.960 + {
1.961 + if (Contents[i] == '"')
1.962 + {
1.963 + in_string = true;
1.964 + }
1.965 + else if (Contents[i] == '{')
1.966 + {
1.967 + numBraces++;
1.968 + }
1.969 + else if (Contents[i] == '}')
1.970 + {
1.971 + if (numBraces == 0)
1.972 + {
1.973 + throw new ParseException("Unable to find a matching open brace");
1.974 + }
1.975 + if (--numBraces == 0)
1.976 + {
1.977 + Contents.Remove(0, i + 1);
1.978 + found_end = true;
1.979 + break;
1.980 + }
1.981 + }
1.982 + }
1.983 + else
1.984 + {
1.985 + /* It is possible to be in a string and at the first character */
1.986 + if (Contents[i] == '"' && Contents[i - 1] != '\\')
1.987 + {
1.988 + in_string = false;
1.989 + }
1.990 + }
1.991 + }
1.992 + if (!found_end)
1.993 + {
1.994 + throw new ParseException("Unable to find a matching close brace");
1.995 + }
1.996 + }
1.997 +
1.998 + /* Strips all non-doc C and C++ style comments. as well as unused pragmas */
1.999 + private void preprocess()
1.1000 + {
1.1001 + if (Contents.Length == 1)
1.1002 + {
1.1003 + return;
1.1004 + }
1.1005 +
1.1006 + bool in_comment = false;
1.1007 + bool is_ml = false;
1.1008 + bool is_doc = false;
1.1009 + bool is_newline = true;
1.1010 +
1.1011 + for (int i=0; i<Contents.Length; i++)
1.1012 + {
1.1013 + if (Contents[i] == '\n')
1.1014 + {
1.1015 + is_newline = true;
1.1016 + }
1.1017 + if (!in_comment)
1.1018 + {
1.1019 + if (Contents[i] == '/')
1.1020 + {
1.1021 + if (Contents[i+1] == '/')
1.1022 + {
1.1023 + in_comment = true;
1.1024 + is_ml = false;
1.1025 + is_doc = false;
1.1026 + is_newline = false;
1.1027 + } else if (Contents[i+1] == '*') {
1.1028 + /* Detect whether this is a doc comment or not */
1.1029 + if (Contents.Length - i >= 3
1.1030 + && Contents[i+2] == '*')
1.1031 + {
1.1032 + is_doc = true;
1.1033 + } else {
1.1034 + is_doc = false;
1.1035 + }
1.1036 + in_comment = true;
1.1037 + is_ml = true;
1.1038 + }
1.1039 + if (in_comment && !is_doc)
1.1040 + {
1.1041 + Contents[i] = ' ';
1.1042 + Contents[i+1] = ' '; /* Just for safety */
1.1043 + }
1.1044 + }
1.1045 + else if (is_newline)
1.1046 + {
1.1047 + if (Contents[i] == '#')
1.1048 + {
1.1049 + if (!CheckString(i, "#define")
1.1050 + && !CheckString(i, "#include"))
1.1051 + {
1.1052 + /* Ignore! */
1.1053 + Contents[i] = ' ';
1.1054 + in_comment = true;
1.1055 + is_doc = false;
1.1056 + is_ml = false;
1.1057 + }
1.1058 + is_newline = false;
1.1059 + }
1.1060 + else if (!Char.IsWhiteSpace(Contents[i]))
1.1061 + {
1.1062 + is_newline = false;
1.1063 + }
1.1064 + }
1.1065 + } else {
1.1066 + if (is_ml)
1.1067 + {
1.1068 + if (Contents[i] == '*' && Contents[i+1] == '/')
1.1069 + {
1.1070 + if (!is_doc)
1.1071 + {
1.1072 + Contents[i] = ' ';
1.1073 + Contents[i+1] = ' ';
1.1074 + }
1.1075 + in_comment = false;
1.1076 + i++; /* Don't look at the next character */
1.1077 + }
1.1078 + else if (!is_doc && Contents[i] != '\n')
1.1079 + {
1.1080 + /* Erase comment */
1.1081 + Contents[i] = ' ';
1.1082 + }
1.1083 + } else {
1.1084 + if (is_newline)
1.1085 + {
1.1086 + in_comment = false;
1.1087 + }
1.1088 + else
1.1089 + {
1.1090 + /* Erase comment */
1.1091 + Contents[i] = ' ';
1.1092 + }
1.1093 + }
1.1094 + }
1.1095 + }
1.1096 +
1.1097 + if (in_comment)
1.1098 + {
1.1099 + throw new ParseException("Multi-line comment has no terminator");
1.1100 + }
1.1101 + }
1.1102 +
1.1103 + /* Strips whitespace */
1.1104 + private void trim_whitespace_pre()
1.1105 + {
1.1106 + int spaces = 0;
1.1107 + while (spaces < Contents.Length
1.1108 + && Contents[spaces] != '\0'
1.1109 + && Char.IsWhiteSpace(Contents[spaces]))
1.1110 + {
1.1111 + if (Contents[spaces] == '\n')
1.1112 + {
1.1113 + LineNo++;
1.1114 + }
1.1115 + spaces++;
1.1116 + }
1.1117 + if (spaces > 0)
1.1118 + {
1.1119 + Contents.Remove(0, spaces);
1.1120 + }
1.1121 + }
1.1122 +
1.1123 + private void preprocess_line()
1.1124 + {
1.1125 + trim_whitespace_pre();
1.1126 + }
1.1127 +
1.1128 + private bool IsSymbolChar(char c, bool first)
1.1129 + {
1.1130 + bool valid = (first ? Char.IsLetter(c) : Char.IsLetterOrDigit(c));
1.1131 + return (valid || (c == '_'));
1.1132 + }
1.1133 +
1.1134 + private bool IsEscapedChar(char c)
1.1135 + {
1.1136 + return (c == 'n'
1.1137 + || c == 'r'
1.1138 + || c == 't'
1.1139 + || c == 'v'
1.1140 + || c == '\\'
1.1141 + || c == '"');
1.1142 + }
1.1143 +
1.1144 + private bool IsNotKeywordChar(char c)
1.1145 + {
1.1146 + return (Char.IsWhiteSpace(c)
1.1147 + || (c == '-' || c == '+')
1.1148 + || (c == '/' || c == '*')
1.1149 + || (c == '(' || c == ')')
1.1150 + || (c == '=' || c == '%')
1.1151 + || (c == '>' || c == '<')
1.1152 + || (c == '!' || c == '~')
1.1153 + );
1.1154 + }
1.1155 +
1.1156 + /* Consumes one lexical token and gathers information about it */
1.1157 + private LexToken lex()
1.1158 + {
1.1159 + if (LexPushed)
1.1160 + {
1.1161 + LexPushed = false;
1.1162 + return _LastToken;
1.1163 + }
1.1164 +
1.1165 + /* Number of chars we will be deleting from the input */
1.1166 + int stripchars = 0;
1.1167 +
1.1168 + /* Clear our state */
1.1169 + _LastToken = LexToken.TOKEN_NONE;
1.1170 +
1.1171 + /* Remove stuff we don't want from the line */
1.1172 + preprocess_line();
1.1173 +
1.1174 + if (Contents.Length < 1)
1.1175 + {
1.1176 + _LastToken = LexToken.TOKEN_EOF;
1.1177 + return _LastToken;
1.1178 + }
1.1179 +
1.1180 + /* Get the token list */
1.1181 + KeywordToken[] tokens = Tokenizer.Tokens;
1.1182 + for (int i = 0; i < tokens.Length; i++)
1.1183 + {
1.1184 + if (!CheckString(0, tokens[i].keyword))
1.1185 + {
1.1186 + continue;
1.1187 + }
1.1188 + int len = tokens[i].keyword.Length;
1.1189 + /* Now check to see what the next token is */
1.1190 + if (Contents.Length == len
1.1191 + || IsNotKeywordChar(Contents[len]))
1.1192 + {
1.1193 + /* We have a token match! */
1.1194 + _LastToken = tokens[i].token;
1.1195 + _LexString = null;
1.1196 + _LexChar = '\0';
1.1197 + stripchars = len;
1.1198 + break;
1.1199 + }
1.1200 + }
1.1201 +
1.1202 + if (_LastToken == LexToken.TOKEN_NONE)
1.1203 + {
1.1204 + /* See if we can try to read a symbol */
1.1205 + if (IsSymbolChar(Contents[0], true))
1.1206 + {
1.1207 + int characters = 1;
1.1208 + while (Contents[characters] != '\0'
1.1209 + && IsSymbolChar(Contents[characters], false))
1.1210 + {
1.1211 + characters++;
1.1212 + }
1.1213 + stripchars = characters;
1.1214 + /* We're done! See what's next.. */
1.1215 + if (Contents[characters] == ':')
1.1216 + {
1.1217 + _LastToken = LexToken.TOKEN_LABEL;
1.1218 + stripchars++;
1.1219 + }
1.1220 + else
1.1221 + {
1.1222 + _LastToken = LexToken.TOKEN_SYMBOL;
1.1223 + }
1.1224 + _LexString = Contents.ToString().Substring(0, characters);
1.1225 + _LexChar = _LexString[0];
1.1226 + }/*
1.1227 + else if (Contents[0] == '"')
1.1228 + {
1.1229 + bool reached_end = false;
1.1230 + StringBuilder builder = new StringBuilder();
1.1231 + for (int i = 1; i < Contents.Length; i++)
1.1232 + {
1.1233 + if (i < Contents.Length - 1
1.1234 + && Contents[i] == '\\'
1.1235 + && IsEscapedChar(Contents[i+1]))
1.1236 + {
1.1237 + i++;
1.1238 + }
1.1239 + else if (Contents[i] == '"')
1.1240 + {
1.1241 + reached_end = true;
1.1242 + break;
1.1243 + }
1.1244 + builder.Append(Contents[i]);
1.1245 + }
1.1246 + if (!reached_end)
1.1247 + {
1.1248 + throw new ParseException("Expected end of string; none found");
1.1249 + }
1.1250 + _LexString = builder.ToString();
1.1251 + _LexChar = _LexString.Length > 0 ? _LexString[0] : '\0';
1.1252 + }*/
1.1253 + else if (Contents[0] == '\'')
1.1254 + {
1.1255 + char c = '\0';
1.1256 + if (Contents.Length < 3)
1.1257 + {
1.1258 + throw new ParseException("Expected end of character; none found");
1.1259 + }
1.1260 + else if (Contents[1] == '\\')
1.1261 + {
1.1262 + if (Contents.Length < 4 || !IsEscapedChar(Contents[2]))
1.1263 + {
1.1264 + throw new ParseException("Expected end of character; none found");
1.1265 + }
1.1266 + if (Contents[2] == 'r')
1.1267 + {
1.1268 + c = '\r';
1.1269 + }
1.1270 + else if (Contents[2] == 'n')
1.1271 + {
1.1272 + c = '\n';
1.1273 + }
1.1274 + else if (Contents[2] == 't')
1.1275 + {
1.1276 + c = '\t';
1.1277 + }
1.1278 + else if (Contents[2] == 'v')
1.1279 + {
1.1280 + c = '\v';
1.1281 + }
1.1282 + else if (Contents[2] == '\\')
1.1283 + {
1.1284 + c = '\\';
1.1285 + }
1.1286 + else if (Contents[2] == '"')
1.1287 + {
1.1288 + c = '"';
1.1289 + }
1.1290 + stripchars = 4;
1.1291 + }
1.1292 + else
1.1293 + {
1.1294 + c = Contents[1];
1.1295 + stripchars = 3;
1.1296 + }
1.1297 + _LexString = c.ToString();
1.1298 + _LexChar = c;
1.1299 + _LastToken = LexToken.TOKEN_QUOTCHAR;
1.1300 + }
1.1301 + else if (CheckString(0, "/**"))
1.1302 + {
1.1303 + int endpoint = 0;
1.1304 + for (int i=3; i<Contents.Length-1; i++)
1.1305 + {
1.1306 + if (Contents[i] == '\n')
1.1307 + {
1.1308 + LineNo++;
1.1309 + }
1.1310 + else if (Contents[i] == '*' && Contents[i+1] == '/')
1.1311 + {
1.1312 + endpoint = i+1;
1.1313 + break;
1.1314 + }
1.1315 + }
1.1316 + if (endpoint == 0)
1.1317 + {
1.1318 + throw new ParseException("Expected end of multi-line comment; found end of file");
1.1319 + }
1.1320 + /* We have to factor one more character in because we're zero based */
1.1321 + endpoint++;
1.1322 + _LastToken = LexToken.TOKEN_DOCBLOCK;
1.1323 + _LexString = Contents.ToString().Substring(0, endpoint);
1.1324 + _LexChar = _LexString[0];
1.1325 + stripchars = endpoint;
1.1326 + }
1.1327 + else
1.1328 + {
1.1329 + _LastToken = LexToken.TOKEN_CHARACTER;
1.1330 + _LexString = Contents[0].ToString();
1.1331 + _LexChar = Contents[0];
1.1332 + stripchars = 1;
1.1333 + }
1.1334 + }
1.1335 +
1.1336 + /* Strip N chars */
1.1337 + if (stripchars > 0)
1.1338 + {
1.1339 + Contents.Remove(0, stripchars);
1.1340 + }
1.1341 +
1.1342 + return _LastToken;
1.1343 + }
1.1344 + }
1.1345 +}