UNPKG

plaxtony

Version:

Static code analysis of SC2 Galaxy Script

992 lines 42.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Parser = void 0; const Types = require("./types"); const scanner_1 = require("./scanner"); const utils_1 = require("./utils"); var ParsingContext; (function (ParsingContext) { ParsingContext[ParsingContext["SourceElements"] = 0] = "SourceElements"; ParsingContext[ParsingContext["BlockStatements"] = 1] = "BlockStatements"; ParsingContext[ParsingContext["StructMembers"] = 2] = "StructMembers"; ParsingContext[ParsingContext["Parameters"] = 3] = "Parameters"; ParsingContext[ParsingContext["TypeArguments"] = 4] = "TypeArguments"; ParsingContext[ParsingContext["ArgumentExpressions"] = 5] = "ArgumentExpressions"; })(ParsingContext || (ParsingContext = {})); class Parser { constructor() { this.parsingContext = 0; this.scanner = new scanner_1.Scanner((message, pos, length) => { this.parseErrorAtPosition(pos, length, message.message); }); } token() { return this.currentToken; } nextToken() { this.currentToken = this.scanner.scan(); while (this.currentToken === 1 /* SingleLineCommentTrivia */) { const commentToken = this.createNode(this.token(), undefined, false); this.currentToken = this.scanner.scan(); this.finishNode(commentToken, undefined, false); this.sourceFile.commentsLineMap.set(commentToken.line, commentToken); } return this.currentToken; } parseErrorAtCurrentToken(message, arg0) { const start = this.scanner.getStartPos(); const length = this.scanner.getCurrentPos() - start; this.parseErrorAtPosition(start, length, message, arg0); } parseErrorAtPosition(start, length, message, arg0) { const diag = utils_1.createFileDiagnostic(this.sourceFile, start, length, { code: 1001, category: Types.DiagnosticCategory.Error, message: message, }); // TODO: line & col should not be here diag.line = this.scanner.getLine(); diag.col = this.scanner.getChar(); this.sourceFile.parseDiagnostics.push(diag); // throw new Error(`${diag.file!.fileName} [${diag.start}]: ${diag.messageText}`); // throw new Error(`${diag.file!.fileName} [${this.scanner.getLine()}:${this.scanner.getCol()}]: ${diag.messageText}`); } speculationHelper(callback, isLookAhead) { // Keep track of the state we'll need to rollback to if lookahead fails (or if the // caller asked us to always reset our state). const saveToken = this.currentToken; const saveSyntaxTokensLength = this.syntaxTokens.length; const saveSyntaxTokensCurrentLength = this.syntaxTokens[this.syntaxTokens.length - 1].length; const saveParseDiagnosticsLength = this.sourceFile.parseDiagnostics.length; // const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode; // Note: it is not actually necessary to save/restore the context flags here. That's // because the saving/restoring of these flags happens naturally through the recursive // descent nature of our parser. However, we still store this here just so we can // assert that invariant holds. // const saveContextFlags = contextFlags; // If we're only looking ahead, then tell the scanner to only lookahead as well. // Otherwise, if we're actually speculatively parsing, then tell the scanner to do the // same. const result = isLookAhead ? this.scanner.lookAhead(callback) : this.scanner.tryScan(callback); // Debug.assert(saveContextFlags === contextFlags); // If our callback returned something 'falsy' or we're just looking ahead, // then unconditionally restore us to where we were. if (!result || isLookAhead) { this.currentToken = saveToken; if (this.syntaxTokens.length > saveSyntaxTokensLength) { this.syntaxTokens = this.syntaxTokens.slice(0, saveSyntaxTokensLength); } if (this.syntaxTokens[this.syntaxTokens.length - 1].length > saveSyntaxTokensCurrentLength) { this.syntaxTokens[this.syntaxTokens.length - 1] = this.syntaxTokens[this.syntaxTokens.length - 1].slice(0, saveSyntaxTokensCurrentLength); } this.sourceFile.parseDiagnostics.length = saveParseDiagnosticsLength; // parseErrorBeforeNextFinishedNode = saveParseErrorBeforeNextFinishedNode; } return result; } lookAhead(callback) { return this.speculationHelper(callback, true); } parseExpected(kind, diagnosticMessage, shouldAdvance = true) { if (this.token() === kind) { if (shouldAdvance) { this.syntaxTokens[this.syntaxTokens.length - 1].push(this.parseTokenNode()); } return true; } if (diagnosticMessage == null) { diagnosticMessage = "Expected " + utils_1.getKindName(kind) + ", found " + utils_1.getKindName(this.currentToken); } this.parseErrorAtCurrentToken(diagnosticMessage); return false; } parseOptional(t) { if (this.token() === t) { this.syntaxTokens[this.syntaxTokens.length - 1].push(this.parseTokenNode()); return true; } return false; } parseTokenNode() { const node = this.createNode(this.token(), undefined, false); this.nextToken(); return this.finishNode(node, undefined, false); } createNode(kind, pos, assignSyntaxTokens = true) { const node = {}; node.kind = kind; node.pos = pos === undefined ? this.scanner.getTokenPos() : pos; node.end = node.pos; node.line = this.scanner.getLine(); node.char = this.scanner.getChar(); if (process.env.PLAXTONY_DEBUG) { node.kindName = utils_1.getKindName(node.kind); } if (assignSyntaxTokens) { this.syntaxTokens.push([]); } return node; } createNodeArray(elements, pos) { const array = (elements || []); if (pos === undefined) { pos = this.scanner.getStartPos(); } array.pos = pos; array.end = pos; return array; } createMissingNode(kind, emitErrorMessage = true) { if (emitErrorMessage) { this.parseErrorAtCurrentToken(`Missing node: ${utils_1.getKindName(kind)}`); } return this.createNode(0 /* Unknown */, undefined, false); } createMissingList() { return this.createNodeArray(); } finishNode(node, end, assignSyntaxTokens = true) { node.end = end === undefined ? this.scanner.getStartPos() : end; if (assignSyntaxTokens) { node.syntaxTokens = this.syntaxTokens.pop(); for (const token of node.syntaxTokens) { token.parent = node; } } return node; } isListTerminator(kind) { if (this.token() === 114 /* EndOfFileToken */) { // Being at the end of the file ends all lists. return true; } switch (kind) { case 0 /* SourceElements */: return false; case 1 /* BlockStatements */: case 2 /* StructMembers */: return this.token() === 5 /* CloseBraceToken */; case 5 /* ArgumentExpressions */: case 3 /* Parameters */: return this.token() === 7 /* CloseParenToken */; case 4 /* TypeArguments */: return this.token() === 14 /* GreaterThanToken */; } } parsingContextErrors(context) { switch (context) { case 0 /* SourceElements */: return 'expected declaration'; case 1 /* BlockStatements */: return 'expected declaration or statement'; case 2 /* StructMembers */: return 'expected property declaration'; case 4 /* TypeArguments */: return 'expected type argumnt definition'; case 5 /* ArgumentExpressions */: return 'expected argumnt expression'; case 3 /* Parameters */: return 'expected parameter declaration'; } } isListElement(parsingContext, inErrorRecovery) { switch (parsingContext) { case 0 /* SourceElements */: return this.isStartOfRootStatement(); case 1 /* BlockStatements */: return this.isStartOfStatement(); case 2 /* StructMembers */: return this.isStartOfTypeDefinition(); case 4 /* TypeArguments */: return this.isStartOfTypeDefinition(); case 3 /* Parameters */: return this.isStartOfParameter(); case 5 /* ArgumentExpressions */: return this.isStartOfExpression(); } } parseList(kind, parseElement) { const saveParsingContext = this.parsingContext; this.parsingContext |= 1 << kind; const result = this.createNodeArray(); while (!this.isListTerminator(kind)) { if (this.isListElement(kind, false)) { result.push(parseElement()); continue; } const start = this.scanner.getTokenPos(); this.nextToken(); this.parseErrorAtPosition(start, this.scanner.getTokenPos() - start, this.parsingContextErrors(kind)); if (kind !== 0 /* SourceElements */ && kind !== 1 /* BlockStatements */) { break; } } result.end = this.scanner.getTokenPos(); this.parsingContext = saveParsingContext; return result; } parseBracketedList(kind, parseElement, open, close) { if (this.parseExpected(open)) { const result = this.parseDelimitedList(kind, parseElement); this.parseExpected(close); return result; } return this.createMissingList(); } parseDelimitedList(kind, parseElement) { const saveParsingContext = this.parsingContext; this.parsingContext |= 1 << kind; const result = this.createNodeArray(); let commaStart = -1; // Meaning the previous token was not a comma while (true) { if (this.isListElement(kind, false)) { const startPos = this.scanner.getTokenPos(); result.push(parseElement()); commaStart = this.scanner.getTokenPos(); if (this.parseOptional(12 /* CommaToken */)) { // No need to check for a zero length node since we know we parsed a comma continue; } commaStart = -1; // Back to the state where the last token was not a comma if (this.isListTerminator(kind)) { break; } // We didn't get a comma, and the list wasn't terminated, explicitly parse // out a comma so we give a good error message. this.parseExpected(12 /* CommaToken */); continue; } else if (this.token() === 12 /* CommaToken */) { // If list element was *invalid* it might as well be empty // swallow the comma and try again this.parseErrorAtCurrentToken(this.parsingContextErrors(kind)); this.nextToken(); continue; } if (this.isListTerminator(kind)) { break; } this.parseErrorAtCurrentToken(this.parsingContextErrors(kind)); this.nextToken(); // give up, even if list wasn't properly terminated. because it might never be, if termination character is missing.. break; } if (commaStart >= 0) { this.parseErrorAtPosition(commaStart, 1, 'trailing comma'); } result.end = this.scanner.getTokenPos(); this.parsingContext = saveParsingContext; return result; } isVariableDeclaration() { while (this.token() === 53 /* ConstKeyword */ || this.token() === 52 /* StaticKeyword */) { this.nextToken(); } if (!utils_1.isKeywordTypeKind(this.token()) && this.token() !== 113 /* Identifier */) { return false; } this.parseTypeDefinition(); if (this.token() !== 113 /* Identifier */) { return false; } this.nextToken(); switch (this.token()) { // we're expecting ";" or "=", but let's allow everything else than "(" for better error tolerance // "(" indicates that it might be CallExpression or FunctionDeclaration case 6 /* OpenParenToken */: return false; } return true; } isFunctionDeclaration() { while (this.token() === 54 /* NativeKeyword */ || this.token() === 52 /* StaticKeyword */) { this.nextToken(); } if (!utils_1.isKeywordTypeKind(this.token()) && this.token() !== 113 /* Identifier */) { return false; } this.parseTypeDefinition(); if (this.token() !== 113 /* Identifier */) { return false; } this.nextToken(); if (this.token() !== 6 /* OpenParenToken */) { return false; } return true; } isParameter() { this.parseTypeDefinition(); if (this.token() !== 113 /* Identifier */) { return false; } return true; } isStartOfExpression() { if (this.isStartOfLeftHandSideExpression()) { return true; } switch (this.token()) { case 20 /* PlusToken */: case 21 /* MinusToken */: case 33 /* TildeToken */: case 32 /* ExclamationToken */: case 25 /* PlusPlusToken */: case 26 /* MinusMinusToken */: return true; default: // Error tolerance. If we see the start of some binary operator, we consider // that the start of an expression. That way we'll parse out a missing identifier, // give a good message about an identifier being missing, and then consume the // rest of the binary expression. if (this.isBinaryOperator()) { return true; } return false; } } isStartOfStatement() { switch (this.token()) { case 51 /* StructKeyword */: case 50 /* IncludeKeyword */: case 70 /* TypedefKeyword */: return false; } return true; } isStartOfVariableDeclaration() { return this.lookAhead(this.isVariableDeclaration.bind(this)); } isStartOfFunctionDeclaration() { return this.lookAhead(this.isFunctionDeclaration.bind(this)); } isStartOfRootStatement() { switch (this.token()) { case 65 /* IfKeyword */: case 62 /* DoKeyword */: case 64 /* WhileKeyword */: case 63 /* ForKeyword */: case 56 /* ContinueKeyword */: case 55 /* BreakKeyword */: case 58 /* ReturnKeyword */: case 57 /* BreakpointKeyword */: case 4 /* OpenBraceToken */: return false; } return true; } isStartOfTypeDefinition() { return utils_1.isKeywordTypeKind(this.token()) || this.token() === 113 /* Identifier */; } isStartOfParameter() { return this.lookAhead(this.isParameter.bind(this)); } parseLiteral(kind) { const node = this.createNode(kind, void 0, false); node.end = this.scanner.getCurrentPos(); node.value = this.scanner.getTokenValue() || ''; node.text = this.scanner.getTokenText() || ''; if (this.parseExpected(kind, void 0, false)) { this.nextToken(); } return this.finishNode(node, node.end, false); } parseInclude() { const node = this.createNode(136 /* IncludeStatement */); this.parseExpected(50 /* IncludeKeyword */); node.path = this.parseLiteral(3 /* StringLiteral */); return this.finishNode(node); } parseIdentifier(alwaysAdvance = true) { const identifier = this.createNode(113 /* Identifier */); this.parseExpected(113 /* Identifier */, null, false); identifier.name = this.scanner.getTokenValue() || ''; if (alwaysAdvance || this.token() === 113 /* Identifier */) { this.nextToken(); } return this.finishNode(identifier); } parseExpectedIdentifier() { return this.parseIdentifier(false); } parseTypeDefinition() { let baseType; if (this.token() === 113 /* Identifier */) { baseType = this.parseIdentifier(); } else if (utils_1.isKeywordTypeKind(this.token())) { baseType = this.parseTokenNode(); } else { this.parseErrorAtCurrentToken('expected identifier or keyword'); baseType = this.createMissingNode(113 /* Identifier */); } if (utils_1.isReferenceKeywordKind(baseType.kind)) { if (this.token() === 13 /* LessThanToken */) { const mappedType = this.createNode(116 /* MappedType */, baseType.pos); mappedType.returnType = baseType; mappedType.typeArguments = this.parseBracketedList(4 /* TypeArguments */, this.parseTypeDefinition.bind(this), 13 /* LessThanToken */, 14 /* GreaterThanToken */); baseType = this.finishNode(mappedType); } } while (this.token() === 8 /* OpenBracketToken */) { let arrayType = this.createNode(117 /* ArrayType */, baseType.pos); this.parseExpected(8 /* OpenBracketToken */); arrayType.size = this.parseExpectedExpression(); arrayType.elementType = baseType; this.parseExpected(9 /* CloseBracketToken */); baseType = this.finishNode(arrayType); } return baseType; } parseParameter() { const param = this.createNode(143 /* ParameterDeclaration */); param.type = this.parseTypeDefinition(); param.name = this.parseIdentifier(); return this.finishNode(param); } parsePropertyDeclaration() { const property = this.createNode(144 /* PropertyDeclaration */); property.type = this.parseTypeDefinition(); property.name = this.parseExpectedIdentifier(); this.parseExpected(11 /* SemicolonToken */); return this.finishNode(property); } parseStructDeclaration() { const node = this.createNode(140 /* StructDeclaration */); this.parseExpected(51 /* StructKeyword */); node.name = this.parseIdentifier(); this.parseExpected(4 /* OpenBraceToken */); node.members = this.parseList(2 /* StructMembers */, this.parsePropertyDeclaration.bind(this)); this.parseExpected(5 /* CloseBraceToken */); this.parseExpected(11 /* SemicolonToken */); return this.finishNode(node); } parseModifiers() { let mods = this.createNodeArray(); while (utils_1.isModifierKind(this.token())) { mods.push(this.parseTokenNode()); } mods.end = this.scanner.getTokenPos(); return mods; } parseFunctionDeclaration() { const func = this.createNode(142 /* FunctionDeclaration */); func.modifiers = this.parseModifiers(); func.type = this.parseTypeDefinition(); func.name = this.parseIdentifier(); func.parameters = this.parseBracketedList(3 /* Parameters */, this.parseParameter.bind(this), 6 /* OpenParenToken */, 7 /* CloseParenToken */); if (this.token() === 4 /* OpenBraceToken */) { func.body = this.parseBlock(true); } else { this.parseExpected(11 /* SemicolonToken */); } return this.finishNode(func); } parseVariableDeclaration() { const variable = this.createNode(141 /* VariableDeclaration */); variable.modifiers = this.parseModifiers(); variable.type = this.parseTypeDefinition(); variable.name = this.parseIdentifier(); if (this.token() === 39 /* EqualsToken */) { this.parseExpected(39 /* EqualsToken */); variable.initializer = this.parseBinaryExpressionOrHigher(0); } this.parseExpected(11 /* SemicolonToken */); return this.finishNode(variable); } parseBlock(allowVarDeclarations = false) { if (this.parseExpected(4 /* OpenBraceToken */, null, false)) { const node = this.createNode(128 /* Block */); this.parseExpected(4 /* OpenBraceToken */); node.statements = this.parseList(1 /* BlockStatements */, () => { const child = this.parseStatement(); if (child.kind === 141 /* VariableDeclaration */) { if (!allowVarDeclarations) { this.parseErrorAtPosition(child.pos, child.end - child.pos, 'Local variables must be declared at the begining of function block'); } } else { allowVarDeclarations = false; } return child; }); this.parseExpected(5 /* CloseBraceToken */); return this.finishNode(node); } else { return this.createMissingNode(128 /* Block */); } } isUpdateExpression() { // This function is called inside parseUnaryExpression to decide // whether to call parseSimpleUnaryExpression or call parseUpdateExpression directly switch (this.token()) { case 20 /* PlusToken */: case 21 /* MinusToken */: case 33 /* TildeToken */: case 32 /* ExclamationToken */: return false; default: return true; } } isStartOfLeftHandSideExpression() { switch (this.token()) { case 69 /* NullKeyword */: case 67 /* TrueKeyword */: case 68 /* FalseKeyword */: case 2 /* NumericLiteral */: case 3 /* StringLiteral */: case 6 /* OpenParenToken */: case 113 /* Identifier */: return true; default: return false; } } makeBinaryExpression(left, operatorToken, right) { const node = this.createNode(124 /* BinaryExpression */, left.pos); node.left = left; node.operatorToken = operatorToken; node.right = right; return this.finishNode(node); } isBinaryOperator() { return this.getBinaryOperatorPrecedence() > 0; } getBinaryOperatorPrecedence() { switch (this.token()) { case 35 /* BarBarToken */: return 1; case 34 /* AmpersandAmpersandToken */: return 2; case 30 /* BarToken */: return 3; case 31 /* CaretToken */: return 4; case 29 /* AmpersandToken */: return 5; case 17 /* EqualsEqualsToken */: case 18 /* ExclamationEqualsToken */: return 6; case 13 /* LessThanToken */: case 14 /* GreaterThanToken */: case 15 /* LessThanEqualsToken */: case 16 /* GreaterThanEqualsToken */: return 7; case 27 /* LessThanLessThanToken */: case 28 /* GreaterThanGreaterThanToken */: return 8; case 20 /* PlusToken */: case 21 /* MinusToken */: return 9; case 22 /* AsteriskToken */: case 23 /* SlashToken */: case 24 /* PercentToken */: return 10; } // -1 is lower than all other precedences. Returning it will cause binary expression // parsing to stop. return -1; } parsePrimaryExpression() { switch (this.token()) { case 2 /* NumericLiteral */: case 3 /* StringLiteral */: return this.parseLiteral(this.token()); case 69 /* NullKeyword */: case 67 /* TrueKeyword */: case 68 /* FalseKeyword */: return this.parseTokenNode(); case 6 /* OpenParenToken */: return this.parseParenthesizedExpression(); case 113 /* Identifier */: return this.parseIdentifier(); } this.parseErrorAtCurrentToken(`Invalid expression`); return this.createNode(0 /* Unknown */, undefined, false); } parseParenthesizedExpression() { const node = this.createNode(126 /* ParenthesizedExpression */); this.parseExpected(6 /* OpenParenToken */); node.expression = this.parseExpectedExpression(); this.parseExpected(7 /* CloseParenToken */); return this.finishNode(node); } parseMemberExpressionOrHigher() { const expression = this.parsePrimaryExpression(); return this.parseMemberExpressionRest(expression); } parseMemberExpressionRest(expression) { while (true) { if (this.token() === 10 /* DotToken */) { const propertyAccess = this.createNode(120 /* PropertyAccessExpression */, expression.pos); this.parseExpected(10 /* DotToken */); propertyAccess.expression = expression; propertyAccess.name = this.parseExpectedIdentifier(); expression = this.finishNode(propertyAccess); continue; } if (this.token() === 8 /* OpenBracketToken */) { const indexedAccess = this.createNode(119 /* ElementAccessExpression */, expression.pos); this.parseExpected(8 /* OpenBracketToken */); indexedAccess.expression = expression; indexedAccess.argumentExpression = this.parseExpectedExpression(); this.parseExpected(9 /* CloseBracketToken */); expression = this.finishNode(indexedAccess); continue; } return expression; } } parseCallExpressionRest(expression) { while (true) { expression = this.parseMemberExpressionRest(expression); if (this.token() === 6 /* OpenParenToken */) { const callExpr = this.createNode(121 /* CallExpression */, expression.pos); callExpr.expression = expression; this.parseExpected(6 /* OpenParenToken */); callExpr.arguments = this.parseDelimitedList(5 /* ArgumentExpressions */, this.parseExpression.bind(this)); this.parseExpected(7 /* CloseParenToken */); expression = this.finishNode(callExpr); continue; } return expression; } } parseLeftHandSideExpressionOrHigher() { let expression; expression = this.parseMemberExpressionOrHigher(); return this.parseCallExpressionRest(expression); } parseUpdateExpression() { if (this.token() === 25 /* PlusPlusToken */ || this.token() === 26 /* MinusMinusToken */) { this.parseErrorAtCurrentToken('unary increment operators not allowed'); const node = this.createNode(122 /* PrefixUnaryExpression */); node.operator = this.parseTokenNode(); node.operand = this.parseLeftHandSideExpressionOrHigher(); return this.finishNode(node); } const expression = this.parseLeftHandSideExpressionOrHigher(); if ((this.token() === 25 /* PlusPlusToken */ || this.token() === 26 /* MinusMinusToken */)) { this.parseErrorAtCurrentToken('unary increment operators not supported'); const node = this.createNode(123 /* PostfixUnaryExpression */, expression.pos); node.operand = expression; node.operator = this.parseTokenNode(); return this.finishNode(node); } return expression; } parsePrefixUnaryExpression() { const node = this.createNode(122 /* PrefixUnaryExpression */); node.operator = this.parseTokenNode(); node.operand = this.parseSimpleUnaryExpression(); return this.finishNode(node); } parseSimpleUnaryExpression() { switch (this.token()) { case 20 /* PlusToken */: case 21 /* MinusToken */: case 33 /* TildeToken */: case 32 /* ExclamationToken */: return this.parsePrefixUnaryExpression(); default: return this.parseUpdateExpression(); } } parseUnaryExpressionOrHigher() { /** * UpdateExpression: * 1) LeftHandSideExpression * 2) LeftHandSideExpression++ * 3) LeftHandSideExpression-- * 4) ++UnaryExpression * 5) --UnaryExpression */ if (this.isUpdateExpression()) { return this.parseUpdateExpression(); } /** * UnaryExpression: * 1) UpdateExpression * 2) + UpdateExpression * 3) - UpdateExpression * 4) ~ UpdateExpression * 5) ! UpdateExpression */ return this.parseSimpleUnaryExpression(); } parseBinaryExpressionOrHigher(precedence) { const leftOperand = this.parseUnaryExpressionOrHigher(); return this.parseBinaryExpressionRest(precedence, leftOperand); } parseBinaryExpressionRest(precedence, leftOperand) { while (true) { const newPrecedence = this.getBinaryOperatorPrecedence(); // Check the precedence to see if we should "take" this operator // - For left associative operator, consume the operator, // recursively call the function below, and parse binaryExpression as a rightOperand // of the caller if the new precedence of the operator is greater then or equal to the current precedence. // For example: // a - b - c; // ^token; leftOperand = b. Return b to the caller as a rightOperand // a * b - c // ^token; leftOperand = b. Return b to the caller as a rightOperand // a - b * c; // ^token; leftOperand = b. Return b * c to the caller as a rightOperand const consumeCurrentOperator = newPrecedence > precedence; if (!consumeCurrentOperator) { break; } leftOperand = this.makeBinaryExpression(leftOperand, this.parseTokenNode(), this.parseBinaryExpressionOrHigher(newPrecedence)); } return leftOperand; } parseAssignmentExpressionOrHigher() { let expr = this.parseBinaryExpressionOrHigher(0); if (utils_1.isLeftHandSideExpression(expr) && utils_1.isAssignmentOperator(this.token())) { // multiple assigments in single statement is not allowed // return this.makeBinaryExpression(expr, <Types.BinaryOperatorToken>this.parseTokenNode(), this.parseAssignmentExpressionOrHigher()); return this.makeBinaryExpression(expr, this.parseTokenNode(), this.parseBinaryExpressionOrHigher(0)); } return expr; } parseExpression(allowAssignment = false) { const expr = this.parseAssignmentExpressionOrHigher(); if (!allowAssignment && utils_1.isAssignmentExpression(expr)) { this.parseErrorAtPosition(expr.pos, expr.end - expr.pos, `Assignment expression not allowed in this context`); } return expr; } parseExpectedExpression(allowAssignment = false) { if (this.isStartOfExpression()) { return this.parseExpression(allowAssignment); } else { this.parseErrorAtCurrentToken('Expected expression'); return this.createNode(0 /* Unknown */, undefined, false); } } parseTypedefDeclaration() { const node = this.createNode(145 /* TypedefDeclaration */); this.parseExpected(70 /* TypedefKeyword */); node.type = this.parseTypeDefinition(); node.name = this.parseIdentifier(); return this.finishNode(node); } parseReturnStatement() { const node = this.createNode(137 /* ReturnStatement */); this.parseExpected(58 /* ReturnKeyword */); if (this.token() !== 11 /* SemicolonToken */) { node.expression = this.parseExpectedExpression(); } this.parseExpected(11 /* SemicolonToken */); return this.finishNode(node); } parseBreakOrContinueStatement(kind) { const node = this.createNode(kind); this.parseExpected(kind === 133 /* BreakStatement */ ? 55 /* BreakKeyword */ : 56 /* ContinueKeyword */); this.parseExpected(11 /* SemicolonToken */); return this.finishNode(node); } parseBreakpointStatement() { const node = this.createNode(135 /* BreakpointStatement */); this.parseExpected(57 /* BreakpointKeyword */); this.parseExpected(11 /* SemicolonToken */); return this.finishNode(node); } parseExpressionStatement() { const node = this.createNode(138 /* ExpressionStatement */); node.expression = this.parseAssignmentExpressionOrHigher(); this.parseExpected(11 /* SemicolonToken */); this.finishNode(node); switch (node.expression.kind) { case 121 /* CallExpression */: break; case 124 /* BinaryExpression */: if (utils_1.isAssignmentOperator(node.expression.operatorToken.kind)) break; /* falls through */ default: this.parseErrorAtPosition(node.pos, node.end - node.pos, 'Statement has no effect'); } return node; } parseEmptyStatement() { const node = this.createNode(139 /* EmptyStatement */); this.parseExpected(11 /* SemicolonToken */); return this.finishNode(node); } parseIfStatement() { const node = this.createNode(129 /* IfStatement */); this.parseExpected(65 /* IfKeyword */); this.parseExpected(6 /* OpenParenToken */); node.expression = this.parseExpectedExpression(); this.parseExpected(7 /* CloseParenToken */); node.thenStatement = this.parseBlock(); if (this.parseOptional(66 /* ElseKeyword */)) { node.elseStatement = this.token() === 65 /* IfKeyword */ ? this.parseIfStatement() : this.parseBlock(); } return this.finishNode(node); } parseDoStatement() { const node = this.createNode(130 /* DoStatement */); this.parseExpected(62 /* DoKeyword */); node.statement = this.parseBlock(); this.parseExpected(64 /* WhileKeyword */); this.parseExpected(6 /* OpenParenToken */); node.expression = this.parseExpectedExpression(); this.parseExpected(7 /* CloseParenToken */); this.parseExpected(11 /* SemicolonToken */); return this.finishNode(node); } parseWhileStatement() { const node = this.createNode(131 /* WhileStatement */); this.parseExpected(64 /* WhileKeyword */); this.parseExpected(6 /* OpenParenToken */); node.expression = this.parseExpectedExpression(); this.parseExpected(7 /* CloseParenToken */); node.statement = this.parseBlock(); return this.finishNode(node); } parseForStatement() { const node = this.createNode(132 /* ForStatement */); this.parseExpected(63 /* ForKeyword */); this.parseExpected(6 /* OpenParenToken */); if (this.token() !== 11 /* SemicolonToken */ && this.token() !== 7 /* CloseParenToken */) { node.initializer = this.parseExpectedExpression(true); } this.parseExpected(11 /* SemicolonToken */); if (this.token() !== 11 /* SemicolonToken */ && this.token() !== 7 /* CloseParenToken */) { node.condition = this.parseExpectedExpression(); } this.parseExpected(11 /* SemicolonToken */); if (this.token() !== 7 /* CloseParenToken */) { node.incrementor = this.parseExpectedExpression(true); } this.parseExpected(7 /* CloseParenToken */); node.statement = this.parseBlock(); return this.finishNode(node); } parseStatement() { switch (this.token()) { case 11 /* SemicolonToken */: return this.parseEmptyStatement(); case 50 /* IncludeKeyword */: return this.parseInclude(); case 51 /* StructKeyword */: return this.parseStructDeclaration(); case 65 /* IfKeyword */: return this.parseIfStatement(); case 62 /* DoKeyword */: return this.parseDoStatement(); case 64 /* WhileKeyword */: return this.parseWhileStatement(); case 63 /* ForKeyword */: return this.parseForStatement(); case 56 /* ContinueKeyword */: return this.parseBreakOrContinueStatement(134 /* ContinueStatement */); case 55 /* BreakKeyword */: return this.parseBreakOrContinueStatement(133 /* BreakStatement */); case 57 /* BreakpointKeyword */: return this.parseBreakpointStatement(); case 58 /* ReturnKeyword */: return this.parseReturnStatement(); case 70 /* TypedefKeyword */: return this.parseTypedefDeclaration(); case 4 /* OpenBraceToken */: return this.parseBlock(false); case 113 /* Identifier */: case 53 /* ConstKeyword */: case 52 /* StaticKeyword */: case 54 /* NativeKeyword */: case 77 /* AbilcmdKeyword */: case 78 /* ActorKeyword */: case 79 /* ActorscopeKeyword */: case 80 /* AifilterKeyword */: case 81 /* BankKeyword */: case 82 /* BitmaskKeyword */: case 71 /* BoolKeyword */: case 72 /* ByteKeyword */: case 83 /* CamerainfoKeyword */: case 73 /* CharKeyword */: case 84 /* ColorKeyword */: case 86 /* DoodadKeyword */: case 85 /* DatetimeKeyword */: case 75 /* FixedKeyword */: case 87 /* HandleKeyword */: case 88 /* GenerichandleKeyword */: case 89 /* EffecthistoryKeyword */: case 74 /* IntKeyword */: case 90 /* MarkerKeyword */: case 91 /* OrderKeyword */: case 92 /* PlayergroupKeyword */: case 93 /* PointKeyword */: case 94 /* RegionKeyword */: case 95 /* RevealerKeyword */: case 96 /* SoundKeyword */: case 97 /* SoundlinkKeyword */: case 76 /* StringKeyword */: case 98 /* TextKeyword */: case 99 /* TimerKeyword */: case 100 /* TransmissionsourceKeyword */: case 101 /* TriggerKeyword */: case 102 /* UnitKeyword */: case 103 /* UnitfilterKeyword */: case 104 /* UnitgroupKeyword */: case 105 /* UnitrefKeyword */: case 106 /* VoidKeyword */: case 107 /* WaveKeyword */: case 108 /* WaveinfoKeyword */: case 109 /* WavetargetKeyword */: case 110 /* ArrayrefKeyword */: case 111 /* StructrefKeyword */: case 112 /* FuncrefKeyword */: if (!(this.parsingContext & (1 << 1 /* BlockStatements */)) && this.isStartOfFunctionDeclaration()) { return this.parseFunctionDeclaration(); } else if (this.isStartOfVariableDeclaration()) { return this.parseVariableDeclaration(); } else if ((this.parsingContext & (1 << 0 /* SourceElements */) || this.parsingContext & (1 << 1 /* BlockStatements */) || this.parsingContext & (1 << 4 /* TypeArguments */) || this.parsingContext & (1 << 5 /* ArgumentExpressions */)) && this.isStartOfExpression()) { return this.parseExpressionStatement(); } /* falls through */ default: this.parseErrorAtCurrentToken(`Expected declaration or statement, found ${utils_1.getKindName(this.token())}`); const node = this.createMissingNode(138 /* ExpressionStatement */, false); this.nextToken(); return node; } } setText(text) { this.scanner.setText(text); } parseFile(fileName, text) { this.scanner.setText(text); this.syntaxTokens = []; this.sourceFile = this.createNode(127 /* SourceFile */, 0); this.sourceFile.commentsLineMap = new Map(); this.sourceFile.parseDiagnostics = []; this.sourceFile.bindDiagnostics = []; this.sourceFile.additionalSyntacticDiagnostics = []; this.sourceFile.fileName = fileName; this.nextToken(); this.sourceFile.statements = this.parseList(0 /* SourceElements */, this.parseStatement.bind(this)); this.finishNode(this.sourceFile); this.sourceFile.lineMap = this.scanner.getLineMap(); this.sourceFile.text = text; utils_1.fixupParentReferences(this.sourceFile); return this.sourceFile; } } exports.Parser = Parser; //# sourceMappingURL=parser.js.map