tools/incparser/IncParser.cs
author Scott Ehlert <ds@alliedmods.net>
Sun Jul 15 08:00:26 2007 +0000 (2007-07-15)
changeset 1116 40cc5e9f0f1b
parent 824 0e9a2c98c9aa
child 2019 9f473114a511
permissions -rw-r--r--
Removed strange debugging code
dvander@824
     1
using System;
dvander@824
     2
using System.Collections.Generic;
dvander@824
     3
using System.IO;
dvander@824
     4
using System.Text;
dvander@824
     5
dvander@824
     6
namespace incparser
dvander@824
     7
{
dvander@824
     8
    enum LexToken
dvander@824
     9
    {
dvander@824
    10
        TOKEN_NONE,
dvander@824
    11
        TOKEN_INCLUDE,
dvander@824
    12
        TOKEN_DEFINE,
dvander@824
    13
        TOKEN_CONST,
dvander@824
    14
        TOKEN_STRUCT,
dvander@824
    15
        TOKEN_FUNCTAG,
dvander@824
    16
        TOKEN_FUNCENUM,
dvander@824
    17
        TOKEN_ENUM,
dvander@824
    18
        TOKEN_NATIVE,
dvander@824
    19
        TOKEN_STOCK,
dvander@824
    20
        TOKEN_PUBLIC,
dvander@824
    21
        TOKEN_FORWARD,
dvander@824
    22
        TOKEN_QUOTCHAR,
dvander@824
    23
        TOKEN_OPERATOR,
dvander@824
    24
        TOKEN_CHARACTER,
dvander@824
    25
        /*TOKEN_STRING,*/
dvander@824
    26
        TOKEN_LABEL,
dvander@824
    27
        TOKEN_SYMBOL,
dvander@824
    28
        TOKEN_DOCBLOCK,
dvander@824
    29
        TOKEN_EOF,
dvander@824
    30
    }
dvander@824
    31
dvander@824
    32
    class ParseException : System.Exception
dvander@824
    33
    {
dvander@824
    34
        public ParseException(string message) : base(message)
dvander@824
    35
        {
dvander@824
    36
        }
dvander@824
    37
    };
dvander@824
    38
         
dvander@824
    39
    class KeywordToken
dvander@824
    40
    {
dvander@824
    41
        public KeywordToken(string k, LexToken t)
dvander@824
    42
        {
dvander@824
    43
            keyword = k;
dvander@824
    44
            token = t;
dvander@824
    45
        }
dvander@824
    46
        public string keyword;
dvander@824
    47
        public LexToken token;
dvander@824
    48
    };
dvander@824
    49
dvander@824
    50
    class Tokenizer
dvander@824
    51
    {
dvander@824
    52
        private static bool s_initialized = false;
dvander@824
    53
        private static int s_position = 0;
dvander@824
    54
        private static KeywordToken [] s_tokens = null;
dvander@824
    55
        public static KeywordToken[] Tokens = null;
dvander@824
    56
dvander@824
    57
        public static void Initialize()
dvander@824
    58
        {
dvander@824
    59
            if (s_initialized)
dvander@824
    60
            {
dvander@824
    61
                return;
dvander@824
    62
            }
dvander@824
    63
            s_tokens = new KeywordToken[12];
dvander@824
    64
            s_initialized = true;
dvander@824
    65
            s_position = 0;
dvander@824
    66
dvander@824
    67
            AddToken("#include", LexToken.TOKEN_INCLUDE);
dvander@824
    68
            AddToken("#define", LexToken.TOKEN_DEFINE);
dvander@824
    69
            AddToken("native", LexToken.TOKEN_NATIVE);
dvander@824
    70
            AddToken("functag", LexToken.TOKEN_FUNCTAG);
dvander@824
    71
            AddToken("funcenum", LexToken.TOKEN_FUNCENUM);
dvander@824
    72
            AddToken("enum", LexToken.TOKEN_ENUM);
dvander@824
    73
            AddToken("stock", LexToken.TOKEN_STOCK);
dvander@824
    74
            AddToken("public", LexToken.TOKEN_PUBLIC);
dvander@824
    75
            AddToken("forward", LexToken.TOKEN_FORWARD);
dvander@824
    76
            AddToken("const", LexToken.TOKEN_CONST);
dvander@824
    77
            AddToken("struct", LexToken.TOKEN_STRUCT);
dvander@824
    78
            AddToken("operator", LexToken.TOKEN_OPERATOR);
dvander@824
    79
dvander@824
    80
            Tokens = s_tokens;
dvander@824
    81
        }
dvander@824
    82
dvander@824
    83
        private static void AddToken(string lex, LexToken tok)
dvander@824
    84
        {
dvander@824
    85
            s_tokens[s_position] = new KeywordToken(lex, tok);
dvander@824
    86
            s_position++;
dvander@824
    87
        }
dvander@824
    88
    };
dvander@824
    89
dvander@824
    90
    class IncParser
dvander@824
    91
    {
dvander@824
    92
        private string FileName;
dvander@824
    93
        private StringBuilder Contents;
dvander@824
    94
        private bool LexPushed;
dvander@824
    95
        private LexToken _LastToken;
dvander@824
    96
        private string _LexString;
dvander@824
    97
        private char _LexChar;
dvander@824
    98
        private uint LineNo;
dvander@824
    99
dvander@824
   100
        public string LexString
dvander@824
   101
        {
dvander@824
   102
            get
dvander@824
   103
            {
dvander@824
   104
                return _LexString;
dvander@824
   105
            }
dvander@824
   106
        }
dvander@824
   107
dvander@824
   108
        public char LexChar
dvander@824
   109
        {
dvander@824
   110
            get
dvander@824
   111
            {
dvander@824
   112
                return _LexChar;
dvander@824
   113
            }
dvander@824
   114
        }
dvander@824
   115
dvander@824
   116
        public uint GetLineNumber()
dvander@824
   117
        {
dvander@824
   118
            return LineNo;
dvander@824
   119
        }
dvander@824
   120
dvander@824
   121
        public IncParser(string file)
dvander@824
   122
        {
dvander@824
   123
            FileName = file;
dvander@824
   124
dvander@824
   125
            /* Clear out lexer stuff */
dvander@824
   126
            LexPushed = false;
dvander@824
   127
            _LastToken = LexToken.TOKEN_NONE;
dvander@824
   128
            _LexString = null;
dvander@824
   129
            _LexChar = '\0';
dvander@824
   130
            LineNo = 1;
dvander@824
   131
dvander@824
   132
            /* Initialize; this can throw an exception */
dvander@824
   133
            Initialize();
dvander@824
   134
        }
dvander@824
   135
dvander@824
   136
        public void Parse(ParseWriter w)
dvander@824
   137
        {
dvander@824
   138
            w.BeginSection(System.IO.Path.GetFileName(FileName));
dvander@824
   139
            LexToken tok = LexToken.TOKEN_NONE;
dvander@824
   140
            while ((tok = lex()) != LexToken.TOKEN_EOF)
dvander@824
   141
            {
dvander@824
   142
                switch (tok)
dvander@824
   143
                {
dvander@824
   144
                    case LexToken.TOKEN_DOCBLOCK:
dvander@824
   145
                        {
dvander@824
   146
                            w.WritePair("doc", LexString);
dvander@824
   147
                            break;
dvander@824
   148
                        }
dvander@824
   149
                    case LexToken.TOKEN_DEFINE:
dvander@824
   150
                        {
dvander@824
   151
                            PARSE_Define(w);
dvander@824
   152
                            break;
dvander@824
   153
                        }
dvander@824
   154
                    case LexToken.TOKEN_ENUM:
dvander@824
   155
                        {
dvander@824
   156
                            PARSE_Enum(w);
dvander@824
   157
                            break;
dvander@824
   158
                        }
dvander@824
   159
                    case LexToken.TOKEN_FORWARD:
dvander@824
   160
                        {
dvander@824
   161
                            PARSE_Function(tok, w);
dvander@824
   162
                            break;
dvander@824
   163
                        }
dvander@824
   164
                    case LexToken.TOKEN_NATIVE:
dvander@824
   165
                        {
dvander@824
   166
                            PARSE_Function(tok, w);
dvander@824
   167
                            break;
dvander@824
   168
                        }
dvander@824
   169
                    case LexToken.TOKEN_STOCK:
dvander@824
   170
                        {
dvander@824
   171
                            PARSE_Function(tok, w);
dvander@824
   172
                            break;
dvander@824
   173
                        }
dvander@824
   174
                    case LexToken.TOKEN_FUNCTAG:
dvander@824
   175
                        {
dvander@824
   176
                            PARSE_FuncTag(w);
dvander@824
   177
                            break;
dvander@824
   178
                        }
dvander@824
   179
                    case LexToken.TOKEN_FUNCENUM:
dvander@824
   180
                        {
dvander@824
   181
                            PARSE_FuncEnum(w);
dvander@824
   182
                            break;
dvander@824
   183
                        }
dvander@824
   184
                    case LexToken.TOKEN_INCLUDE:
dvander@824
   185
                        {
dvander@824
   186
                            PARSE_Include(w);
dvander@824
   187
                            break;
dvander@824
   188
                        }
dvander@824
   189
                    case LexToken.TOKEN_STRUCT:
dvander@824
   190
                        {
dvander@824
   191
                            PARSE_Struct(w);
dvander@824
   192
                            break;
dvander@824
   193
                        }
dvander@824
   194
                    case LexToken.TOKEN_PUBLIC:
dvander@824
   195
                        {
dvander@824
   196
                            PARSE_Public(w);
dvander@824
   197
                            break;
dvander@824
   198
                        }
dvander@824
   199
                    default:
dvander@824
   200
                        {
dvander@824
   201
                            throw new ParseException("Unrecognized token: " + tok);
dvander@824
   202
                        }
dvander@824
   203
                }
dvander@824
   204
            }
dvander@824
   205
            w.EndSection();
dvander@824
   206
        }
dvander@824
   207
dvander@824
   208
        private void PARSE_Public(ParseWriter w)
dvander@824
   209
        {
dvander@824
   210
            /* Eat up an optional tag */
dvander@824
   211
            MatchToken(LexToken.TOKEN_LABEL);
dvander@824
   212
            
dvander@824
   213
            /* Eat up a name */
dvander@824
   214
            NeedToken(LexToken.TOKEN_SYMBOL);
dvander@824
   215
dvander@824
   216
            if (MatchChar('='))
dvander@824
   217
            {
dvander@824
   218
                ignore_block();
dvander@824
   219
            }
dvander@824
   220
dvander@824
   221
            NeedChar(';');
dvander@824
   222
        }
dvander@824
   223
dvander@824
   224
        private void PARSE_Struct(ParseWriter w)
dvander@824
   225
        {
dvander@824
   226
            /* For now, we completely ignore these (they're not written to the output) */
dvander@824
   227
            NeedToken(LexToken.TOKEN_SYMBOL);
dvander@824
   228
            NeedChar('{');
dvander@824
   229
dvander@824
   230
            bool need_closebrace = true;
dvander@824
   231
            bool more_entries = true;
dvander@824
   232
            do
dvander@824
   233
            {
dvander@824
   234
                if (MatchChar('}'))
dvander@824
   235
                {
dvander@824
   236
                    need_closebrace = false;
dvander@824
   237
                    break;
dvander@824
   238
                }
dvander@824
   239
                MatchToken(LexToken.TOKEN_CONST);
dvander@824
   240
                MatchToken(LexToken.TOKEN_LABEL);
dvander@824
   241
                NeedToken(LexToken.TOKEN_SYMBOL);
dvander@824
   242
                while (MatchChar('['))
dvander@824
   243
                {
dvander@824
   244
                    DiscardUntilChar(']');
dvander@824
   245
                    NeedChar(']');
dvander@824
   246
                }
dvander@824
   247
                more_entries = MatchChar(',');
dvander@824
   248
                MatchToken(LexToken.TOKEN_DOCBLOCK);
dvander@824
   249
                if (!more_entries)
dvander@824
   250
                {
dvander@824
   251
                    break;
dvander@824
   252
                }
dvander@824
   253
            } while (true);
dvander@824
   254
            if (need_closebrace)
dvander@824
   255
            {
dvander@824
   256
                NeedChar('}');
dvander@824
   257
            }
dvander@824
   258
            NeedChar(';');
dvander@824
   259
        }
dvander@824
   260
dvander@824
   261
        private void PARSE_Include(ParseWriter w)
dvander@824
   262
        {
dvander@824
   263
            /* read until the end of the line */
dvander@824
   264
            int index = Contents.ToString().IndexOf('\n');
dvander@824
   265
            string value;
dvander@824
   266
            if (index == -1)
dvander@824
   267
            {
dvander@824
   268
                value = Contents.ToString();
dvander@824
   269
                Contents.Remove(0, value.Length);
dvander@824
   270
            }
dvander@824
   271
            else if (index == 0)
dvander@824
   272
            {
dvander@824
   273
                value = "";
dvander@824
   274
            }
dvander@824
   275
            else
dvander@824
   276
            {
dvander@824
   277
                value = Contents.ToString().Substring(0, index);
dvander@824
   278
                Contents.Remove(0, index);
dvander@824
   279
            }
dvander@824
   280
dvander@824
   281
            /* Strip whitespace */
dvander@824
   282
            value = value.Trim('\r', '\t', ' ');
dvander@824
   283
dvander@824
   284
            /* Write */
dvander@824
   285
            w.BeginSection("include");
dvander@824
   286
            w.WritePair("name", value);
dvander@824
   287
            w.EndSection();
dvander@824
   288
        }
dvander@824
   289
dvander@824
   290
        private void PARSE_FuncEnum(ParseWriter w)
dvander@824
   291
        {
dvander@824
   292
            w.BeginSection("funcenum");
dvander@824
   293
dvander@824
   294
            /* Get the functag name */
dvander@824
   295
            NeedToken(LexToken.TOKEN_SYMBOL);
dvander@824
   296
            w.WritePair("name", LexString);
dvander@824
   297
dvander@824
   298
            NeedChar('{');
dvander@824
   299
dvander@824
   300
            bool need_closebrace = true;
dvander@824
   301
            do
dvander@824
   302
            {
dvander@824
   303
                /* Shortcut out? */
dvander@824
   304
                if (MatchChar('}'))
dvander@824
   305
                {
dvander@824
   306
                    need_closebrace = false;
dvander@824
   307
                    break;
dvander@824
   308
                }
dvander@824
   309
dvander@824
   310
                /* Start function section */
dvander@824
   311
                w.BeginSection("function");
dvander@824
   312
dvander@824
   313
                if (MatchToken(LexToken.TOKEN_DOCBLOCK))
dvander@824
   314
                {
dvander@824
   315
                    w.WritePair("doc", LexString);
dvander@824
   316
                }
dvander@824
   317
                
dvander@824
   318
                /* Get the return tag */
dvander@824
   319
                if (MatchToken(LexToken.TOKEN_LABEL))
dvander@824
   320
                {
dvander@824
   321
                    w.WritePair("return", LexString);
dvander@824
   322
                }
dvander@824
   323
dvander@824
   324
                /* Get function type */
dvander@824
   325
                if (MatchToken(LexToken.TOKEN_PUBLIC))
dvander@824
   326
                {
dvander@824
   327
                    w.WritePair("type", "public");
dvander@824
   328
                }
dvander@824
   329
                else if (MatchToken(LexToken.TOKEN_STOCK))
dvander@824
   330
                {
dvander@824
   331
                    w.WritePair("type", "stock");
dvander@824
   332
                }
dvander@824
   333
                
dvander@824
   334
                /* Parse the parameters */
dvander@824
   335
                ParseParameters(w);
dvander@824
   336
dvander@824
   337
                /* End the section */
dvander@824
   338
                w.EndSection();
dvander@824
   339
dvander@824
   340
                if (!MatchChar(','))
dvander@824
   341
                {
dvander@824
   342
                    break;
dvander@824
   343
                }
dvander@824
   344
            } while (true);
dvander@824
   345
dvander@824
   346
            if (need_closebrace)
dvander@824
   347
            {
dvander@824
   348
                NeedChar('}');
dvander@824
   349
            }
dvander@824
   350
            NeedChar(';');
dvander@824
   351
dvander@824
   352
            w.EndSection();
dvander@824
   353
        }
dvander@824
   354
dvander@824
   355
        private void PARSE_FuncTag(ParseWriter w)
dvander@824
   356
        {
dvander@824
   357
            w.BeginSection("functag");
dvander@824
   358
dvander@824
   359
            /* Get the functag name */
dvander@824
   360
            NeedToken(LexToken.TOKEN_SYMBOL);
dvander@824
   361
            w.WritePair("name", LexString);
dvander@824
   362
dvander@824
   363
            if (MatchToken(LexToken.TOKEN_LABEL))
dvander@824
   364
            {
dvander@824
   365
                w.WritePair("return", LexString);
dvander@824
   366
            }
dvander@824
   367
dvander@824
   368
            if (MatchToken(LexToken.TOKEN_PUBLIC))
dvander@824
   369
            {
dvander@824
   370
                w.WritePair("type", "public");
dvander@824
   371
            }
dvander@824
   372
            else if (MatchToken(LexToken.TOKEN_STOCK))
dvander@824
   373
            {
dvander@824
   374
                w.WritePair("type", "stock");
dvander@824
   375
            }
dvander@824
   376
dvander@824
   377
            ParseParameters(w);
dvander@824
   378
            NeedChar(';');
dvander@824
   379
            
dvander@824
   380
            w.EndSection();
dvander@824
   381
        }
dvander@824
   382
dvander@824
   383
        private void PARSE_Function(LexToken tok, ParseWriter w)
dvander@824
   384
        {
dvander@824
   385
            string tag="", name;
dvander@824
   386
dvander@824
   387
            /* Get the return value */
dvander@824
   388
            if (MatchToken(LexToken.TOKEN_LABEL))
dvander@824
   389
            {
dvander@824
   390
                tag = LexString;
dvander@824
   391
            }
dvander@824
   392
dvander@824
   393
            if (MatchToken(LexToken.TOKEN_OPERATOR))
dvander@824
   394
            {
dvander@824
   395
                /* Check if we're some sort of invalid function */
dvander@824
   396
                DiscardUntilCharOrComment('(', false);
dvander@824
   397
                ParseParameters(null);
dvander@824
   398
                if (tok == LexToken.TOKEN_STOCK)
dvander@824
   399
                {
dvander@824
   400
                    ignore_block();
dvander@824
   401
                }
dvander@824
   402
                else if (tok == LexToken.TOKEN_NATIVE || tok == LexToken.TOKEN_FORWARD)
dvander@824
   403
                {
dvander@824
   404
                    if (MatchChar('='))
dvander@824
   405
                    {
dvander@824
   406
                        DiscardUntilCharOrComment(';', false);
dvander@824
   407
                    }
dvander@824
   408
                    NeedChar(';');
dvander@824
   409
                }
dvander@824
   410
                return;
dvander@824
   411
            }
dvander@824
   412
dvander@824
   413
            /* Get the name */
dvander@824
   414
            NeedToken(LexToken.TOKEN_SYMBOL);
dvander@824
   415
            name = LexString;
dvander@824
   416
dvander@824
   417
            PARSE_Function(tok, w, tag, name);
dvander@824
   418
        }
dvander@824
   419
dvander@824
   420
        private void PARSE_Function(LexToken tok, ParseWriter w, string tag, string name)
dvander@824
   421
        {
dvander@824
   422
            if (tok == LexToken.TOKEN_FORWARD)
dvander@824
   423
            {
dvander@824
   424
                w.BeginSection("forward");
dvander@824
   425
            }
dvander@824
   426
            else if (tok == LexToken.TOKEN_NATIVE)
dvander@824
   427
            {
dvander@824
   428
                w.BeginSection("native");
dvander@824
   429
            }
dvander@824
   430
            else if (tok == LexToken.TOKEN_STOCK)
dvander@824
   431
            {
dvander@824
   432
                w.BeginSection("stock");
dvander@824
   433
            }
dvander@824
   434
dvander@824
   435
            w.WritePair("name", name);
dvander@824
   436
dvander@824
   437
            if (tag.Length > 0)
dvander@824
   438
            {
dvander@824
   439
                w.WritePair("return", tag);
dvander@824
   440
            }
dvander@824
   441
dvander@824
   442
            ParseParameters(w);
dvander@824
   443
dvander@824
   444
            if (tok == LexToken.TOKEN_STOCK)
dvander@824
   445
            {
dvander@824
   446
                ignore_block();
dvander@824
   447
            }
dvander@824
   448
            else
dvander@824
   449
            {
dvander@824
   450
                /* Make sure there's a semicolon */
dvander@824
   451
                if (MatchChar('='))
dvander@824
   452
                {
dvander@824
   453
                    DiscardUntilCharOrComment(';', false);
dvander@824
   454
                }
dvander@824
   455
                NeedChar(';');
dvander@824
   456
            }
dvander@824
   457
dvander@824
   458
            w.EndSection();
dvander@824
   459
        }
dvander@824
   460
dvander@824
   461
        private void ParseParameters(ParseWriter w)
dvander@824
   462
        {
dvander@824
   463
            NeedChar('(');
dvander@824
   464
            string extra;
dvander@824
   465
            do
dvander@824
   466
            {
dvander@824
   467
                if (MatchChar(')'))
dvander@824
   468
                {
dvander@824
   469
                    return;
dvander@824
   470
                }
dvander@824
   471
dvander@824
   472
                /* Start the parameter */
dvander@824
   473
                if (w != null)
dvander@824
   474
                {
dvander@824
   475
                    w.BeginSection("parameter");
dvander@824
   476
                }
dvander@824
   477
dvander@824
   478
                /* Check for a 'const' token */
dvander@824
   479
                if (MatchToken(LexToken.TOKEN_CONST) && w != null)
dvander@824
   480
                {
dvander@824
   481
                    w.WritePair("const", "true");
dvander@824
   482
                }
dvander@824
   483
dvander@824
   484
                /* Check if it's by reference */
dvander@824
   485
                if (MatchChar('&') && w != null)
dvander@824
   486
                {
dvander@824
   487
                    w.WritePair("byref", "true");
dvander@824
   488
                }
dvander@824
   489
dvander@824
   490
                /* Check for a tag */
dvander@824
   491
                if (MatchToken(LexToken.TOKEN_LABEL) && w != null)
dvander@824
   492
                {
dvander@824
   493
                    w.WritePair("tag", LexString);
dvander@824
   494
                }
dvander@824
   495
dvander@824
   496
                /* Get the symbol and write it */
dvander@824
   497
                if (MatchString(0, "...") && w != null)
dvander@824
   498
                {
dvander@824
   499
                    w.WritePair("name", "...");
dvander@824
   500
                }
dvander@824
   501
                else
dvander@824
   502
                {
dvander@824
   503
                    NeedToken(LexToken.TOKEN_SYMBOL);
dvander@824
   504
                    if (w != null)
dvander@824
   505
                    {
dvander@824
   506
                        w.WritePair("name", LexString);
dvander@824
   507
                    }
dvander@824
   508
                }
dvander@824
   509
dvander@824
   510
                /* Check if we have a default value yet */
dvander@824
   511
                bool default_value = false;
dvander@824
   512
                if (MatchChar('='))
dvander@824
   513
                {
dvander@824
   514
                    default_value = true;
dvander@824
   515
                }
dvander@824
   516
                else if (MatchChar('['))
dvander@824
   517
                {
dvander@824
   518
                    extra = "";
dvander@824
   519
                    string temp;
dvander@824
   520
                    do
dvander@824
   521
                    {
dvander@824
   522
                        extra += "[";
dvander@824
   523
                        temp = ConsumeUntilChar(']');
dvander@824
   524
                        if (temp != null)
dvander@824
   525
                        {
dvander@824
   526
                            extra += temp;
dvander@824
   527
                        }
dvander@824
   528
                        extra += "]";
dvander@824
   529
                        NeedChar(']');
dvander@824
   530
                    } while (MatchChar('['));
dvander@824
   531
                    w.WritePair("decoration", extra);
dvander@824
   532
                }
dvander@824
   533
dvander@824
   534
                /* If we have a default value, get it */
dvander@824
   535
                if (default_value || MatchChar('='))
dvander@824
   536
                {
dvander@824
   537
                    if ((extra = ConsumeParamDefValue()) == null)
dvander@824
   538
                    {
dvander@824
   539
                        throw new ParseException("Expected a default value; found none");
dvander@824
   540
                    }
dvander@824
   541
                    if (w != null)
dvander@824
   542
                    {
dvander@824
   543
                        w.WritePair("defval", extra);
dvander@824
   544
                    }
dvander@824
   545
                }
dvander@824
   546
dvander@824
   547
                /* End the parameter */
dvander@824
   548
                if (w != null)
dvander@824
   549
                {
dvander@824
   550
                    w.EndSection();
dvander@824
   551
                }
dvander@824
   552
dvander@824
   553
                if (!MatchChar(','))
dvander@824
   554
                {
dvander@824
   555
                    break;
dvander@824
   556
                }
dvander@824
   557
            } while (true);
dvander@824
   558
dvander@824
   559
            NeedChar(')');
dvander@824
   560
        }
dvander@824
   561
dvander@824
   562
        private void PARSE_Enum(ParseWriter w)
dvander@824
   563
        {
dvander@824
   564
            string name = "";
dvander@824
   565
            if (MatchToken(LexToken.TOKEN_SYMBOL))
dvander@824
   566
            {
dvander@824
   567
                name = LexString;
dvander@824
   568
            }
dvander@824
   569
dvander@824
   570
            w.BeginSection("enum");
dvander@824
   571
            w.WritePair("name", name);
dvander@824
   572
            w.BeginSection("properties");
dvander@824
   573
dvander@824
   574
            NeedChar('{');
dvander@824
   575
            bool more_entries = true;
dvander@824
   576
            bool need_closebrace = true;
dvander@824
   577
            do
dvander@824
   578
            {
dvander@824
   579
                if (MatchChar('}'))
dvander@824
   580
                {
dvander@824
   581
                    need_closebrace = false;
dvander@824
   582
                    break;
dvander@824
   583
                }
dvander@824
   584
                NeedToken(LexToken.TOKEN_SYMBOL);
dvander@824
   585
                name = LexString;
dvander@824
   586
                if (MatchChar('='))
dvander@824
   587
                {
dvander@824
   588
                    DiscardUntilCharOrComment(',', true);
dvander@824
   589
                }
dvander@824
   590
                more_entries = MatchChar(',');
dvander@824
   591
                w.WritePair("name", name);
dvander@824
   592
                if (MatchToken(LexToken.TOKEN_DOCBLOCK))
dvander@824
   593
                {
dvander@824
   594
                    w.WritePair("doc", LexString);
dvander@824
   595
                }
dvander@824
   596
            } while (more_entries);
dvander@824
   597
            if (need_closebrace)
dvander@824
   598
            {
dvander@824
   599
                NeedChar('}');
dvander@824
   600
            }
dvander@824
   601
            NeedChar(';');
dvander@824
   602
            w.EndSection();
dvander@824
   603
            w.EndSection();
dvander@824
   604
        }
dvander@824
   605
dvander@824
   606
        private void PARSE_Define(ParseWriter w)
dvander@824
   607
        {
dvander@824
   608
            /* first, get the symbol name */
dvander@824
   609
            NeedToken(LexToken.TOKEN_SYMBOL);
dvander@824
   610
            string name = LexString;
dvander@824
   611
            string value;
dvander@824
   612
dvander@824
   613
            /* read until we hit the end of a line OR a comment */
dvander@824
   614
            int endpoint = -1;
dvander@824
   615
            for (int i = 0; i < Contents.Length; i++)
dvander@824
   616
            {
dvander@824
   617
                if (Contents[i] == '\n')
dvander@824
   618
                {
dvander@824
   619
                    endpoint = i;
dvander@824
   620
                    break;
dvander@824
   621
                }
dvander@824
   622
                else if (CheckString(i, "/**"))
dvander@824
   623
                {
dvander@824
   624
                    endpoint = i;
dvander@824
   625
                    break;
dvander@824
   626
                }
dvander@824
   627
            }
dvander@824
   628
dvander@824
   629
            if (endpoint < 1)
dvander@824
   630
            {
dvander@824
   631
                value = "";
dvander@824
   632
            }
dvander@824
   633
            else
dvander@824
   634
            {
dvander@824
   635
                value = Contents.ToString().Substring(0, endpoint);
dvander@824
   636
                Contents.Remove(0, endpoint);
dvander@824
   637
            }
dvander@824
   638
dvander@824
   639
            /* Write */
dvander@824
   640
            w.BeginSection("define");
dvander@824
   641
            w.WritePair("name", name);
dvander@824
   642
            w.WritePair("linetext", value);
dvander@824
   643
            w.EndSection();
dvander@824
   644
        }
dvander@824
   645
dvander@824
   646
        /* Reads up to a character and discards all text before it.
dvander@824
   647
         */
dvander@824
   648
        private void DiscardUntilChar(char c)
dvander@824
   649
        {
dvander@824
   650
            int eol = Contents.ToString().IndexOf(c);
dvander@824
   651
dvander@824
   652
            if (eol < 1)
dvander@824
   653
            {
dvander@824
   654
                return;
dvander@824
   655
            }
dvander@824
   656
dvander@824
   657
            Contents.Remove(0, eol);
dvander@824
   658
        }
dvander@824
   659
dvander@824
   660
        /* Same as DiscardUntilChar, except the discarded
dvander@824
   661
         * text is returned first.
dvander@824
   662
         */
dvander@824
   663
        private string ConsumeUntilChar(char c)
dvander@824
   664
        {
dvander@824
   665
            int eol = Contents.ToString().IndexOf(c);
dvander@824
   666
dvander@824
   667
            if (eol < 1)
dvander@824
   668
            {
dvander@824
   669
                return null;
dvander@824
   670
            }
dvander@824
   671
dvander@824
   672
            string rest = Contents.ToString().Substring(0, eol);
dvander@824
   673
            Contents.Remove(0, eol);
dvander@824
   674
            return rest;
dvander@824
   675
        }
dvander@824
   676
dvander@824
   677
        /**
dvander@824
   678
         * Returns a parameter's default value.
dvander@824
   679
         */
dvander@824
   680
        private string ConsumeParamDefValue()
dvander@824
   681
        {
dvander@824
   682
            int eol = FindEndOfExprLine(0);
dvander@824
   683
            if (eol < 1)
dvander@824
   684
            {
dvander@824
   685
                return null;
dvander@824
   686
            }
dvander@824
   687
dvander@824
   688
            string rest = Contents.ToString().Substring(0, eol);
dvander@824
   689
            Contents.Remove(0, eol);
dvander@824
   690
            return rest;
dvander@824
   691
        }
dvander@824
   692
dvander@824
   693
        /**
dvander@824
   694
         * Discards all data up to a certain character, ignoring
dvander@824
   695
         * the contents, unless specified to stop at a doc block comment.
dvander@824
   696
         */
dvander@824
   697
        private void DiscardUntilCharOrComment(char c, bool stop_at_comment)
dvander@824
   698
        {
dvander@824
   699
            for (int i = 0; i < Contents.Length; i++)
dvander@824
   700
            {
dvander@824
   701
                if (Contents[i] == '\n')
dvander@824
   702
                {
dvander@824
   703
                    LineNo++;
dvander@824
   704
                }
dvander@824
   705
                else if (Contents[i] == c)
dvander@824
   706
                {
dvander@824
   707
                    if (i > 0)
dvander@824
   708
                    {
dvander@824
   709
                        Contents.Remove(0, i);
dvander@824
   710
                    }
dvander@824
   711
                    return;
dvander@824
   712
                }
dvander@824
   713
                else if (stop_at_comment && CheckString(i, "/**"))
dvander@824
   714
                {
dvander@824
   715
                    if (i > 0)
dvander@824
   716
                    {
dvander@824
   717
                        Contents.Remove(0, i);
dvander@824
   718
                    }
dvander@824
   719
                    return;
dvander@824
   720
                }
dvander@824
   721
            }
dvander@824
   722
        }
dvander@824
   723
dvander@824
   724
        /**
dvander@824
   725
         * Finds the end of an expression
dvander@824
   726
         */
dvander@824
   727
        private int FindEndOfExprLine(int start)
dvander@824
   728
        {
dvander@824
   729
            for (int i = start; i < Contents.Length; i++)
dvander@824
   730
            {
dvander@824
   731
                if (Contents[i] == '{')
dvander@824
   732
                {
dvander@824
   733
                    if ((i = FindEndOfConsumptionLine('}', start+1)) == -1)
dvander@824
   734
                    {
dvander@824
   735
                        throw new ParseException("Expected token: } (found none)");
dvander@824
   736
                    }
dvander@824
   737
                }
dvander@824
   738
                else if (Contents[i] == ')' || Contents[i] == ',')
dvander@824
   739
                {
dvander@824
   740
                    return i;
dvander@824
   741
                }
dvander@824
   742
            }
dvander@824
   743
dvander@824
   744
            return -1;
dvander@824
   745
        }
dvander@824
   746
dvander@824
   747
        private int FindEndOfConsumptionLine(char c, int start)
dvander@824
   748
        {
dvander@824
   749
            for (int i = start; i < Contents.Length; i++)
dvander@824
   750
            {
dvander@824
   751
                if (Contents[i] == '{')
dvander@824
   752
                {
dvander@824
   753
                    if ((i = FindEndOfConsumptionLine('}', i + 1)) == -1)
dvander@824
   754
                    {
dvander@824
   755
                        throw new ParseException("Expected token: } (found none)");
dvander@824
   756
                    }
dvander@824
   757
                }
dvander@824
   758
                else if (Contents[i] == c)
dvander@824
   759
                {
dvander@824
   760
                    return i;
dvander@824
   761
                }
dvander@824
   762
            }
dvander@824
   763
dvander@824
   764
            return -1;
dvander@824
   765
        }
dvander@824
   766
dvander@824
   767
        private void Initialize()
dvander@824
   768
        {
dvander@824
   769
            /* Open file, read it, and close it */
dvander@824
   770
            StreamReader sr = File.OpenText(FileName);
dvander@824
   771
            Contents = new StringBuilder(sr.ReadToEnd());
dvander@824
   772
            sr.Close();
dvander@824
   773
dvander@824
   774
            /* Strip comments (may throw an exception) */
dvander@824
   775
            preprocess();
dvander@824
   776
dvander@824
   777
            /* Lastly, initialize the tokenizer */
dvander@824
   778
            Tokenizer.Initialize();
dvander@824
   779
        }
dvander@824
   780
dvander@824
   781
        /* Consumes a token iff it matches */
dvander@824
   782
        private bool MatchToken(LexToken token)
dvander@824
   783
        {
dvander@824
   784
            LexToken tok = lex();
dvander@824
   785
            if (tok != token)
dvander@824
   786
            {
dvander@824
   787
                lexpush();
dvander@824
   788
                return false;
dvander@824
   789
            }
dvander@824
   790
            return true;
dvander@824
   791
        }
dvander@824
   792
dvander@824
   793
        /* Consumes a token and aborts the parser if there is no match */
dvander@824
   794
        private void NeedToken(LexToken token)
dvander@824
   795
        {
dvander@824
   796
            if (!MatchToken(token))
dvander@824
   797
            {
dvander@824
   798
                throw new ParseException("Expected token: " + token);
dvander@824
   799
            }
dvander@824
   800
        }
dvander@824
   801
dvander@824
   802
        /* Consumes a character iff it matches */
dvander@824
   803
        private bool MatchChar(char c)
dvander@824
   804
        {
dvander@824
   805
            if (lex() != LexToken.TOKEN_CHARACTER)
dvander@824
   806
            {
dvander@824
   807
                lexpush();
dvander@824
   808
                return false;
dvander@824
   809
            }
dvander@824
   810
            if (_LexChar != c)
dvander@824
   811
            {
dvander@824
   812
                lexpush();
dvander@824
   813
                return false;
dvander@824
   814
            }
dvander@824
   815
            return true;
dvander@824
   816
        }
dvander@824
   817
dvander@824
   818
        /* Consumes a character or aborts the parser if there is no match */
dvander@824
   819
        private void NeedChar(char c)
dvander@824
   820
        {
dvander@824
   821
            if (!MatchChar(c))
dvander@824
   822
            {
dvander@824
   823
                throw new ParseException("Expected character: " + c);
dvander@824
   824
            }
dvander@824
   825
        }
dvander@824
   826
        
dvander@824
   827
        /* Saves the lexer state so we don't reparse unnecessarily */
dvander@824
   828
        private void lexpush()
dvander@824
   829
        {
dvander@824
   830
            if (LexPushed)
dvander@824
   831
            {
dvander@824
   832
                throw new ParseException("Lexer cannot be pushed twice");
dvander@824
   833
            }
dvander@824
   834
            LexPushed = true;
dvander@824
   835
        }
dvander@824
   836
dvander@824
   837
        /* Attempts to match a string against the next stream tokens.
dvander@824
   838
         * On success, the token stream is advanced.
dvander@824
   839
         */
dvander@824
   840
        private bool MatchString(int startPos, string str)
dvander@824
   841
        {
dvander@824
   842
            if (str.Length > Contents.Length - startPos)
dvander@824
   843
            {
dvander@824
   844
                return false;
dvander@824
   845
            }
dvander@824
   846
            for (int i = 0; i < str.Length; i++)
dvander@824
   847
            {
dvander@824
   848
                if (str[i] != Contents[startPos + i])
dvander@824
   849
                {
dvander@824
   850
                    return false;
dvander@824
   851
                }
dvander@824
   852
            }
dvander@824
   853
            Contents.Remove(startPos, str.Length);
dvander@824
   854
            return true;
dvander@824
   855
        }
dvander@824
   856
dvander@824
   857
        /* Attempts to match a string against the next stream tokens */
dvander@824
   858
        private bool CheckString(int startPos, string str)
dvander@824
   859
        {
dvander@824
   860
            if (str.Length > Contents.Length - startPos)
dvander@824
   861
            {
dvander@824
   862
                return false;
dvander@824
   863
            }
dvander@824
   864
            for (int i = 0; i < str.Length; i++)
dvander@824
   865
            {
dvander@824
   866
                if (str[i] != Contents[startPos + i])
dvander@824
   867
                {
dvander@824
   868
                    return false;
dvander@824
   869
                }
dvander@824
   870
            }
dvander@824
   871
            return true;
dvander@824
   872
        }
dvander@824
   873
dvander@824
   874
        /* Ignores a block starting and ending with { and } respectively */
dvander@824
   875
        private void ignore_block()
dvander@824
   876
        {
dvander@824
   877
            /* Hrm, this is tricky.  First, expect an open brace. */
dvander@824
   878
            NeedChar('{');
dvander@824
   879
            /* Consume the entire function by keeping track of braces */
dvander@824
   880
            int numBraces = 1;
dvander@824
   881
            bool in_string = false;
dvander@824
   882
            bool found_end = false;
dvander@824
   883
            for (int i = 0; i < Contents.Length; i++)
dvander@824
   884
            {
dvander@824
   885
                if (Contents[i] == '\n')
dvander@824
   886
                {
dvander@824
   887
                    LineNo++;
dvander@824
   888
                }
dvander@824
   889
                if (!in_string)
dvander@824
   890
                {
dvander@824
   891
                    if (Contents[i] == '"')
dvander@824
   892
                    {
dvander@824
   893
                        in_string = true;
dvander@824
   894
                    }
dvander@824
   895
                    else if (Contents[i] == '{')
dvander@824
   896
                    {
dvander@824
   897
                        numBraces++;
dvander@824
   898
                    }
dvander@824
   899
                    else if (Contents[i] == '}')
dvander@824
   900
                    {
dvander@824
   901
                        if (numBraces == 0)
dvander@824
   902
                        {
dvander@824
   903
                            throw new ParseException("Unable to find a matching open brace");
dvander@824
   904
                        }
dvander@824
   905
                        if (--numBraces == 0)
dvander@824
   906
                        {
dvander@824
   907
                            Contents.Remove(0, i + 1);
dvander@824
   908
                            found_end = true;
dvander@824
   909
                            break;
dvander@824
   910
                        }
dvander@824
   911
                    }
dvander@824
   912
                }
dvander@824
   913
                else
dvander@824
   914
                {
dvander@824
   915
                    /* It is possible to be in a string and at the first character */
dvander@824
   916
                    if (Contents[i] == '"' && Contents[i - 1] != '\\')
dvander@824
   917
                    {
dvander@824
   918
                        in_string = false;
dvander@824
   919
                    }
dvander@824
   920
                }
dvander@824
   921
            }
dvander@824
   922
            if (!found_end)
dvander@824
   923
            {
dvander@824
   924
                throw new ParseException("Unable to find a matching close brace");
dvander@824
   925
            }
dvander@824
   926
        }
dvander@824
   927
dvander@824
   928
        /* Strips all non-doc C and C++ style comments. as well as unused pragmas */
dvander@824
   929
        private void preprocess()
dvander@824
   930
        {
dvander@824
   931
            if (Contents.Length == 1)
dvander@824
   932
            {
dvander@824
   933
                return;
dvander@824
   934
            }
dvander@824
   935
dvander@824
   936
            bool in_comment = false;
dvander@824
   937
            bool is_ml = false;
dvander@824
   938
            bool is_doc = false;
dvander@824
   939
            bool is_newline = true;
dvander@824
   940
dvander@824
   941
            for (int i=0; i<Contents.Length-1; i++)
dvander@824
   942
            {
dvander@824
   943
                if (Contents[i] == '\n')
dvander@824
   944
                {
dvander@824
   945
                    is_newline = true;
dvander@824
   946
                }
dvander@824
   947
                if (!in_comment)
dvander@824
   948
                {
dvander@824
   949
                    if (Contents[i] == '/')
dvander@824
   950
                    {
dvander@824
   951
                        if (Contents[i+1] == '/')
dvander@824
   952
                        {
dvander@824
   953
                            in_comment = true;
dvander@824
   954
                            is_ml = false;
dvander@824
   955
                            is_doc = false;
dvander@824
   956
                        } else if (Contents[i+1] == '*') {
dvander@824
   957
                            /* Detect whether this is a doc comment or not */
dvander@824
   958
                            if (Contents.Length - i >= 3
dvander@824
   959
                                && Contents[i+2] == '*')
dvander@824
   960
                            {
dvander@824
   961
                                is_doc = true;
dvander@824
   962
                            } else {
dvander@824
   963
                                is_doc = false;
dvander@824
   964
                            }
dvander@824
   965
                            in_comment = true;
dvander@824
   966
                            is_ml = true;
dvander@824
   967
                        }
dvander@824
   968
                        if (in_comment && !is_doc)
dvander@824
   969
                        {
dvander@824
   970
                            Contents[i] = ' ';
dvander@824
   971
                            Contents[i+1] = ' ';        /* Just for safety */
dvander@824
   972
                        }
dvander@824
   973
                    }
dvander@824
   974
                    else if (is_newline)
dvander@824
   975
                    {
dvander@824
   976
                        if (Contents[i] == '#')
dvander@824
   977
                        {
dvander@824
   978
                            if (!CheckString(i, "#define")
dvander@824
   979
                                && !CheckString(i, "#include"))
dvander@824
   980
                            {
dvander@824
   981
                                /* Ignore! */
dvander@824
   982
                                Contents[i] = ' ';
dvander@824
   983
                                in_comment = true;
dvander@824
   984
                                is_doc = false;
dvander@824
   985
                                is_ml = false;
dvander@824
   986
                            }
dvander@824
   987
                            is_newline = false;
dvander@824
   988
                        }
dvander@824
   989
                        else if (!Char.IsWhiteSpace(Contents[i]))
dvander@824
   990
                        {
dvander@824
   991
                            is_newline = false;
dvander@824
   992
                        }
dvander@824
   993
                    }
dvander@824
   994
                } else {
dvander@824
   995
                    if (is_ml)
dvander@824
   996
                    {
dvander@824
   997
                        if (Contents[i] == '*' && Contents[i+1] == '/')
dvander@824
   998
                        {
dvander@824
   999
                            if (!is_doc)
dvander@824
  1000
                            {
dvander@824
  1001
                                Contents[i] = ' ';
dvander@824
  1002
                                Contents[i+1] = ' ';
dvander@824
  1003
                            }
dvander@824
  1004
                            in_comment = false;
dvander@824
  1005
                            i++;    /* Don't look at the next character */
dvander@824
  1006
                        }
dvander@824
  1007
                        else if (!is_doc && Contents[i] != '\n')
dvander@824
  1008
                        {
dvander@824
  1009
                            /* Erase comment */
dvander@824
  1010
                            Contents[i] = ' ';
dvander@824
  1011
                        }
dvander@824
  1012
                    } else {
dvander@824
  1013
                        if (is_newline)
dvander@824
  1014
                        {
dvander@824
  1015
                            in_comment = false;
dvander@824
  1016
                        }
dvander@824
  1017
                        else
dvander@824
  1018
                        {
dvander@824
  1019
                            /* Erase comment */
dvander@824
  1020
                            Contents[i] = ' ';
dvander@824
  1021
                        }
dvander@824
  1022
                    }
dvander@824
  1023
                }
dvander@824
  1024
            }
dvander@824
  1025
dvander@824
  1026
            if (in_comment)
dvander@824
  1027
            {
dvander@824
  1028
                throw new ParseException("Multi-line comment has no terminator");
dvander@824
  1029
            }
dvander@824
  1030
        }
dvander@824
  1031
dvander@824
  1032
        /* Strips whitespace */
dvander@824
  1033
        private void trim_whitespace_pre()
dvander@824
  1034
        {
dvander@824
  1035
            int spaces = 0;
dvander@824
  1036
            while (spaces < Contents.Length
dvander@824
  1037
                   && Contents[spaces] != '\0'
dvander@824
  1038
                   && Char.IsWhiteSpace(Contents[spaces]))
dvander@824
  1039
            {
dvander@824
  1040
                if (Contents[spaces] == '\n')
dvander@824
  1041
                {
dvander@824
  1042
                    LineNo++;
dvander@824
  1043
                }
dvander@824
  1044
                spaces++;
dvander@824
  1045
            }
dvander@824
  1046
            if (spaces > 0)
dvander@824
  1047
            {
dvander@824
  1048
                Contents.Remove(0, spaces);
dvander@824
  1049
            }
dvander@824
  1050
        }
dvander@824
  1051
dvander@824
  1052
        private void preprocess_line()
dvander@824
  1053
        {
dvander@824
  1054
            trim_whitespace_pre();
dvander@824
  1055
        }
dvander@824
  1056
dvander@824
  1057
        private bool IsSymbolChar(char c, bool first)
dvander@824
  1058
        {
dvander@824
  1059
            bool valid = (first ? Char.IsLetter(c) : Char.IsLetterOrDigit(c));
dvander@824
  1060
            return (valid || (c == '_'));
dvander@824
  1061
        }
dvander@824
  1062
dvander@824
  1063
        private bool IsEscapedChar(char c)
dvander@824
  1064
        {
dvander@824
  1065
            return (c == 'n'
dvander@824
  1066
                    || c == 'r'
dvander@824
  1067
                    || c == 't'
dvander@824
  1068
                    || c == 'v'
dvander@824
  1069
                    || c == '\\'
dvander@824
  1070
                    || c == '"');
dvander@824
  1071
        }
dvander@824
  1072
dvander@824
  1073
        private bool IsNotKeywordChar(char c)
dvander@824
  1074
        {
dvander@824
  1075
            return (Char.IsWhiteSpace(c)
dvander@824
  1076
                    || (c == '-' || c == '+')
dvander@824
  1077
                    || (c == '/' || c == '*')
dvander@824
  1078
                    || (c == '(' || c == ')')
dvander@824
  1079
                    || (c == '=' || c == '%')
dvander@824
  1080
                    || (c == '>' || c == '<')
dvander@824
  1081
                    || (c == '!' || c == '~')
dvander@824
  1082
                    );
dvander@824
  1083
        }
dvander@824
  1084
dvander@824
  1085
        /* Consumes one lexical token and gathers information about it */
dvander@824
  1086
        private LexToken lex()
dvander@824
  1087
        {
dvander@824
  1088
            if (LexPushed)
dvander@824
  1089
            {
dvander@824
  1090
                LexPushed = false;
dvander@824
  1091
                return _LastToken;
dvander@824
  1092
            }
dvander@824
  1093
dvander@824
  1094
            /* Number of chars we will be deleting from the input */
dvander@824
  1095
            int stripchars = 0;
dvander@824
  1096
dvander@824
  1097
            /* Clear our state */
dvander@824
  1098
            _LastToken = LexToken.TOKEN_NONE;
dvander@824
  1099
dvander@824
  1100
            /* Remove stuff we don't want from the line */
dvander@824
  1101
            preprocess_line();
dvander@824
  1102
dvander@824
  1103
            if (Contents.Length < 1)
dvander@824
  1104
            {
dvander@824
  1105
                _LastToken = LexToken.TOKEN_EOF;
dvander@824
  1106
                return _LastToken;
dvander@824
  1107
            }
dvander@824
  1108
            
dvander@824
  1109
            /* Get the token list */
dvander@824
  1110
            KeywordToken[] tokens = Tokenizer.Tokens;
dvander@824
  1111
            for (int i = 0; i < tokens.Length; i++)
dvander@824
  1112
            {
dvander@824
  1113
                if (!CheckString(0, tokens[i].keyword))
dvander@824
  1114
                {
dvander@824
  1115
                    continue;
dvander@824
  1116
                }
dvander@824
  1117
                int len = tokens[i].keyword.Length;
dvander@824
  1118
                /* Now check to see what the next token is */
dvander@824
  1119
                if (Contents.Length == len
dvander@824
  1120
                    || IsNotKeywordChar(Contents[len]))
dvander@824
  1121
                {
dvander@824
  1122
                    /* We have a token match! */
dvander@824
  1123
                    _LastToken = tokens[i].token;
dvander@824
  1124
                    _LexString = null;
dvander@824
  1125
                    _LexChar = '\0';
dvander@824
  1126
                    stripchars = len;
dvander@824
  1127
                    break;
dvander@824
  1128
                }
dvander@824
  1129
            }
dvander@824
  1130
dvander@824
  1131
            if (_LastToken == LexToken.TOKEN_NONE)
dvander@824
  1132
            {
dvander@824
  1133
                /* See if we can try to read a symbol */
dvander@824
  1134
                if (IsSymbolChar(Contents[0], true))
dvander@824
  1135
                {
dvander@824
  1136
                    int characters = 1;
dvander@824
  1137
                    while (Contents[characters] != '\0'
dvander@824
  1138
                           && IsSymbolChar(Contents[characters], false))
dvander@824
  1139
                    {
dvander@824
  1140
                        characters++;
dvander@824
  1141
                    }
dvander@824
  1142
                    stripchars = characters;
dvander@824
  1143
                    /* We're done! See what's next.. */
dvander@824
  1144
                    if (Contents[characters] == ':')
dvander@824
  1145
                    {
dvander@824
  1146
                        _LastToken = LexToken.TOKEN_LABEL;
dvander@824
  1147
                        stripchars++;
dvander@824
  1148
                    }
dvander@824
  1149
                    else
dvander@824
  1150
                    {
dvander@824
  1151
                        _LastToken = LexToken.TOKEN_SYMBOL;
dvander@824
  1152
                    }
dvander@824
  1153
                    _LexString = Contents.ToString().Substring(0, characters);
dvander@824
  1154
                    _LexChar = _LexString[0];
dvander@824
  1155
                }/*
dvander@824
  1156
                else if (Contents[0] == '"')
dvander@824
  1157
                {
dvander@824
  1158
                    bool reached_end = false;
dvander@824
  1159
                    StringBuilder builder = new StringBuilder();
dvander@824
  1160
                    for (int i = 1; i < Contents.Length; i++)
dvander@824
  1161
                    {
dvander@824
  1162
                        if (i < Contents.Length - 1
dvander@824
  1163
                            && Contents[i] == '\\'
dvander@824
  1164
                            && IsEscapedChar(Contents[i+1]))
dvander@824
  1165
                        {
dvander@824
  1166
                            i++;
dvander@824
  1167
                        }
dvander@824
  1168
                        else if (Contents[i] == '"')
dvander@824
  1169
                        {
dvander@824
  1170
                            reached_end = true;
dvander@824
  1171
                            break;
dvander@824
  1172
                        }
dvander@824
  1173
                        builder.Append(Contents[i]);
dvander@824
  1174
                    }
dvander@824
  1175
                    if (!reached_end)
dvander@824
  1176
                    {
dvander@824
  1177
                        throw new ParseException("Expected end of string; none found");
dvander@824
  1178
                    }
dvander@824
  1179
                    _LexString = builder.ToString();
dvander@824
  1180
                    _LexChar = _LexString.Length > 0 ? _LexString[0] : '\0';
dvander@824
  1181
                }*/
dvander@824
  1182
                else if (Contents[0] == '\'')
dvander@824
  1183
                {
dvander@824
  1184
                    char c = '\0';
dvander@824
  1185
                    if (Contents.Length < 3)
dvander@824
  1186
                    {
dvander@824
  1187
                        throw new ParseException("Expected end of character; none found");
dvander@824
  1188
                    }
dvander@824
  1189
                    else if (Contents[1] == '\\')
dvander@824
  1190
                    {
dvander@824
  1191
                        if (Contents.Length < 4 || !IsEscapedChar(Contents[2]))
dvander@824
  1192
                        {
dvander@824
  1193
                            throw new ParseException("Expected end of character; none found");
dvander@824
  1194
                        }
dvander@824
  1195
                        if (Contents[2] == 'r')
dvander@824
  1196
                        {
dvander@824
  1197
                            c = '\r';
dvander@824
  1198
                        }
dvander@824
  1199
                        else if (Contents[2] == 'n')
dvander@824
  1200
                        {
dvander@824
  1201
                            c = '\n';
dvander@824
  1202
                        }
dvander@824
  1203
                        else if (Contents[2] == 't')
dvander@824
  1204
                        {
dvander@824
  1205
                            c = '\t';
dvander@824
  1206
                        }
dvander@824
  1207
                        else if (Contents[2] == 'v')
dvander@824
  1208
                        {
dvander@824
  1209
                            c = '\v';
dvander@824
  1210
                        }
dvander@824
  1211
                        else if (Contents[2] == '\\')
dvander@824
  1212
                        {
dvander@824
  1213
                            c = '\\';
dvander@824
  1214
                        }
dvander@824
  1215
                        else if (Contents[2] == '"')
dvander@824
  1216
                        {
dvander@824
  1217
                            c = '"';
dvander@824
  1218
                        }
dvander@824
  1219
                        stripchars = 4;
dvander@824
  1220
                    }
dvander@824
  1221
                    else
dvander@824
  1222
                    {
dvander@824
  1223
                        c = Contents[1];
dvander@824
  1224
                        stripchars = 3;
dvander@824
  1225
                    }
dvander@824
  1226
                    _LexString = c.ToString();
dvander@824
  1227
                    _LexChar = c;
dvander@824
  1228
                    _LastToken = LexToken.TOKEN_QUOTCHAR;
dvander@824
  1229
                }
dvander@824
  1230
                else if (CheckString(0, "/**"))
dvander@824
  1231
                {
dvander@824
  1232
                    int endpoint = 0;
dvander@824
  1233
                    for (int i=3; i<Contents.Length-1; i++)
dvander@824
  1234
                    {
dvander@824
  1235
                        if (Contents[i] == '\n')
dvander@824
  1236
                        {
dvander@824
  1237
                            LineNo++;
dvander@824
  1238
                        }
dvander@824
  1239
                        else if (Contents[i] == '*' && Contents[i+1] == '/')
dvander@824
  1240
                        {
dvander@824
  1241
                            endpoint = i+1;
dvander@824
  1242
                            break;
dvander@824
  1243
                        }
dvander@824
  1244
                    }
dvander@824
  1245
                    if (endpoint == 0)
dvander@824
  1246
                    {
dvander@824
  1247
                        throw new ParseException("Expected end of multi-line comment; found end of file");
dvander@824
  1248
                    }
dvander@824
  1249
                    /* We have to factor one more character in because we're zero based */
dvander@824
  1250
                    endpoint++;
dvander@824
  1251
                    _LastToken = LexToken.TOKEN_DOCBLOCK;
dvander@824
  1252
                    _LexString = Contents.ToString().Substring(0, endpoint);
dvander@824
  1253
                    _LexChar = _LexString[0];
dvander@824
  1254
                    stripchars = endpoint;
dvander@824
  1255
                }
dvander@824
  1256
                else
dvander@824
  1257
                {
dvander@824
  1258
                    _LastToken = LexToken.TOKEN_CHARACTER;
dvander@824
  1259
                    _LexString = Contents[0].ToString();
dvander@824
  1260
                    _LexChar = Contents[0];
dvander@824
  1261
                    stripchars = 1;
dvander@824
  1262
                }
dvander@824
  1263
            }
dvander@824
  1264
dvander@824
  1265
            /* Strip N chars */
dvander@824
  1266
            if (stripchars > 0)
dvander@824
  1267
            {
dvander@824
  1268
                Contents.Remove(0, stripchars);
dvander@824
  1269
            }
dvander@824
  1270
dvander@824
  1271
            return _LastToken;
dvander@824
  1272
        }
dvander@824
  1273
    }
dvander@824
  1274
}