Use callbacks for speculative parsing.

This commit is contained in:
Cyrus Najmabadi
2014-12-04 19:05:01 -08:00
parent 308d8e5d1e
commit 8032c0f950
8 changed files with 158 additions and 300 deletions
+1 -1
View File
@@ -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
+26 -42
View File
@@ -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
View File
@@ -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 {
+12 -47
View File
@@ -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,
+1 -1
View File
@@ -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);
}