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