mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Use callbacks for speculative parsing.
This commit is contained in:
@@ -1138,7 +1138,7 @@ var definitions = [
|
||||
children: [
|
||||
{ name: 'asyncKeyword', isToken: true, isOptional: true },
|
||||
{ name: 'callSignature', type: 'CallSignatureSyntax' },
|
||||
{ name: 'equalsGreaterThanToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'equalsGreaterThanToken', isToken: true, isOptional: true },
|
||||
{ name: 'body', type: 'BlockSyntax | IExpressionSyntax' }
|
||||
],
|
||||
isTypeScriptSpecific: true
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,11 +1,6 @@
|
||||
///<reference path="references.ts" />
|
||||
|
||||
module TypeScript.IncrementalParser {
|
||||
interface IParserRewindPoint {
|
||||
// Information used by the incremental parser source.
|
||||
oldSourceUnitCursor: SyntaxCursor;
|
||||
}
|
||||
|
||||
interface ISyntaxElementInternal extends ISyntaxElement {
|
||||
intersectsChange: boolean;
|
||||
}
|
||||
@@ -35,7 +30,7 @@ module TypeScript.IncrementalParser {
|
||||
// The cursor we use to navigate through and retrieve nodes and tokens from the old tree.
|
||||
var oldSourceUnit = oldSyntaxTree.sourceUnit();
|
||||
|
||||
var _outstandingRewindPointCount = 0;
|
||||
var _isSpeculativelyParsing = false;
|
||||
|
||||
// Start the cursor pointing at the first element in the source unit (if it exists).
|
||||
var _oldSourceUnitCursor = getSyntaxCursor();
|
||||
@@ -81,7 +76,7 @@ module TypeScript.IncrementalParser {
|
||||
_scannerParserSource.release();
|
||||
_scannerParserSource = undefined;
|
||||
_oldSourceUnitCursor = undefined;
|
||||
_outstandingRewindPointCount = 0;
|
||||
_isSpeculativelyParsing = false;
|
||||
}
|
||||
|
||||
function extendToAffectedRange(changeRange: TextChangeRange, sourceUnit: SourceUnitSyntax): TextChangeRange {
|
||||
@@ -123,44 +118,35 @@ module TypeScript.IncrementalParser {
|
||||
return _scannerParserSource.tokenDiagnostics();
|
||||
}
|
||||
|
||||
function getRewindPoint() {
|
||||
// Get a rewind point for our new text reader and for our old source unit cursor.
|
||||
var rewindPoint = <IParserRewindPoint>_scannerParserSource.getRewindPoint();
|
||||
|
||||
function tryParse<T extends ISyntaxNode>(callback: () => T): T {
|
||||
// Clone our cursor. That way we can restore to that point if the parser needs to rewind.
|
||||
rewindPoint.oldSourceUnitCursor = cloneSyntaxCursor(_oldSourceUnitCursor);
|
||||
var savedOldSourceUnitCursor = cloneSyntaxCursor(_oldSourceUnitCursor);
|
||||
var savedIsSpeculativelyParsing = _isSpeculativelyParsing;
|
||||
|
||||
_outstandingRewindPointCount++;
|
||||
return rewindPoint;
|
||||
}
|
||||
// Mark that we're speculative parsing. During speculative parsing we cannot ruse
|
||||
// nodes from the parse tree. See the comment in trySynchronizeCursorToPosition for
|
||||
// the reasons why.
|
||||
_isSpeculativelyParsing = true;
|
||||
|
||||
function rewind(rewindPoint: IParserRewindPoint): void {
|
||||
// Restore our state to the values when the rewind point was created.
|
||||
// Now defer to our underlying scanner source to actually invoke the callback. That
|
||||
// way, if the parser decides to rewind, both the scanner source and this incremental
|
||||
// source will rewind appropriately.
|
||||
var result = _scannerParserSource.tryParse(callback);
|
||||
|
||||
// Reset the cursor to what it was when we got the rewind point. Make sure to return
|
||||
// our existing cursor to the pool so it can be reused.
|
||||
returnSyntaxCursor(_oldSourceUnitCursor);
|
||||
_oldSourceUnitCursor = rewindPoint.oldSourceUnitCursor;
|
||||
_isSpeculativelyParsing = savedIsSpeculativelyParsing;
|
||||
|
||||
// Clear the cursor that the rewind point points to. This way we don't try
|
||||
// to return it in 'releaseRewindPoint'.
|
||||
rewindPoint.oldSourceUnitCursor = undefined;
|
||||
|
||||
_scannerParserSource.rewind(rewindPoint);
|
||||
}
|
||||
|
||||
function releaseRewindPoint(rewindPoint: IParserRewindPoint): void {
|
||||
if (rewindPoint.oldSourceUnitCursor) {
|
||||
returnSyntaxCursor(rewindPoint.oldSourceUnitCursor);
|
||||
if (!result) {
|
||||
// We're rewinding. Reset the cursor to what it was when we got the rewind point.
|
||||
// Make sure to return our existing cursor to the pool so it can be reused.
|
||||
returnSyntaxCursor(_oldSourceUnitCursor);
|
||||
_oldSourceUnitCursor = savedOldSourceUnitCursor;
|
||||
}
|
||||
else {
|
||||
// We're not rewinding. Return the cloned original cursor back to the pool.
|
||||
returnSyntaxCursor(savedOldSourceUnitCursor);
|
||||
}
|
||||
|
||||
_scannerParserSource.releaseRewindPoint(rewindPoint);
|
||||
_outstandingRewindPointCount--;
|
||||
Debug.assert(_outstandingRewindPointCount >= 0);
|
||||
}
|
||||
|
||||
function isPinned() {
|
||||
return _outstandingRewindPointCount > 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
function trySynchronizeCursorToPosition() {
|
||||
@@ -181,7 +167,7 @@ module TypeScript.IncrementalParser {
|
||||
//
|
||||
// As such, the rule is simple. We only return nodes/tokens from teh original tree if
|
||||
// we know the parser will accept and consume them and never rewind back before them.
|
||||
if (isPinned()) {
|
||||
if (_isSpeculativelyParsing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -394,9 +380,7 @@ module TypeScript.IncrementalParser {
|
||||
currentContextualToken: currentContextualToken,
|
||||
peekToken: peekToken,
|
||||
consumeNodeOrToken: consumeNodeOrToken,
|
||||
getRewindPoint: getRewindPoint,
|
||||
rewind: rewind,
|
||||
releaseRewindPoint: releaseRewindPoint,
|
||||
tryParse: tryParse,
|
||||
tokenDiagnostics: tokenDiagnostics,
|
||||
release: release
|
||||
};
|
||||
|
||||
+112
-207
@@ -72,40 +72,7 @@ module TypeScript.Parser {
|
||||
// current one.
|
||||
consumeNodeOrToken(node: ISyntaxNodeOrToken): void;
|
||||
|
||||
// Gets a rewind point that the parser can use to move back to after it speculatively
|
||||
// parses something. The source guarantees that if the parser calls 'rewind' with that
|
||||
// point that it will be mostly in the same state that it was in when 'getRewindPoint'
|
||||
// was called. i.e. calling currentToken, peekToken, tokenDiagnostics, etc. will result
|
||||
// in the same values. One allowed exemption to this is 'currentNode'. If a rewind point
|
||||
// is requested and rewound, then getting the currentNode may not be possible. However,
|
||||
// as this is purely a performance optimization, it will not affect correctness.
|
||||
//
|
||||
// Note: that rewind points are not free (but they should also not be too expensive). So
|
||||
// they should be used judiciously. While a rewind point is held by the parser, the source
|
||||
// is not free to do things that it would normally do. For example, it cannot throw away
|
||||
// tokens that it has scanned on or after the rewind point as it must keep them alive for
|
||||
// the parser to move back to.
|
||||
//
|
||||
// Rewind points also work in a stack fashion. The first rewind point given out must be
|
||||
// the last rewind point released. Do not release them out of order, or bad things can
|
||||
// happen.
|
||||
//
|
||||
// Do *NOT* forget to release a rewind point. Always put them in a finally block to ensure
|
||||
// that they are released. If they are not released, things will still work, you will just
|
||||
// consume far more memory than necessary.
|
||||
getRewindPoint(): IRewindPoint;
|
||||
|
||||
// Rewinds the source to the position and state it was at when this rewind point was created.
|
||||
// This does not need to be called if the parser decides it does not need to rewind. For
|
||||
// example, the parser may speculatively parse out a lambda expression when it sees something
|
||||
// ambiguous like "(a = b, c = ...". If it succeeds parsing that as a lambda, then it will
|
||||
// just return that result. However, if it fails *then* it will rewind and try it again as
|
||||
// a parenthesized expression.
|
||||
rewind(rewindPoint: IRewindPoint): void;
|
||||
|
||||
// Called when the parser is done speculative parsing and no longer needs the rewind point.
|
||||
// Must be called for every rewind point retrived.
|
||||
releaseRewindPoint(rewindPoint: IRewindPoint): void;
|
||||
tryParse<T extends ISyntaxNode>(callback: () => T): T;
|
||||
|
||||
// Retrieves the diagnostics generated while the source was producing nodes or tokens.
|
||||
// Should generally only be called after the document has been completely parsed.
|
||||
@@ -114,24 +81,6 @@ module TypeScript.Parser {
|
||||
release(): void;
|
||||
}
|
||||
|
||||
// Information the parser needs to effectively rewind.
|
||||
export interface IRewindPoint {
|
||||
}
|
||||
|
||||
interface IParserRewindPoint extends IRewindPoint {
|
||||
// As we speculatively parse, we may build up diagnostics. When we rewind we want to
|
||||
// 'forget' that information.In order to do that we store the count of diagnostics and
|
||||
// when we start speculating, and we reset to that count when we're done. That way the
|
||||
// speculative parse does not affect any further results.
|
||||
diagnosticsCount: number;
|
||||
|
||||
// As we speculatively parse we may end up adding additional skipped tokens to the
|
||||
// _skippedTokens array in the parser. When we rewind we don't want those items in the
|
||||
// array. We may also, during speculative parsing, attach our skipped tokens to some
|
||||
// new token. When we rewind we need to restore whatever skipped tokens we started with.
|
||||
skippedTokens: ISyntaxToken[];
|
||||
}
|
||||
|
||||
// Contains the actual logic to parse typescript/javascript. This is the code that generally
|
||||
// represents the logic necessary to handle all the language grammar constructs. When the
|
||||
// language changes, this should generally only be the place necessary to fix up.
|
||||
@@ -358,29 +307,20 @@ module TypeScript.Parser {
|
||||
return new SyntaxTree(sourceUnit, isDeclaration, allDiagnostics, fileName, source.text, languageVersion);
|
||||
}
|
||||
|
||||
function getRewindPoint(): IParserRewindPoint {
|
||||
var rewindPoint = <IParserRewindPoint>source.getRewindPoint();
|
||||
|
||||
function tryParse<T extends ISyntaxNode>(callback: () => T): T {
|
||||
// See the comments in IParserRewindPoint for the explanation on why we need to store
|
||||
// this data, and what it is used for.
|
||||
rewindPoint.diagnosticsCount = diagnostics.length;
|
||||
rewindPoint.skippedTokens = _skippedTokens ? _skippedTokens.slice(0) : undefined;
|
||||
var savedDiagnosticsCount = diagnostics.length;
|
||||
var savedSkippedTokens = _skippedTokens ? _skippedTokens.slice(0) : undefined;
|
||||
|
||||
return rewindPoint;
|
||||
}
|
||||
var result = source.tryParse(callback);
|
||||
|
||||
function rewind(rewindPoint: IParserRewindPoint): void {
|
||||
source.rewind(rewindPoint);
|
||||
if (!result) {
|
||||
diagnostics.length = savedDiagnosticsCount;
|
||||
_skippedTokens = savedSkippedTokens;
|
||||
}
|
||||
|
||||
diagnostics.length = rewindPoint.diagnosticsCount;
|
||||
_skippedTokens = rewindPoint.skippedTokens;
|
||||
}
|
||||
|
||||
function releaseRewindPoint(rewindPoint: IParserRewindPoint): void {
|
||||
// Debug.assert(listParsingState === rewindPoint.listParsingState);
|
||||
// Debug.assert(isInStrictMode === rewindPoint.isInStrictMode);
|
||||
|
||||
source.releaseRewindPoint(rewindPoint);
|
||||
return result;
|
||||
}
|
||||
|
||||
function currentNode(): ISyntaxNode {
|
||||
@@ -916,80 +856,6 @@ module TypeScript.Parser {
|
||||
return new ModuleNameModuleReferenceSyntax(contextFlags, parseName(/*allowIdentifierNames:*/ false));
|
||||
}
|
||||
|
||||
function tryParseTypeArgumentList(inExpression: boolean): TypeArgumentListSyntax {
|
||||
var _currentToken = currentToken();
|
||||
if (_currentToken.kind !== SyntaxKind.LessThanToken) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!inExpression) {
|
||||
// if we're not in an expression, this must be a type argument list. Just parse
|
||||
// it out as such.
|
||||
return new TypeArgumentListSyntax(contextFlags,
|
||||
consumeToken(_currentToken),
|
||||
parseSeparatedSyntaxList<ITypeSyntax>(ListParsingState.TypeArgumentList_Types),
|
||||
eatToken(SyntaxKind.GreaterThanToken));
|
||||
}
|
||||
|
||||
// If we're in an expression, then we only want to consume this as a type argument list
|
||||
// if we're sure that it's a type arg list and not an arithmetic expression.
|
||||
|
||||
var rewindPoint = getRewindPoint();
|
||||
|
||||
// We've seen a '<'. Try to parse it out as a type argument list.
|
||||
var lessThanToken = consumeToken(_currentToken);
|
||||
var typeArguments = parseSeparatedSyntaxList<ITypeSyntax>(ListParsingState.TypeArgumentList_Types);
|
||||
var greaterThanToken = eatToken(SyntaxKind.GreaterThanToken);
|
||||
|
||||
// We're in a context where '<' could be the start of a type argument list, or part
|
||||
// of an arithmetic expression. We'll presume it's the latter unless we see the '>'
|
||||
// and a following token that guarantees that it's supposed to be a type argument list.
|
||||
if (greaterThanToken.fullWidth() === 0 || !canFollowTypeArgumentListInExpression(currentToken().kind)) {
|
||||
rewind(rewindPoint);
|
||||
releaseRewindPoint(rewindPoint);
|
||||
return undefined;
|
||||
}
|
||||
else {
|
||||
releaseRewindPoint(rewindPoint);
|
||||
return new TypeArgumentListSyntax(contextFlags, lessThanToken, typeArguments, greaterThanToken);
|
||||
}
|
||||
}
|
||||
|
||||
function canFollowTypeArgumentListInExpression(kind: SyntaxKind): boolean {
|
||||
switch (kind) {
|
||||
case SyntaxKind.OpenParenToken: // foo<x>(
|
||||
case SyntaxKind.DotToken: // foo<x>.
|
||||
// These two cases are the only cases where this token can legally follow a
|
||||
// type argument list. So we definitely want to treat this as a type arg list.
|
||||
|
||||
case SyntaxKind.CloseParenToken: // foo<x>)
|
||||
case SyntaxKind.CloseBracketToken: // foo<x>]
|
||||
case SyntaxKind.ColonToken: // foo<x>:
|
||||
case SyntaxKind.SemicolonToken: // foo<x>;
|
||||
case SyntaxKind.CommaToken: // foo<x>,
|
||||
case SyntaxKind.QuestionToken: // foo<x>?
|
||||
case SyntaxKind.EqualsEqualsToken: // foo<x> ==
|
||||
case SyntaxKind.EqualsEqualsEqualsToken: // foo<x> ===
|
||||
case SyntaxKind.ExclamationEqualsToken: // foo<x> !=
|
||||
case SyntaxKind.ExclamationEqualsEqualsToken: // foo<x> !==
|
||||
case SyntaxKind.AmpersandAmpersandToken: // foo<x> &&
|
||||
case SyntaxKind.BarBarToken: // foo<x> ||
|
||||
case SyntaxKind.CaretToken: // foo<x> ^
|
||||
case SyntaxKind.AmpersandToken: // foo<x> &
|
||||
case SyntaxKind.BarToken: // foo<x> |
|
||||
case SyntaxKind.CloseBraceToken: // foo<x> }
|
||||
case SyntaxKind.EndOfFileToken: // foo<x>
|
||||
// these cases can't legally follow a type arg list. However, they're not legal
|
||||
// expressions either. The user is probably in the middle of a generic type. So
|
||||
// treat it as such.
|
||||
return true;
|
||||
|
||||
default:
|
||||
// Anything else treat as an expression.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function parseName(allowIdentifierName: boolean): INameSyntax {
|
||||
return tryParseName(allowIdentifierName) || eatIdentifierToken();
|
||||
}
|
||||
@@ -3042,44 +2908,79 @@ module TypeScript.Parser {
|
||||
// Debug.assert(currentToken().kind === SyntaxKind.LessThanToken);
|
||||
// If we have a '<', then only parse this as a arugment list if the type arguments
|
||||
// are complete and we have an open paren. if we don't, rewind and return nothing.
|
||||
var rewindPoint = getRewindPoint();
|
||||
|
||||
var typeArgumentList = tryParseTypeArgumentList(/*inExpression:*/ true);
|
||||
var token0 = currentToken();
|
||||
var tokenKind = token0.kind;
|
||||
|
||||
var isOpenParen = tokenKind === SyntaxKind.OpenParenToken;
|
||||
var isDot = tokenKind === SyntaxKind.DotToken;
|
||||
var isOpenParenOrDot = isOpenParen || isDot;
|
||||
|
||||
var argumentList: ArgumentListSyntax = undefined;
|
||||
if (!typeArgumentList || !isOpenParenOrDot) {
|
||||
// Wasn't generic. Rewind to where we started so this can be parsed as an
|
||||
// arithmetic expression.
|
||||
rewind(rewindPoint);
|
||||
releaseRewindPoint(rewindPoint);
|
||||
var typeArgumentList = tryParse(speculativeParseTypeArgumentList);
|
||||
if (!typeArgumentList) {
|
||||
return undefined;
|
||||
}
|
||||
else {
|
||||
Debug.assert(typeArgumentList && isOpenParenOrDot);
|
||||
|
||||
releaseRewindPoint(rewindPoint);
|
||||
// It's not uncommon for a user to type: "Foo<T>."
|
||||
//
|
||||
// This is not legal in typescript (as an parameter list must follow the type
|
||||
// arguments). We want to give a good error message for this as otherwise
|
||||
// we'll bail out here and give a poor error message when we try to parse this
|
||||
// as an arithmetic expression.
|
||||
if (isDot) {
|
||||
return new ArgumentListSyntax(contextFlags, typeArgumentList,
|
||||
createMissingToken(SyntaxKind.OpenParenToken, undefined, DiagnosticCode.A_parameter_list_must_follow_a_generic_type_argument_list_expected),
|
||||
<any>[],
|
||||
eatToken(SyntaxKind.CloseParenToken));
|
||||
}
|
||||
else {
|
||||
Debug.assert(token0.kind === SyntaxKind.OpenParenToken);
|
||||
return parseArgumentList(typeArgumentList, token0);
|
||||
}
|
||||
var _currentToken = currentToken();
|
||||
if (_currentToken.kind === SyntaxKind.OpenParenToken) {
|
||||
return parseArgumentList(typeArgumentList, _currentToken);
|
||||
}
|
||||
|
||||
// It's not uncommon for a user to type: "Foo<T>."
|
||||
//
|
||||
// This is not legal in typescript (as an parameter list must follow the type
|
||||
// arguments). We want to give a good error message for this as otherwise
|
||||
// we'll bail out here and give a poor error message when we try to parse this
|
||||
// as an arithmetic expression.
|
||||
return new ArgumentListSyntax(contextFlags, typeArgumentList,
|
||||
createMissingToken(SyntaxKind.OpenParenToken, undefined, DiagnosticCode.A_parameter_list_must_follow_a_generic_type_argument_list_expected),
|
||||
<any>[],
|
||||
eatToken(SyntaxKind.CloseParenToken));
|
||||
}
|
||||
|
||||
function speculativeParseTypeArgumentList(): TypeArgumentListSyntax {
|
||||
// If we're in an expression, then we only want to consume this as a type argument list
|
||||
// if we're sure that it's a type arg list and not an arithmetic expression.
|
||||
|
||||
// We've seen a '<'. Try to parse it out as a type argument list.
|
||||
var lessThanToken = consumeToken(currentToken());
|
||||
var typeArguments = parseSeparatedSyntaxList<ITypeSyntax>(ListParsingState.TypeArgumentList_Types);
|
||||
var greaterThanToken = tryEatToken(SyntaxKind.GreaterThanToken);
|
||||
|
||||
// We're in a context where '<' could be the start of a type argument list, or part
|
||||
// of an arithmetic expression. We'll presume it's the latter unless we see the '>'
|
||||
// and a following token that guarantees that it's supposed to be a type argument list.
|
||||
if (greaterThanToken === undefined || !canFollowTypeArgumentListInExpression(currentToken().kind)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return new TypeArgumentListSyntax(contextFlags, lessThanToken, typeArguments, greaterThanToken);
|
||||
}
|
||||
|
||||
function canFollowTypeArgumentListInExpression(kind: SyntaxKind): boolean {
|
||||
switch (kind) {
|
||||
case SyntaxKind.OpenParenToken: // foo<x>(
|
||||
case SyntaxKind.DotToken: // foo<x>.
|
||||
// These two cases are the only cases where this token can legally follow a
|
||||
// type argument list. So we definitely want to treat this as a type arg list.
|
||||
|
||||
case SyntaxKind.CloseParenToken: // foo<x>)
|
||||
case SyntaxKind.CloseBracketToken: // foo<x>]
|
||||
case SyntaxKind.ColonToken: // foo<x>:
|
||||
case SyntaxKind.SemicolonToken: // foo<x>;
|
||||
case SyntaxKind.CommaToken: // foo<x>,
|
||||
case SyntaxKind.QuestionToken: // foo<x>?
|
||||
case SyntaxKind.EqualsEqualsToken: // foo<x> ==
|
||||
case SyntaxKind.EqualsEqualsEqualsToken: // foo<x> ===
|
||||
case SyntaxKind.ExclamationEqualsToken: // foo<x> !=
|
||||
case SyntaxKind.ExclamationEqualsEqualsToken: // foo<x> !==
|
||||
case SyntaxKind.AmpersandAmpersandToken: // foo<x> &&
|
||||
case SyntaxKind.BarBarToken: // foo<x> ||
|
||||
case SyntaxKind.CaretToken: // foo<x> ^
|
||||
case SyntaxKind.AmpersandToken: // foo<x> &
|
||||
case SyntaxKind.BarToken: // foo<x> |
|
||||
case SyntaxKind.CloseBraceToken: // foo<x> }
|
||||
case SyntaxKind.EndOfFileToken: // foo<x>
|
||||
// these cases can't legally follow a type arg list. However, they're not legal
|
||||
// expressions either. The user is probably in the middle of a generic type. So
|
||||
// treat it as such.
|
||||
return true;
|
||||
|
||||
default:
|
||||
// Anything else treat as an expression.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3335,28 +3236,24 @@ module TypeScript.Parser {
|
||||
if (isDefinitelyArrowFunctionExpression()) {
|
||||
// We have something like "() =>" or "(a) =>". Definitely a lambda, so parse it
|
||||
// unilaterally as such.
|
||||
return tryParseParenthesizedArrowFunctionExpressionWorker(/*requiresArrow:*/ false);
|
||||
return parseParenthesizedArrowFunctionExpressionWorker(/*requireArrow:*/ false);
|
||||
}
|
||||
|
||||
// Now, look for cases where we're sure it's not an arrow function. This will help save us
|
||||
// a costly parse.
|
||||
|
||||
if (!isPossiblyArrowFunctionExpression()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Then, try to actually parse it as a arrow function, and only return if we see an =>
|
||||
var rewindPoint = getRewindPoint();
|
||||
|
||||
var arrowFunction = tryParseParenthesizedArrowFunctionExpressionWorker(/*requiresArrow:*/ true);
|
||||
if (arrowFunction === undefined) {
|
||||
rewind(rewindPoint);
|
||||
}
|
||||
|
||||
releaseRewindPoint(rewindPoint);
|
||||
return arrowFunction;
|
||||
return tryParse(speculativeParseParenthesizedArrowFunctionExpression);
|
||||
}
|
||||
|
||||
function tryParseParenthesizedArrowFunctionExpressionWorker(requireArrow: boolean): ParenthesizedArrowFunctionExpressionSyntax {
|
||||
function speculativeParseParenthesizedArrowFunctionExpression() {
|
||||
return parseParenthesizedArrowFunctionExpressionWorker(/*requireArrow:*/ true);
|
||||
}
|
||||
|
||||
function parseParenthesizedArrowFunctionExpressionWorker(requireArrow: boolean): ParenthesizedArrowFunctionExpressionSyntax {
|
||||
var asyncKeyword = tryEatToken(SyntaxKind.AsyncKeyword);
|
||||
|
||||
// From the static semantic section:
|
||||
@@ -3367,7 +3264,6 @@ module TypeScript.Parser {
|
||||
// return the result of parsing the lexical token stream matched by CoverParenthesizedExpressionAndArrowParameterList
|
||||
// using ArrowFormalParameters as the goal symbol.
|
||||
var callSignature = parseCallSignatureWithoutSemicolonOrComma(/*requireCompleteTypeParameterList:*/ true, /*yieldAndGeneratorParameterContext:*/ inYieldContext(), /*asyncContext:*/ !!asyncKeyword);
|
||||
|
||||
if (requireArrow && currentToken().kind !== SyntaxKind.EqualsGreaterThanToken) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -3885,23 +3781,25 @@ module TypeScript.Parser {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var rewindPoint = getRewindPoint();
|
||||
return requireCompleteTypeParameterList
|
||||
? tryParse(speculativeParseTypeParameterList)
|
||||
: parseTypeArgumentListWorker(/*requireCompleteTypeParameterList:*/ false);
|
||||
}
|
||||
|
||||
var lessThanToken = consumeToken(_currentToken);
|
||||
function speculativeParseTypeParameterList() {
|
||||
return parseTypeArgumentListWorker(/*requireCompleteTypeParameterList:*/ true);
|
||||
}
|
||||
|
||||
function parseTypeArgumentListWorker(requireCompleteTypeParameterList: boolean) {
|
||||
var lessThanToken = consumeToken(currentToken());
|
||||
var typeParameters = parseSeparatedSyntaxList<TypeParameterSyntax>(ListParsingState.TypeParameterList_TypeParameters);
|
||||
|
||||
var greaterThanToken = eatToken(SyntaxKind.GreaterThanToken);
|
||||
|
||||
// return undefined if we were required to have a '>' token and we did not have one.
|
||||
if (requireCompleteTypeParameterList && greaterThanToken.fullWidth() === 0) {
|
||||
rewind(rewindPoint);
|
||||
releaseRewindPoint(rewindPoint);
|
||||
if (requireCompleteTypeParameterList && currentToken().kind !== SyntaxKind.GreaterThanToken) {
|
||||
return undefined;
|
||||
}
|
||||
else {
|
||||
releaseRewindPoint(rewindPoint);
|
||||
return new TypeParameterListSyntax(contextFlags, lessThanToken, typeParameters, greaterThanToken);
|
||||
}
|
||||
|
||||
return new TypeParameterListSyntax(contextFlags, lessThanToken, typeParameters, eatToken(SyntaxKind.GreaterThanToken));
|
||||
}
|
||||
|
||||
function isTypeParameter(): boolean {
|
||||
@@ -4147,10 +4045,17 @@ module TypeScript.Parser {
|
||||
return name;
|
||||
}
|
||||
|
||||
var typeArgumentList = tryParseTypeArgumentList(/*inExpression:*/ false);
|
||||
return !typeArgumentList
|
||||
? name
|
||||
: new GenericTypeSyntax(contextFlags, name, typeArgumentList);
|
||||
var _currentToken = currentToken();
|
||||
if (_currentToken.kind !== SyntaxKind.LessThanToken) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return new GenericTypeSyntax(contextFlags,
|
||||
name,
|
||||
new TypeArgumentListSyntax(contextFlags,
|
||||
consumeToken(_currentToken),
|
||||
parseSeparatedSyntaxList<ITypeSyntax>(ListParsingState.TypeArgumentList_Types),
|
||||
eatToken(SyntaxKind.GreaterThanToken)));
|
||||
}
|
||||
|
||||
function isFunctionType(): boolean {
|
||||
|
||||
@@ -1432,12 +1432,6 @@ module TypeScript.Scanner {
|
||||
return !hadError && SyntaxFacts.isIdentifierNameOrAnyKeyword(token) && width(token) === text.length();
|
||||
}
|
||||
|
||||
interface IScannerRewindPoint extends Parser.IRewindPoint {
|
||||
// Information used by normal parser source.
|
||||
absolutePosition: number;
|
||||
slidingWindowIndex: number;
|
||||
}
|
||||
|
||||
// Parser source used in batch scenarios. Directly calls into an underlying text scanner and
|
||||
// supports none of the functionality to reuse nodes. Good for when you just want want to do
|
||||
// a single parse of a file.
|
||||
@@ -1451,10 +1445,6 @@ module TypeScript.Scanner {
|
||||
// reparse a / or /= as a regular expression.
|
||||
var _tokenDiagnostics: Diagnostic[] = [];
|
||||
|
||||
// Pool of rewind points we give out if the parser needs one.
|
||||
var rewindPointPool: IScannerRewindPoint[] = [];
|
||||
var rewindPointPoolCount = 0;
|
||||
|
||||
var lastDiagnostic: Diagnostic = undefined;
|
||||
var reportDiagnostic = (position: number, fullWidth: number, diagnosticKey: string, args: any[]) => {
|
||||
lastDiagnostic = new Diagnostic(fileName, text.lineMap(), position, fullWidth, diagnosticKey, args);
|
||||
@@ -1470,7 +1460,6 @@ module TypeScript.Scanner {
|
||||
slidingWindow = undefined;
|
||||
scanner = undefined;
|
||||
_tokenDiagnostics = [];
|
||||
rewindPointPool = [];
|
||||
lastDiagnostic = undefined;
|
||||
reportDiagnostic = undefined;
|
||||
}
|
||||
@@ -1490,44 +1479,19 @@ module TypeScript.Scanner {
|
||||
return _tokenDiagnostics;
|
||||
}
|
||||
|
||||
function getOrCreateRewindPoint(): IScannerRewindPoint {
|
||||
if (rewindPointPoolCount === 0) {
|
||||
return <IScannerRewindPoint>{};
|
||||
function tryParse<T extends ISyntaxNode>(callback: () => T): T {
|
||||
var savedSlidingWindowIndex = slidingWindow.getAndPinAbsoluteIndex();
|
||||
var savedAbsolutePosition = _absolutePosition;
|
||||
|
||||
var result = callback();
|
||||
if (!result) {
|
||||
slidingWindow.rewindToPinnedIndex(savedSlidingWindowIndex);
|
||||
_absolutePosition = savedAbsolutePosition;
|
||||
}
|
||||
|
||||
rewindPointPoolCount--;
|
||||
var result = rewindPointPool[rewindPointPoolCount];
|
||||
rewindPointPool[rewindPointPoolCount] = undefined;
|
||||
return result;
|
||||
}
|
||||
|
||||
function getRewindPoint(): IScannerRewindPoint {
|
||||
var slidingWindowIndex = slidingWindow.getAndPinAbsoluteIndex();
|
||||
|
||||
var rewindPoint = getOrCreateRewindPoint();
|
||||
|
||||
rewindPoint.slidingWindowIndex = slidingWindowIndex;
|
||||
rewindPoint.absolutePosition = _absolutePosition;
|
||||
|
||||
// rewindPoint.pinCount = slidingWindow.pinCount();
|
||||
|
||||
return rewindPoint;
|
||||
}
|
||||
|
||||
function rewind(rewindPoint: IScannerRewindPoint): void {
|
||||
slidingWindow.rewindToPinnedIndex(rewindPoint.slidingWindowIndex);
|
||||
|
||||
_absolutePosition = rewindPoint.absolutePosition;
|
||||
}
|
||||
|
||||
function releaseRewindPoint(rewindPoint: IScannerRewindPoint): void {
|
||||
// Debug.assert(slidingWindow.pinCount() === rewindPoint.pinCount);
|
||||
slidingWindow.releaseAndUnpinAbsoluteIndex((<any>rewindPoint).absoluteIndex);
|
||||
|
||||
rewindPointPool[rewindPointPoolCount] = rewindPoint;
|
||||
rewindPointPoolCount++;
|
||||
}
|
||||
|
||||
function fetchNextItem(allowContextualToken: boolean): ISyntaxToken {
|
||||
// Assert disabled because it is actually expensive enugh to affect perf.
|
||||
// Debug.assert(spaceAvailable > 0);
|
||||
@@ -1641,9 +1605,10 @@ module TypeScript.Scanner {
|
||||
currentContextualToken: currentContextualToken,
|
||||
peekToken: peekToken,
|
||||
consumeNodeOrToken: consumeNodeOrToken,
|
||||
getRewindPoint: getRewindPoint,
|
||||
rewind: rewind,
|
||||
releaseRewindPoint: releaseRewindPoint,
|
||||
//getRewindPoint: getRewindPoint,
|
||||
//rewind: rewind,
|
||||
//releaseRewindPoint: releaseRewindPoint,
|
||||
tryParse: tryParse,
|
||||
tokenDiagnostics: tokenDiagnostics,
|
||||
release: release,
|
||||
absolutePosition: absolutePosition,
|
||||
|
||||
@@ -275,7 +275,7 @@ var definitions:ITypeDefinition[] = [
|
||||
children: [
|
||||
<any>{ name: 'asyncKeyword', isToken: true, isOptional: true },
|
||||
<any>{ name: 'callSignature', type: 'CallSignatureSyntax' },
|
||||
<any>{ name: 'equalsGreaterThanToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'equalsGreaterThanToken', isToken: true, isOptional: true },
|
||||
<any>{ name: 'body', type: 'BlockSyntax | IExpressionSyntax' }
|
||||
],
|
||||
isTypeScriptSpecific: true
|
||||
|
||||
@@ -1286,7 +1286,7 @@ module TypeScript {
|
||||
this.body = body,
|
||||
asyncKeyword && (asyncKeyword.parent = this),
|
||||
callSignature.parent = this,
|
||||
equalsGreaterThanToken.parent = this,
|
||||
equalsGreaterThanToken && (equalsGreaterThanToken.parent = this),
|
||||
body.parent = this;
|
||||
};
|
||||
ParenthesizedArrowFunctionExpressionSyntax.prototype.kind = SyntaxKind.ParenthesizedArrowFunctionExpression;
|
||||
|
||||
@@ -428,7 +428,7 @@ module TypeScript {
|
||||
public visitParenthesizedArrowFunctionExpression(node: ParenthesizedArrowFunctionExpressionSyntax): void {
|
||||
this.visitOptionalToken(node.asyncKeyword);
|
||||
visitNodeOrToken(this, node.callSignature);
|
||||
this.visitToken(node.equalsGreaterThanToken);
|
||||
this.visitOptionalToken(node.equalsGreaterThanToken);
|
||||
visitNodeOrToken(this, node.body);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user