pjs/js/js2/parser.h

1064 строки
40 KiB
C++

// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
//
// The contents of this file are subject to the Netscape Public
// License Version 1.1 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of
// the License at http://www.mozilla.org/NPL/
//
// Software distributed under the License is distributed on an "AS
// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
// implied. See the License for the specific language governing
// rights and limitations under the License.
//
// The Original Code is the JavaScript 2 Prototype.
//
// The Initial Developer of the Original Code is Netscape
// Communications Corporation. Portions created by Netscape are
// Copyright (C) 1998 Netscape Communications Corporation. All
// Rights Reserved.
#ifndef parser_h
#define parser_h
#include "utilities.h"
#include <vector>
namespace JavaScript {
class StringAtom;
class World;
//
// Reader
//
// A Reader reads Unicode characters from a source string.
// Calling get() yields all of the characters in sequence. One character may be read
// past the end of the source; that character appears as a null.
class Reader {
const char16 *begin; // Beginning of source text
const char16 *p; // Position in source text
const char16 *end; // End of source text; *end is a null character
public:
const String source; // Source text
const String sourceLocation; // Description of location from which the source text came
private:
const uint32 initialLineNum; // One-based number of current line
std::vector<const char16 *> linePositions; // Array of line starts recorded by beginLine()
String *recordString; // String, if any, into which recordChar() records characters; not owned by the Reader
const char16 *recordBase; // Position of last beginRecording() call
const char16 *recordPos; // Position of last recordChar() call; nil if a discrepancy occurred
public:
Reader(const String &source, const String &sourceLocation, uint32 initialLineNum = 1);
private:
Reader(const Reader&); // No copy constructor
void operator=(const Reader&); // No assignment operator
public:
char16 get() {ASSERT(p <= end); return *p++;}
char16 peek() {ASSERT(p <= end); return *p;}
void unget(uint32 n = 1) {ASSERT(p >= begin + n); p -= n;}
uint32 getPos() const {return static_cast<uint32>(p - begin);}
void setPos(uint32 pos) {ASSERT(pos <= getPos()); p = begin + pos;}
bool eof() const {ASSERT(p <= end); return p == end;}
bool peekEof(char16 ch) const {ASSERT(p <= end && *p == ch); return !ch && p == end;} // Faster version. ch is the result of a peek
bool pastEof() const {return p == end+1;}
bool getEof(char16 ch) const {ASSERT(p[-1] == ch); return !ch && p == end+1;} // Faster version. ch is the result of a get
void beginLine();
uint32 posToLineNum(uint32 pos) const;
uint32 getLine(uint32 lineNum, const char16 *&lineBegin, const char16 *&lineEnd) const;
void beginRecording(String &recordString);
void recordChar(char16 ch);
String &endRecording();
void error(Exception::Kind kind, const String &message, uint32 pos);
};
//
// Lexer
//
void escapeString(Formatter &f, const char16 *begin, const char16 *end, char16 quote);
void quoteString(Formatter &f, const String &s, char16 quote);
class Token {
public:
enum Kind { // Keep synchronized with kindNames, kindFlags, and tokenBinaryOperatorInfos tables
// Special
end, // End of token stream
number, // Number
string, // String
unit, // Unit after number
regExp, // Regular expression
// Punctuators
openParenthesis, // (
closeParenthesis, // )
openBracket, // [
closeBracket, // ]
openBrace, // {
closeBrace, // }
comma, // ,
semicolon, // ;
dot, // .
doubleDot, // ..
tripleDot, // ...
arrow, // ->
colon, // :
doubleColon, // ::
pound, // #
at, // @
increment, // ++
decrement, // --
complement, // ~
logicalNot, // !
times, // *
divide, // /
modulo, // %
plus, // +
minus, // -
leftShift, // <<
rightShift, // >>
logicalRightShift, // >>>
logicalAnd, // &&
logicalXor, // ^^
logicalOr, // ||
bitwiseAnd, // & // These must be at constant offsets from logicalAnd ... logicalOr
bitwiseXor, // ^
bitwiseOr, // |
assignment, // =
timesEquals, // *= // These must be at constant offsets from times ... bitwiseOr
divideEquals, // /=
moduloEquals, // %=
plusEquals, // +=
minusEquals, // -=
leftShiftEquals, // <<=
rightShiftEquals, // >>=
logicalRightShiftEquals, // >>>=
logicalAndEquals, // &&=
logicalXorEquals, // ^^=
logicalOrEquals, // ||=
bitwiseAndEquals, // &=
bitwiseXorEquals, // ^=
bitwiseOrEquals, // |=
equal, // ==
notEqual, // !=
lessThan, // <
lessThanOrEqual, // <=
greaterThan, // > // >, >= must be at constant offsets from <, <=
greaterThanOrEqual, // >=
identical, // ===
notIdentical, // !==
question, // ?
// Reserved words
Abstract, // abstract
Break, // break
Case, // case
Catch, // catch
Class, // class
Const, // const
Continue, // continue
Debugger, // debugger
Default, // default
Delete, // delete
Do, // do
Else, // else
Enum, // enum
Eval, // eval
Export, // export
Extends, // extends
False, // false
Final, // final
Finally, // finally
For, // for
Function, // function
Goto, // goto
If, // if
Implements, // implements
Import, // import
In, // in
Instanceof, // instanceof
Interface, // interface
Native, // native
New, // new
Null, // null
Package, // package
Private, // private
Protected, // protected
Public, // public
Return, // return
Static, // static
Super, // super
Switch, // switch
Synchronized, // synchronized
This, // this
Throw, // throw
Throws, // throws
Transient, // transient
True, // true
Try, // try
Typeof, // typeof
Var, // var
Volatile, // volatile
While, // while
With, // with
// Non-reserved words
Attribute, // attribute
Constructor, // constructor
Get, // get
Language, // language
Namespace, // namespace
Set, // set
Use, // use
identifier, // Non-keyword identifier (may be same as a keyword if it contains an escape code)
kindsEnd, // End of token kinds
keywordsBegin = Abstract, // Beginning of range of special identifier tokens
keywordsEnd = identifier, // End of range of special identifier tokens
nonreservedBegin = Attribute,// Beginning of range of non-reserved words
nonreservedEnd = identifier,// End of range of non-reserved words
kindsWithCharsBegin = number,// Beginning of range of tokens for which the chars field (below) is valid
kindsWithCharsEnd = regExp+1// End of range of tokens for which the chars field (below) is valid
};
#define CASE_TOKEN_ATTRIBUTE_IDENTIFIER \
Token::Get: \
case Token::Set: \
case Token::identifier
#define CASE_TOKEN_NONRESERVED \
Token::Attribute: \
case Token::Constructor: \
case Token::Get: \
case Token::Language: \
case Token::Namespace: \
case Token::Set: \
case Token::Use: \
case Token::identifier
enum Flag {
isAttribute, // True if this token is an attribute
canFollowAttribute, // True if this token is an attribute or can follow an attribute
canFollowReturn, // True if this token can follow a return without an expression
canFollowGet // True if this token can follow a get or set in a FunctionName
};
private:
static const char *const kindNames[kindsEnd];
static const uchar kindFlags[kindsEnd];
#ifdef DEBUG
bool valid; // True if this token has been initialized
#endif
Kind kind; // The token's kind
bool lineBreak; // True if line break precedes this token
uint32 pos; // Source position of this token
const StringAtom *id; // The token's characters; non-nil for identifiers, keywords, and regular expressions only
String chars; // The token's characters; valid for strings, units, numbers, and regular expression flags only
float64 value; // The token's value (numbers only)
#ifdef DEBUG
Token(): valid(false) {}
#endif
public:
static void initKeywords(World &world);
static bool isSpecialKind(Kind kind) {return kind <= regExp || kind == identifier;}
static const char *kindName(Kind kind) {ASSERT(uint(kind) < kindsEnd); return kindNames[kind];}
Kind getKind() const {ASSERT(valid); return kind;}
bool hasKind(Kind k) const {ASSERT(valid); return kind == k;}
bool hasIdentifierKind() const {ASSERT(nonreservedEnd == identifier && kindsEnd == identifier+1); return kind >= nonreservedBegin;}
bool getFlag(Flag f) const {ASSERT(valid); return (kindFlags[kind] & 1<<f) != 0;}
bool getLineBreak() const {ASSERT(valid); return lineBreak;}
uint32 getPos() const {ASSERT(valid); return pos;}
const StringAtom &getIdentifier() const {ASSERT(valid && id); return *id;}
const String &getChars() const {ASSERT(valid && kind >= kindsWithCharsBegin && kind < kindsWithCharsEnd); return chars;}
float64 getValue() const {ASSERT(valid && kind == number); return value;}
friend Formatter &operator<<(Formatter &f, Kind k) {f << kindName(k); return f;}
void print(Formatter &f, bool debug = false) const;
friend class Lexer;
};
class Lexer {
enum {tokenLookahead = 2}; // Number of tokens that can be simultaneously live
#ifdef DEBUG
enum {tokenGuard = 10}; // Number of invalid tokens added to circular token buffer to catch references to old tokens
#else
enum {tokenGuard = 0}; // Number of invalid tokens added to circular token buffer to catch references to old tokens
#endif
enum {tokenBufferSize = tokenLookahead + tokenGuard}; // Token lookahead buffer size
Token tokens[tokenBufferSize]; // Circular buffer of recently read or lookahead tokens
Token *nextToken; // Address of next Token in the circular buffer to be returned by get()
int nTokensFwd; // Net number of Tokens on which unget() has been called; these Tokens are ahead of nextToken
#ifdef DEBUG
int nTokensBack; // Number of Tokens on which unget() can be called; these Tokens are beind nextToken
bool savedPreferRegExp[tokenBufferSize]; // Circular buffer of saved values of preferRegExp to get() calls
#endif
bool lexingUnit; // True if lexing a unit identifier immediately following a number
public:
World &world;
Reader reader;
Lexer(World &world, const String &source, const String &sourceLocation, uint32 initialLineNum = 1);
const Token &get(bool preferRegExp);
const Token *eat(bool preferRegExp, Token::Kind kind);
const Token &peek(bool preferRegExp);
void redesignate(bool preferRegExp);
void unget();
uint32 getPos() const;
private:
void syntaxError(const char *message, uint backUp = 1);
char16 getChar();
char16 internalGetChar(char16 ch);
char16 peekChar();
char16 internalPeekChar(char16 ch);
bool testChar(char16 ch);
char16 lexEscape(bool unicodeOnly);
bool lexIdentifier(String &s, bool allowLeadingDigit);
bool lexNumeral();
void lexString(String &s, char16 separator);
void lexRegExp();
void lexToken(bool preferRegExp);
};
#ifndef DEBUG
inline void Lexer::redesignate(bool) {} // See description for the DEBUG version inside parser.cpp
#endif
// Return the position of the first character of the next token, which must have been peeked.
inline uint32 Lexer::getPos() const
{
ASSERT(nTokensFwd);
return nextToken->getPos();
}
//
// Language Selectors
//
class Language {
enum {
minorVersion = 0, // Single BCD digit representing the minor JavaScript version
minorVersionBits = 4,
majorVersion = 4, // Single BCD digit representing the major JavaScript version
majorVersionBits = 4,
strictSemantics = 8, // True when strict semantics are in use
strictSyntax = 9, // True when strict syntax is in use
unknown = 31 // True when language is unknown
};
uint32 flags; // Bitmap of flags at locations as above
};
//
// Parser
//
// The structures below are generally allocated inside an arena. The structures' destructors
// may never be called, so these structures should not hold onto any data that needs to be
// destroyed explicitly. Strings are allocated via newArenaString.
struct ParseNode: ArenaObject {
uint32 pos; // Position of this statement or expression
explicit ParseNode(uint32 pos): pos(pos) {}
};
// A helper template for creating linked lists of ParseNode subtypes. N should be derived
// from a ParseNode and should have an instance variable called <next> of type N* and that
// is initialized to nil when an N instance is created.
template<class N>
class NodeQueue {
public:
N *first; // Head of queue
private:
N **last; // Next link of last element of queue
public:
NodeQueue(): first(0), last(&first) {}
private:
NodeQueue(const NodeQueue&); // No copy constructor
void operator=(const NodeQueue&); // No assignment operator
public:
void operator+=(N *elt) {ASSERT(elt && !elt->next); *last = elt; last = &elt->next;}
};
struct ExprNode;
struct AttributeStmtNode;
struct BlockStmtNode;
struct FunctionName {
enum Prefix {
normal, // No prefix
Get, // get
Set // set
};
Prefix prefix; // The name's prefix, if any
ExprNode *name; // The name; nil if omitted
FunctionName(): prefix(normal), name(0) {}
void print(PrettyPrinter &f) const;
};
struct VariableBinding: ParseNode {
VariableBinding *next; // Next binding in a linked list of variable or parameter bindings
ExprNode *name; // The variable's name; nil if omitted, which currently can only happen for ... parameters
ExprNode *type; // Type expression or nil if not provided
ExprNode *initializer; // Initial value expression or nil if not provided
VariableBinding(uint32 pos, ExprNode *name, ExprNode *type, ExprNode *initializer):
ParseNode(pos), next(0), name(name), type(type), initializer(initializer) {}
void print(PrettyPrinter &f) const;
};
struct FunctionDefinition: FunctionName {
VariableBinding *parameters; // Linked list of all parameters, including optional and rest parameters, if any
VariableBinding *optParameters; // Pointer to first non-required parameter inside parameters list; nil if none
VariableBinding *restParameter; // Pointer to rest parameter inside parameters list; nil if none
ExprNode *resultType; // Result type expression or nil if not provided
BlockStmtNode *body; // Body; nil if none
void print(PrettyPrinter &f, bool isConstructor, const AttributeStmtNode *attributes, bool noSemi) const;
};
struct ExprNode: ParseNode {
enum Kind { // Actual class Operands // Keep synchronized with kindNames
none,
identifier, // IdentifierExprNode <name> // Begin of isPostfix()
number, // NumberExprNode <value>
string, // StringExprNode <str>
regExp, // RegExpExprNode /<re>/<flags>
Null, // ExprNode null
True, // ExprNode true
False, // ExprNode false
This, // ExprNode this
Super, // ExprNode super
parentheses, // UnaryExprNode (<op>)
numUnit, // NumUnitExprNode <num> "<str>" or <num><str>
exprUnit, // ExprUnitExprNode (<op>) "<str>"
qualify, // BinaryExprNode <op1> :: <op2> (right-associative: a::b::c represented as a::(b::c))
objectLiteral, // PairListExprNode {<field>:<value>, <field>:<value>, ..., <field>:<value>}
arrayLiteral, // PairListExprNode [<value>, <value>, ..., <value>]
functionLiteral, // FunctionExprNode function <function>
call, // InvokeExprNode <op>(<field>:<value>, <field>:<value>, ..., <field>:<value>)
New, // InvokeExprNode new <op>(<field>:<value>, <field>:<value>, ..., <field>:<value>)
index, // InvokeExprNode <op>[<field>:<value>, <field>:<value>, ..., <field>:<value>]
dot, // BinaryExprNode <op1> . <op2> // <op2> must be identifier or qualify
dotParen, // BinaryExprNode <op1> .( <op2> )
at, // BinaryExprNode <op1> @ <op2> or <op1> @( <op2> )
// End of isPostfix()
Delete, // UnaryExprNode delete <op>
Typeof, // UnaryExprNode typeof <op>
Eval, // UnaryExprNode eval <op>
preIncrement, // UnaryExprNode ++ <op>
preDecrement, // UnaryExprNode -- <op>
postIncrement, // UnaryExprNode <op> ++
postDecrement, // UnaryExprNode <op> --
plus, // UnaryExprNode + <op>
minus, // UnaryExprNode - <op>
complement, // UnaryExprNode ~ <op>
logicalNot, // UnaryExprNode ! <op>
add, // BinaryExprNode <op1> + <op2>
subtract, // BinaryExprNode <op1> - <op2>
multiply, // BinaryExprNode <op1> * <op2>
divide, // BinaryExprNode <op1> / <op2>
modulo, // BinaryExprNode <op1> % <op2>
leftShift, // BinaryExprNode <op1> << <op2>
rightShift, // BinaryExprNode <op1> >> <op2>
logicalRightShift, // BinaryExprNode <op1> >>> <op2>
bitwiseAnd, // BinaryExprNode <op1> & <op2>
bitwiseXor, // BinaryExprNode <op1> ^ <op2>
bitwiseOr, // BinaryExprNode <op1> | <op2>
logicalAnd, // BinaryExprNode <op1> && <op2>
logicalXor, // BinaryExprNode <op1> ^^ <op2>
logicalOr, // BinaryExprNode <op1> || <op2>
equal, // BinaryExprNode <op1> == <op2>
notEqual, // BinaryExprNode <op1> != <op2>
lessThan, // BinaryExprNode <op1> < <op2>
lessThanOrEqual, // BinaryExprNode <op1> <= <op2>
greaterThan, // BinaryExprNode <op1> > <op2>
greaterThanOrEqual, // BinaryExprNode <op1> >= <op2>
identical, // BinaryExprNode <op1> === <op2>
notIdentical, // BinaryExprNode <op1> !== <op2>
In, // BinaryExprNode <op1> in <op2>
Instanceof, // BinaryExprNode <op1> instanceof <op2>
assignment, // BinaryExprNode <op1> = <op2> // Begin of isAssigningKind()
addEquals, // BinaryExprNode <op1> += <op2>
subtractEquals, // BinaryExprNode <op1> -= <op2>
multiplyEquals, // BinaryExprNode <op1> *= <op2>
divideEquals, // BinaryExprNode <op1> /= <op2>
moduloEquals, // BinaryExprNode <op1> %= <op2>
leftShiftEquals, // BinaryExprNode <op1> <<= <op2>
rightShiftEquals, // BinaryExprNode <op1> >>= <op2>
logicalRightShiftEquals, // BinaryExprNode <op1> >>>= <op2>
bitwiseAndEquals, // BinaryExprNode <op1> &= <op2>
bitwiseXorEquals, // BinaryExprNode <op1> ^= <op2>
bitwiseOrEquals, // BinaryExprNode <op1> |= <op2>
logicalAndEquals, // BinaryExprNode <op1> &&= <op2>
logicalXorEquals, // BinaryExprNode <op1> ^^= <op2>
logicalOrEquals, // BinaryExprNode <op1> ||= <op2> // End of isAssigningKind()
conditional, // TernaryExprNode <op1> ? <op2> : <op3>
comma, // BinaryExprNode <op1> , <op2> // Comma expressions only
kindsEnd
};
private:
Kind kind; // The node's kind
static const char *const kindNames[kindsEnd];
public:
ExprNode(uint32 pos, Kind kind): ParseNode(pos), kind(kind) {}
Kind getKind() const {return kind;}
bool hasKind(Kind k) const {return kind == k;}
static bool isFieldKind(Kind kind) {return kind == identifier || kind == number || kind == string || kind == qualify;}
static bool isAssigningKind(Kind kind) {return kind >= assignment && kind <= logicalOrEquals;}
static const char *kindName(Kind kind) {ASSERT(uint(kind) < kindsEnd); return kindNames[kind];}
bool isPostfix() const {return kind >= identifier && kind <= at;}
virtual void print(PrettyPrinter &f) const;
};
// Print e onto f.
inline PrettyPrinter &operator<<(PrettyPrinter &f, const ExprNode *e) {ASSERT(e); e->print(f); return f;}
struct IdentifierExprNode: ExprNode {
const StringAtom &name; // The identifier
IdentifierExprNode(uint32 pos, Kind kind, const StringAtom &name): ExprNode(pos, kind), name(name) {}
explicit IdentifierExprNode(const Token &t): ExprNode(t.getPos(), identifier), name(t.getIdentifier()) {}
void print(PrettyPrinter &f) const;
};
struct NumberExprNode: ExprNode {
float64 value; // The number's value
NumberExprNode(uint32 pos, float64 value): ExprNode(pos, number), value(value) {}
explicit NumberExprNode(const Token &t): ExprNode(t.getPos(), number), value(t.getValue()) {}
void print(PrettyPrinter &f) const;
};
struct StringExprNode: ExprNode {
String &str; // The string
StringExprNode(uint32 pos, Kind kind, String &str): ExprNode(pos, kind), str(str) {}
void print(PrettyPrinter &f) const;
};
struct RegExpExprNode: ExprNode {
const StringAtom &re; // The regular expression's contents
String &flags; // The regular expression's flags
RegExpExprNode(uint32 pos, Kind kind, const StringAtom &re, String &flags):
ExprNode(pos, kind), re(re), flags(flags) {}
void print(PrettyPrinter &f) const;
};
struct NumUnitExprNode: StringExprNode { // str is the unit string
String &numStr; // The number's source string
float64 num; // The number's value
NumUnitExprNode(uint32 pos, Kind kind, String &numStr, float64 num, String &unitStr):
StringExprNode(pos, kind, unitStr), numStr(numStr), num(num) {}
void print(PrettyPrinter &f) const;
};
struct ExprUnitExprNode: StringExprNode { // str is the unit string
ExprNode *op; // The expression to which the unit is applied; non-nil only
ExprUnitExprNode(uint32 pos, Kind kind, ExprNode *op, String &unitStr):
StringExprNode(pos, kind, unitStr), op(op) {ASSERT(op);}
void print(PrettyPrinter &f) const;
};
struct FunctionExprNode: ExprNode {
FunctionDefinition function; // Function definition
explicit FunctionExprNode(uint32 pos): ExprNode(pos, functionLiteral) {}
void print(PrettyPrinter &f) const;
};
struct ExprPairList: ArenaObject {
ExprPairList *next; // Next pair in linked list
ExprNode *field; // Field expression or nil if not provided
ExprNode *value; // Value expression or nil if not provided
explicit ExprPairList(ExprNode *field = 0, ExprNode *value = 0): next(0), field(field), value(value) {}
};
struct PairListExprNode: ExprNode {
ExprPairList *pairs; // Linked list of pairs
PairListExprNode(uint32 pos, Kind kind, ExprPairList *pairs): ExprNode(pos, kind), pairs(pairs) {}
void print(PrettyPrinter &f) const;
};
struct InvokeExprNode: PairListExprNode {
ExprNode *op; // The called function, called constructor, or indexed object; non-nil only
InvokeExprNode(uint32 pos, Kind kind, ExprNode *op, ExprPairList *pairs):
PairListExprNode(pos, kind, pairs), op(op) {ASSERT(op);}
void print(PrettyPrinter &f) const;
};
struct UnaryExprNode: ExprNode {
ExprNode *op; // The unary operator's operand; non-nil only
UnaryExprNode(uint32 pos, Kind kind, ExprNode *op): ExprNode(pos, kind), op(op) {ASSERT(op);}
void print(PrettyPrinter &f) const;
};
struct BinaryExprNode: ExprNode {
ExprNode *op1; // The binary operator's first operand; non-nil only
ExprNode *op2; // The binary operator's second operand; non-nil only
BinaryExprNode(uint32 pos, Kind kind, ExprNode *op1, ExprNode *op2):
ExprNode(pos, kind), op1(op1), op2(op2) {ASSERT(op1 && op2);}
void print(PrettyPrinter &f) const;
};
struct TernaryExprNode: ExprNode {
ExprNode *op1; // The ternary operator's first operand; non-nil only
ExprNode *op2; // The ternary operator's second operand; non-nil only
ExprNode *op3; // The ternary operator's third operand; non-nil only
TernaryExprNode(uint32 pos, Kind kind, ExprNode *op1, ExprNode *op2, ExprNode *op3):
ExprNode(pos, kind), op1(op1), op2(op2), op3(op3) {ASSERT(op1 && op2 && op3);}
void print(PrettyPrinter &f) const;
};
struct StmtNode: ParseNode {
enum Kind { // Actual class Operands
empty, // StmtNode ;
expression, // ExprStmtNode <expr> ;
block, // BlockStmtNode <attributes> { <statements> }
label, // LabelStmtNode <name> : <stmt>
If, // UnaryStmtNode if ( <expr> ) <stmt>
IfElse, // BinaryStmtNode if ( <expr> ) <stmt> else <stmt2>
Switch, // SwitchStmtNode switch ( <expr> ) <statements>
While, // UnaryStmtNode while ( <expr> ) <stmt>
DoWhile, // UnaryStmtNode do <stmt> while ( <expr> )
With, // UnaryStmtNode with ( <expr> ) <stmt>
For, // ForStmtNode for ( <initializer> ; <expr2> ; <expr3> ) <stmt>
ForIn, // ForStmtNode for ( <initializer> in <expr2> ) <stmt>
Case, // ExprStmtNode case <expr> : or default : // Only occurs directly inside a Switch
Break, // GoStmtNode break ; or break <name> ;
Continue, // GoStmtNode continue ; or continue <name> ;
Return, // ExprStmtNode return ; or return <expr> ;
Throw, // ExprStmtNode throw <expr> ;
Try, // TryStmtNode try <stmt> <catches> <finally>
Import, // ImportStmtNode import <bindings> ;
UseImport, // ImportStmtNode use import <bindings> ;
Use, // UseStmtNode use namespace <exprs> ;
Export, // ExportStmtNode <attributes> export <bindings> ;
Const, // VariableStmtNode <attributes> const <bindings> ;
Var, // VariableStmtNode <attributes> var <bindings> ;
Function, // FunctionStmtNode <attributes> function <function>
Constructor, // FunctionStmtNode <attributes> constructor <function>
Class, // ClassStmtNode <attributes> class <name> extends <superclass> implements <supers> <body>
Interface, // ClassStmtNode <attributes> interface <name> extends <supers> <body>
Namespace, // NamespaceStmtNode <attributes> namespace <name> extends <supers>
Language, // LanguageStmtNode language <language> ;
Package, // PackageStmtNode package <packageName> <body>
Debugger // ExprStmtNode debugger ;
};
private:
Kind kind; // The node's kind
public:
StmtNode *next; // Next statement in a linked list of statements in this block
StmtNode(uint32 pos, Kind kind): ParseNode(pos), kind(kind), next(0) {}
Kind getKind() const {return kind;}
bool hasKind(Kind k) const {return kind == k;}
static void printStatements(PrettyPrinter &f, const StmtNode *statements);
static void printBlockStatements(PrettyPrinter &f, const StmtNode *statements, bool loose);
static void printSemi(PrettyPrinter &f, bool noSemi);
void printSubstatement(PrettyPrinter &f, bool noSemi, const char *continuation = 0) const;
virtual void print(PrettyPrinter &f, bool noSemi) const;
};
struct ExprStmtNode: StmtNode {
ExprNode *expr; // The expression statement's expression. May be nil for default: or return-with-no-expression statements.
ExprStmtNode(uint32 pos, Kind kind, ExprNode *expr): StmtNode(pos, kind), expr(expr) {}
void print(PrettyPrinter &f, bool noSemi) const;
};
struct DebuggerStmtNode: StmtNode {
DebuggerStmtNode(uint32 pos, Kind kind): StmtNode(pos, kind) {}
void print(PrettyPrinter &f, bool noSemi) const;
};
struct IdentifierList: ArenaObject {
IdentifierList *next; // Next identifier in linked list
const StringAtom &name; // The identifier
explicit IdentifierList(const StringAtom &name): next(0), name(name) {}
};
struct AttributeStmtNode: StmtNode {
IdentifierList *attributes; // Linked list of block or definition's attributes
AttributeStmtNode(uint32 pos, Kind kind, IdentifierList *attributes): StmtNode(pos, kind), attributes(attributes) {}
void printAttributes(PrettyPrinter &f) const;
};
struct BlockStmtNode: AttributeStmtNode {
StmtNode *statements; // Linked list of block's statements
BlockStmtNode(uint32 pos, Kind kind, IdentifierList *attributes, StmtNode *statements):
AttributeStmtNode(pos, kind, attributes), statements(statements) {}
void print(PrettyPrinter &f, bool noSemi) const;
void printBlock(PrettyPrinter &f, bool loose) const;
};
struct LabelStmtNode: StmtNode {
const StringAtom &name; // The label
StmtNode *stmt; // Labeled statement; non-nil only
LabelStmtNode(uint32 pos, const StringAtom &name, StmtNode *stmt):
StmtNode(pos, label), name(name), stmt(stmt) {ASSERT(stmt);}
void print(PrettyPrinter &f, bool noSemi) const;
};
struct UnaryStmtNode: ExprStmtNode {
StmtNode *stmt; // First substatement; non-nil only
UnaryStmtNode(uint32 pos, Kind kind, ExprNode *expr, StmtNode *stmt):
ExprStmtNode(pos, kind, expr), stmt(stmt) {ASSERT(stmt);}
void print(PrettyPrinter &f, bool noSemi) const;
virtual void printContents(PrettyPrinter &f, bool noSemi) const;
};
struct BinaryStmtNode: UnaryStmtNode {
StmtNode *stmt2; // Second substatement; non-nil only
BinaryStmtNode(uint32 pos, Kind kind, ExprNode *expr, StmtNode *stmt1, StmtNode *stmt2):
UnaryStmtNode(pos, kind, expr, stmt1), stmt2(stmt2) {ASSERT(stmt2);}
void printContents(PrettyPrinter &f, bool noSemi) const;
};
struct ForStmtNode: StmtNode {
StmtNode *initializer; // For: First item in parentheses; either nil (if not provided), an expression, or a Var, or a Const.
// ForIn: Expression or declaration before 'in'; either an expression, or a Var or a Const with exactly one binding.
ExprNode *expr2; // For: Second item in parentheses; nil if not provided
// ForIn: Subexpression after 'in'; non-nil only
ExprNode *expr3; // For: Third item in parentheses; nil if not provided
// ForIn: nil
StmtNode *stmt; // Substatement; non-nil only
ForStmtNode(uint32 pos, Kind kind, StmtNode *initializer, ExprNode *expr2, ExprNode *expr3, StmtNode *stmt):
StmtNode(pos, kind), initializer(initializer), expr2(expr2), expr3(expr3), stmt(stmt) {ASSERT(stmt);}
void print(PrettyPrinter &f, bool noSemi) const;
};
struct SwitchStmtNode: ExprStmtNode {
StmtNode *statements; // Linked list of switch block's statements, which may include Case and Default statements
SwitchStmtNode(uint32 pos, ExprNode *expr, StmtNode *statements): ExprStmtNode(pos, Switch, expr), statements(statements) {}
void print(PrettyPrinter &f, bool noSemi) const;
};
struct GoStmtNode: StmtNode {
const StringAtom *name; // The label; nil if none
GoStmtNode(uint32 pos, Kind kind, const StringAtom *name): StmtNode(pos, kind), name(name) {}
void print(PrettyPrinter &f, bool noSemi) const;
};
struct CatchClause: ParseNode {
CatchClause *next; // Next catch clause in a linked list of catch clauses
const StringAtom &name; // The name of the variable that will hold the exception
ExprNode *type; // Type expression or nil if not provided
StmtNode *stmt; // The catch clause's body; non-nil only
CatchClause(uint32 pos, const StringAtom &name, ExprNode *type, StmtNode *stmt):
ParseNode(pos), next(0), name(name), type(type), stmt(stmt) {ASSERT(stmt);}
};
struct TryStmtNode: StmtNode {
StmtNode *stmt; // Substatement being tried; usually a block; non-nil only
CatchClause *catches; // Linked list of catch blocks; may be nil
StmtNode *finally; // Finally block or nil if none
TryStmtNode(uint32 pos, StmtNode *stmt, CatchClause *catches, StmtNode *finally):
StmtNode(pos, Try), stmt(stmt), catches(catches), finally(finally) {ASSERT(stmt);}
void print(PrettyPrinter &f, bool noSemi) const;
};
struct PackageName: ArenaObject { // Either idList or str may be null, but not both
IdentifierList *idList; // The package name as an identifier list
String *str; // The package name as a string
explicit PackageName(IdentifierList *idList): idList(idList), str(0) {ASSERT(idList);}
explicit PackageName(String &str): idList(0), str(&str) {}
};
struct ImportBinding: ParseNode {
ImportBinding *next; // Next binding in a linked list of import bindings
const StringAtom *name; // The package variable's name; nil if omitted
PackageName &packageName; // The package's name
ImportBinding(uint32 pos, const StringAtom *name, PackageName &packageName):
ParseNode(pos), next(0), name(name), packageName(packageName) {}
};
struct ImportStmtNode: StmtNode {
ImportBinding *bindings; // Linked list of import bindings
ImportStmtNode(uint32 pos, Kind kind, ImportBinding *bindings): StmtNode(pos, kind), bindings(bindings) {}
};
struct ExprList: ArenaObject {
ExprList *next; // Next expression in linked list
ExprNode *expr; // Attribute expression; non-nil only
explicit ExprList(ExprNode *expr): next(0), expr(expr) {ASSERT(expr);}
void printCommaList(PrettyPrinter &f) const;
static void printOptionalCommaList(PrettyPrinter &f, const char *name, const ExprList *list);
};
struct UseStmtNode: StmtNode {
ExprList *exprs; // Linked list of namespace expressions
UseStmtNode(uint32 pos, Kind kind, ExprList *exprs): StmtNode(pos, kind), exprs(exprs) {}
};
struct ExportBinding: ParseNode {
ExportBinding *next; // Next binding in a linked list of export bindings
FunctionName name; // The exported variable's name
FunctionName *initializer; // The original variable's name or nil if not provided
ExportBinding(uint32 pos, FunctionName *initializer): ParseNode(pos), next(0), initializer(initializer) {}
};
struct ExportStmtNode: AttributeStmtNode {
ExportBinding *bindings; // Linked list of export bindings
ExportStmtNode(uint32 pos, Kind kind, IdentifierList *attributes, ExportBinding *bindings):
AttributeStmtNode(pos, kind, attributes), bindings(bindings) {}
};
struct VariableStmtNode: AttributeStmtNode {
VariableBinding *bindings; // Linked list of variable bindings
VariableStmtNode(uint32 pos, Kind kind, IdentifierList *attributes, VariableBinding *bindings):
AttributeStmtNode(pos, kind, attributes), bindings(bindings) {}
void print(PrettyPrinter &f, bool noSemi) const;
};
struct FunctionStmtNode: AttributeStmtNode {
FunctionDefinition function; // Function definition
FunctionStmtNode(uint32 pos, Kind kind, IdentifierList *attributes): AttributeStmtNode(pos, kind, attributes) {}
void print(PrettyPrinter &f, bool noSemi) const;
};
struct NamespaceStmtNode: AttributeStmtNode {
ExprNode *name; // The namespace's, interfaces, or class's name; non-nil only
ExprList *supers; // Linked list of supernamespace or superinterface expressions
NamespaceStmtNode(uint32 pos, Kind kind, IdentifierList *attributes, ExprNode *name, ExprList *supers):
AttributeStmtNode(pos, kind, attributes), name(name), supers(supers) {ASSERT(name);}
void print(PrettyPrinter &f, bool noSemi) const;
};
struct ClassStmtNode: NamespaceStmtNode {
ExprNode *superclass; // Superclass expression (classes only); nil if omitted
BlockStmtNode *body; // The class's body; nil if omitted
ClassStmtNode(uint32 pos, Kind kind, IdentifierList *attributes, ExprNode *name, ExprNode *superclass, ExprList *superinterfaces,
BlockStmtNode *body):
NamespaceStmtNode(pos, kind, attributes, name, superinterfaces), superclass(superclass), body(body) {}
void print(PrettyPrinter &f, bool noSemi) const;
};
struct LanguageStmtNode: StmtNode {
JavaScript::Language language; // The selected language
LanguageStmtNode(uint32 pos, Kind kind, JavaScript::Language language):
StmtNode(pos, kind), language(language) {}
};
struct PackageStmtNode: StmtNode {
PackageName &packageName; // The package's name
BlockStmtNode *body; // The package's body; non-nil only
PackageStmtNode(uint32 pos, Kind kind, PackageName &packageName, BlockStmtNode *body):
StmtNode(pos, kind), packageName(packageName), body(body) {ASSERT(body);}
};
class Parser {
public:
Lexer lexer;
Arena &arena;
bool lineBreaksSignificant; // If false, line breaks between tokens are treated as though they were spaces instead
Parser(World &world, Arena &arena, const String &source, const String &sourceLocation, uint32 initialLineNum = 1);
private:
Reader &getReader() {return lexer.reader;}
World &getWorld() {return lexer.world;}
public:
void syntaxError(const char *message, uint backUp = 1);
void syntaxError(const String &message, uint backUp = 1);
const Token &require(bool preferRegExp, Token::Kind kind);
private:
String &copyTokenChars(const Token &t);
bool lineBreakBefore(const Token &t) const {return lineBreaksSignificant && t.getLineBreak();}
bool lineBreakBefore() {return lineBreaksSignificant && lexer.peek(true).getLineBreak();}
ExprNode *makeIdentifierExpression(const Token &t) const;
ExprNode *parseIdentifierQualifiers(ExprNode *e, bool &foundQualifiers, bool preferRegExp);
ExprNode *parseParenthesesAndIdentifierQualifiers(const Token &tParen, bool &foundQualifiers, bool preferRegExp);
ExprNode *parseQualifiedIdentifier(const Token &t, bool preferRegExp);
PairListExprNode *parseArrayLiteral(const Token &initialToken);
PairListExprNode *parseObjectLiteral(const Token &initialToken);
ExprNode *parsePrimaryExpression();
BinaryExprNode *parseMember(ExprNode *target, const Token &tOperator, ExprNode::Kind kind, ExprNode::Kind parenKind);
InvokeExprNode *parseInvoke(ExprNode *target, uint32 pos, Token::Kind closingTokenKind, ExprNode::Kind invokeKind);
ExprNode *parsePostfixExpression(bool newExpression = false);
void ensurePostfix(const ExprNode *e);
ExprNode *parseUnaryExpression();
enum Precedence {
pNone, // End tag
pExpression, // Expression
pAssignment, // AssignmentExpression
pConditional, // ConditionalExpression
pLogicalOr, // LogicalOrExpression
pLogicalXor, // LogicalXorExpression
pLogicalAnd, // LogicalAndExpression
pBitwiseOr, // BitwiseOrExpression
pBitwiseXor, // BitwiseXorExpression
pBitwiseAnd, // BitwiseAndExpression
pEquality, // EqualityExpression
pRelational, // RelationalExpression
pShift, // ShiftExpression
pAdditive, // AdditiveExpression
pMultiplicative, // MultiplicativeExpression
pUnary, // UnaryExpression
pPostfix // PostfixExpression
};
struct BinaryOperatorInfo {
ExprNode::Kind kind; // The kind of BinaryExprNode the operator should generate; ExprNode::none if not a binary operator
Precedence precedenceLeft; // Operators in this operator's left subexpression with precedenceLeft or higher are reduced
Precedence precedenceRight; // This operator's precedence
};
static const BinaryOperatorInfo tokenBinaryOperatorInfos[Token::kindsEnd];
struct StackedSubexpression;
public:
ExprNode *parseExpression(bool noIn, bool noAssignment = false, bool noComma = false);
ExprNode *parseNonAssignmentExpression(bool noIn) {return parseExpression(noIn, true, true);}
ExprNode *parseAssignmentExpression(bool noIn) {return parseExpression(noIn, false, true);}
private:
ExprNode *parseParenthesizedExpression();
ExprNode *parseTypeExpression(bool noIn);
const StringAtom &parseTypedIdentifier(ExprNode *&type);
ExprNode *parseTypeBinding(Token::Kind kind, bool noIn);
ExprList *parseTypeListBinding(Token::Kind kind);
VariableBinding *parseVariableBinding(bool noQualifiers, bool noIn);
void parseFunctionName(FunctionName &fn);
void parseFunctionSignature(FunctionDefinition &fd);
enum SemicolonState {semiNone, semiNoninsertable, semiInsertable};
enum AttributeStatement {asAny, asBlock, asConstVar};
StmtNode *parseBlock(bool inSwitch, bool noCloseBrace);
BlockStmtNode *parseBody(SemicolonState *semicolonState);
StmtNode *parseAttributeStatement(uint32 pos, IdentifierList *attributes, const Token &t, bool noIn, SemicolonState &semicolonState);
StmtNode *parseAttributesAndStatement(const Token *t, AttributeStatement as, SemicolonState &semicolonState);
StmtNode *parseAnnotatedBlock();
StmtNode *parseFor(uint32 pos, SemicolonState &semicolonState);
StmtNode *parseTry(uint32 pos);
public:
StmtNode *parseStatement(bool topLevel, bool inSwitch, SemicolonState &semicolonState);
StmtNode *parseStatementAndSemicolon(SemicolonState &semicolonState);
StmtNode *parseProgram() {return parseBlock(false, true);}
};
}
#endif