The Labs \ Source Viewer \ SSCLI \ Microsoft.JScript \ JSScanner

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. namespace Microsoft.JScript
  16. {
  17.    
  18.     using Microsoft.JScript.Vsa;
  19.     using System;
  20.     using System.Collections;
  21.     using System.Globalization;
  22.     using System.Reflection;
  23.     using Microsoft.Vsa;
  24.     using System.Text;
  25.    
  26.     public sealed class JSScanner
  27.     {
  28.         // scanner main data
  29.         private string strSourceCode;
  30.         private int startPos;
  31.         private int endPos;
  32.         private int currentPos;
  33.         private int currentLine;
  34.         private int startLinePos;
  35.        
  36.         // token information
  37.         private Context currentToken;
  38.         private string escapedString;
  39.         private StringBuilder identifier;
  40.         private int idLastPosOnBuilder;
  41.        
  42.         // flags
  43.         private bool gotEndOfLine;
  44.         private bool IsAuthoring;
  45.         private bool peekModeOn;
  46.         private bool scanForDebugger;
  47.        
  48.         // keyword table
  49.         private JSKeyword[] keywords;
  50.         private static readonly JSKeyword[] s_Keywords = JSKeyword.InitKeywords();
  51.        
  52.         // pre process information
  53.         private bool preProcessorOn;
  54.         private int matchIf;
  55.         private object preProcessorValue;
  56.         private SimpleHashtable ppTable;
  57.        
  58.         // environmental information
  59.         private DocumentContext currentDocument;
  60.         private Globals globals;
  61.        
  62.         public JSScanner()
  63.         {
  64.             this.keywords = JSScanner.s_Keywords;
  65.             this.strSourceCode = null;
  66.             this.startPos = 0;
  67.             this.endPos = 0;
  68.             this.currentPos = 0;
  69.             this.currentLine = 1;
  70.             this.startLinePos = 0;
  71.             this.currentToken = null;
  72.             this.escapedString = null;
  73.             this.identifier = new StringBuilder(128);
  74.             this.idLastPosOnBuilder = 0;
  75.             this.gotEndOfLine = false;
  76.             this.IsAuthoring = false;
  77.             this.peekModeOn = false;
  78.             this.preProcessorOn = false;
  79.             this.matchIf = 0;
  80.             this.ppTable = null;
  81.             this.currentDocument = null;
  82.             this.globals = null;
  83.             this.scanForDebugger = false;
  84.         }
  85.        
  86.         public JSScanner(Context sourceContext)
  87.         {
  88.             this.IsAuthoring = false;
  89.             this.peekModeOn = false;
  90.             this.keywords = s_Keywords;
  91.             this.preProcessorOn = false;
  92.             this.matchIf = 0;
  93.             this.ppTable = null;
  94.             this.SetSource(sourceContext);
  95.             this.currentDocument = null;
  96.             this.globals = sourceContext.document.engine.Globals;
  97.         }
  98.        
  99.         public void SetAuthoringMode(bool mode)
  100.         {
  101.             this.IsAuthoring = mode;
  102.         }
  103.        
  104.         public void SetSource(Context sourceContext)
  105.         {
  106.             this.strSourceCode = sourceContext.source_string;
  107.             this.startPos = sourceContext.startPos;
  108.             this.startLinePos = sourceContext.startLinePos;
  109.             this.endPos = (0 < sourceContext.endPos && sourceContext.endPos < this.strSourceCode.Length) ? sourceContext.endPos : this.strSourceCode.Length;
  110.             this.currentToken = sourceContext;
  111.             this.escapedString = null;
  112.             this.identifier = new StringBuilder(128);
  113.             this.idLastPosOnBuilder = 0;
  114.             this.currentPos = this.startPos;
  115.             this.currentLine = (sourceContext.lineNumber > 0) ? sourceContext.lineNumber : 1;
  116.             this.gotEndOfLine = false;
  117.             this.scanForDebugger = sourceContext.document != null && sourceContext.document.engine != null && VsaEngine.executeForJSEE;
  118.         }
  119.        
  120.         internal JSToken PeekToken()
  121.         {
  122.             int currentPos = this.currentPos;
  123.             int currentLine = this.currentLine;
  124.             int startLinePos = this.startLinePos;
  125.             bool gotEndOfLine = this.gotEndOfLine;
  126.             int idLastPosOnBuilder = this.idLastPosOnBuilder;
  127.             this.peekModeOn = true;
  128.             JSToken token = JSToken.None;
  129.             // temporary switch the token
  130.             Context currentToken = this.currentToken;
  131.             this.currentToken = this.currentToken.Clone();
  132.             try {
  133.                 GetNextToken();
  134.                 token = this.currentToken.token;
  135.             }
  136.             finally {
  137.                 this.currentToken = currentToken;
  138.                 this.currentPos = currentPos;
  139.                 this.currentLine = currentLine;
  140.                 this.startLinePos = startLinePos;
  141.                 this.gotEndOfLine = gotEndOfLine;
  142.                 this.identifier.Length = 0;
  143.                 this.idLastPosOnBuilder = idLastPosOnBuilder;
  144.                 this.peekModeOn = false;
  145.                 this.escapedString = null;
  146.             }
  147.             return token;
  148.         }
  149.        
  150.         public void GetNextToken()
  151.         {
  152.             JSToken token = JSToken.None;
  153.             this.gotEndOfLine = false;
  154.             try {
  155.                 int currentLine = this.currentLine;
  156.                 nextToken:
  157.                 this.SkipBlanks();
  158.                 this.currentToken.startPos = this.currentPos;
  159.                 this.currentToken.lineNumber = this.currentLine;
  160.                 this.currentToken.startLinePos = this.startLinePos;
  161.                 char c = GetChar(this.currentPos++);
  162.                 switch (c) {
  163.                     case (char)0:
  164.                         if (this.currentPos >= this.endPos) {
  165.                             this.currentPos--;
  166.                             token = JSToken.EndOfFile;
  167.                             if (this.matchIf > 0) {
  168.                                 this.currentToken.endLineNumber = this.currentLine;
  169.                                 this.currentToken.endLinePos = this.startLinePos;
  170.                                 this.currentToken.endPos = this.currentPos;
  171.                                 HandleError(JSError.NoCcEnd);
  172.                             }
  173.                             break;
  174.                         }
  175.                         goto nextToken;
  176.                         break;
  177.                     case '=':
  178.                         token = JSToken.Assign;
  179.                         if ('=' == GetChar(this.currentPos)) {
  180.                             this.currentPos++;
  181.                             token = JSToken.Equal;
  182.                             if ('=' == GetChar(this.currentPos)) {
  183.                                 this.currentPos++;
  184.                                 token = JSToken.StrictEqual;
  185.                             }
  186.                         }
  187.                         break;
  188.                     case '>':
  189.                         token = JSToken.GreaterThan;
  190.                         if ('>' == GetChar(this.currentPos)) {
  191.                             this.currentPos++;
  192.                             token = JSToken.RightShift;
  193.                             if ('>' == GetChar(this.currentPos)) {
  194.                                 this.currentPos++;
  195.                                 token = JSToken.UnsignedRightShift;
  196.                             }
  197.                         }
  198.                         if ('=' == GetChar(this.currentPos)) {
  199.                             this.currentPos++;
  200.                             switch (token) {
  201.                                 case JSToken.GreaterThan:
  202.                                     token = JSToken.GreaterThanEqual;
  203.                                     break;
  204.                                 case JSToken.RightShift:
  205.                                     token = JSToken.RightShiftAssign;
  206.                                     break;
  207.                                 case JSToken.UnsignedRightShift:
  208.                                     token = JSToken.UnsignedRightShiftAssign;
  209.                                     break;
  210.                             }
  211.                         }
  212.                         break;
  213.                     case '<':
  214.                         token = JSToken.LessThan;
  215.                         if ('<' == GetChar(this.currentPos)) {
  216.                             this.currentPos++;
  217.                             token = JSToken.LeftShift;
  218.                         }
  219.                         if ('=' == GetChar(this.currentPos)) {
  220.                             this.currentPos++;
  221.                             if (token == JSToken.LessThan)
  222.                                 token = JSToken.LessThanEqual;
  223.                             else
  224.                                 token = JSToken.LeftShiftAssign;
  225.                         }
  226.                         break;
  227.                     case '!':
  228.                         token = JSToken.LogicalNot;
  229.                         if ('=' == GetChar(this.currentPos)) {
  230.                             this.currentPos++;
  231.                             token = JSToken.NotEqual;
  232.                             if ('=' == GetChar(this.currentPos)) {
  233.                                 this.currentPos++;
  234.                                 token = JSToken.StrictNotEqual;
  235.                             }
  236.                         }
  237.                         break;
  238.                     case ',':
  239.                         token = JSToken.Comma;
  240.                         break;
  241.                     case '~':
  242.                         token = JSToken.BitwiseNot;
  243.                         break;
  244.                     case '?':
  245.                         token = JSToken.ConditionalIf;
  246.                         break;
  247.                     case ':':
  248.                         token = JSToken.Colon;
  249.                         if (':' == GetChar(this.currentPos)) {
  250.                             this.currentPos++;
  251.                             token = JSToken.DoubleColon;
  252.                         }
  253.                         break;
  254.                     case '.':
  255.                         token = JSToken.AccessField;
  256.                         c = GetChar(this.currentPos);
  257.                         if (JSScanner.IsDigit(c)) {
  258.                             token = ScanNumber('.');
  259.                         }
  260.                         else if ('.' == c) {
  261.                             c = GetChar(this.currentPos + 1);
  262.                             if ('.' == c) {
  263.                                 this.currentPos += 2;
  264.                                 token = JSToken.ParamArray;
  265.                             }
  266.                         }
  267.                         break;
  268.                     case '&':
  269.                         token = JSToken.BitwiseAnd;
  270.                         c = GetChar(this.currentPos);
  271.                         if ('&' == c) {
  272.                             this.currentPos++;
  273.                             token = JSToken.LogicalAnd;
  274.                         }
  275.                         else if ('=' == c) {
  276.                             this.currentPos++;
  277.                             token = JSToken.BitwiseAndAssign;
  278.                         }
  279.                         break;
  280.                     case '|':
  281.                         token = JSToken.BitwiseOr;
  282.                         c = GetChar(this.currentPos);
  283.                         if ('|' == c) {
  284.                             this.currentPos++;
  285.                             token = JSToken.LogicalOr;
  286.                         }
  287.                         else if ('=' == c) {
  288.                             this.currentPos++;
  289.                             token = JSToken.BitwiseOrAssign;
  290.                         }
  291.                         break;
  292.                     case '+':
  293.                         token = JSToken.Plus;
  294.                         c = GetChar(this.currentPos);
  295.                         if ('+' == c) {
  296.                             this.currentPos++;
  297.                             token = JSToken.Increment;
  298.                         }
  299.                         else if ('=' == c) {
  300.                             this.currentPos++;
  301.                             token = JSToken.PlusAssign;
  302.                         }
  303.                         break;
  304.                     case '-':
  305.                         token = JSToken.Minus;
  306.                         c = GetChar(this.currentPos);
  307.                         if ('-' == c) {
  308.                             this.currentPos++;
  309.                             token = JSToken.Decrement;
  310.                         }
  311.                         else if ('=' == c) {
  312.                             this.currentPos++;
  313.                             token = JSToken.MinusAssign;
  314.                         }
  315.                         break;
  316.                     case '*':
  317.                         token = JSToken.Multiply;
  318.                         if ('=' == GetChar(this.currentPos)) {
  319.                             this.currentPos++;
  320.                             token = JSToken.MultiplyAssign;
  321.                         }
  322.                         break;
  323.                     case '\\':
  324.                         this.currentPos--;
  325.                         if (IsIdentifierStartChar(ref c)) {
  326.                             this.currentPos++;
  327.                             ScanIdentifier();
  328.                             token = JSToken.Identifier;
  329.                             break;
  330.                         }
  331.                         this.currentPos++;
  332.                         // move on
  333.                         c = GetChar(this.currentPos);
  334.                         if ('a' <= c && c <= 'z') {
  335.                             JSKeyword keyword = this.keywords[c - 'a'];
  336.                             if (null != keyword) {
  337.                                 this.currentToken.startPos++;
  338.                                 token = ScanKeyword(keyword);
  339.                                 if (token != JSToken.Identifier) {
  340.                                     token = JSToken.Identifier;
  341.                                     break;
  342.                                 }
  343.                                 this.currentToken.startPos--;
  344.                             }
  345.                         }
  346.                         this.currentPos = this.currentToken.startPos + 1;
  347.                         HandleError(JSError.IllegalChar);
  348.                         break;
  349.                     case '/':
  350.                         token = JSToken.Divide;
  351.                         c = GetChar(this.currentPos);
  352.                         bool preProcess = false;
  353.                         switch (c) {
  354.                             case '/':
  355.                                 if (GetChar(++this.currentPos) == '@' && !this.peekModeOn) {
  356.                                     // we got //@
  357.                                     if (!this.preProcessorOn) {
  358.                                         // preprocessing was not on, check if it's going to be turned on
  359.                                         if ('c' == GetChar(++this.currentPos) && 'c' == GetChar(++this.currentPos) && '_' == GetChar(++this.currentPos) && 'o' == GetChar(++this.currentPos) && 'n' == GetChar(++this.currentPos)) {
  360.                                             char nextChar = GetChar(this.currentPos + 1);
  361.                                             if (!JSScanner.IsDigit(nextChar) && !JSScanner.IsAsciiLetter(nextChar) && !JSScanner.IsUnicodeLetter(nextChar)) {
  362.                                                 SetPreProcessorOn();
  363.                                                 this.currentPos++;
  364.                                                 goto nextToken;
  365.                                             }
  366.                                         }
  367.                                         // it will fall into a "standard" comment processing
  368.                                     }
  369.                                     else {
  370.                                         // if it is a space we got '//@ ' that has to be ignored
  371.                                         if (JSScanner.IsBlankSpace(GetChar(++this.currentPos)))
  372.                                             goto nextToken;
  373.                                         preProcess = true;
  374.                                         break;
  375.                                     }
  376.                                 }
  377.                                 SkipSingleLineComment();
  378.                                 if (this.IsAuthoring) {
  379.                                     token = JSToken.Comment;
  380.                                     break;
  381.                                     // just break out and return
  382.                                 }
  383.                                 else
  384.                                     goto nextToken;
  385.                                 break;
  386.                             case '*':
  387.                                 // read another token this last one was a comment
  388.                                 if (GetChar(++this.currentPos) == '@' && !this.peekModeOn) {
  389.                                     if (!this.preProcessorOn) {
  390.                                         if ('c' == GetChar(++this.currentPos) && 'c' == GetChar(++this.currentPos) && '_' == GetChar(++this.currentPos) && 'o' == GetChar(++this.currentPos) && 'n' == GetChar(++this.currentPos)) {
  391.                                             char nextChar = GetChar(this.currentPos + 1);
  392.                                             if (!JSScanner.IsDigit(nextChar) && !JSScanner.IsAsciiLetter(nextChar) && !JSScanner.IsUnicodeLetter(nextChar)) {
  393.                                                 SetPreProcessorOn();
  394.                                                 this.currentPos++;
  395.                                                 goto nextToken;
  396.                                             }
  397.                                         }
  398.                                     }
  399.                                     else {
  400.                                         // if it is a space we got '/*@ ' that has to be ignored
  401.                                         if (JSScanner.IsBlankSpace(GetChar(++this.currentPos)))
  402.                                             goto nextToken;
  403.                                         preProcess = true;
  404.                                         break;
  405.                                     }
  406.                                 }
  407.                                
  408.                                 SkipMultiLineComment();
  409.                                 if (this.IsAuthoring) {
  410.                                     if (this.currentPos > this.endPos) {
  411.                                         token = JSToken.UnterminatedComment;
  412.                                         this.currentPos = this.endPos;
  413.                                     }
  414.                                     else
  415.                                         token = JSToken.Comment;
  416.                                     break;
  417.                                 }
  418.                                 else
  419.                                     goto nextToken;
  420.                                 break;
  421.                             case '=':
  422.                                 // read another token this last one was a comment
  423.                                 this.currentPos++;
  424.                                 token = JSToken.DivideAssign;
  425.                                 break;
  426.                         }
  427.                         if (preProcess)
  428.                             goto case '@';
  429.                        
  430.                         break;
  431.                     case '^':
  432.                         token = JSToken.BitwiseXor;
  433.                         if ('=' == GetChar(this.currentPos)) {
  434.                             this.currentPos++;
  435.                             token = JSToken.BitwiseXorAssign;
  436.                         }
  437.                         break;
  438.                     case '%':
  439.                         token = JSToken.Modulo;
  440.                         if ('=' == GetChar(this.currentPos)) {
  441.                             this.currentPos++;
  442.                             token = JSToken.ModuloAssign;
  443.                         }
  444.                         break;
  445.                     case '(':
  446.                         token = JSToken.LeftParen;
  447.                         break;
  448.                     case ')':
  449.                         token = JSToken.RightParen;
  450.                         break;
  451.                     case '{':
  452.                         token = JSToken.LeftCurly;
  453.                         break;
  454.                     case '}':
  455.                         token = JSToken.RightCurly;
  456.                         break;
  457.                     case '[':
  458.                         token = JSToken.LeftBracket;
  459.                         break;
  460.                     case ']':
  461.                         token = JSToken.RightBracket;
  462.                         break;
  463.                     case ';':
  464.                         token = JSToken.Semicolon;
  465.                         break;
  466.                     case '"':
  467.                         goto case '\'';
  468.                         break;
  469.                     case '\'':
  470.                         token = JSToken.StringLiteral;
  471.                         ScanString(c);
  472.                         break;
  473.                     case '\r':
  474.                        
  475.                         if (GetChar(this.currentPos) == '\n')
  476.                             this.currentPos++;
  477.                         this.currentLine++;
  478.                         this.startLinePos = this.currentPos;
  479.                         goto nextToken;
  480.                         break;
  481.                     case '\n':
  482.                         this.currentLine++;
  483.                         this.startLinePos = this.currentPos;
  484.                         goto nextToken;
  485.                         break;
  486.                     case (char)8232:
  487.                         this.currentLine++;
  488.                         this.startLinePos = this.currentPos;
  489.                         goto nextToken;
  490.                         break;
  491.                     case (char)8233:
  492.                         this.currentLine++;
  493.                         this.startLinePos = this.currentPos;
  494.                         goto nextToken;
  495.                         break;
  496.                     case '@':
  497.                        
  498.                         //The sick beauty of pre processing
  499.                         if (this.scanForDebugger)
  500.                             HandleError(JSError.CcInvalidInDebugger);
  501.                         if (this.peekModeOn) {
  502.                             this.currentToken.token = JSToken.PreProcessDirective;
  503.                             break;
  504.                         }
  505.                         int startPos = this.currentPos;
  506.                         this.currentToken.startPos = startPos;
  507.                         this.currentToken.lineNumber = this.currentLine;
  508.                         this.currentToken.startLinePos = this.startLinePos;
  509.                         ScanIdentifier();
  510.                         switch (this.currentPos - startPos) {
  511.                             case 0:
  512.                                 // Ignore '@*/'.
  513.                                 if (this.preProcessorOn && '*' == GetChar(this.currentPos) && '/' == GetChar(++this.currentPos))
  514.                                     this.currentPos++;
  515.                                 else
  516.                                     HandleError(JSError.IllegalChar);
  517.                                 goto nextToken;
  518.                                 break;
  519.                             case 2:
  520.                                 if ('i' == this.strSourceCode[startPos] && 'f' == this.strSourceCode[startPos + 1]) {
  521.                                     if (!this.preProcessorOn)
  522.                                         SetPreProcessorOn();
  523.                                     this.matchIf++;
  524.                                     if (!PPTestCond())
  525.                                         PPSkipToNextCondition(true);
  526.                                     goto nextToken;
  527.                                 }
  528.                                 goto default;
  529.                                 break;
  530.                             case 3:
  531.                                 if ('s' == this.strSourceCode[startPos] && 'e' == this.strSourceCode[startPos + 1] && 't' == this.strSourceCode[startPos + 2]) {
  532.                                     if (!this.preProcessorOn)
  533.                                         SetPreProcessorOn();
  534.                                     PPScanSet();
  535.                                     goto nextToken;
  536.                                 }
  537.                                 else if ('e' == this.strSourceCode[startPos] && 'n' == this.strSourceCode[startPos + 1] && 'd' == this.strSourceCode[startPos + 2]) {
  538.                                     if (0 >= this.matchIf)
  539.                                         HandleError(JSError.CcInvalidEnd);
  540.                                     else
  541.                                         this.matchIf--;
  542.                                     goto nextToken;
  543.                                 }
  544.                                 goto default;
  545.                                 break;
  546.                             case 4:
  547.                                 if ('e' == this.strSourceCode[startPos] && 'l' == this.strSourceCode[startPos + 1] && 's' == this.strSourceCode[startPos + 2] && 'e' == this.strSourceCode[startPos + 3]) {
  548.                                     if (0 >= this.matchIf)
  549.                                         HandleError(JSError.CcInvalidElse);
  550.                                     else
  551.                                         PPSkipToNextCondition(false);
  552.                                     goto nextToken;
  553.                                 }
  554.                                 else if ('e' == this.strSourceCode[startPos] && 'l' == this.strSourceCode[startPos + 1] && 'i' == this.strSourceCode[startPos + 2] && 'f' == this.strSourceCode[startPos + 3]) {
  555.                                     if (0 >= this.matchIf)
  556.                                         HandleError(JSError.CcInvalidElif);
  557.                                     else
  558.                                         PPSkipToNextCondition(false);
  559.                                     goto nextToken;
  560.                                 }
  561.                                 goto default;
  562.                                 break;
  563.                             case 5:
  564.                                 if ('c' == GetChar(startPos) && 'c' == GetChar(startPos + 1) && '_' == GetChar(startPos + 2) && 'o' == GetChar(startPos + 3) && 'n' == GetChar(startPos + 4)) {
  565.                                     if (!this.preProcessorOn)
  566.                                         SetPreProcessorOn();
  567.                                     goto nextToken;
  568.                                 }
  569.                                 goto default;
  570.                                 break;
  571.                             default:
  572.                                 if (!this.preProcessorOn) {
  573.                                     HandleError(JSError.CcOff);
  574.                                     goto nextToken;
  575.                                 }
  576.                                 object value = this.ppTable[this.strSourceCode.Substring(startPos, this.currentPos - startPos)];
  577.                                 if (null == value)
  578.                                     this.preProcessorValue = Double.NaN;
  579.                                 else
  580.                                     this.preProcessorValue = value;
  581.                                 token = JSToken.PreProcessorConstant;
  582.                                 break;
  583.                         }
  584.                         break;
  585.                     case '$':
  586.                        
  587.                         goto case '_';
  588.                         break;
  589.                     case '_':
  590.                         ScanIdentifier();
  591.                         token = JSToken.Identifier;
  592.                         break;
  593.                     default:
  594.                         if ('a' <= c && c <= 'z') {
  595.                             JSKeyword keyword = this.keywords[c - 'a'];
  596.                             if (null != keyword)
  597.                                 token = ScanKeyword(keyword);
  598.                             else {
  599.                                 token = JSToken.Identifier;
  600.                                 ScanIdentifier();
  601.                             }
  602.                         }
  603.                         else if (JSScanner.IsDigit(c)) {
  604.                             token = ScanNumber(c);
  605.                         }
  606.                         else if ('A' <= c && c <= 'Z' || JSScanner.IsUnicodeLetter(c)) {
  607.                             token = JSToken.Identifier;
  608.                             ScanIdentifier();
  609.                         }
  610.                         else {
  611.                             HandleError(JSError.IllegalChar);
  612.                             goto nextToken;
  613.                         }
  614.                         break;
  615.                 }
  616.                 this.currentToken.endLineNumber = this.currentLine;
  617.                 this.currentToken.endLinePos = this.startLinePos;
  618.                 this.currentToken.endPos = this.currentPos;
  619.                 this.gotEndOfLine = (this.currentLine > currentLine || token == JSToken.EndOfFile) ? true : false;
  620.                 if (this.gotEndOfLine && token == JSToken.StringLiteral && this.currentToken.lineNumber == currentLine)
  621.                     this.gotEndOfLine = false;
  622.             }
  623.             catch (IndexOutOfRangeException) {
  624.                 token = JSToken.None;
  625.                 this.currentToken.endPos = this.currentPos;
  626.                 this.currentToken.endLineNumber = this.currentLine;
  627.                 this.currentToken.endLinePos = this.startLinePos;
  628.                 throw new ScannerException(JSError.ErrEOF);
  629.             }
  630.             this.currentToken.token = token;
  631.         }
  632.        
  633.         private char GetChar(int index)
  634.         {
  635.             if (index < this.endPos)
  636.                 return this.strSourceCode[index];
  637.             else
  638.                 return (char)0;
  639.         }
  640.        
  641.         public int GetCurrentPosition(bool absolute)
  642.         {
  643.             return this.currentPos;
  644.         }
  645.        
  646.         public int GetCurrentLine()
  647.         {
  648.             return this.currentLine;
  649.         }
  650.        
  651.         public int GetStartLinePosition()
  652.         {
  653.             return this.startLinePos;
  654.         }
  655.        
  656.         public string GetStringLiteral()
  657.         {
  658.             return this.escapedString;
  659.         }
  660.        
  661.         public string GetSourceCode()
  662.         {
  663.             return this.strSourceCode;
  664.         }
  665.        
  666.         public bool GotEndOfLine()
  667.         {
  668.             return this.gotEndOfLine;
  669.         }
  670.        
  671.         internal string GetIdentifier()
  672.         {
  673.             string id = null;
  674.             if (this.identifier.Length > 0) {
  675.                 id = this.identifier.ToString();
  676.                 this.identifier.Length = 0;
  677.             }
  678.             else
  679.                 id = this.currentToken.GetCode();
  680.             if (id.Length > 500)
  681.                 //The EE sometimes gets into trouble if presented with a name > 1023 bytes, make this less likely
  682.                 id = id.Substring(0, 500) + id.GetHashCode().ToString(CultureInfo.InvariantCulture);
  683.             return id;
  684.         }
  685.        
  686.         private void ScanIdentifier()
  687.         {
  688.             for (;;) {
  689.                 char c = GetChar(this.currentPos);
  690.                 if (!IsIdentifierPartChar(c))
  691.                     break;
  692.                 ++this.currentPos;
  693.             }
  694.             if (this.idLastPosOnBuilder > 0) {
  695.                 this.identifier.Append(this.strSourceCode.Substring(this.idLastPosOnBuilder, this.currentPos - this.idLastPosOnBuilder));
  696.                 this.idLastPosOnBuilder = 0;
  697.             }
  698.         }
  699.        
  700.         private JSToken ScanKeyword(JSKeyword keyword)
  701.         {
  702.             for (;;) {
  703.                 char c = GetChar(this.currentPos);
  704.                 if ('a' <= c && c <= 'z') {
  705.                     this.currentPos++;
  706.                     continue;
  707.                 }
  708.                 else {
  709.                     if (IsIdentifierPartChar(c)) {
  710.                         ScanIdentifier();
  711.                         return JSToken.Identifier;
  712.                     }
  713.                     break;
  714.                 }
  715.             }
  716.             return keyword.GetKeyword(this.currentToken, this.currentPos - this.currentToken.startPos);
  717.         }
  718.        
  719.         private JSToken ScanNumber(char leadChar)
  720.         {
  721.             bool noMoreDot = '.' == leadChar;
  722.             JSToken token = noMoreDot ? JSToken.NumericLiteral : JSToken.IntegerLiteral;
  723.             bool exponent = false;
  724.             char c;
  725.            
  726.             if ('0' == leadChar) {
  727.                 c = GetChar(this.currentPos);
  728.                 if ('x' == c || 'X' == c) {
  729.                     if (!JSScanner.IsHexDigit(GetChar(this.currentPos + 1)))
  730.                         HandleError(JSError.BadHexDigit);
  731.                     while (JSScanner.IsHexDigit(GetChar(++this.currentPos)))
  732.                         ;
  733.                     return token;
  734.                 }
  735.             }
  736.            
  737.             for (;;) {
  738.                 c = GetChar(this.currentPos);
  739.                 if (!JSScanner.IsDigit(c)) {
  740.                     if ('.' == c) {
  741.                         if (noMoreDot)
  742.                             break;
  743.                         else {
  744.                             noMoreDot = true;
  745.                             token = JSToken.NumericLiteral;
  746.                         }
  747.                     }
  748.                     else if ('e' == c || 'E' == c) {
  749.                         if (exponent)
  750.                             break;
  751.                         else {
  752.                             exponent = true;
  753.                             token = JSToken.NumericLiteral;
  754.                         }
  755.                     }
  756.                     else if ('+' == c || '-' == c) {
  757.                         char e = GetChar(this.currentPos - 1);
  758.                         if ('e' != e && 'E' != e)
  759.                             break;
  760.                     }
  761.                     else
  762.                         break;
  763.                 }
  764.                 this.currentPos++;
  765.             }
  766.            
  767.             c = GetChar(this.currentPos - 1);
  768.             if ('+' == c || '-' == c) {
  769.                 this.currentPos--;
  770.                 c = GetChar(this.currentPos - 1);
  771.             }
  772.             if ('e' == c || 'E' == c) {
  773.                 this.currentPos--;
  774.             }
  775.            
  776.             return token;
  777.         }
  778.        
  779.         internal string ScanRegExp()
  780.         {
  781.             int pos = this.currentPos;
  782.             bool isEscape = false;
  783.             char c;
  784.             while (!IsEndLineOrEOF(c = GetChar(this.currentPos++), 0)) {
  785.                 if (isEscape)
  786.                     isEscape = false;
  787.                 else {
  788.                     if (c == '/') {
  789.                         if (pos == this.currentPos)
  790.                             return null;
  791.                         this.currentToken.endPos = this.currentPos;
  792.                         this.currentToken.endLinePos = this.startLinePos;
  793.                         this.currentToken.endLineNumber = this.currentLine;
  794.                         return this.strSourceCode.Substring(this.currentToken.startPos + 1, this.currentToken.endPos - this.currentToken.startPos - 2);
  795.                     }
  796.                     if (c == '\\')
  797.                         isEscape = true;
  798.                 }
  799.             }
  800.             // reset and return null. Assume it is not a reg exp
  801.             this.currentPos = pos;
  802.             return null;
  803.         }
  804.        
  805.         internal string ScanRegExpFlags()
  806.         {
  807.             int pos = this.currentPos;
  808.             while (JSScanner.IsAsciiLetter(GetChar(this.currentPos)))
  809.                 this.currentPos++;
  810.             if (pos != this.currentPos) {
  811.                 this.currentToken.endPos = this.currentPos;
  812.                 this.currentToken.endLineNumber = this.currentLine;
  813.                 this.currentToken.endLinePos = this.startLinePos;
  814.                 return this.strSourceCode.Substring(pos, this.currentToken.endPos - pos);
  815.             }
  816.             return null;
  817.         }
  818.        
  819.         //--------------------------------------------------------------------------------------------------
  820.         // ScanString
  821.         //
  822.         // Scan a string dealing with escape sequences.
  823.         // On exit this.escapedString will contain the string with all escape sequences replaced
  824.         // On exit this.currentPos must be at the next char to scan after the string
  825.         // This method wiil report an error when the string is unterminated or for a bad escape sequence
  826.         //--------------------------------------------------------------------------------------------------
  827.         private void ScanString(char cStringTerminator)
  828.         {
  829.             char ch;
  830.             int start = this.currentPos;
  831.             this.escapedString = null;
  832.             StringBuilder result = null;
  833.             do {
  834.                 ch = GetChar(this.currentPos++);
  835.                
  836.                 if (ch != '\\') {
  837.                     // this is the common non escape case
  838.                     if (IsLineTerminator(ch, 0)) {
  839.                         HandleError(JSError.UnterminatedString);
  840.                         this.currentPos--;
  841.                         //this.currentLine++;
  842.                         //this.startLinePos = this.currentPos;
  843.                         break;
  844.                     }
  845.                     else if ((char)0 == ch) {
  846.                         this.currentPos--;
  847.                         HandleError(JSError.UnterminatedString);
  848.                         break;
  849.                     }
  850.                 }
  851.                 else {
  852.                     // ESCAPE CASE
  853.                    
  854.                     // got an escape of some sort. Have to use the StringBuilder
  855.                     if (null == result)
  856.                         result = new StringBuilder(128);
  857.                    
  858.                     // start points to the first position that has not been written to the StringBuilder.
  859.                     // The first time we get in here that position is the beginning of the string, after that
  860.                     // is the character immediately following the escape sequence
  861.                     if (this.currentPos - start - 1 > 0) {
  862.                         // append all the non escape chars to the string builder
  863.                         result.Append(this.strSourceCode, start, this.currentPos - start - 1);
  864.                     }
  865.                    
  866.                     // state variable to be reset
  867.                     bool seqOfThree = false;
  868.                     int esc = 0;
  869.                    
  870.                     ch = GetChar(this.currentPos++);
  871.                     switch (ch) {
  872.                         case '\r':
  873.                            
  874.                             if ('\n' == GetChar(this.currentPos)) {
  875.                                 this.currentPos++;
  876.                             }
  877.                             goto case '\n';
  878.                             break;
  879.                         case '\n':
  880.                         case (char)8232:
  881.                         case (char)8233:
  882.                             this.currentLine++;
  883.                             this.startLinePos = this.currentPos;
  884.                             break;
  885.                         case 'b':
  886.                            
  887.                             // classic single char escape sequences
  888.                             result.Append((char)8);
  889.                             break;
  890.                         case 't':
  891.                             result.Append((char)9);
  892.                             break;
  893.                         case 'n':
  894.                             result.Append((char)10);
  895.                             break;
  896.                         case 'v':
  897.                             result.Append((char)11);
  898.                             break;
  899.                         case 'f':
  900.                             result.Append((char)12);
  901.                             break;
  902.                         case 'r':
  903.                             result.Append((char)13);
  904.                             break;
  905.                         case '"':
  906.                             result.Append('"');
  907.                             ch = (char)0;
  908.                             // so it does not exit the loop
  909.                             break;
  910.                         case '\'':
  911.                             result.Append('\'');
  912.                             ch = (char)0;
  913.                             // so it does not exit the loop
  914.                             break;
  915.                         case '\\':
  916.                             result.Append('\\');
  917.                             break;
  918.                         case 'x':
  919.                            
  920.                             // hexadecimal escape sequence /xHH
  921.                             ch = GetChar(this.currentPos++);
  922.                             if (unchecked((UInt32)(ch - '0')) <= '9' - '0')
  923.                                 esc = (ch - '0') << 4;
  924.                             else if (unchecked((UInt32)(ch - 'A')) <= 'F' - 'A')
  925.                                 esc = (ch + 10 - 'A') << 4;
  926.                             else if (unchecked((UInt32)(ch - 'a')) <= 'f' - 'a')
  927.                                 esc = (ch + 10 - 'a') << 4;
  928.                             else {
  929.                                 HandleError(JSError.BadHexDigit);
  930.                                 if (ch != cStringTerminator)
  931.                                     --this.currentPos;
  932.                                 // do not skip over this char we have to read it back
  933.                                 break;
  934.                             }
  935.                             ch = GetChar(this.currentPos++);
  936.                             if (unchecked((UInt32)(ch - '0')) <= '9' - '0')
  937.                                 esc |= (ch - '0');
  938.                             else if (unchecked((UInt32)(ch - 'A')) <= 'F' - 'A')
  939.                                 esc |= (ch + 10 - 'A');
  940.                             else if (unchecked((UInt32)(ch - 'a')) <= 'f' - 'a')
  941.                                 esc |= (ch + 10 - 'a');
  942.                             else {
  943.                                 HandleError(JSError.BadHexDigit);
  944.                                 if (ch != cStringTerminator)
  945.                                     --this.currentPos;
  946.                                 // do not skip over this char we have to read it back
  947.                                 break;
  948.                             }
  949.                             result.Append((char)esc);
  950.                             break;
  951.                         case 'u':
  952.                            
  953.                             // unicode escape sequence /uHHHH
  954.                             ch = GetChar(this.currentPos++);
  955.                             if (unchecked((UInt32)(ch - '0')) <= '9' - '0')
  956.                                 esc = (ch - '0') << 12;
  957.                             else if (unchecked((UInt32)(ch - 'A')) <= 'F' - 'A')
  958.                                 esc = (ch + 10 - 'A') << 12;
  959.                             else if (unchecked((UInt32)(ch - 'a')) <= 'f' - 'a')
  960.                                 esc = (ch + 10 - 'a') << 12;
  961.                             else {
  962.                                 HandleError(JSError.BadHexDigit);
  963.                                 if (ch != cStringTerminator)
  964.                                     --this.currentPos;
  965.                                 // do not skip over this char we have to read it back
  966.                                 break;
  967.                             }
  968.                             ch = GetChar(this.currentPos++);
  969.                             if (unchecked((UInt32)(ch - '0')) <= '9' - '0')
  970.                                 esc |= (ch - '0') << 8;
  971.                             else if (unchecked((UInt32)(ch - 'A')) <= 'F' - 'A')
  972.                                 esc |= (ch + 10 - 'A') << 8;
  973.                             else if (unchecked((UInt32)(ch - 'a')) <= 'f' - 'a')
  974.                                 esc |= (ch + 10 - 'a') << 8;
  975.                             else {
  976.                                 HandleError(JSError.BadHexDigit);
  977.                                 if (ch != cStringTerminator)
  978.                                     --this.currentPos;
  979.                                 // do not skip over this char we have to read it back
  980.                                 break;
  981.                             }
  982.                             ch = GetChar(this.currentPos++);
  983.                             if (unchecked((UInt32)(ch - '0')) <= '9' - '0')
  984.                                 esc |= (ch - '0') << 4;
  985.                             else if (unchecked((UInt32)(ch - 'A')) <= 'F' - 'A')
  986.                                 esc |= (ch + 10 - 'A') << 4;
  987.                             else if (unchecked((UInt32)(ch - 'a')) <= 'f' - 'a')
  988.                                 esc |= (ch + 10 - 'a') << 4;
  989.                             else {
  990.                                 HandleError(JSError.BadHexDigit);
  991.                                 if (ch != cStringTerminator)
  992.                                     --this.currentPos;
  993.                                 // do not skip over this char we have to read it back
  994.                                 break;
  995.                             }
  996.                             ch = GetChar(this.currentPos++);
  997.                             if (unchecked((UInt32)(ch - '0')) <= '9' - '0')
  998.                                 esc |= (ch - '0');
  999.                             else if (unchecked((UInt32)(ch - 'A')) <= 'F' - 'A')
  1000.                                 esc |= (ch + 10 - 'A');
  1001.                             else if (unchecked((UInt32)(ch - 'a')) <= 'f' - 'a')
  1002.                                 esc |= (ch + 10 - 'a');
  1003.                             else {
  1004.                                 HandleError(JSError.BadHexDigit);
  1005.                                 if (ch != cStringTerminator)
  1006.                                     --this.currentPos;
  1007.                                 // do not skip over this char we have to read it back
  1008.                                 break;
  1009.                             }
  1010.                             result.Append((char)esc);
  1011.                             break;
  1012.                         case '0':
  1013.                         case '1':
  1014.                         case '2':
  1015.                         case '3':
  1016.                            
  1017.                             seqOfThree = true;
  1018.                             esc = (ch - '0') << 6;
  1019.                             goto case '4';
  1020.                             break;
  1021.                         case '4':
  1022.                         case '5':
  1023.                         case '6':
  1024.                         case '7':
  1025.                             // esc is reset at the beginning of the loop and it is used to check that we did not go through the cases 1, 2 or 3
  1026.                             if (!seqOfThree)
  1027.                                 esc = (ch - '0') << 3;
  1028.                            
  1029.                             ch = GetChar(this.currentPos++);
  1030.                             if (unchecked((UInt32)(ch - '0')) <= '7' - '0') {
  1031.                                 if (seqOfThree) {
  1032.                                     esc |= (ch - '0') << 3;
  1033.                                     ch = GetChar(this.currentPos++);
  1034.                                     if (unchecked((UInt32)(ch - '0')) <= '7' - '0') {
  1035.                                         esc |= ch - '0';
  1036.                                         result.Append((char)esc);
  1037.                                     }
  1038.                                     else {
  1039.                                         result.Append((char)(esc >> 3));
  1040.                                         if (ch != cStringTerminator)
  1041.                                             --this.currentPos;
  1042.                                         // do not skip over this char we have to read it back
  1043.                                     }
  1044.                                 }
  1045.                                 else {
  1046.                                     esc |= ch - '0';
  1047.                                     result.Append((char)esc);
  1048.                                 }
  1049.                             }
  1050.                             else {
  1051.                                 if (seqOfThree)
  1052.                                     result.Append((char)(esc >> 6));
  1053.                                 else
  1054.                                     result.Append((char)(esc >> 3));
  1055.                                 if (ch != cStringTerminator)
  1056.                                     --this.currentPos;
  1057.                                 // do not skip over this char we have to read it back
  1058.                             }
  1059.                             break;
  1060.                         default:
  1061.                            
  1062.                             // not an octal number, ignore the escape '/' and simply append the current char
  1063.                             result.Append(ch);
  1064.                             break;
  1065.                     }
  1066.                     start = this.currentPos;
  1067.                 }
  1068.             }
  1069.             while (ch != cStringTerminator);
  1070.            
  1071.             // update this.escapedString
  1072.             if (null != result) {
  1073.                 if (this.currentPos - start - 1 > 0) {
  1074.                     // append all the non escape chars to the string builder
  1075.                     result.Append(this.strSourceCode, start, this.currentPos - start - 1);
  1076.                 }
  1077.                 this.escapedString = result.ToString();
  1078.             }
  1079.             else {
  1080.                 if (this.currentPos <= this.currentToken.startPos + 2)
  1081.                     this.escapedString = "";
  1082.                 else
  1083.                     this.escapedString = this.currentToken.source_string.Substring(this.currentToken.startPos + 1, this.currentPos - this.currentToken.startPos - 2);
  1084.             }
  1085.         }
  1086.        
  1087.         private void SkipSingleLineComment()
  1088.         {
  1089.             while (!IsEndLineOrEOF(GetChar(this.currentPos++), 0))
  1090.                 ;
  1091.             if (this.IsAuthoring) {
  1092.                 this.currentToken.endPos = this.currentPos;
  1093.                 this.currentToken.endLineNumber = this.currentLine;
  1094.                 this.currentToken.endLinePos = this.startLinePos;
  1095.                 this.gotEndOfLine = true;
  1096.             }
  1097.             this.currentLine++;
  1098.             this.startLinePos = this.currentPos;
  1099.         }
  1100.        
  1101.         // this method is public because it's used from the authoring code
  1102.         public int SkipMultiLineComment()
  1103.         {
  1104.             for (;;) {
  1105.                 char c = GetChar(this.currentPos);
  1106.                
  1107.                 while ('*' == c) {
  1108.                     c = GetChar(++this.currentPos);
  1109.                     if ('/' == c) {
  1110.                         this.currentPos++;
  1111.                         return this.currentPos;
  1112.                     }
  1113.                     else if ((char)0 == c)
  1114.                         break;
  1115.                     else if (IsLineTerminator(c, 1)) {
  1116.                         c = GetChar(++this.currentPos);
  1117.                         this.currentLine++;
  1118.                         this.startLinePos = this.currentPos + 1;
  1119.                     }
  1120.                 }
  1121.                
  1122.                 if ((char)0 == c && this.currentPos >= this.endPos)
  1123.                     break;
  1124.                
  1125.                 if (IsLineTerminator(c, 1)) {
  1126.                     this.currentLine++;
  1127.                     this.startLinePos = this.currentPos + 1;
  1128.                 }
  1129.                
  1130.                 ++this.currentPos;
  1131.             }
  1132.            
  1133.             // if we are here we got EOF
  1134.             if (!this.IsAuthoring) {
  1135.                 this.currentToken.endPos = --this.currentPos;
  1136.                 this.currentToken.endLinePos = this.startLinePos;
  1137.                 this.currentToken.endLineNumber = this.currentLine;
  1138.                 throw new ScannerException(JSError.NoCommentEnd);
  1139.             }
  1140.             return this.currentPos;
  1141.         }
  1142.        
  1143.         private void SkipBlanks()
  1144.         {
  1145.             char c = GetChar(this.currentPos);
  1146.             while (JSScanner.IsBlankSpace(c))
  1147.                 c = GetChar(++this.currentPos);
  1148.         }
  1149.        
  1150.         private static bool IsBlankSpace(char c)
  1151.         {
  1152.             switch (c) {
  1153.                 case (char)9:
  1154.                 case (char)11:
  1155.                 case (char)12:
  1156.                 case (char)32:
  1157.                 case (char)160:
  1158.                     return true;
  1159.                 default:
  1160.                     if (c >= 128)
  1161.                         return Char.GetUnicodeCategory(c) == UnicodeCategory.SpaceSeparator;
  1162.                     else
  1163.                         return false;
  1164.                     break;
  1165.             }
  1166.         }
  1167.        
  1168.         private bool IsLineTerminator(char c, int increment)
  1169.         {
  1170.             switch (c) {
  1171.                 case (char)13:
  1172.                     // treat 0x0D0x0A as a single character
  1173.                     if (10 == GetChar(this.currentPos + increment))
  1174.                         this.currentPos++;
  1175.                     return true;
  1176.                 case (char)10:
  1177.                     return true;
  1178.                 case (char)8232:
  1179.                     return true;
  1180.                 case (char)8233:
  1181.                     return true;
  1182.                 default:
  1183.                     return false;
  1184.             }
  1185.         }
  1186.        
  1187.         private bool IsEndLineOrEOF(char c, int increment)
  1188.         {
  1189.             return IsLineTerminator(c, increment) || (char)0 == c && this.currentPos >= this.endPos;
  1190.         }
  1191.        
  1192.         private int GetHexValue(char hex)
  1193.         {
  1194.             int hexValue;
  1195.             if ('0' <= hex && hex <= '9')
  1196.                 hexValue = hex - '0';
  1197.             else if ('a' <= hex && hex <= 'f')
  1198.                 hexValue = hex - 'a' + 10;
  1199.             else
  1200.                 hexValue = hex - 'A' + 10;
  1201.             return hexValue;
  1202.         }
  1203.        
  1204.         internal bool IsIdentifierPartChar(char c)
  1205.         {
  1206.             if (this.IsIdentifierStartChar(ref c))
  1207.                 return true;
  1208.             if ('0' <= c && c <= '9')
  1209.                 return true;
  1210.             if (c < 128)
  1211.                 return false;
  1212.             UnicodeCategory ccat = Char.GetUnicodeCategory(c);
  1213.             switch (ccat) {
  1214.                 case UnicodeCategory.NonSpacingMark:
  1215.                 case UnicodeCategory.SpacingCombiningMark:
  1216.                 case UnicodeCategory.DecimalDigitNumber:
  1217.                 case UnicodeCategory.ConnectorPunctuation:
  1218.                     return true;
  1219.                 default:
  1220.                     return false;
  1221.             }
  1222.         }
  1223.        
  1224.         internal bool IsIdentifierStartChar(ref char c)
  1225.         {
  1226.             bool isEscapeChar = false;
  1227.             if ('\\' == c) {
  1228.                 if ('u' == GetChar(this.currentPos + 1)) {
  1229.                     char h1 = GetChar(this.currentPos + 2);
  1230.                     if (IsHexDigit(h1)) {
  1231.                         char h2 = GetChar(this.currentPos + 3);
  1232.                         if (IsHexDigit(h2)) {
  1233.                             char h3 = GetChar(this.currentPos + 4);
  1234.                             if (IsHexDigit(h3)) {
  1235.                                 char h4 = GetChar(this.currentPos + 5);
  1236.                                 if (IsHexDigit(h4)) {
  1237.                                     isEscapeChar = true;
  1238.                                     c = (char)(GetHexValue(h1) << 12 | GetHexValue(h2) << 8 | GetHexValue(h3) << 4 | GetHexValue(h4));
  1239.                                 }
  1240.                             }
  1241.                         }
  1242.                     }
  1243.                 }
  1244.             }
  1245.             if ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '_' == c || '$' == c)
  1246.                 goto isIdentifierChar;
  1247.             if (c < 128)
  1248.                 return false;
  1249.             UnicodeCategory ccat = Char.GetUnicodeCategory(c);
  1250.             switch (ccat) {
  1251.                 case UnicodeCategory.UppercaseLetter:
  1252.                 case UnicodeCategory.LowercaseLetter:
  1253.                 case UnicodeCategory.TitlecaseLetter:
  1254.                 case UnicodeCategory.ModifierLetter:
  1255.                 case UnicodeCategory.OtherLetter:
  1256.                 case UnicodeCategory.LetterNumber:
  1257.                     goto isIdentifierChar;
  1258.                     break;
  1259.                 default:
  1260.                     return false;
  1261.             }
  1262.             isIdentifierChar:
  1263.             if (isEscapeChar) {
  1264.                 int startPos = (this.idLastPosOnBuilder > 0) ? this.idLastPosOnBuilder : this.currentToken.startPos;
  1265.                 if (this.currentPos - startPos > 0)
  1266.                     this.identifier.Append(this.strSourceCode.Substring(startPos, this.currentPos - startPos));
  1267.                 this.identifier.Append(c);
  1268.                 this.currentPos += 5;
  1269.                 this.idLastPosOnBuilder = this.currentPos + 1;
  1270.             }
  1271.             return true;
  1272.         }
  1273.        
  1274.         static internal bool IsDigit(char c)
  1275.         {
  1276.             return '0' <= c && c <= '9';
  1277.         }
  1278.        
  1279.         static internal bool IsHexDigit(char c)
  1280.         {
  1281.             return JSScanner.IsDigit(c) || 'A' <= c && c <= 'F' || 'a' <= c && c <= 'f';
  1282.         }
  1283.        
  1284.        
  1285.         static internal bool IsAsciiLetter(char c)
  1286.         {
  1287.             return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z';
  1288.         }
  1289.        
  1290.         static internal bool IsUnicodeLetter(char c)
  1291.         {
  1292.             return c >= 128 && Char.IsLetter(c);
  1293.         }
  1294.        
  1295.         private void SetPreProcessorOn()
  1296.         {
  1297.             this.preProcessorOn = true;
  1298.             this.ppTable = new SimpleHashtable(16);
  1299.            
  1300.             // define constants
  1301.            
  1302.             this.ppTable["_debug"] = this.globals.engine.GenerateDebugInfo;
  1303.             this.ppTable["_fast"] = ((IActivationObject)this.globals.ScopeStack.Peek()).GetGlobalScope().fast;
  1304.             this.ppTable["_jscript"] = true;
  1305.             this.ppTable["_jscript_build"] = GlobalObject.ScriptEngineBuildVersion();
  1306.             this.ppTable["_jscript_version"] = Convert.ToNumber(GlobalObject.ScriptEngineMajorVersion() + "." + GlobalObject.ScriptEngineMinorVersion());
  1307.             this.ppTable["_microsoft"] = true;
  1308.            
  1309.             // define command-line symbols
  1310.             Hashtable userDefines = (Hashtable)this.globals.engine.GetOption("defines");
  1311.             if (userDefines != null)
  1312.                 foreach (DictionaryEntry def in userDefines)
  1313.                     this.ppTable[def.Key] = def.Value;
  1314.         }
  1315.        
  1316.         private bool PPTestCond()
  1317.         {
  1318.             this.SkipBlanks();
  1319.            
  1320.             if ('(' != GetChar(this.currentPos)) {
  1321.                 this.currentToken.startPos = this.currentPos - 1;
  1322.                 this.currentToken.lineNumber = this.currentLine;
  1323.                 this.currentToken.startLinePos = this.startLinePos;
  1324.                 ;
  1325.                 HandleError(JSError.NoLeftParen);
  1326.             }
  1327.             else
  1328.                 this.currentPos++;
  1329.             object value = PPScanExpr();
  1330.             if (')' != GetChar(this.currentPos++)) {
  1331.                 this.currentToken.startPos = this.currentPos - 1;
  1332.                 this.currentToken.lineNumber = this.currentLine;
  1333.                 this.currentToken.startLinePos = this.startLinePos;
  1334.                 ;
  1335.                 HandleError(JSError.NoRightParen);
  1336.                 this.currentPos--;
  1337.             }
  1338.            
  1339.             return Convert.ToBoolean(value);
  1340.         }
  1341.        
  1342.         private void PPSkipToNextCondition(bool checkCondition)
  1343.         {
  1344.             int ifCount = 0;
  1345.             for (;;) {
  1346.                 char c = GetChar(this.currentPos++);
  1347.                 switch (c) {
  1348.                     case (char)0:
  1349.                         if (this.currentPos >= this.endPos) {
  1350.                             this.currentPos--;
  1351.                             this.currentToken.endPos = this.currentPos;
  1352.                             this.currentToken.endLineNumber = this.currentLine;
  1353.                             this.currentToken.endLinePos = this.startLinePos;
  1354.                             HandleError(JSError.NoCcEnd);
  1355.                             throw new ScannerException(JSError.ErrEOF);
  1356.                         }
  1357.                         break;
  1358.                     case '\r':
  1359.                        
  1360.                         if (GetChar(this.currentPos) == '\n') {
  1361.                             this.currentPos++;
  1362.                         }
  1363.                         this.currentLine++;
  1364.                         this.startLinePos = this.currentPos;
  1365.                         break;
  1366.                     case '\n':
  1367.                         this.currentLine++;
  1368.                         this.startLinePos = this.currentPos;
  1369.                         break;
  1370.                     case (char)8232:
  1371.                         this.currentLine++;
  1372.                         this.startLinePos = this.currentPos;
  1373.                         break;
  1374.                     case (char)8233:
  1375.                         this.currentLine++;
  1376.                         this.startLinePos = this.currentPos;
  1377.                         break;
  1378.                     case '@':
  1379.                        
  1380.                         this.currentToken.startPos = this.currentPos;
  1381.                         this.currentToken.lineNumber = this.currentLine;
  1382.                         this.currentToken.startLinePos = this.startLinePos;
  1383.                         ScanIdentifier();
  1384.                         switch (this.currentPos - this.currentToken.startPos) {
  1385.                             case 2:
  1386.                                 if ('i' == this.strSourceCode[this.currentToken.startPos] && 'f' == this.strSourceCode[this.currentToken.startPos + 1]) {
  1387.                                     ifCount++;
  1388.                                 }
  1389.                                 break;
  1390.                             case 3:
  1391.                                 if ('e' == this.strSourceCode[this.currentToken.startPos] && 'n' == this.strSourceCode[this.currentToken.startPos + 1] && 'd' == this.strSourceCode[this.currentToken.startPos + 2]) {
  1392.                                     if (ifCount == 0) {
  1393.                                         this.matchIf--;
  1394.                                         return;
  1395.                                     }
  1396.                                     ifCount--;
  1397.                                 }
  1398.                                 break;
  1399.                             case 4:
  1400.                                 if ('e' == this.strSourceCode[this.currentToken.startPos] && 'l' == this.strSourceCode[this.currentToken.startPos + 1] && 's' == this.strSourceCode[this.currentToken.startPos + 2] && 'e' == this.strSourceCode[this.currentToken.startPos + 3]) {
  1401.                                     if (0 == ifCount && checkCondition)
  1402.                                         return;
  1403.                                 }
  1404.                                 else if ('e' == this.strSourceCode[this.currentToken.startPos] && 'l' == this.strSourceCode[this.currentToken.startPos + 1] && 'i' == this.strSourceCode[this.currentToken.startPos + 2] && 'f' == this.strSourceCode[this.currentToken.startPos + 3]) {
  1405.                                     if (0 == ifCount && checkCondition && PPTestCond())
  1406.                                         return;
  1407.                                 }
  1408.                                 break;
  1409.                         }
  1410.                         break;
  1411.                 }
  1412.             }
  1413.         }
  1414.        
  1415.         private void PPScanSet()
  1416.         {
  1417.             this.SkipBlanks();
  1418.             if ('@' != GetChar(this.currentPos++)) {
  1419.                 HandleError(JSError.NoAt);
  1420.                 this.currentPos--;
  1421.             }
  1422.             int startIdentifier = this.currentPos;
  1423.             ScanIdentifier();
  1424.             int length = this.currentPos - startIdentifier;
  1425.            
  1426.             string id = null;
  1427.             if (length == 0) {
  1428.                 this.currentToken.startPos = this.currentPos - 1;
  1429.                 this.currentToken.lineNumber = this.currentLine;
  1430.                 this.currentToken.startLinePos = this.startLinePos;
  1431.                 HandleError(JSError.NoIdentifier);
  1432.                 id = "#_Missing CC Identifier_#";
  1433.             }
  1434.             else
  1435.                 id = this.strSourceCode.Substring(startIdentifier, length);
  1436.            
  1437.             this.SkipBlanks();
  1438.            
  1439.             char c = GetChar(this.currentPos++);
  1440.             if ('(' == c) {
  1441.                 if (id.Equals("position")) {
  1442.                     PPRemapPositionInfo();
  1443.                 }
  1444.                 else if (id.Equals("option")) {
  1445.                     PPLanguageOption();
  1446.                 }
  1447.                 else if (id.Equals("debug")) {
  1448.                     PPDebugDirective();
  1449.                 }
  1450.                 else {
  1451.                     this.currentToken.startPos = this.currentPos - 1;
  1452.                     this.currentToken.lineNumber = this.currentLine;
  1453.                     this.currentToken.startLinePos = this.startLinePos;
  1454.                     HandleError(JSError.NoEqual);
  1455.                     this.currentPos--;
  1456.                 }
  1457.             }
  1458.             else {
  1459.                 if ('=' != c) {
  1460.                     this.currentToken.startPos = this.currentPos - 1;
  1461.                     this.currentToken.lineNumber = this.currentLine;
  1462.                     this.currentToken.startLinePos = this.startLinePos;
  1463.                     HandleError(JSError.NoEqual);
  1464.                     this.currentPos--;
  1465.                 }
  1466.                
  1467.                 object value = PPScanConstant();
  1468.                 this.ppTable[id] = value;
  1469.             }
  1470.         }
  1471.        
  1472.         private object PPScanExpr()
  1473.         {
  1474.             OpListItem opsStack = new OpListItem(JSToken.None, OpPrec.precNone, null);
  1475.             // dummy element
  1476.             ConstantListItem termStack = new ConstantListItem(PPScanConstant(), null);
  1477.             object value;
  1478.             for (;;) {
  1479.                 GetNextToken();
  1480.                 if (JSScanner.IsPPOperator(this.currentToken.token)) {
  1481.                    
  1482.                     OpPrec prec = JSScanner.GetPPOperatorPrecedence(this.currentToken.token);
  1483.                     while (prec < opsStack._prec) {
  1484.                         //Console.Out.WriteLine("lower prec or same and left assoc");
  1485.                         value = PPGetValue(opsStack._operator, termStack.prev.term, termStack.term);
  1486.                        
  1487.                         // pop the operator stack
  1488.                         opsStack = opsStack._prev;
  1489.                         // pop the term stack twice
  1490.                         termStack = termStack.prev.prev;
  1491.                         // push node onto the stack
  1492.                         termStack = new ConstantListItem(value, termStack);
  1493.                     }
  1494.                     // the current operator has higher precedence that every scanned operators on the stack, or
  1495.                     // it has the same precedence as the one at the top of the stack and it is right associative
  1496.                     // push the operator onto the operators stack
  1497.                     opsStack = new OpListItem(this.currentToken.token, prec, opsStack);
  1498.                     // push new term
  1499.                     termStack = new ConstantListItem(PPScanConstant(), termStack);
  1500.                 }
  1501.                 else {
  1502.                     //Console.Out.WriteLine("unwinding stack");
  1503.                     // there are still operators to be processed
  1504.                     while (opsStack._operator != JSToken.None) {
  1505.                         // make the ast operator node
  1506.                         value = PPGetValue(opsStack._operator, termStack.prev.term, termStack.term);
  1507.                        
  1508.                         // pop the operator stack
  1509.                         opsStack = opsStack._prev;
  1510.                         // pop the term stack twice
  1511.                         termStack = termStack.prev.prev;
  1512.                        
  1513.                         // push node onto the stack
  1514.                         termStack = new ConstantListItem(value, termStack);
  1515.                     }
  1516.                    
  1517.                     // reset position into stream
  1518.                     this.currentPos = this.currentToken.startPos;
  1519.                    
  1520.                     break;
  1521.                     // done
  1522.                 }
  1523.             }
  1524.            
  1525.             Debug.Assert(termStack.prev == null);
  1526.             return termStack.term;
  1527.         }
  1528.        
  1529.         private void PPRemapPositionInfo()
  1530.         {
  1531.             GetNextToken();
  1532.             string filename = null;
  1533.             // this.currentToken.document.documentName;
  1534.             int line = 0;
  1535.             int col = -1;
  1536.             bool endDirective = false;
  1537.             while (JSToken.RightParen != this.currentToken.token) {
  1538.                 if (JSToken.Identifier == this.currentToken.token) {
  1539.                     if (this.currentToken.Equals("file")) {
  1540.                         if (null == this.currentDocument) {
  1541.                             if (null == filename) {
  1542.                                 GetNextToken();
  1543.                                 if (JSToken.Assign != this.currentToken.token) {
  1544.                                     HandleError(JSError.InvalidPositionDirective);
  1545.                                     goto ignoreDirective;
  1546.                                 }
  1547.                                 GetNextToken();
  1548.                                 if (JSToken.StringLiteral != this.currentToken.token) {
  1549.                                     HandleError(JSError.InvalidPositionDirective);
  1550.                                     goto ignoreDirective;
  1551.                                 }
  1552.                                 filename = GetStringLiteral();
  1553.                                 if (filename == this.currentToken.document.documentName) {
  1554.                                     filename = null;
  1555.                                     HandleError(JSError.InvalidPositionDirective);
  1556.                                     goto ignoreDirective;
  1557.                                 }
  1558.                             }
  1559.                             else {
  1560.                                 HandleError(JSError.InvalidPositionDirective);
  1561.                                 goto ignoreDirective;
  1562.                             }
  1563.                         }
  1564.                         else {
  1565.                             HandleError(JSError.CannotNestPositionDirective);
  1566.                             goto ignoreDirective;
  1567.                         }
  1568.                     }
  1569.                     else if (this.currentToken.Equals("line")) {
  1570.                         if (null == this.currentDocument) {
  1571.                             if (line == 0) {
  1572.                                 GetNextToken();
  1573.                                 if (JSToken.Assign != this.currentToken.token) {
  1574.                                     HandleError(JSError.InvalidPositionDirective);
  1575.                                     goto ignoreDirective;
  1576.                                 }
  1577.                                 GetNextToken();
  1578.                                 if (JSToken.IntegerLiteral != this.currentToken.token) {
  1579.                                     HandleError(JSError.InvalidPositionDirective);
  1580.                                     goto ignoreDirective;
  1581.                                 }
  1582.                                 string number = this.currentToken.GetCode();
  1583.                                 double i = Convert.ToNumber(number, true, true, Missing.Value);
  1584.                                 if ((double)(int)i == i && i > 0)
  1585.                                     line = (int)i;
  1586.                                 else {
  1587.                                     line = 1;
  1588.                                     HandleError(JSError.InvalidPositionDirective);
  1589.                                     goto ignoreDirective;
  1590.                                 }
  1591.                             }
  1592.                             else {
  1593.                                 HandleError(JSError.InvalidPositionDirective);
  1594.                                 goto ignoreDirective;
  1595.                             }
  1596.                         }
  1597.                         else {
  1598.                             HandleError(JSError.CannotNestPositionDirective);
  1599.                             goto ignoreDirective;
  1600.                         }
  1601.                     }
  1602.                     else if (this.currentToken.Equals("column")) {
  1603.                         if (null == this.currentDocument) {
  1604.                             if (col == -1) {
  1605.                                 GetNextToken();
  1606.                                 if (JSToken.Assign != this.currentToken.token) {
  1607.                                     HandleError(JSError.InvalidPositionDirective);
  1608.                                     goto ignoreDirective;
  1609.                                 }
  1610.                                 GetNextToken();
  1611.                                 if (JSToken.IntegerLiteral != this.currentToken.token) {
  1612.                                     HandleError(JSError.InvalidPositionDirective);
  1613.                                     goto ignoreDirective;
  1614.                                 }
  1615.                                 string number = this.currentToken.GetCode();
  1616.                                 double i = Convert.ToNumber(number, true, true, Missing.Value);
  1617.                                 if ((double)(int)i == i && i >= 0)
  1618.                                     col = (int)i;
  1619.                                 else {
  1620.                                     col = 0;
  1621.                                     HandleError(JSError.InvalidPositionDirective);
  1622.                                     goto ignoreDirective;
  1623.                                 }
  1624.                             }
  1625.                             else {
  1626.                                 HandleError(JSError.InvalidPositionDirective);
  1627.                                 goto ignoreDirective;
  1628.                             }
  1629.                         }
  1630.                         else {
  1631.                             HandleError(JSError.CannotNestPositionDirective);
  1632.                             goto ignoreDirective;
  1633.                         }
  1634.                     }
  1635.                     else if (this.currentToken.Equals("end")) {
  1636.                         // end of the position mapping directive. Put back the old document
  1637.                         if (null != this.currentDocument) {
  1638.                             // make sure this is the end of it and if not just go to the end of this directive and ignore it
  1639.                             GetNextToken();
  1640.                             if (JSToken.RightParen != this.currentToken.token) {
  1641.                                 HandleError(JSError.InvalidPositionDirective);
  1642.                                 goto ignoreDirective;
  1643.                             }
  1644.                             else {
  1645.                                 // restore the main document
  1646.                                 this.currentToken.document = this.currentDocument;
  1647.                                 this.currentDocument = null;
  1648.                                 endDirective = true;
  1649.                                 break;
  1650.                             }
  1651.                         }
  1652.                         else {
  1653.                             // we got an end with no position ever started. Report an error and ignore
  1654.                             HandleError(JSError.WrongDirective);
  1655.                             goto ignoreDirective;
  1656.                         }
  1657.                     }
  1658.                     else {
  1659.                         HandleError(JSError.InvalidPositionDirective);
  1660.                         goto ignoreDirective;
  1661.                     }
  1662.                    
  1663.                     GetNextToken();
  1664.                     if (JSToken.RightParen == this.currentToken.token)
  1665.                         break;
  1666.                     else if (JSToken.Semicolon == this.currentToken.token)
  1667.                         GetNextToken();
  1668.                     continue;
  1669.                 }
  1670.                
  1671.                 HandleError(JSError.InvalidPositionDirective);
  1672.                 ignoreDirective:
  1673.                 // ignore the directive
  1674.                 while (JSToken.RightParen != this.currentToken.token && JSToken.EndOfFile != this.currentToken.token)
  1675.                     GetNextToken();
  1676.                 break;
  1677.             }
  1678.             this.SkipBlanks();
  1679.             if (';' == GetChar(this.currentPos)) {
  1680.                 ++this.currentPos;
  1681.                 this.SkipBlanks();
  1682.             }
  1683.            
  1684.             // the next char must be an end of line or we skip to it
  1685.             if (this.currentPos < this.endPos && !IsLineTerminator(GetChar(this.currentPos++), 0)) {
  1686.                 HandleError(JSError.MustBeEOL);
  1687.                 while (this.currentPos < this.endPos && !IsLineTerminator(GetChar(this.currentPos++), 0))
  1688.                     ;
  1689.             }
  1690.             this.currentLine++;
  1691.             this.startLinePos = this.currentPos;
  1692.            
  1693.             // set up or restore the document context
  1694.             if (!endDirective) {
  1695.                 if (null == filename && line == 0 && col == -1)
  1696.                     HandleError(JSError.InvalidPositionDirective);
  1697.                 else {
  1698.                     if (filename == null)
  1699.                         filename = this.currentToken.document.documentName;
  1700.                     if (line == 0)
  1701.                         line = 1;
  1702.                     if (col == -1)
  1703.                         col = 0;
  1704.                     this.currentDocument = this.currentToken.document;
  1705.                     this.currentToken.document = new DocumentContext(filename, line, col, this.currentLine, this.currentDocument.sourceItem);
  1706.                 }
  1707.             }
  1708.         }
  1709.        
  1710.         private void PPDebugDirective()
  1711.         {
  1712.             GetNextToken();
  1713.             bool debugOn = false;
  1714.             if (JSToken.Identifier == this.currentToken.token) {
  1715.                 if (this.currentToken.Equals("off"))
  1716.                     debugOn = false;
  1717.                 else if (this.currentToken.Equals("on"))
  1718.                     debugOn = true;
  1719.                 else {
  1720.                     HandleError(JSError.InvalidDebugDirective);
  1721.                     goto ignoreDirective;
  1722.                 }
  1723.                 GetNextToken();
  1724.                 if (JSToken.RightParen != this.currentToken.token) {
  1725.                     HandleError(JSError.InvalidDebugDirective);
  1726.                     goto ignoreDirective;
  1727.                 }
  1728.             }
  1729.             else {
  1730.                 HandleError(JSError.InvalidDebugDirective);
  1731.                 goto ignoreDirective;
  1732.             }
  1733.             this.currentToken.document.debugOn = debugOn && this.globals.engine.GenerateDebugInfo;
  1734.             this.ppTable["_debug"] = debugOn;
  1735.             ignoreDirective:
  1736.            
  1737.             // ignore the directive
  1738.             while (JSToken.RightParen != this.currentToken.token)
  1739.                 GetNextToken();
  1740.            
  1741.             this.SkipBlanks();
  1742.             if (';' == GetChar(this.currentPos)) {
  1743.                 ++this.currentPos;
  1744.                 this.SkipBlanks();
  1745.             }
  1746.            
  1747.             // the next char must be an end of line or we skip to it
  1748.             if (!IsLineTerminator(GetChar(this.currentPos++), 0)) {
  1749.                 HandleError(JSError.MustBeEOL);
  1750.                 while (!IsLineTerminator(GetChar(this.currentPos++), 0))
  1751.                     ;
  1752.             }
  1753.             this.currentLine++;
  1754.             this.startLinePos = this.currentPos;
  1755.         }
  1756.        
  1757.         private void PPLanguageOption()
  1758.         {
  1759.             GetNextToken();
  1760.             /*
  1761.         if (JSToken.Identifier == this.currentToken.token && this.globals != null){
  1762.           if (this.currentToken.Equals("fast")){
  1763.             ((GlobalScope)this.globals.ScopeStack.Peek()).SetFast();
  1764.             this.ppTable["_fast"] = true;
  1765.           }else if (this.currentToken.Equals("versionsafe")){
  1766.             this.globals.engine.versionSafe = true;
  1767.           }else{
  1768.             HandleError(JSError.InvalidLanguageOption);
  1769.           }
  1770.         }else
  1771.         */HandleError(JSError.InvalidLanguageOption);
  1772.             GetNextToken();
  1773.             Context errorToken = null;
  1774.             while (JSToken.RightParen != this.currentToken.token) {
  1775.                 if (null == errorToken)
  1776.                     errorToken = this.currentToken.Clone();
  1777.                 else
  1778.                     errorToken.UpdateWith(this.currentToken);
  1779.                 GetNextToken();
  1780.             }
  1781.             if (null != errorToken)
  1782.                 HandleError(JSError.NoRightParen);
  1783.         }
  1784.        
  1785.         private object PPScanConstant()
  1786.         {
  1787.             object value;
  1788.             GetNextToken();
  1789.             switch (this.currentToken.token) {
  1790.                 case JSToken.Plus:
  1791.                     value = Convert.ToNumber(PPScanConstant());
  1792.                     break;
  1793.                 case JSToken.Minus:
  1794.                     value = -Convert.ToNumber(PPScanConstant());
  1795.                     break;
  1796.                 case JSToken.BitwiseNot:
  1797.                     value = ~Convert.ToInt32(PPScanConstant());
  1798.                     break;
  1799.                 case JSToken.LogicalNot:
  1800.                     value = !Convert.ToBoolean(PPScanConstant());
  1801.                     break;
  1802.                 case JSToken.IntegerLiteral:
  1803.                     value = Convert.ToNumber(this.currentToken.GetCode(), true, true, Missing.Value);
  1804.                     break;
  1805.                 case JSToken.NumericLiteral:
  1806.                     value = Convert.ToNumber(this.currentToken.GetCode(), false, false, Missing.Value);
  1807.                     break;
  1808.                 case JSToken.PreProcessorConstant:
  1809.                     value = this.preProcessorValue;
  1810.                     break;
  1811.                 case JSToken.True:
  1812.                     value = true;
  1813.                     break;
  1814.                 case JSToken.False:
  1815.                     value = false;
  1816.                     break;
  1817.                 case JSToken.LeftParen:
  1818.                     value = PPScanExpr();
  1819.                     GetNextToken();
  1820.                     if (JSToken.RightParen != this.currentToken.token) {
  1821.                         this.currentToken.endPos = this.currentToken.startPos + 1;
  1822.                         this.currentToken.endLineNumber = this.currentLine;
  1823.                         this.currentToken.endLinePos = this.startLinePos;
  1824.                         HandleError(JSError.NoRightParen);
  1825.                         this.currentPos = this.currentToken.startPos;
  1826.                     }
  1827.                     break;
  1828.                 default:
  1829.                     HandleError(JSError.NotConst);
  1830.                     this.currentPos = this.currentToken.startPos;
  1831.                     value = true;
  1832.                     break;
  1833.             }
  1834.             return value;
  1835.         }
  1836.        
  1837.         object PPGetValue(JSToken op, object op1, object op2)
  1838.         {
  1839.             switch (op) {
  1840.                 case JSToken.Plus:
  1841.                     return Convert.ToNumber(op1) + Convert.ToNumber(op2);
  1842.                 case JSToken.Minus:
  1843.                     return Convert.ToNumber(op1) - Convert.ToNumber(op2);
  1844.                 case JSToken.LogicalOr:
  1845.                     return Convert.ToBoolean(op1) || Convert.ToBoolean(op2);
  1846.                 case JSToken.LogicalAnd:
  1847.                     return Convert.ToBoolean(op1) && Convert.ToBoolean(op2);
  1848.                 case JSToken.BitwiseOr:
  1849.                     return Convert.ToInt32(op1) | Convert.ToInt32(op2);
  1850.                 case JSToken.BitwiseXor:
  1851.                     return Convert.ToInt32(op1) ^ Convert.ToInt32(op2);
  1852.                 case JSToken.BitwiseAnd:
  1853.                     return Convert.ToInt32(op1) & Convert.ToInt32(op2);
  1854.                 case JSToken.Equal:
  1855.                     return Convert.ToNumber(op1) == Convert.ToNumber(op2);
  1856.                 case JSToken.NotEqual:
  1857.                     return Convert.ToNumber(op1) != Convert.ToNumber(op2);
  1858.                 case JSToken.StrictEqual:
  1859.                     return op1 == op2;
  1860.                 case JSToken.StrictNotEqual:
  1861.                     return op1 != op2;
  1862.                 case JSToken.GreaterThan:
  1863.                     return Convert.ToNumber(op1) > Convert.ToNumber(op2);
  1864.                 case JSToken.LessThan:
  1865.                     return Convert.ToNumber(op1) < Convert.ToNumber(op2);
  1866.                 case JSToken.LessThanEqual:
  1867.                     return Convert.ToNumber(op1) <= Convert.ToNumber(op2);
  1868.                 case JSToken.GreaterThanEqual:
  1869.                     return Convert.ToNumber(op1) >= Convert.ToNumber(op2);
  1870.                 case JSToken.LeftShift:
  1871.                     return Convert.ToInt32(op1) << (Convert.ToInt32(op2) & 31);
  1872.                 case JSToken.RightShift:
  1873.                     return Convert.ToInt32(op1) >> (Convert.ToInt32(op2) & 31);
  1874.                 case JSToken.UnsignedRightShift:
  1875.                     return ((uint)Convert.ToInt32(op1)) >> (Convert.ToInt32(op2) & 31);
  1876.                 case JSToken.Multiply:
  1877.                     return Convert.ToNumber(op1) * Convert.ToNumber(op2);
  1878.                 case JSToken.Divide:
  1879.                     return Convert.ToNumber(op1) / Convert.ToNumber(op2);
  1880.                 case JSToken.Modulo:
  1881.                     return Convert.ToInt32(op1) % Convert.ToInt32(op2);
  1882.                 default:
  1883.                     Debug.Assert(false);
  1884.                     break;
  1885.             }
  1886.             return null;
  1887.         }
  1888.        
  1889.         internal object GetPreProcessorValue()
  1890.         {
  1891.             return this.preProcessorValue;
  1892.         }
  1893.        
  1894.         private void HandleError(JSError error)
  1895.         {
  1896.             if (!this.IsAuthoring)
  1897.                 this.currentToken.HandleError(error);
  1898.         }
  1899.        
  1900.         public static bool IsOperator(JSToken token)
  1901.         {
  1902.             return JSToken.FirstOp <= token && token <= JSToken.LastOp;
  1903.         }
  1904.        
  1905.         static internal bool IsAssignmentOperator(JSToken token)
  1906.         {
  1907.             return JSToken.Assign <= token && token <= JSToken.LastAssign;
  1908.         }
  1909.        
  1910.         static internal bool CanStartStatement(JSToken token)
  1911.         {
  1912.             return JSToken.If <= token && token <= JSToken.Function;
  1913.         }
  1914.        
  1915.        
  1916.         static internal bool CanParseAsExpression(JSToken token)
  1917.         {
  1918.             return JSToken.FirstBinaryOp <= token && token <= JSToken.LastOp || JSToken.LeftParen <= token && token <= JSToken.AccessField;
  1919.         }
  1920.        
  1921.         static internal bool IsRightAssociativeOperator(JSToken token)
  1922.         {
  1923.             return JSToken.Assign <= token && token <= JSToken.ConditionalIf;
  1924.         }
  1925.        
  1926.         public static bool IsKeyword(JSToken token)
  1927.         {
  1928.             switch (token) {
  1929.                 case JSToken.If:
  1930.                 case JSToken.For:
  1931.                 case JSToken.Do:
  1932.                 case JSToken.While:
  1933.                 case JSToken.Continue:
  1934.                 case JSToken.Break:
  1935.                 case JSToken.Return:
  1936.                 case JSToken.Import:
  1937.                 case JSToken.With:
  1938.                 case JSToken.Switch:
  1939.                 case JSToken.Throw:
  1940.                 case JSToken.Try:
  1941.                 case JSToken.Package:
  1942.                 case JSToken.Abstract:
  1943.                 case JSToken.Public:
  1944.                 case JSToken.Static:
  1945.                 case JSToken.Private:
  1946.                 case JSToken.Protected:
  1947.                 case JSToken.Var:
  1948.                 case JSToken.Class:
  1949.                 case JSToken.Function:
  1950.                 case JSToken.Null:
  1951.                 case JSToken.True:
  1952.                 case JSToken.False:
  1953.                 case JSToken.This:
  1954.                 case JSToken.Final:
  1955.                 case JSToken.Const:
  1956.                 case JSToken.Delete:
  1957.                 case JSToken.Void:
  1958.                 case JSToken.Typeof:
  1959.                 case JSToken.Instanceof:
  1960.                 case JSToken.In:
  1961.                 case JSToken.Case:
  1962.                 case JSToken.Catch:
  1963.                 case JSToken.Debugger:
  1964.                 case JSToken.Default:
  1965.                 case JSToken.Else:
  1966.                 case JSToken.Export:
  1967.                 case JSToken.Extends:
  1968.                 case JSToken.Finally:
  1969.                 case JSToken.Get:
  1970.                 case JSToken.Implements:
  1971.                 case JSToken.Interface:
  1972.                 case JSToken.New:
  1973.                 case JSToken.Set:
  1974.                 case JSToken.Super:
  1975.                 case JSToken.Boolean:
  1976.                 case JSToken.Byte:
  1977.                 case JSToken.Char:
  1978.                 case JSToken.Double:
  1979.                 case JSToken.Enum:
  1980.                 case JSToken.Float:
  1981.                 case JSToken.Goto:
  1982.                 case JSToken.Int:
  1983.                 case JSToken.Long:
  1984.                 case JSToken.Native:
  1985.                 case JSToken.Short:
  1986.                 case JSToken.Synchronized:
  1987.                 case JSToken.Transient:
  1988.                 case JSToken.Throws:
  1989.                 case JSToken.Volatile:
  1990.                     return true;
  1991.             }
  1992.             return false;
  1993.         }
  1994.        
  1995.         // This function return whether an operator is processable in ParseExpression.
  1996.         // Comma is out of this list and so are the unary ops
  1997.         static internal bool IsProcessableOperator(JSToken token)
  1998.         {
  1999.             return JSToken.FirstBinaryOp <= token && token <= JSToken.ConditionalIf;
  2000.         }
  2001.        
  2002.         // This function return whether an operator is processable in pre processing.
  2003.         static internal bool IsPPOperator(JSToken token)
  2004.         {
  2005.             return JSToken.FirstBinaryOp <= token && token <= JSToken.LastPPOperator;
  2006.         }
  2007.        
  2008.         private static readonly OpPrec[] s_OperatorsPrec = InitOperatorsPrec();
  2009.         private static readonly OpPrec[] s_PPOperatorsPrec = InitPPOperatorsPrec();
  2010.        
  2011.         static internal OpPrec GetOperatorPrecedence(JSToken token)
  2012.         {
  2013.             return JSScanner.s_OperatorsPrec[token - JSToken.FirstBinaryOp];
  2014.         }
  2015.        
  2016.         static internal OpPrec GetPPOperatorPrecedence(JSToken token)
  2017.         {
  2018.             return JSScanner.s_PPOperatorsPrec[token - JSToken.FirstBinaryOp];
  2019.         }
  2020.        
  2021.         private static OpPrec[] InitOperatorsPrec()
  2022.         {
  2023.             OpPrec[] operatorsPrec = new OpPrec[JSToken.ConditionalIf - JSToken.FirstBinaryOp + 1];
  2024.            
  2025.             operatorsPrec[JSToken.Plus - JSToken.FirstBinaryOp] = OpPrec.precAdditive;
  2026.             operatorsPrec[JSToken.Minus - JSToken.FirstBinaryOp] = OpPrec.precAdditive;
  2027.            
  2028.             operatorsPrec[JSToken.LogicalOr - JSToken.FirstBinaryOp] = OpPrec.precLogicalOr;
  2029.             operatorsPrec[JSToken.LogicalAnd - JSToken.FirstBinaryOp] = OpPrec.precLogicalAnd;
  2030.             operatorsPrec[JSToken.BitwiseOr - JSToken.FirstBinaryOp] = OpPrec.precBitwiseOr;
  2031.             operatorsPrec[JSToken.BitwiseXor - JSToken.FirstBinaryOp] = OpPrec.precBitwiseXor;
  2032.             operatorsPrec[JSToken.BitwiseAnd - JSToken.FirstBinaryOp] = OpPrec.precBitwiseAnd;
  2033.            
  2034.             operatorsPrec[JSToken.Equal - JSToken.FirstBinaryOp] = OpPrec.precEquality;
  2035.             operatorsPrec[JSToken.NotEqual - JSToken.FirstBinaryOp] = OpPrec.precEquality;
  2036.             operatorsPrec[JSToken.StrictEqual - JSToken.FirstBinaryOp] = OpPrec.precEquality;
  2037.             operatorsPrec[JSToken.StrictNotEqual - JSToken.FirstBinaryOp] = OpPrec.precEquality;
  2038.            
  2039.             operatorsPrec[JSToken.Instanceof - JSToken.FirstBinaryOp] = OpPrec.precRelational;
  2040.             operatorsPrec[JSToken.In - JSToken.FirstBinaryOp] = OpPrec.precRelational;
  2041.             operatorsPrec[JSToken.GreaterThan - JSToken.FirstBinaryOp] = OpPrec.precRelational;
  2042.             operatorsPrec[JSToken.LessThan - JSToken.FirstBinaryOp] = OpPrec.precRelational;
  2043.             operatorsPrec[JSToken.LessThanEqual - JSToken.FirstBinaryOp] = OpPrec.precRelational;
  2044.             operatorsPrec[JSToken.GreaterThanEqual - JSToken.FirstBinaryOp] = OpPrec.precRelational;
  2045.            
  2046.             operatorsPrec[JSToken.LeftShift - JSToken.FirstBinaryOp] = OpPrec.precShift;
  2047.             operatorsPrec[JSToken.RightShift - JSToken.FirstBinaryOp] = OpPrec.precShift;
  2048.             operatorsPrec[JSToken.UnsignedRightShift - JSToken.FirstBinaryOp] = OpPrec.precShift;
  2049.            
  2050.             operatorsPrec[JSToken.Multiply - JSToken.FirstBinaryOp] = OpPrec.precMultiplicative;
  2051.             operatorsPrec[JSToken.Divide - JSToken.FirstBinaryOp] = OpPrec.precMultiplicative;
  2052.             operatorsPrec[JSToken.Modulo - JSToken.FirstBinaryOp] = OpPrec.precMultiplicative;
  2053.            
  2054.             operatorsPrec[JSToken.Assign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
  2055.             operatorsPrec[JSToken.PlusAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
  2056.             operatorsPrec[JSToken.MinusAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
  2057.             operatorsPrec[JSToken.MultiplyAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
  2058.             operatorsPrec[JSToken.DivideAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
  2059.             operatorsPrec[JSToken.BitwiseAndAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
  2060.             operatorsPrec[JSToken.BitwiseOrAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
  2061.             operatorsPrec[JSToken.BitwiseXorAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
  2062.             operatorsPrec[JSToken.ModuloAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
  2063.             operatorsPrec[JSToken.LeftShiftAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
  2064.             operatorsPrec[JSToken.RightShiftAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
  2065.             operatorsPrec[JSToken.UnsignedRightShiftAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
  2066.            
  2067.             operatorsPrec[JSToken.ConditionalIf - JSToken.FirstBinaryOp] = OpPrec.precConditional;
  2068.            
  2069.             return operatorsPrec;
  2070.         }
  2071.        
  2072.         private static OpPrec[] InitPPOperatorsPrec()
  2073.         {
  2074.             OpPrec[] operatorsPrec = new OpPrec[JSToken.LastPPOperator - JSToken.FirstBinaryOp + 1];
  2075.            
  2076.             operatorsPrec[JSToken.Plus - JSToken.FirstBinaryOp] = OpPrec.precAdditive;
  2077.             operatorsPrec[JSToken.Minus - JSToken.FirstBinaryOp] = OpPrec.precAdditive;
  2078.            
  2079.             operatorsPrec[JSToken.LogicalOr - JSToken.FirstBinaryOp] = OpPrec.precLogicalOr;
  2080.             operatorsPrec[JSToken.LogicalAnd - JSToken.FirstBinaryOp] = OpPrec.precLogicalAnd;
  2081.             operatorsPrec[JSToken.BitwiseOr - JSToken.FirstBinaryOp] = OpPrec.precBitwiseOr;
  2082.             operatorsPrec[JSToken.BitwiseXor - JSToken.FirstBinaryOp] = OpPrec.precBitwiseXor;
  2083.             operatorsPrec[JSToken.BitwiseAnd - JSToken.FirstBinaryOp] = OpPrec.precBitwiseAnd;
  2084.            
  2085.             operatorsPrec[JSToken.Equal - JSToken.FirstBinaryOp] = OpPrec.precEquality;
  2086.             operatorsPrec[JSToken.NotEqual - JSToken.FirstBinaryOp] = OpPrec.precEquality;
  2087.             operatorsPrec[JSToken.StrictEqual - JSToken.FirstBinaryOp] = OpPrec.precEquality;
  2088.             operatorsPrec[JSToken.StrictNotEqual - JSToken.FirstBinaryOp] = OpPrec.precEquality;
  2089.            
  2090.             operatorsPrec[JSToken.GreaterThan - JSToken.FirstBinaryOp] = OpPrec.precRelational;
  2091.             operatorsPrec[JSToken.LessThan - JSToken.FirstBinaryOp] = OpPrec.precRelational;
  2092.             operatorsPrec[JSToken.LessThanEqual - JSToken.FirstBinaryOp] = OpPrec.precRelational;
  2093.             operatorsPrec[JSToken.GreaterThanEqual - JSToken.FirstBinaryOp] = OpPrec.precRelational;
  2094.            
  2095.             operatorsPrec[JSToken.LeftShift - JSToken.FirstBinaryOp] = OpPrec.precShift;
  2096.             operatorsPrec[JSToken.RightShift - JSToken.FirstBinaryOp] = OpPrec.precShift;
  2097.             operatorsPrec[JSToken.UnsignedRightShift - JSToken.FirstBinaryOp] = OpPrec.precShift;
  2098.            
  2099.             operatorsPrec[JSToken.Multiply - JSToken.FirstBinaryOp] = OpPrec.precMultiplicative;
  2100.             operatorsPrec[JSToken.Divide - JSToken.FirstBinaryOp] = OpPrec.precMultiplicative;
  2101.             operatorsPrec[JSToken.Modulo - JSToken.FirstBinaryOp] = OpPrec.precMultiplicative;
  2102.            
  2103.             return operatorsPrec;
  2104.         }
  2105.     }
  2106. }

Developer Fusion