mirror of
https://github.com/SSCLI/sscli_20021101
synced 2026-06-08 12:28:57 +00:00
9fa3874800
Moved the original file to the archive subfolder.
2004 lines
75 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
|