Files
yallie 9fa3874800 Added the contents of the archive.
Moved the original file to the archive subfolder.
2017-11-20 16:11:42 +03:00

2004 lines
75 KiB
C#

// ==++==
//
//
// Copyright (c) 2002 Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are contained in the file
// named license.txt, which can be found in the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by the
// terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
// ==--==
namespace Microsoft.JScript
{
using Microsoft.JScript.Vsa;
using System;
using System.Collections;
using System.Globalization;
using Microsoft.Vsa;
using System.Text;
public sealed class JSScanner{
// scanner main data
private string strSourceCode;
private int startPos;
private int endPos;
private int currentPos;
private int currentLine;
private int startLinePos;
// token information
private Context currentToken;
private String escapedString;
private StringBuilder identifier;
private int idLastPosOnBuilder;
// flags
private bool gotEndOfLine;
private bool IsAuthoring;
private bool peekModeOn;
private bool scanForDebugger;
// keyword table
private JSKeyword[] keywords;
private static readonly JSKeyword[] s_Keywords = JSKeyword.InitKeywords();
// pre process information
private bool preProcessorOn;
private int matchIf;
private Object preProcessorValue;
private SimpleHashtable ppTable;
// environmental information
private DocumentContext currentDocument;
private Globals globals;
public JSScanner(){
this.keywords = JSScanner.s_Keywords;
this.strSourceCode = null;
this.startPos = 0;
this.endPos = 0;
this.currentPos = 0;
this.currentLine = 1;
this.startLinePos = 0;
this.currentToken = null;
this.escapedString = null;
this.identifier = new StringBuilder(128);
this.idLastPosOnBuilder = 0;
this.gotEndOfLine = false;
this.IsAuthoring = false;
this.peekModeOn = false;
this.preProcessorOn = false;
this.matchIf = 0;
this.ppTable = null;
this.currentDocument = null;
this.globals = null;
this.scanForDebugger = false;
}
public JSScanner(Context sourceContext){
this.IsAuthoring = false;
this.peekModeOn = false;
this.keywords = s_Keywords;
this.preProcessorOn = false;
this.matchIf = 0;
this.ppTable = null;
this.SetSource(sourceContext);
this.currentDocument = null;
this.globals = sourceContext.document.engine.Globals;
}
public void SetAuthoringMode(bool mode){
this.IsAuthoring = mode;
}
public void SetSource(Context sourceContext){
this.strSourceCode = sourceContext.source_string;
this.startPos = sourceContext.startPos;
this.startLinePos = sourceContext.startLinePos;
this.endPos = (0 < sourceContext.endPos && sourceContext.endPos < this.strSourceCode.Length) ?
sourceContext.endPos :
this.strSourceCode.Length;
this.currentToken = sourceContext;
this.escapedString = null;
this.identifier = new StringBuilder(128);
this.idLastPosOnBuilder = 0;
this.currentPos = this.startPos;
this.currentLine = (sourceContext.lineNumber > 0) ? sourceContext.lineNumber : 1;
this.gotEndOfLine = false;
this.scanForDebugger = sourceContext.document != null && sourceContext.document.engine != null && VsaEngine.executeForJSEE;
}
internal JSToken PeekToken(){
int currentPos = this.currentPos;
int currentLine = this.currentLine;
int startLinePos = this.startLinePos;
bool gotEndOfLine = this.gotEndOfLine;
int idLastPosOnBuilder = this.idLastPosOnBuilder;
this.peekModeOn = true;
JSToken token = JSToken.None;
// temporary switch the token
Context currentToken = this.currentToken;
this.currentToken = this.currentToken.Clone();
try{
GetNextToken();
token = this.currentToken.token;
}finally{
this.currentToken = currentToken;
this.currentPos = currentPos;
this.currentLine = currentLine;
this.startLinePos = startLinePos;
this.gotEndOfLine = gotEndOfLine;
this.identifier.Length = 0;
this.idLastPosOnBuilder = idLastPosOnBuilder;
this.peekModeOn = false;
this.escapedString = null;
}
return token;
}
public void GetNextToken(){
JSToken token = JSToken.None;
this.gotEndOfLine = false;
try{
int currentLine = this.currentLine;
nextToken:
this.SkipBlanks();
this.currentToken.startPos = this.currentPos;
this.currentToken.lineNumber = this.currentLine;
this.currentToken.startLinePos = this.startLinePos;
char c = GetChar(this.currentPos++);
switch (c){
case (char)0:
if (this.currentPos >= this.endPos){
this.currentPos--;
token = JSToken.EndOfFile;
if (this.matchIf > 0){
this.currentToken.endLineNumber = this.currentLine;
this.currentToken.endLinePos = this.startLinePos;
this.currentToken.endPos = this.currentPos;
HandleError(JSError.NoCcEnd);
}
break;
}
goto nextToken;
case '=':
token = JSToken.Assign;
if ('=' == GetChar(this.currentPos)){
this.currentPos++;
token = JSToken.Equal;
if ('=' == GetChar(this.currentPos)){
this.currentPos++;
token = JSToken.StrictEqual;
}
}
break;
case '>':
token = JSToken.GreaterThan;
if ('>' == GetChar(this.currentPos)){
this.currentPos++;
token = JSToken.RightShift;
if ('>' == GetChar(this.currentPos)){
this.currentPos++;
token = JSToken.UnsignedRightShift;
}
}
if ('=' == GetChar(this.currentPos)){
this.currentPos++;
switch (token){
case JSToken.GreaterThan:
token = JSToken.GreaterThanEqual;
break;
case JSToken.RightShift:
token = JSToken.RightShiftAssign;
break;
case JSToken.UnsignedRightShift:
token = JSToken.UnsignedRightShiftAssign;
break;
}
}
break;
case '<':
token = JSToken.LessThan;
if ('<' == GetChar(this.currentPos)){
this.currentPos++;
token = JSToken.LeftShift;
}
if ('=' == GetChar(this.currentPos)){
this.currentPos++;
if (token == JSToken.LessThan)
token = JSToken.LessThanEqual;
else
token = JSToken.LeftShiftAssign;
}
break;
case '!':
token = JSToken.LogicalNot;
if ('=' == GetChar(this.currentPos)){
this.currentPos++;
token = JSToken.NotEqual;
if ('=' == GetChar(this.currentPos)){
this.currentPos++;
token = JSToken.StrictNotEqual;
}
}
break;
case ',':
token = JSToken.Comma;
break;
case '~':
token = JSToken.BitwiseNot;
break;
case '?':
token = JSToken.ConditionalIf;
break;
case ':':
token = JSToken.Colon;
if (':' == GetChar(this.currentPos)){
this.currentPos++;
token = JSToken.DoubleColon;
}
break;
case '.':
token = JSToken.AccessField;
c = GetChar(this.currentPos);
if (JSScanner.IsDigit(c)){
token = ScanNumber('.');
}else if ('.' == c){
c = GetChar(this.currentPos+1);
if ('.' == c){
this.currentPos += 2;
token = JSToken.ParamArray;
}
}
break;
case '&':
token = JSToken.BitwiseAnd;
c = GetChar(this.currentPos);
if ('&' == c){
this.currentPos++;
token = JSToken.LogicalAnd;
}else if ('=' == c){
this.currentPos++;
token = JSToken.BitwiseAndAssign;
}
break;
case '|':
token = JSToken.BitwiseOr;
c = GetChar(this.currentPos);
if ('|' == c){
this.currentPos++;
token = JSToken.LogicalOr;
}else if ('=' == c){
this.currentPos++;
token = JSToken.BitwiseOrAssign;
}
break;
case '+':
token = JSToken.Plus;
c = GetChar(this.currentPos);
if ('+' == c){
this.currentPos++;
token = JSToken.Increment;
}else if ('=' == c){
this.currentPos++;
token = JSToken.PlusAssign;
}
break;
case '-':
token = JSToken.Minus;
c = GetChar(this.currentPos);
if ('-' == c){
this.currentPos++;
token = JSToken.Decrement;
}else if ('=' == c){
this.currentPos++;
token = JSToken.MinusAssign;
}
break;
case '*':
token = JSToken.Multiply;
if ('=' == GetChar(this.currentPos)){
this.currentPos++;
token = JSToken.MultiplyAssign;
}
break;
case '\\':
this.currentPos--;
if (IsIdentifierStartChar(ref c)){
this.currentPos++;
ScanIdentifier();
token = JSToken.Identifier;
break;
}
this.currentPos++; // move on
c = GetChar(this.currentPos);
if ('a' <= c && c <= 'z'){
JSKeyword keyword = this.keywords[c - 'a'];
if (null != keyword){
this.currentToken.startPos++;
token = ScanKeyword(keyword);
if (token != JSToken.Identifier){
token = JSToken.Identifier;
break;
}
this.currentToken.startPos--;
}
}
this.currentPos = this.currentToken.startPos+1;
HandleError(JSError.IllegalChar);
break;
case '/':
token = JSToken.Divide;
c = GetChar(this.currentPos);
bool preProcess = false;
switch (c){
case '/':
if (GetChar(++this.currentPos) == '@' && !this.peekModeOn){
// we got //@
if (!this.preProcessorOn){
// preprocessing was not on, check if it's going to be turned on
if ('c' == GetChar(++this.currentPos) &&
'c' == GetChar(++this.currentPos) &&
'_' == GetChar(++this.currentPos) &&
'o' == GetChar(++this.currentPos) &&
'n' == GetChar(++this.currentPos)){
char nextChar = GetChar(this.currentPos + 1);
if (!JSScanner.IsDigit(nextChar) && !JSScanner.IsAsciiLetter(nextChar) && !JSScanner.IsUnicodeLetter(nextChar)){
SetPreProcessorOn();
this.currentPos++;
goto nextToken;
}
}
// it will fall into a "standard" comment processing
}else{
// if it is a space we got '//@ ' that has to be ignored
if (JSScanner.IsBlankSpace(GetChar(++this.currentPos)))
goto nextToken;
preProcess = true;
break;
}
}
SkipSingleLineComment();
if (this.IsAuthoring){
token = JSToken.Comment;
break; // just break out and return
}else
goto nextToken; // read another token this last one was a comment
case '*':
if (GetChar(++this.currentPos) == '@' && !this.peekModeOn){
if (!this.preProcessorOn){
if ('c' == GetChar(++this.currentPos) &&
'c' == GetChar(++this.currentPos) &&
'_' == GetChar(++this.currentPos) &&
'o' == GetChar(++this.currentPos) &&
'n' == GetChar(++this.currentPos)){
char nextChar = GetChar(this.currentPos + 1);
if (!JSScanner.IsDigit(nextChar) && !JSScanner.IsAsciiLetter(nextChar) && !JSScanner.IsUnicodeLetter(nextChar)){
SetPreProcessorOn();
this.currentPos++;
goto nextToken;
}
}
}else{
// if it is a space we got '/*@ ' that has to be ignored
if (JSScanner.IsBlankSpace(GetChar(++this.currentPos)))
goto nextToken;
preProcess = true;
break;
}
}
SkipMultiLineComment();
if (this.IsAuthoring){
if (this.currentPos > this.endPos){
token = JSToken.UnterminatedComment;
this.currentPos = this.endPos;
}else
token = JSToken.Comment;
break;
}else
goto nextToken; // read another token this last one was a comment
case '=':
this.currentPos++;
token = JSToken.DivideAssign;
break;
}
if (preProcess)
goto case '@';
break;
case '^':
token = JSToken.BitwiseXor;
if ('=' == GetChar(this.currentPos)){
this.currentPos++;
token = JSToken.BitwiseXorAssign;
}
break;
case '%':
token = JSToken.Modulo;
if ('=' == GetChar(this.currentPos)){
this.currentPos++;
token = JSToken.ModuloAssign;
}
break;
case '(':
token = JSToken.LeftParen;
break;
case ')':
token = JSToken.RightParen;
break;
case '{':
token = JSToken.LeftCurly;
break;
case '}':
token = JSToken.RightCurly;
break;
case '[':
token = JSToken.LeftBracket;
break;
case ']':
token = JSToken.RightBracket;
break;
case ';':
token = JSToken.Semicolon;
break;
case '"':
goto case '\'';
case '\'':
token = JSToken.StringLiteral;
ScanString(c);
break;
// line terminator crap
case '\r':
if (GetChar(this.currentPos) == '\n')
this.currentPos++;
this.currentLine++;
this.startLinePos = this.currentPos;
goto nextToken;
case '\n':
this.currentLine++;
this.startLinePos = this.currentPos;
goto nextToken;
case (char)0x2028:
this.currentLine++;
this.startLinePos = this.currentPos;
goto nextToken;
case (char)0x2029:
this.currentLine++;
this.startLinePos = this.currentPos;
goto nextToken;
//The sick beauty of pre processing
case '@':
if (this.scanForDebugger)
HandleError(JSError.CcInvalidInDebugger);
if (this.peekModeOn){
this.currentToken.token = JSToken.PreProcessDirective;
break;
}
int startPos = this.currentPos;
this.currentToken.startPos = startPos;
this.currentToken.lineNumber = this.currentLine;
this.currentToken.startLinePos = this.startLinePos;
ScanIdentifier();
switch (this.currentPos - startPos){
case 0:
// Ignore '@*/'.
if (this.preProcessorOn && '*' == GetChar(this.currentPos) && '/' == GetChar(++this.currentPos))
this.currentPos++;
else
HandleError(JSError.IllegalChar);
goto nextToken;
case 2:
if ('i' == this.strSourceCode[startPos] &&
'f' == this.strSourceCode[startPos + 1]){
if (!this.preProcessorOn)
SetPreProcessorOn();
this.matchIf++;
if (!PPTestCond())
PPSkipToNextCondition(true);
goto nextToken;
}
goto default;
case 3:
if ('s' == this.strSourceCode[startPos] &&
'e' == this.strSourceCode[startPos + 1] &&
't' == this.strSourceCode[startPos + 2]){
if (!this.preProcessorOn)
SetPreProcessorOn();
PPScanSet();
goto nextToken;
}else if ('e' == this.strSourceCode[startPos] &&
'n' == this.strSourceCode[startPos + 1] &&
'd' == this.strSourceCode[startPos + 2]){
if (0 >= this.matchIf)
HandleError(JSError.CcInvalidEnd);
else
this.matchIf--;
goto nextToken;
}
goto default;
case 4:
if ('e' == this.strSourceCode[startPos] &&
'l' == this.strSourceCode[startPos + 1] &&
's' == this.strSourceCode[startPos + 2] &&
'e' == this.strSourceCode[startPos + 3]){
if (0 >= this.matchIf)
HandleError(JSError.CcInvalidElse);
else
PPSkipToNextCondition(false);
goto nextToken;
}else if ('e' == this.strSourceCode[startPos] &&
'l' == this.strSourceCode[startPos + 1] &&
'i' == this.strSourceCode[startPos + 2] &&
'f' == this.strSourceCode[startPos + 3]){
if (0 >= this.matchIf)
HandleError(JSError.CcInvalidElif);
else
PPSkipToNextCondition(false);
goto nextToken;
}
goto default;
case 5:
if ('c' == GetChar(startPos) &&
'c' == GetChar(startPos + 1) &&
'_' == GetChar(startPos + 2) &&
'o' == GetChar(startPos + 3) &&
'n' == GetChar(startPos + 4)){
if (!this.preProcessorOn)
SetPreProcessorOn();
goto nextToken;
}
goto default;
default:
if (!this.preProcessorOn){
HandleError(JSError.CcOff);
goto nextToken;
}
Object value = this.ppTable[this.strSourceCode.Substring(startPos, this.currentPos - startPos)];
if (null == value)
this.preProcessorValue = Double.NaN;
else
this.preProcessorValue = value;
token = JSToken.PreProcessorConstant;
break;
}
break;
case '$':
goto case '_';
case '_':
ScanIdentifier();
token = JSToken.Identifier;
break;
default:
if ('a' <= c && c <= 'z'){
JSKeyword keyword = this.keywords[c - 'a'];
if (null != keyword)
token = ScanKeyword(keyword);
else{
token = JSToken.Identifier;
ScanIdentifier();
}
}else if (JSScanner.IsDigit(c)){
token = ScanNumber(c);
}else if ('A' <= c && c <= 'Z' || JSScanner.IsUnicodeLetter(c)){
token = JSToken.Identifier;
ScanIdentifier();
}else{
HandleError(JSError.IllegalChar);
goto nextToken;
}
break;
}
this.currentToken.endLineNumber = this.currentLine;
this.currentToken.endLinePos = this.startLinePos;
this.currentToken.endPos = this.currentPos;
this.gotEndOfLine = (this.currentLine > currentLine || token == JSToken.EndOfFile) ? true : false;
if (this.gotEndOfLine && token == JSToken.StringLiteral && this.currentToken.lineNumber == currentLine)
this.gotEndOfLine = false;
}catch(IndexOutOfRangeException){
token = JSToken.None;
this.currentToken.endPos = this.currentPos;
this.currentToken.endLineNumber = this.currentLine;
this.currentToken.endLinePos = this.startLinePos;
throw new ScannerException(JSError.ErrEOF);
}
this.currentToken.token = token;
}
private char GetChar(int index){
if (index < this.endPos)
return this.strSourceCode[index];
else
return (char)0;
}
public int GetCurrentPosition(bool absolute){
return this.currentPos;
}
public int GetCurrentLine(){
return this.currentLine;
}
public int GetStartLinePosition(){
return this.startLinePos;
}
public string GetStringLiteral(){
return this.escapedString;
}
public string GetSourceCode(){
return this.strSourceCode;
}
public bool GotEndOfLine() {
return this.gotEndOfLine;
}
internal String GetIdentifier(){
String id = null;
if (this.identifier.Length > 0){
id = this.identifier.ToString();
this.identifier.Length = 0;
}else
id = this.currentToken.GetCode();
if (id.Length > 500) //The EE sometimes gets into trouble if presented with a name > 1023 bytes, make this less likely
id = id.Substring(0, 500) + id.GetHashCode().ToString(CultureInfo.InvariantCulture);
return id;
}
private void ScanIdentifier(){
for(;;){
char c = GetChar(this.currentPos);
if (!IsIdentifierPartChar(c))
break;
++this.currentPos;
}
if (this.idLastPosOnBuilder > 0){
this.identifier.Append(this.strSourceCode.Substring(this.idLastPosOnBuilder, this.currentPos - this.idLastPosOnBuilder));
this.idLastPosOnBuilder = 0;
}
}
private JSToken ScanKeyword(JSKeyword keyword){
for(;;){
char c = GetChar(this.currentPos);
if ('a' <= c && c <= 'z'){
this.currentPos++;
continue;
}else{
if (IsIdentifierPartChar(c)){
ScanIdentifier();
return JSToken.Identifier;
}
break;
}
}
return keyword.GetKeyword(this.currentToken, this.currentPos - this.currentToken.startPos);
}
private JSToken ScanNumber(char leadChar){
bool noMoreDot = '.' == leadChar;
JSToken token = noMoreDot ? JSToken.NumericLiteral : JSToken.IntegerLiteral;
bool exponent = false;
char c;
if ('0' == leadChar){
c = GetChar(this.currentPos);
if ('x' == c || 'X' == c){
if (!JSScanner.IsHexDigit(GetChar(this.currentPos + 1)))
HandleError(JSError.BadHexDigit);
while (JSScanner.IsHexDigit(GetChar(++this.currentPos)));
return token;
}
}
for (;;){
c = GetChar(this.currentPos);
if (!JSScanner.IsDigit(c)){
if ('.' == c){
if (noMoreDot)
break;
else{
noMoreDot = true;
token = JSToken.NumericLiteral;
}
}else if ('e' == c || 'E' == c){
if (exponent)
break;
else{
exponent = true;
token = JSToken.NumericLiteral;
}
}else if ('+' == c || '-' == c){
char e = GetChar(this.currentPos - 1);
if ('e' != e && 'E' != e)
break;
}else
break;
}
this.currentPos++;
}
c = GetChar(this.currentPos - 1);
if ('+' == c || '-' == c){
this.currentPos--;
c = GetChar(this.currentPos - 1);
}
if ('e' == c || 'E' == c){
this.currentPos--;
}
return token;
}
internal String ScanRegExp(){
int pos = this.currentPos;
bool isEscape = false;
char c;
while (!IsEndLineOrEOF(c = GetChar(this.currentPos++), 0)) {
if (isEscape)
isEscape = false;
else{
if (c == '/') {
if (pos == this.currentPos) return null;
this.currentToken.endPos = this.currentPos;
this.currentToken.endLinePos = this.startLinePos;
this.currentToken.endLineNumber = this.currentLine;
return this.strSourceCode.Substring(
this.currentToken.startPos + 1,
this.currentToken.endPos - this.currentToken.startPos - 2);
}
if (c == '\\') isEscape = true;
}
}
// reset and return null. Assume it is not a reg exp
this.currentPos = pos;
return null;
}
internal String ScanRegExpFlags(){
int pos = this.currentPos;
while (JSScanner.IsAsciiLetter(GetChar(this.currentPos)))
this.currentPos++;
if (pos != this.currentPos){
this.currentToken.endPos = this.currentPos;
this.currentToken.endLineNumber = this.currentLine;
this.currentToken.endLinePos = this.startLinePos;
return this.strSourceCode.Substring(pos, this.currentToken.endPos - pos);
}
return null;
}
//--------------------------------------------------------------------------------------------------
// ScanString
//
// Scan a string dealing with escape sequences.
// On exit this.escapedString will contain the string with all escape sequences replaced
// On exit this.currentPos must be at the next char to scan after the string
// This method wiil report an error when the string is unterminated or for a bad escape sequence
//--------------------------------------------------------------------------------------------------
private void ScanString(char cStringTerminator){
char ch;
int start = this.currentPos;
this.escapedString = null;
StringBuilder result = null;
do{
ch = GetChar(this.currentPos++);
if (ch != '\\'){
// this is the common non escape case
if (IsLineTerminator(ch, 0)){
HandleError(JSError.UnterminatedString);
this.currentPos--;
//this.currentLine++;
//this.startLinePos = this.currentPos;
break;
}else if ((char)0 == ch){
this.currentPos--;
HandleError(JSError.UnterminatedString);
break;
}
}else{
// ESCAPE CASE
// got an escape of some sort. Have to use the StringBuilder
if (null == result)
result = new StringBuilder(128);
// start points to the first position that has not been written to the StringBuilder.
// The first time we get in here that position is the beginning of the string, after that
// is the character immediately following the escape sequence
if (this.currentPos - start - 1 > 0) {
// append all the non escape chars to the string builder
result.Append(this.strSourceCode, start, this.currentPos - start - 1);
}
// state variable to be reset
bool seqOfThree = false;
int esc = 0;
ch = GetChar(this.currentPos++);
switch(ch) {
// line terminator crap
case '\r':
if('\n' == GetChar(this.currentPos)){
this.currentPos++;
}
goto case '\n';
case '\n':
case (char)0x2028:
case (char)0x2029:
this.currentLine++;
this.startLinePos = this.currentPos;
break;
// classic single char escape sequences
case 'b':
result.Append((char)8);
break;
case 't':
result.Append((char)9);
break;
case 'n':
result.Append((char)10);
break;
case 'v':
result.Append((char)11);
break;
case 'f':
result.Append((char)12);
break;
case 'r':
result.Append((char)13);
break;
case '"':
result.Append('"');
ch = (char)0; // so it does not exit the loop
break;
case '\'':
result.Append('\'');
ch = (char)0; // so it does not exit the loop
break;
case '\\':
result.Append('\\');
break;
// hexadecimal escape sequence /xHH
case 'x':
ch = GetChar(this.currentPos++);
if (unchecked((UInt32)(ch - '0')) <= '9' - '0')
esc = (ch - '0') << 4;
else if (unchecked((UInt32)(ch -'A')) <= 'F' - 'A')
esc = (ch + 10 - 'A') << 4;
else if (unchecked((UInt32)(ch - 'a')) <= 'f' - 'a')
esc = (ch + 10 - 'a') << 4;
else{
HandleError(JSError.BadHexDigit);
if (ch != cStringTerminator)
--this.currentPos; // do not skip over this char we have to read it back
break;
}
ch = GetChar(this.currentPos++);
if (unchecked((UInt32)(ch - '0')) <= '9' - '0')
esc |= (ch - '0');
else if (unchecked((UInt32)(ch - 'A')) <= 'F' - 'A')
esc |= (ch + 10 - 'A');
else if (unchecked((UInt32)(ch - 'a')) <= 'f' - 'a')
esc |= (ch + 10 - 'a');
else{
HandleError(JSError.BadHexDigit);
if (ch != cStringTerminator)
--this.currentPos; // do not skip over this char we have to read it back
break;
}
result.Append((char)esc);
break;
// unicode escape sequence /uHHHH
case 'u':
ch = GetChar(this.currentPos++);
if (unchecked((UInt32)(ch - '0')) <= '9' - '0')
esc = (ch - '0') << 12;
else if (unchecked((UInt32)(ch - 'A')) <= 'F' - 'A')
esc = (ch + 10 - 'A') << 12;
else if (unchecked((UInt32)(ch - 'a')) <= 'f' - 'a')
esc = (ch + 10 - 'a') << 12;
else{
HandleError(JSError.BadHexDigit);
if (ch != cStringTerminator)
--this.currentPos; // do not skip over this char we have to read it back
break;
}
ch = GetChar(this.currentPos++);
if (unchecked((UInt32)(ch - '0')) <= '9' - '0')
esc |= (ch - '0') << 8;
else if (unchecked((UInt32)(ch - 'A')) <= 'F' - 'A')
esc |= (ch + 10 - 'A') << 8;
else if (unchecked((UInt32)(ch - 'a')) <= 'f' - 'a')
esc |= (ch + 10 - 'a') << 8;
else{
HandleError(JSError.BadHexDigit);
if (ch != cStringTerminator)
--this.currentPos; // do not skip over this char we have to read it back
break;
}
ch = GetChar(this.currentPos++);
if (unchecked((UInt32)(ch - '0')) <= '9' - '0')
esc |= (ch - '0') << 4;
else if (unchecked((UInt32)(ch - 'A')) <= 'F' - 'A')
esc |= (ch + 10 - 'A') << 4;
else if (unchecked((UInt32)(ch - 'a')) <= 'f' - 'a')
esc |= (ch + 10 - 'a') << 4;
else{
HandleError(JSError.BadHexDigit);
if (ch != cStringTerminator)
--this.currentPos; // do not skip over this char we have to read it back
break;
}
ch = GetChar(this.currentPos++);
if (unchecked((UInt32)(ch - '0')) <= '9' - '0')
esc |= (ch - '0');
else if (unchecked((UInt32)(ch - 'A')) <= 'F' - 'A')
esc |= (ch + 10 - 'A');
else if (unchecked((UInt32)(ch - 'a')) <= 'f' - 'a')
esc |= (ch + 10 - 'a');
else{
HandleError(JSError.BadHexDigit);
if (ch != cStringTerminator)
--this.currentPos; // do not skip over this char we have to read it back
break;
}
result.Append((char)esc);
break;
case '0':
case '1':
case '2':
case '3':
seqOfThree = true;
esc = (ch - '0') << 6;
goto case '4';
case '4':
case '5':
case '6':
case '7':
// 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
if (!seqOfThree)
esc = (ch - '0') << 3;
ch = GetChar(this.currentPos++);
if (unchecked((UInt32)(ch - '0')) <= '7' - '0'){
if (seqOfThree){
esc |= (ch - '0') << 3;
ch = GetChar(this.currentPos++);
if (unchecked((UInt32)(ch - '0')) <= '7' - '0'){
esc |= ch - '0';
result.Append((char)esc);
}else{
result.Append((char)(esc >> 3));
if (ch != cStringTerminator)
--this.currentPos; // do not skip over this char we have to read it back
}
}else{
esc |= ch - '0';
result.Append((char)esc);
}
}else{
if (seqOfThree)
result.Append((char)(esc >> 6));
else
result.Append((char)(esc >> 3));
if (ch != cStringTerminator)
--this.currentPos; // do not skip over this char we have to read it back
}
break;
default:
// not an octal number, ignore the escape '/' and simply append the current char
result.Append(ch);
break;
}
start = this.currentPos;
}
}while (ch != cStringTerminator);
// update this.escapedString
if (null != result){
if (this.currentPos - start - 1 > 0){
// append all the non escape chars to the string builder
result.Append(this.strSourceCode, start, this.currentPos - start - 1);
}
this.escapedString = result.ToString();
}else{
if (this.currentPos <= this.currentToken.startPos + 2)
this.escapedString = "";
else
this.escapedString = this.currentToken.source_string.Substring(this.currentToken.startPos + 1, this.currentPos - this.currentToken.startPos - 2);
}
}
private void SkipSingleLineComment(){
while(!IsEndLineOrEOF(GetChar(this.currentPos++), 0));
if (this.IsAuthoring){
this.currentToken.endPos = this.currentPos;
this.currentToken.endLineNumber = this.currentLine;
this.currentToken.endLinePos = this.startLinePos;
this.gotEndOfLine = true;
}
this.currentLine++;
this.startLinePos = this.currentPos;
}
// this method is public because it's used from the authoring code
public int SkipMultiLineComment(){
for(;;){
char c = GetChar(this.currentPos);
while ('*' == c){
c = GetChar(++this.currentPos);
if ('/' == c){
this.currentPos++;
return this.currentPos;
}else if ((char)0 == c)
break;
else if (IsLineTerminator(c, 1)){
c = GetChar(++this.currentPos);
this.currentLine++;
this.startLinePos = this.currentPos + 1;
}
}
if ((char)0 == c && this.currentPos >= this.endPos)
break;
if (IsLineTerminator(c, 1)){
this.currentLine++;
this.startLinePos = this.currentPos + 1;
}
++this.currentPos;
}
// if we are here we got EOF
if (!this.IsAuthoring){
this.currentToken.endPos = --this.currentPos;
this.currentToken.endLinePos = this.startLinePos;
this.currentToken.endLineNumber = this.currentLine;
throw new ScannerException(JSError.NoCommentEnd);
}
return this.currentPos;
}
private void SkipBlanks(){
char c = GetChar(this.currentPos);
while(JSScanner.IsBlankSpace(c))
c = GetChar(++this.currentPos);
}
private static bool IsBlankSpace(char c){
switch (c){
case (char)0x09:
case (char)0x0B:
case (char)0x0C:
case (char)0x20:
case (char)0xA0:
return true;
default:
if (c >= 128)
return Char.GetUnicodeCategory(c) == UnicodeCategory.SpaceSeparator;
else
return false;
}
}
private bool IsLineTerminator(char c, int increment){
switch (c){
case (char)0x0D:
// treat 0x0D0x0A as a single character
if (0x0A == GetChar(this.currentPos + increment))
this.currentPos++;
return true;
case (char)0x0A:
return true;
case (char)0x2028:
return true;
case (char)0x2029:
return true;
default:
return false;
}
}
private bool IsEndLineOrEOF(char c, int increment){
return IsLineTerminator(c, increment) || (char)0 == c && this.currentPos >= this.endPos;
}
private int GetHexValue(char hex){
int hexValue;
if ('0' <= hex && hex <= '9')
hexValue = hex - '0';
else if ('a' <= hex && hex <= 'f')
hexValue = hex - 'a' + 10;
else
hexValue = hex - 'A' + 10;
return hexValue;
}
internal bool IsIdentifierPartChar(char c){
if (this.IsIdentifierStartChar(ref c))
return true;
if ('0' <= c && c <= '9')
return true;
if (c < 128)
return false;
UnicodeCategory ccat = Char.GetUnicodeCategory(c);
switch (ccat){
case UnicodeCategory.NonSpacingMark:
case UnicodeCategory.SpacingCombiningMark:
case UnicodeCategory.DecimalDigitNumber:
case UnicodeCategory.ConnectorPunctuation:
return true;
default:
return false;
}
}
internal bool IsIdentifierStartChar(ref char c){
bool isEscapeChar = false;
if ('\\' == c){
if ('u' == GetChar(this.currentPos + 1)){
char h1 = GetChar(this.currentPos + 2);
if (IsHexDigit(h1)){
char h2 = GetChar(this.currentPos + 3);
if (IsHexDigit(h2)){
char h3 = GetChar(this.currentPos + 4);
if (IsHexDigit(h3)){
char h4 = GetChar(this.currentPos + 5);
if (IsHexDigit(h4)){
isEscapeChar = true;
c = (char)(GetHexValue(h1) << 12 | GetHexValue(h2) << 8 | GetHexValue(h3) << 4 | GetHexValue(h4));
}
}
}
}
}
}
if ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '_' == c || '$' == c)
goto isIdentifierChar;
if (c < 128)
return false;
UnicodeCategory ccat = Char.GetUnicodeCategory(c);
switch (ccat){
case UnicodeCategory.UppercaseLetter:
case UnicodeCategory.LowercaseLetter:
case UnicodeCategory.TitlecaseLetter:
case UnicodeCategory.ModifierLetter:
case UnicodeCategory.OtherLetter:
case UnicodeCategory.LetterNumber:
goto isIdentifierChar;
default:
return false;
}
isIdentifierChar:
if (isEscapeChar){
int startPos = (this.idLastPosOnBuilder > 0) ? this.idLastPosOnBuilder : this.currentToken.startPos;
if (this.currentPos - startPos > 0)
this.identifier.Append(this.strSourceCode.Substring(startPos, this.currentPos - startPos));
this.identifier.Append(c);
this.currentPos += 5;
this.idLastPosOnBuilder = this.currentPos + 1;
}
return true;
}
internal static bool IsDigit(char c){
return '0' <= c && c <= '9';
}
internal static bool IsHexDigit(char c){
return JSScanner.IsDigit(c) || 'A' <= c && c <= 'F' || 'a' <= c && c <= 'f';
}
internal static bool IsOctalDigit(char c){
return '0' <= c && c <= '7';
}
internal static bool IsAsciiLetter(char c){
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z';
}
internal static bool IsUnicodeLetter(char c){
return c >= 128 && Char.IsLetter(c);
}
private void SetPreProcessorOn(){
this.preProcessorOn = true;
this.ppTable = new SimpleHashtable(16);
// define constants
this.ppTable["_debug"] = this.globals.engine.GenerateDebugInfo;
this.ppTable["_fast"] = ((IActivationObject)this.globals.ScopeStack.Peek()).GetGlobalScope().fast;
this.ppTable["_jscript"] = true;
this.ppTable["_jscript_build"]= GlobalObject.ScriptEngineBuildVersion();
this.ppTable["_jscript_version"] = Convert.ToNumber(GlobalObject.ScriptEngineMajorVersion() + "." + GlobalObject.ScriptEngineMinorVersion());
this.ppTable["_microsoft"] = true;
#if PLATFORM_WIN32
this.ppTable["_win32"] = System.Environment.OSVersion.Platform.ToString().StartsWith("Win32");
this.ppTable["_x86"] = true;
#else
this.ppTable["_win32"] = false; // There are no win32-specific functions available on Rotor; always report false.
this.ppTable["_x86"] = false; // It is less likely we are on x86 with Rotor, and we cannot be sure either way.
#endif
// define command-line symbols
Hashtable userDefines = (Hashtable)this.globals.engine.GetOption("defines");
if (userDefines != null)
foreach (DictionaryEntry def in userDefines)
this.ppTable[def.Key] = def.Value;
}
private bool PPTestCond(){
this.SkipBlanks();
if ('(' != GetChar(this.currentPos)){
this.currentToken.startPos = this.currentPos - 1;
this.currentToken.lineNumber = this.currentLine;
this.currentToken.startLinePos = this.startLinePos;;
HandleError(JSError.NoLeftParen);
}else
this.currentPos++;
Object value = PPScanExpr();
if (')' != GetChar(this.currentPos++)){
this.currentToken.startPos = this.currentPos - 1;
this.currentToken.lineNumber = this.currentLine;
this.currentToken.startLinePos = this.startLinePos;;
HandleError(JSError.NoRightParen);
this.currentPos--;
}
return Convert.ToBoolean(value);
}
private void PPSkipToNextCondition(bool checkCondition){
int ifCount = 0;
for (;;){
char c = GetChar(this.currentPos++);
switch (c){
case (char)0:
if (this.currentPos >= this.endPos){
this.currentPos--;
this.currentToken.endPos = this.currentPos;
this.currentToken.endLineNumber = this.currentLine;
this.currentToken.endLinePos = this.startLinePos;
HandleError(JSError.NoCcEnd);
throw new ScannerException(JSError.ErrEOF);
}
break;
// line terminator crap
case '\r':
if (GetChar(this.currentPos) == '\n'){
this.currentPos++;
}
this.currentLine++;
this.startLinePos = this.currentPos;
break;
case '\n':
this.currentLine++;
this.startLinePos = this.currentPos;
break;
case (char)0x2028:
this.currentLine++;
this.startLinePos = this.currentPos;
break;
case (char)0x2029:
this.currentLine++;
this.startLinePos = this.currentPos;
break;
case '@':
this.currentToken.startPos = this.currentPos;
this.currentToken.lineNumber = this.currentLine;
this.currentToken.startLinePos = this.startLinePos;
ScanIdentifier();
switch (this.currentPos - this.currentToken.startPos){
case 2:
if ('i' == this.strSourceCode[this.currentToken.startPos] &&
'f' == this.strSourceCode[this.currentToken.startPos + 1]){
ifCount++;
}
break;
case 3:
if ('e' == this.strSourceCode[this.currentToken.startPos] &&
'n' == this.strSourceCode[this.currentToken.startPos + 1] &&
'd' == this.strSourceCode[this.currentToken.startPos + 2]){
if (ifCount == 0){
this.matchIf--;
return;
}
ifCount--;
}
break;
case 4:
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]){
if (0 == ifCount && checkCondition)
return;
}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]){
if (0 == ifCount && checkCondition && PPTestCond())
return;
}
break;
}
break;
}
}
}
private void PPScanSet(){
this.SkipBlanks();
if ('@' != GetChar(this.currentPos++)){
HandleError(JSError.NoAt);
this.currentPos--;
}
int startIdentifier = this.currentPos;
ScanIdentifier();
int length = this.currentPos - startIdentifier;
string id = null;
if (length == 0){
this.currentToken.startPos = this.currentPos - 1;
this.currentToken.lineNumber = this.currentLine;
this.currentToken.startLinePos = this.startLinePos;
HandleError(JSError.NoIdentifier);
id = "#_Missing CC Identifier_#";
}else
id = this.strSourceCode.Substring(startIdentifier, length);
this.SkipBlanks();
char c = GetChar(this.currentPos++);
if ('(' == c){
if (id.Equals("position")){
PPRemapPositionInfo();
}else if (id.Equals("option")){
PPLanguageOption();
}else if (id.Equals("debug")){
PPDebugDirective();
}else{
this.currentToken.startPos = this.currentPos - 1;
this.currentToken.lineNumber = this.currentLine;
this.currentToken.startLinePos = this.startLinePos;
HandleError(JSError.NoEqual);
this.currentPos--;
}
}else{
if ('=' != c){
this.currentToken.startPos = this.currentPos - 1;
this.currentToken.lineNumber = this.currentLine;
this.currentToken.startLinePos = this.startLinePos;
HandleError(JSError.NoEqual);
this.currentPos--;
}
Object value = PPScanConstant();
this.ppTable[id] = value;
}
}
private Object PPScanExpr(){
OpListItem opsStack = new OpListItem(JSToken.None, OpPrec.precNone, null); // dummy element
ConstantListItem termStack = new ConstantListItem(PPScanConstant(), null);
Object value;
for (;;){
GetNextToken();
if (JSScanner.IsPPOperator(this.currentToken.token)){
OpPrec prec = JSScanner.GetPPOperatorPrecedence(this.currentToken.token);
while (prec < opsStack._prec){
//Console.Out.WriteLine("lower prec or same and left assoc");
value = PPGetValue(opsStack._operator, termStack.prev.term, termStack.term);
// pop the operator stack
opsStack = opsStack._prev;
// pop the term stack twice
termStack = termStack.prev.prev;
// push node onto the stack
termStack = new ConstantListItem(value, termStack);
}
// the current operator has higher precedence that every scanned operators on the stack, or
// it has the same precedence as the one at the top of the stack and it is right associative
// push the operator onto the operators stack
opsStack = new OpListItem(this.currentToken.token, prec, opsStack);
// push new term
termStack = new ConstantListItem(PPScanConstant(), termStack);
}else{
//Console.Out.WriteLine("unwinding stack");
// there are still operators to be processed
while (opsStack._operator != JSToken.None){
// make the ast operator node
value = PPGetValue(opsStack._operator, termStack.prev.term, termStack.term);
// pop the operator stack
opsStack = opsStack._prev;
// pop the term stack twice
termStack = termStack.prev.prev;
// push node onto the stack
termStack = new ConstantListItem(value, termStack);
}
// reset position into stream
this.currentPos = this.currentToken.startPos;
break; // done
}
}
Debug.Assert(termStack.prev == null);
return termStack.term;
}
private void PPRemapPositionInfo(){
GetNextToken();
String filename = null; // this.currentToken.document.documentName;
int line = 0;
int col = -1;
bool endDirective = false;
while(JSToken.RightParen != this.currentToken.token){
if (JSToken.Identifier == this.currentToken.token){
if (this.currentToken.Equals("file")){
if (null == this.currentDocument){
if (null == filename){
GetNextToken();
if (JSToken.Assign != this.currentToken.token){
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
GetNextToken();
if (JSToken.StringLiteral != this.currentToken.token){
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
filename = GetStringLiteral();
if (filename == this.currentToken.document.documentName){
filename = null;
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
}else{
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
}else{
HandleError(JSError.CannotNestPositionDirective);
goto ignoreDirective;
}
}else if (this.currentToken.Equals("line")){
if (null == this.currentDocument){
if (line == 0){
GetNextToken();
if (JSToken.Assign != this.currentToken.token){
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
GetNextToken();
if (JSToken.IntegerLiteral != this.currentToken.token){
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
String number = this.currentToken.GetCode();
double i = Convert.ToNumber(number, true, true, Missing.Value);
if ((double)(int)i == i && i > 0)
line = (int)i;
else{
line = 1;
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
}else{
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
}else{
HandleError(JSError.CannotNestPositionDirective);
goto ignoreDirective;
}
}else if (this.currentToken.Equals("column")){
if (null == this.currentDocument){
if (col == -1){
GetNextToken();
if (JSToken.Assign != this.currentToken.token){
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
GetNextToken();
if (JSToken.IntegerLiteral != this.currentToken.token){
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
String number = this.currentToken.GetCode();
double i = Convert.ToNumber(number, true, true, Missing.Value);
if ((double)(int)i == i && i >= 0)
col = (int)i;
else{
col = 0;
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
}else{
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
}else{
HandleError(JSError.CannotNestPositionDirective);
goto ignoreDirective;
}
}else if (this.currentToken.Equals("end")){
// end of the position mapping directive. Put back the old document
if (null != this.currentDocument){
// make sure this is the end of it and if not just go to the end of this directive and ignore it
GetNextToken();
if (JSToken.RightParen != this.currentToken.token){
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}else{
// restore the main document
this.currentToken.document = this.currentDocument;
this.currentDocument = null;
endDirective = true;
break;
}
}else{
// we got an end with no position ever started. Report an error and ignore
HandleError(JSError.WrongDirective);
goto ignoreDirective;
}
}else{
HandleError(JSError.InvalidPositionDirective);
goto ignoreDirective;
}
GetNextToken();
if (JSToken.RightParen == this.currentToken.token)
break;
else if (JSToken.Semicolon == this.currentToken.token)
GetNextToken();
continue;
}
HandleError(JSError.InvalidPositionDirective);
ignoreDirective:
// ignore the directive
while (JSToken.RightParen != this.currentToken.token && JSToken.EndOfFile != this.currentToken.token)
GetNextToken();
break;
}
this.SkipBlanks();
if (';' == GetChar(this.currentPos)){
++this.currentPos;
this.SkipBlanks();
}
// the next char must be an end of line or we skip to it
if (this.currentPos < this.endPos && !IsLineTerminator(GetChar(this.currentPos++), 0)){
HandleError(JSError.MustBeEOL);
while (this.currentPos < this.endPos && !IsLineTerminator(GetChar(this.currentPos++), 0));
}
this.currentLine++;
this.startLinePos = this.currentPos;
// set up or restore the document context
if (!endDirective){
if (null == filename && line == 0 && col == -1)
HandleError(JSError.InvalidPositionDirective);
else{
if (filename == null) filename = this.currentToken.document.documentName;
if (line == 0) line = 1;
if (col == -1) col = 0;
this.currentDocument = this.currentToken.document;
this.currentToken.document = new DocumentContext(filename, line, col, this.currentLine, this.currentDocument.sourceItem);
}
}
}
private void PPDebugDirective(){
GetNextToken();
bool debugOn = false;
if (JSToken.Identifier == this.currentToken.token){
if (this.currentToken.Equals("off"))
debugOn = false;
else if (this.currentToken.Equals("on"))
debugOn = true;
else{
HandleError(JSError.InvalidDebugDirective);
goto ignoreDirective;
}
GetNextToken();
if (JSToken.RightParen != this.currentToken.token){
HandleError(JSError.InvalidDebugDirective);
goto ignoreDirective;
}
}else{
HandleError(JSError.InvalidDebugDirective);
goto ignoreDirective;
}
this.currentToken.document.debugOn = debugOn && this.globals.engine.GenerateDebugInfo;
this.ppTable["_debug"] = debugOn;
ignoreDirective:
// ignore the directive
while (JSToken.RightParen != this.currentToken.token)
GetNextToken();
this.SkipBlanks();
if (';' == GetChar(this.currentPos)){
++this.currentPos;
this.SkipBlanks();
}
// the next char must be an end of line or we skip to it
if (!IsLineTerminator(GetChar(this.currentPos++), 0)){
HandleError(JSError.MustBeEOL);
while (!IsLineTerminator(GetChar(this.currentPos++), 0));
}
this.currentLine++;
this.startLinePos = this.currentPos;
}
private void PPLanguageOption(){
GetNextToken();
/*
if (JSToken.Identifier == this.currentToken.token && this.globals != null){
if (this.currentToken.Equals("fast")){
((GlobalScope)this.globals.ScopeStack.Peek()).SetFast();
this.ppTable["_fast"] = true;
}else if (this.currentToken.Equals("versionsafe")){
this.globals.engine.versionSafe = true;
}else{
HandleError(JSError.InvalidLanguageOption);
}
}else
*/ HandleError(JSError.InvalidLanguageOption);
GetNextToken();
Context errorToken = null;
while (JSToken.RightParen != this.currentToken.token){
if (null == errorToken)
errorToken = this.currentToken.Clone();
else
errorToken.UpdateWith(this.currentToken);
GetNextToken();
}
if (null != errorToken)
HandleError(JSError.NoRightParen);
}
private Object PPScanConstant(){
Object value;
GetNextToken();
switch (this.currentToken.token){
case JSToken.Plus:
value = Convert.ToNumber(PPScanConstant());
break;
case JSToken.Minus:
value = -Convert.ToNumber(PPScanConstant());
break;
case JSToken.BitwiseNot:
value = ~Convert.ToInt32(PPScanConstant());
break;
case JSToken.LogicalNot:
value = !Convert.ToBoolean(PPScanConstant());
break;
case JSToken.IntegerLiteral:
value = Convert.ToNumber(this.currentToken.GetCode(), true, true, Missing.Value);
break;
case JSToken.NumericLiteral:
value = Convert.ToNumber(this.currentToken.GetCode(), false, false, Missing.Value);
break;
case JSToken.PreProcessorConstant:
value = this.preProcessorValue;
break;
case JSToken.True:
value = true;
break;
case JSToken.False:
value = false;
break;
case JSToken.LeftParen:
value = PPScanExpr();
GetNextToken();
if (JSToken.RightParen != this.currentToken.token){
this.currentToken.endPos = this.currentToken.startPos + 1;
this.currentToken.endLineNumber = this.currentLine;
this.currentToken.endLinePos = this.startLinePos;
HandleError(JSError.NoRightParen);
this.currentPos = this.currentToken.startPos;
}
break;
default:
HandleError(JSError.NotConst);
this.currentPos = this.currentToken.startPos;
value = true;
break;
}
return value;
}
Object PPGetValue(JSToken op, Object op1, Object op2){
switch (op){
case JSToken.Plus:
return Convert.ToNumber(op1) + Convert.ToNumber(op2);
case JSToken.Minus:
return Convert.ToNumber(op1) - Convert.ToNumber(op2);
case JSToken.LogicalOr:
return Convert.ToBoolean(op1) || Convert.ToBoolean(op2);
case JSToken.LogicalAnd:
return Convert.ToBoolean(op1) && Convert.ToBoolean(op2);
case JSToken.BitwiseOr:
return Convert.ToInt32(op1) | Convert.ToInt32(op2);
case JSToken.BitwiseXor:
return Convert.ToInt32(op1) ^ Convert.ToInt32(op2);
case JSToken.BitwiseAnd:
return Convert.ToInt32(op1) & Convert.ToInt32(op2);
case JSToken.Equal:
return Convert.ToNumber(op1) == Convert.ToNumber(op2);
case JSToken.NotEqual:
return Convert.ToNumber(op1) != Convert.ToNumber(op2);
case JSToken.StrictEqual:
return op1 == op2;
case JSToken.StrictNotEqual:
return op1 != op2;
case JSToken.GreaterThan:
return Convert.ToNumber(op1) > Convert.ToNumber(op2);
case JSToken.LessThan:
return Convert.ToNumber(op1) < Convert.ToNumber(op2);
case JSToken.LessThanEqual:
return Convert.ToNumber(op1) <= Convert.ToNumber(op2);
case JSToken.GreaterThanEqual:
return Convert.ToNumber(op1) >= Convert.ToNumber(op2);
case JSToken.LeftShift:
return Convert.ToInt32(op1) << (Convert.ToInt32(op2) & 0x1F);
case JSToken.RightShift:
return Convert.ToInt32(op1) >> (Convert.ToInt32(op2) & 0x1F);
case JSToken.UnsignedRightShift:
return ((uint)Convert.ToInt32(op1)) >> (Convert.ToInt32(op2) & 0x1F);
case JSToken.Multiply:
return Convert.ToNumber(op1) * Convert.ToNumber(op2);
case JSToken.Divide:
return Convert.ToNumber(op1) / Convert.ToNumber(op2);
case JSToken.Modulo:
return Convert.ToInt32(op1) % Convert.ToInt32(op2);
default:
Debug.Assert(false);
break;
}
return null;
}
internal Object GetPreProcessorValue(){
return this.preProcessorValue;
}
private void HandleError(JSError error){
if (!this.IsAuthoring)
this.currentToken.HandleError(error);
}
public static bool IsOperator(JSToken token){
return JSToken.FirstOp <= token && token <= JSToken.LastOp;
}
internal static bool IsAssignmentOperator(JSToken token){
return JSToken.Assign <= token && token <= JSToken.LastAssign;
}
internal static bool CanStartStatement(JSToken token){
return JSToken.If <= token && token <= JSToken.Function;
}
internal static bool IsVisibilitySpec(JSToken token){
return JSToken.Package <= token && token <= JSToken.Protected;
}
internal static bool CanParseAsExpression(JSToken token){
return JSToken.FirstBinaryOp <= token && token <= JSToken.LastOp || JSToken.LeftParen <= token && token <= JSToken.AccessField;
}
internal static bool IsRightAssociativeOperator(JSToken token){
return JSToken.Assign <= token && token <= JSToken.ConditionalIf;
}
public static bool IsKeyword(JSToken token){
switch(token){
case JSToken.If:
case JSToken.For:
case JSToken.Do:
case JSToken.While:
case JSToken.Continue:
case JSToken.Break:
case JSToken.Return:
case JSToken.Import:
case JSToken.With:
case JSToken.Switch:
case JSToken.Throw:
case JSToken.Try:
case JSToken.Package:
case JSToken.Abstract:
case JSToken.Public:
case JSToken.Static:
case JSToken.Private:
case JSToken.Protected:
case JSToken.Var:
case JSToken.Class:
case JSToken.Function:
case JSToken.Null:
case JSToken.True:
case JSToken.False:
case JSToken.This:
case JSToken.Final:
case JSToken.Const:
case JSToken.Delete:
case JSToken.Void:
case JSToken.Typeof:
case JSToken.Instanceof:
case JSToken.In:
case JSToken.Case:
case JSToken.Catch:
case JSToken.Debugger:
case JSToken.Default:
case JSToken.Else:
case JSToken.Export:
case JSToken.Extends:
case JSToken.Finally:
case JSToken.Get:
case JSToken.Implements:
case JSToken.Interface:
case JSToken.New:
case JSToken.Set:
case JSToken.Super:
case JSToken.Boolean:
case JSToken.Byte:
case JSToken.Char:
case JSToken.Double:
case JSToken.Enum:
case JSToken.Float:
case JSToken.Goto:
case JSToken.Int:
case JSToken.Long:
case JSToken.Native:
case JSToken.Short:
case JSToken.Synchronized:
case JSToken.Transient:
case JSToken.Throws:
case JSToken.Volatile:
return true;
}
return false;
}
// This function return whether an operator is processable in ParseExpression.
// Comma is out of this list and so are the unary ops
internal static bool IsProcessableOperator(JSToken token){
return JSToken.FirstBinaryOp <= token && token <= JSToken.ConditionalIf;
}
// This function return whether an operator is processable in pre processing.
internal static bool IsPPOperator(JSToken token){
return JSToken.FirstBinaryOp <= token && token <= JSToken.LastPPOperator;
}
private static readonly OpPrec[] s_OperatorsPrec = InitOperatorsPrec();
private static readonly OpPrec[] s_PPOperatorsPrec = InitPPOperatorsPrec();
internal static OpPrec GetOperatorPrecedence(JSToken token){
return JSScanner.s_OperatorsPrec[token - JSToken.FirstBinaryOp];
}
internal static OpPrec GetPPOperatorPrecedence(JSToken token) {
return JSScanner.s_PPOperatorsPrec[token - JSToken.FirstBinaryOp];
}
private static OpPrec[] InitOperatorsPrec(){
OpPrec[] operatorsPrec = new OpPrec[JSToken.ConditionalIf - JSToken.FirstBinaryOp + 1];
operatorsPrec[JSToken.Plus - JSToken.FirstBinaryOp] = OpPrec.precAdditive;
operatorsPrec[JSToken.Minus - JSToken.FirstBinaryOp] = OpPrec.precAdditive;
operatorsPrec[JSToken.LogicalOr - JSToken.FirstBinaryOp] = OpPrec.precLogicalOr;
operatorsPrec[JSToken.LogicalAnd - JSToken.FirstBinaryOp] = OpPrec.precLogicalAnd;
operatorsPrec[JSToken.BitwiseOr - JSToken.FirstBinaryOp] = OpPrec.precBitwiseOr;
operatorsPrec[JSToken.BitwiseXor - JSToken.FirstBinaryOp] = OpPrec.precBitwiseXor;
operatorsPrec[JSToken.BitwiseAnd - JSToken.FirstBinaryOp] = OpPrec.precBitwiseAnd;
operatorsPrec[JSToken.Equal - JSToken.FirstBinaryOp] = OpPrec.precEquality;
operatorsPrec[JSToken.NotEqual - JSToken.FirstBinaryOp] = OpPrec.precEquality;
operatorsPrec[JSToken.StrictEqual - JSToken.FirstBinaryOp] = OpPrec.precEquality;
operatorsPrec[JSToken.StrictNotEqual - JSToken.FirstBinaryOp] = OpPrec.precEquality;
operatorsPrec[JSToken.Instanceof - JSToken.FirstBinaryOp] = OpPrec.precRelational;
operatorsPrec[JSToken.In - JSToken.FirstBinaryOp] = OpPrec.precRelational;
operatorsPrec[JSToken.GreaterThan - JSToken.FirstBinaryOp] = OpPrec.precRelational;
operatorsPrec[JSToken.LessThan - JSToken.FirstBinaryOp] = OpPrec.precRelational;
operatorsPrec[JSToken.LessThanEqual - JSToken.FirstBinaryOp] = OpPrec.precRelational;
operatorsPrec[JSToken.GreaterThanEqual - JSToken.FirstBinaryOp] = OpPrec.precRelational;
operatorsPrec[JSToken.LeftShift - JSToken.FirstBinaryOp] = OpPrec.precShift;
operatorsPrec[JSToken.RightShift - JSToken.FirstBinaryOp] = OpPrec.precShift;
operatorsPrec[JSToken.UnsignedRightShift - JSToken.FirstBinaryOp] = OpPrec.precShift;
operatorsPrec[JSToken.Multiply - JSToken.FirstBinaryOp] = OpPrec.precMultiplicative;
operatorsPrec[JSToken.Divide - JSToken.FirstBinaryOp] = OpPrec.precMultiplicative;
operatorsPrec[JSToken.Modulo - JSToken.FirstBinaryOp] = OpPrec.precMultiplicative;
operatorsPrec[JSToken.Assign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
operatorsPrec[JSToken.PlusAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
operatorsPrec[JSToken.MinusAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
operatorsPrec[JSToken.MultiplyAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
operatorsPrec[JSToken.DivideAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
operatorsPrec[JSToken.BitwiseAndAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
operatorsPrec[JSToken.BitwiseOrAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
operatorsPrec[JSToken.BitwiseXorAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
operatorsPrec[JSToken.ModuloAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
operatorsPrec[JSToken.LeftShiftAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
operatorsPrec[JSToken.RightShiftAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
operatorsPrec[JSToken.UnsignedRightShiftAssign - JSToken.FirstBinaryOp] = OpPrec.precAssignment;
operatorsPrec[JSToken.ConditionalIf - JSToken.FirstBinaryOp] = OpPrec.precConditional;
return operatorsPrec;
}
private static OpPrec[] InitPPOperatorsPrec() {
OpPrec[] operatorsPrec = new OpPrec[JSToken.LastPPOperator - JSToken.FirstBinaryOp + 1];
operatorsPrec[JSToken.Plus - JSToken.FirstBinaryOp] = OpPrec.precAdditive;
operatorsPrec[JSToken.Minus - JSToken.FirstBinaryOp] = OpPrec.precAdditive;
operatorsPrec[JSToken.LogicalOr - JSToken.FirstBinaryOp] = OpPrec.precLogicalOr;
operatorsPrec[JSToken.LogicalAnd - JSToken.FirstBinaryOp] = OpPrec.precLogicalAnd;
operatorsPrec[JSToken.BitwiseOr - JSToken.FirstBinaryOp] = OpPrec.precBitwiseOr;
operatorsPrec[JSToken.BitwiseXor - JSToken.FirstBinaryOp] = OpPrec.precBitwiseXor;
operatorsPrec[JSToken.BitwiseAnd - JSToken.FirstBinaryOp] = OpPrec.precBitwiseAnd;
operatorsPrec[JSToken.Equal - JSToken.FirstBinaryOp] = OpPrec.precEquality;
operatorsPrec[JSToken.NotEqual - JSToken.FirstBinaryOp] = OpPrec.precEquality;
operatorsPrec[JSToken.StrictEqual - JSToken.FirstBinaryOp] = OpPrec.precEquality;
operatorsPrec[JSToken.StrictNotEqual - JSToken.FirstBinaryOp] = OpPrec.precEquality;
operatorsPrec[JSToken.GreaterThan - JSToken.FirstBinaryOp] = OpPrec.precRelational;
operatorsPrec[JSToken.LessThan - JSToken.FirstBinaryOp] = OpPrec.precRelational;
operatorsPrec[JSToken.LessThanEqual - JSToken.FirstBinaryOp] = OpPrec.precRelational;
operatorsPrec[JSToken.GreaterThanEqual - JSToken.FirstBinaryOp] = OpPrec.precRelational;
operatorsPrec[JSToken.LeftShift - JSToken.FirstBinaryOp] = OpPrec.precShift;
operatorsPrec[JSToken.RightShift - JSToken.FirstBinaryOp] = OpPrec.precShift;
operatorsPrec[JSToken.UnsignedRightShift - JSToken.FirstBinaryOp] = OpPrec.precShift;
operatorsPrec[JSToken.Multiply - JSToken.FirstBinaryOp] = OpPrec.precMultiplicative;
operatorsPrec[JSToken.Divide - JSToken.FirstBinaryOp] = OpPrec.precMultiplicative;
operatorsPrec[JSToken.Modulo - JSToken.FirstBinaryOp] = OpPrec.precMultiplicative;
return operatorsPrec;
}
}
}