AstNode* Parser::parseStatement() { if (isKeyword(currentTokenValue())) { if (currentTokenValue() == "function") { return parseFunction(); } else if (currentTokenValue() == "for") { return parseFor(); } else if (currentTokenValue() == "if") { return parseIf(); } else if (currentTokenValue() == "while") { return parseWhile(); } else if (currentTokenValue() == "print") { return parsePrint(); } else if (currentTokenValue() == "int") { return parseDeclaration(VT_INT); } else if (currentTokenValue() == "double") { return parseDeclaration(VT_DOUBLE); } else if (currentTokenValue() == "string") { return parseDeclaration(VT_STRING); } else if (currentTokenValue() == "return") { return parseReturn(); } else { cout << "unhandled keyword " << currentTokenValue() << endl; assert(false); return 0; } } if (currentToken() == tLBRACE) { return parseBlock(true); } if ((currentToken() == tIDENT) && isAssignment(lookaheadToken(1))) { return parseAssignment(); } return parseExpression(); }
ast::Expr *parseIdentifier(TokenIt &it, const TokenIt &end) { Token *current = currentToken(it, end); EXPECT_ANY_TOKEN(current, errorE, "Identifier expected"); std::string identifier = current->value; current = nextToken(it, end); if(!current || current->type != Token::LPAREN) return new ast::Identifier(identifier); current = nextToken(it, end); EXPECT_ANY_TOKEN(current, errorE, "Expression or ')' expected"); std::vector<ast::Expr *> args; if(current->type != Token::RPAREN) while(1) { ast::Expr *arg = parseExpr(it, end); if(!arg) return NULL; args.push_back(arg); current = currentToken(it, end); EXPECT_ANY_TOKEN(current, errorE, "Expected ')' or ','"); if(current->type == Token::RPAREN) break; else if(current->type != Token::COMMA) return errorE("Expected ')' or ','"); current = nextToken(it, end); } nextToken(it, end); return new ast::CallExpr(identifier, args); }
ast::Expr *parseBinOpRHS(int exprPrecendence, ast::Expr *lhs, TokenIt &it, const TokenIt &end) { while(1) { Token *current = currentToken(it, end); char binOp = current ? current->value.c_str()[0] : 0; int precendence = opPrecedence(binOp); if(precendence < exprPrecendence) return lhs; nextToken(it, end); ast::Expr *rhs = parsePrimary(it, end); if(!rhs) return NULL; current = currentToken(it, end); int nextPrecedence = opPrecedence(current ? current->value.c_str()[0] : 0); if(precendence < nextPrecedence) { rhs = parseBinOpRHS(precendence + 1, rhs, it, end); if(!rhs) return NULL; } lhs = new ast::BinaryExpr(binOp, lhs, rhs); } }
StoreNode* Parser::parseAssignment() { assert(currentToken() == tIDENT); AstVar* lhs = _currentScope->lookupVariable(currentTokenValue()); if (lhs == 0) { error("undeclared variable: %s", currentTokenValue().c_str()); } consumeToken(); TokenKind op = currentToken(); if (op == tASSIGN || op == tINCRSET || op == tDECRSET) { consumeToken(); } else { error("assignment expected"); } StoreNode* result = new StoreNode(_currentTokenIndex, lhs, parseExpression(), op); if (currentToken() == tSEMICOLON) { consumeToken(); } return result; }
bool WebSocketExtensionParser::parseExtension(String& extensionToken, HashMap<String, String>& extensionParameters) { // Parse extension-token. if (!consumeToken()) return false; extensionToken = currentToken(); // Parse extension-parameters if exists. while (consumeCharacter(';')) { if (!consumeToken()) return false; String parameterToken = currentToken(); if (consumeCharacter('=')) { if (consumeQuotedStringOrToken()) extensionParameters.add(parameterToken, currentToken()); else return false; } else extensionParameters.add(parameterToken, String()); } if (!finished() && !consumeCharacter(',')) return false; return true; }
PHPEntityBase::Ptr_t PHPExpression::Resolve(PHPLookupTable& lookpTable, const wxString& sourceFileName) { if(m_expression.empty()) return PHPEntityBase::Ptr_t(NULL); PHPSourceFile source(m_text); source.SetParseFunctionBody(true); source.SetFilename(sourceFileName); source.Parse(); wxString asString = SimplifyExpression(source, 0); // Now, use the lookup table std::list<PHPExpression::Part>::iterator iter = m_parts.begin(); PHPEntityBase::Ptr_t currentToken(NULL); for(; iter != m_parts.end(); ++iter) { if(!currentToken) { // first token currentToken = lookpTable.FindScope(iter->m_text); } else { // load the children of the current token (optionally, filter by the text) currentToken = lookpTable.FindMemberOf(currentToken->GetDbId(), iter->m_text); } if(!currentToken) { // return NULL return currentToken; } } return currentToken; }
AstNode* Parser::parseBinary(int minPrecedence) { AstNode* left = parseUnary(); int precedence = tokenPrecedence(currentToken()); while (precedence >= minPrecedence) { while (tokenPrecedence(currentToken()) == precedence) { TokenKind op = currentToken(); uint32_t op_pos = _currentTokenIndex; consumeToken(); AstNode* right = parseBinary(precedence + 1); left = new BinaryOpNode(op_pos, op, left, right); } precedence--; } return left; }
string Parser::currentTokenValue() { TokenKind kind = currentToken(); if (kind == tIDENT || kind == tDOUBLE || kind == tINT || kind == tSTRING) { return _tokens.valueAt(_currentTokenIndex); } return ""; }
PrintNode* Parser::parsePrint() { uint32_t token = _currentToken; ensureKeyword("print"); ensureToken(tLPAREN); PrintNode* result = new PrintNode(token); while (currentToken() != tRPAREN) { AstNode* operand = parseExpression(); result->add(operand); if (currentToken() == tCOMMA) { consumeToken(); } } ensureToken(tRPAREN); ensureToken(tSEMICOLON); return result; }
bool UserGestureIndicator::processingUserGesture() { if (auto* token = currentToken()) { ASSERT(isMainThread()); return token->hasGestures(); } return false; }
ReturnNode* Parser::parseReturn() { uint32_t token = _currentTokenIndex; ensureKeyword("return"); AstNode* returnExpr = 0; if (currentToken() != tSEMICOLON) { returnExpr = parseExpression(); } return new ReturnNode(token, returnExpr); }
// static bool UserGestureIndicator::consumeUserGesture() { if (auto* token = currentToken()) { ASSERT(isMainThread()); if (token->consumeGesture()) { token->userGestureUtilized(); return true; } } return false; }
CallNode* Parser::parseCall() { uint32_t tokenIndex = _currentTokenIndex; assert(currentToken() == tIDENT); const string& callee = currentTokenValue(); consumeToken(); ensureToken(tLPAREN); vector<AstNode*> args; while (currentToken() != tRPAREN) { args.push_back(parseExpression()); if (currentToken() == tCOMMA) { consumeToken(); } else { break; } } ensureToken(tRPAREN); return new CallNode(tokenIndex, callee, args); }
ast::Expr *parseParenExpr(TokenIt &it, const TokenIt &end) { Token *current = nextToken(it, end); ast::Expr *node = parseExpr(it, end); current = currentToken(it, end); EXPECT_TOKEN_E(current, Token::RPAREN, "Expected ')'"); nextToken(it, end); return node; }
ast::Node *parseStatement(TokenIt &it, const TokenIt &end) { Token *current = currentToken(it, end); EXPECT_ANY_TOKEN(current, errorN, "Statement expected"); switch(current->type) { case Token::DIM: return parseDim(it, end); default: return parseExpr(it, end); } }
ast::Expr *parsePrimary(TokenIt &it, const TokenIt &end) { Token *current = currentToken(it, end); EXPECT_ANY_TOKEN(current, errorE, "Expected a primary expression"); switch(current->type) { case Token::IDENTIFIER: return parseIdentifier(it, end); case Token::NUMBER: return parseLiteral(it, end); case Token::LPAREN: return parseParenExpr(it, end); default: return errorE("Unexpected token when expecting an expression"); } }
AstNode* Parser::parseUnary() { if (isUnaryOp(currentToken())) { TokenKind op = currentToken(); consumeToken(); return new UnaryOpNode(_currentTokenIndex, op, parseUnary()); } else if (currentToken() == tIDENT && lookaheadToken(1) == tLPAREN) { AstNode* expr = parseCall(); return expr; } else if (currentToken() == tIDENT) { AstVar* var = _currentScope->lookupVariable(currentTokenValue()); if (var == 0) { error("undeclared variable: %s", currentTokenValue().c_str()); } LoadNode* result = new LoadNode(_currentTokenIndex, var); consumeToken(); return result; } else if (currentToken() == tDOUBLE) { DoubleLiteralNode* result = new DoubleLiteralNode(_currentTokenIndex, parseDouble(currentTokenValue())); consumeToken(); return result; } else if (currentToken() == tINT) { IntLiteralNode* result = new IntLiteralNode(_currentTokenIndex, parseInt(currentTokenValue())); consumeToken(); return result; } else if (currentToken() == tSTRING) { StringLiteralNode* result = new StringLiteralNode(_currentTokenIndex, currentTokenValue()); consumeToken(); return result; } else if (currentToken() == tLPAREN) { consumeToken(); AstNode* expr = parseExpression(); ensureToken(tRPAREN); return expr; } else { error("Unexpected token: %s", tokenStr(currentToken())); return 0; } }
ast::Node *parseFunction(TokenIt &it, const TokenIt &end, bool sub) { Token *current = nextToken(it, end); EXPECT_TOKEN_N(current, Token::IDENTIFIER, "Expected function identifier"); std::string name = current->value; current = nextToken(it, end); EXPECT_TOKEN_N(current, Token::LPAREN, "Expected '('"); std::vector<std::string> args; while(1) { current = nextToken(it, end); if(current && current->type == Token::IDENTIFIER) { args.push_back(current->value); current = nextToken(it, end); if(current->type == Token::RPAREN) break; EXPECT_TOKEN_N(current, Token::COMMA, "Expected ',' in arg list"); } else break; } EXPECT_TOKEN_N(current, Token::RPAREN, "Expected ')'"); current = nextToken(it, end); std::vector<ast::Node *> body; while(1) { current = currentToken(it, end); EXPECT_ANY_TOKEN(current, errorN, "Statement or function end expected"); if(current->type == Token::END) break; if(ast::Node *stmt = parseStatement(it, end)) body.push_back(stmt); else return errorN("Statement or function end expected"); } current = nextToken(it, end); EXPECT_TOKEN_N(current, (sub ? Token::SUB : Token::FUNCTION), "Function end expected"); nextToken(it, end); return new ast::Function(name, args, body, sub); }
BlockNode* Parser::parseBlock(bool needBraces, const bool scoped/* = true */) { if (needBraces) { ensureToken(tLBRACE); } if (scoped) { pushScope(); } BlockNode* block = new BlockNode(_currentTokenIndex, _currentScope); TokenKind sentinel = needBraces ? tRBRACE : tEOF; while (currentToken() != sentinel) { if (currentToken() == tSEMICOLON) { consumeToken(); continue; } AstNode* statement = parseStatement(); // Ignore statements that doesn't result in AST nodes, such // as variable or function declaration. if (statement != 0) { block->add(statement); } } if (scoped) { popScope(); } if (needBraces) { ensureToken(tRBRACE); } return block; }
CommandArg parseCommandArg(int allowRecursive) { CommandArg res; if (currentToken().type == TT_BRACKET_OPEN) { if (allowRecursive) { nextToken(); CommandArg *inner = (CommandArg*)ALLOCATE(sizeof(CommandArg) ); *inner = parseCommandArg(0); res.type = AT_ADDRESS; res.argv.addr = inner; if (currentToken().type != TT_BRACKET_CLOSE) { error(ET_EXPECTED_BRACKET_CLOSE); } } else { error(ET_UNEXPECTED_TOKEN); } } else if (currentToken().type == TT_NUMBER) { res.type = AT_CONST; res.argv._const = currentToken().value._int; } else if (currentToken().type == TT_WORD) { int reg; if ( (reg = findRegister(currentToken().value.str) ) != -1) { res.type = AT_REGISTER; res.argv.r = reg; } else { res.type = AT_LABEL; res.argv.label = currentToken().value.str; } } nextToken(); return res; }
ForNode* Parser::parseFor() { uint32_t token = _currentTokenIndex; ensureKeyword("for"); ensureToken(tLPAREN); if (currentToken() != tIDENT) { error("identifier expected"); } const string& varName = currentTokenValue(); consumeToken(); ensureKeyword("in"); AstNode* inExpr = parseExpression(); ensureToken(tRPAREN); BlockNode* forBody = parseBlock(true); AstVar* forVar = forBody->scope()->lookupVariable(varName); return new ForNode(token, forVar, inExpr, forBody); }
void Parser::ensureKeyword(const string& keyword) { if (currentToken() != tIDENT || currentTokenValue() != keyword) { error("keyword '%s' expected", currentTokenValue().c_str()); } consumeToken(); }
FunctionNode* Parser::parseFunction() { uint32_t tokenIndex = _currentTokenIndex; ensureKeyword("function"); if (currentToken() != tIDENT) { error("identifier expected"); } const string& returnTypeName = currentTokenValue(); VarType returnType = nameToType(returnTypeName); if (returnType == VT_INVALID) { error("wrong return type"); } consumeToken(); if (currentToken() != tIDENT) { error("name expected"); } const string& name = currentTokenValue(); consumeToken(); Signature signature; signature.push_back(SignatureElement(returnType, "return")); ensureToken(tLPAREN); while (currentToken() != tRPAREN) { const string& parameterTypeName = currentTokenValue(); VarType parameterType = nameToType(parameterTypeName); if (parameterType == VT_INVALID) { error("wrong parameter type"); } consumeToken(); const string& parameterName = currentTokenValue(); if (currentToken() != tIDENT) { error("identifier expected"); } consumeToken(); signature.push_back(SignatureElement(parameterType, parameterName)); if (currentToken() == tCOMMA) { consumeToken(); } } ensureToken(tRPAREN); BlockNode* body = 0; pushScope(); for (uint32_t i = 1; i < signature.size(); i++) { const string& name = signature[i].second; VarType type = signature[i].first; if (!_currentScope->declareVariable(name, type)) { error("Formal \"%s\" already declared", name.c_str()); } } if ((currentToken() == tIDENT) && currentTokenValue() == "native") { consumeToken(); if (currentToken() != tSTRING) { error("Native name expected, got %s", tokenStr(currentToken())); } pushScope(); body = new BlockNode(_currentTokenIndex, _currentScope); body->add(new NativeCallNode(tokenIndex, currentTokenValue(), signature)); consumeToken(); ensureToken(tSEMICOLON); body->add(new ReturnNode(0, 0)); popScope(); } else { body = parseBlock(true, false); if (body->nodes() == 0 || !(body->nodeAt(body->nodes() - 1)->isReturnNode())) { body->add(new ReturnNode(0, defaultReturnExpr(returnType))); } } popScope(); if (_currentScope->lookupFunction(name) != 0) { error("Function %s already defined", name.c_str()); } FunctionNode* result = new FunctionNode(tokenIndex, name, signature, body); _currentScope->declareFunction(result); // We don't add function node into AST. return 0; }
PHPEntityBase::Ptr_t PHPExpression::Resolve(PHPLookupTable& lookpTable, const wxString& sourceFileName) { if(m_expression.empty()) return PHPEntityBase::Ptr_t(NULL); m_sourceFile.reset(new PHPSourceFile(m_text)); m_sourceFile->SetParseFunctionBody(true); m_sourceFile->SetFilename(sourceFileName); m_sourceFile->Parse(); if(m_expression.size() == 1 && m_expression.at(0).type == kPHP_T_NS_SEPARATOR) { // user typed '\' return lookpTable.FindScope("\\"); } wxString asString = DoSimplifyExpression(0, m_sourceFile); wxUnusedVar(asString); if(m_parts.empty() && !m_filter.IsEmpty()) { // We have no expression, but the user did type something... // Return the parent scope of what the user typed so far if(m_filter.Contains("\\")) { // A namespace separator was found in the filter, break // the filter into 2: // scope + filter wxString scopePart = m_filter.BeforeLast('\\'); if(!scopePart.StartsWith("\\")) { scopePart.Prepend("\\"); } wxString filterPart = m_filter.AfterLast('\\'); // Adjust the filter m_filter.swap(filterPart); // Fix the scope part scopePart = m_sourceFile->MakeIdentifierAbsolute(scopePart); return lookpTable.FindScope(scopePart); } else { // No namespace separator was typed // try to guess: // if the m_filter contains "(" -> user wants a global functions // else we use the current file scope return lookpTable.FindScope(m_sourceFile->Namespace()->GetFullName()); } } // Now, use the lookup table std::list<PHPExpression::Part>::iterator iter = m_parts.begin(); PHPEntityBase::Ptr_t currentToken(NULL); PHPEntityBase::Ptr_t parentToken(NULL); for(; iter != m_parts.end(); ++iter) { Part& part = *iter; if(!currentToken) { // first token // Check locks first if(part.m_text.StartsWith("$") && m_sourceFile->CurrentScope()) { // a variable currentToken = m_sourceFile->CurrentScope()->FindChild(part.m_text); } if(!currentToken) { currentToken = lookpTable.FindScope(part.m_text); if(!currentToken) { // If we are inside a namespace, try prepending the namespace // to the first token if(m_sourceFile->Namespace() && m_sourceFile->Namespace()->GetFullName() != "\\") { // Not the global namespace wxString fullns = PHPEntityNamespace::BuildNamespace(m_sourceFile->Namespace()->GetFullName(), part.m_text); // Check if it exists PHPEntityBase::Ptr_t pGuess = lookpTable.FindScope(fullns); if(pGuess) { currentToken = pGuess; part.m_text = fullns; } else { // Maybe its a global function.. currentToken = lookpTable.FindFunction(part.m_text); } } else { // Maybe its a global function.. currentToken = lookpTable.FindFunction(part.m_text); } } } } else { // load the children of the current token (optionally, filter by the text) currentToken = lookpTable.FindMemberOf(currentToken->GetDbId(), part.m_text); if(currentToken && currentToken->Is(kEntityTypeFunctionAlias)) { // If the member is a function-alias, use the actual function instead currentToken = currentToken->Cast<PHPEntityFunctionAlias>()->GetFunc(); } } // If the current "part" of the expression ends with a scope resolving operator ("::") or // an object operator ("->") we need to resolve the operator to the actual type ( // incase of a functin it will be the return value, and in case of a variable it will be // the type hint) if(currentToken) { if(part.m_operator == kPHP_T_OBJECT_OPERATOR || part.m_operator == kPHP_T_PAAMAYIM_NEKUDOTAYIM) { wxString actualType; if(currentToken->Is(kEntityTypeFunction)) { // return the function return value actualType = currentToken->Cast<PHPEntityFunction>()->GetReturnValue(); } else if(currentToken->Is(kEntityTypeVariable)) { // return the type hint actualType = currentToken->Cast<PHPEntityVariable>()->GetTypeHint(); } wxString fixedpath; if(!actualType.IsEmpty() && FixReturnValueNamespace(lookpTable, parentToken, actualType, fixedpath)) { actualType.swap(fixedpath); } if(!actualType.IsEmpty()) { currentToken = lookpTable.FindScope(actualType); } } } if(!currentToken) { // return NULL return currentToken; } parentToken = currentToken; } return currentToken; }
Token::Ptr Tokenizer::nextToken () { __currentToken = extractToken (); return currentToken (); }
pStatement Statement::parse() { if (currentToken().type == TT_NAMESPACE) { nextToken(); _namespace = new_Namespace()->parse(); } else if(currentToken().type == TT_TYPEDEF) { } else if(currentToken().type == TT_STRUCT || currentToken().type == TT_CLASS) { isClass = true; if(currentToken().type == TT_STRUCT) { isStruct = true; } nextToken(); _namespace = new_Namespace(); _namespace->statementsBlock = new_StatementsBlock(); _namespace->id = currentToken().strVal; pType newType = pType(new Type(owner, false) ); pExprResult res = owner->new_ExprResult(); res->evalType = ERT_TYPE; res->type = newType; newType->definition = this; owner->parsingStatementsBlockStack.back()->vars[_namespace->id] = res; _namespace->parse(); if(currentToken().type != TT_SEMICOLON) { Parser::get()->parsingException(__FUNCTION__, __FILE__, __LINE__, currentToken(), "Expected semicolon"); } nextToken(); } else if (currentToken().type == TT_BRACE_OPEN) { this->sb = new_StatementsBlock()->parse(); } else if (currentToken().type == TT_BREAK || currentToken().type == TT_RETURN || currentToken().type == TT_ECHO || currentToken().type == TT_CONTINUE) { isSpecial = true; specialType = currentToken().type; nextToken(); if (specialType == TT_RETURN || specialType == TT_ECHO) { expr = new_Expr()->parse(); } if (currentToken().type != TT_SEMICOLON) { throwTokenExpected(TT_SEMICOLON); } nextToken(); } else if (currentToken().type == TT_IF) { isSpecial = true; specialType = currentToken().type; nextToken(); expr = new_Expr()->parse(); statement1 = new_Statement()->parse(); if (currentToken().type == TT_ELSE) { nextToken(); statement2 = new_Statement()->parse(); } } else if (currentToken().type == TT_WHILE) { localStatementsBlock = new_StatementsBlock(); owner->parsingStatementsBlockStack.push_back(localStatementsBlock); isSpecial = true; specialType = currentToken().type; if (nextToken().type != TT_PARENTHESIS_OPEN) { Parser::get()->parsingException(__FUNCTION__, __FILE__, __LINE__, currentToken() ); } nextToken(); expr = new_Expr()->parse(); if (currentToken().type != TT_PARENTHESIS_CLOSE) { Parser::get()->parsingException(__FUNCTION__, __FILE__, __LINE__, currentToken() ); } nextToken(); statement1 = new_Statement()->parse(); owner->parsingStatementsBlockStack.pop_back(); } else if (currentToken().type == TT_FOR) { localStatementsBlock = new_StatementsBlock(); owner->parsingStatementsBlockStack.push_back(localStatementsBlock); isSpecial = true; specialType = currentToken().type; if (nextToken().type != TT_PARENTHESIS_OPEN) { Parser::get()->parsingException(__FUNCTION__, __FILE__, __LINE__, currentToken() ); } nextToken(); statement1 = new_Statement()->parse(); expr = new_Expr()->parse(); if (currentToken().type != TT_SEMICOLON) { Parser::get()->parsingException(__FUNCTION__, __FILE__, __LINE__, currentToken() ); } nextToken(); expr2 = new_Expr()->parse(); if (currentToken().type != TT_PARENTHESIS_CLOSE) { Parser::get()->parsingException(__FUNCTION__, __FILE__, __LINE__, currentToken() ); } nextToken(); statement2 = new_Statement()->parse(); owner->parsingStatementsBlockStack.pop_back(); } else { tryParse([this]() { tn = new_Typename()->parse(); pVarDeclarationAllowDefault vd = new_VarDeclarationAllowDefault()->parse(); if (owner->currentToken().type == TT_PARENTHESIS_OPEN) { if (!vd->canBeFunctionDeclaration() ) { Parser::get()->parsingException(__FUNCTION__, __FILE__, __LINE__, currentToken() ); } nextToken(); this->function = pFunction(new Function(owner, vd->vd->id) ); this->function->args = new_FunctionArgs()->parse(); if (currentToken().type == TT_PARENTHESIS_CLOSE) { if (nextToken().type == ';') { this->function->statementsBlock = nullptr; } else { this->function->statementsBlock = new_StatementsBlock()->parse(); for (auto vd: this->function->args->vds) { this->function->statementsBlock->vars[vd->vd->id] = nullptr; } } } else { Parser::get()->parsingException(__FUNCTION__, __FILE__, __LINE__, currentToken(), "Unexpeted token"); } } else { if (currentToken().type == TT_COMMA || currentToken().type == TT_SEMICOLON) { vds.push_back(vd); } while (currentToken().type == TT_COMMA) { nextToken(); vds.push_back(new_VarDeclarationAllowDefault()->parse() ); } if (currentToken().type != TT_SEMICOLON) { Parser::get()->parsingException(__FUNCTION__, __FILE__, __LINE__, currentToken() ); } nextToken(); } }, [this](exception &e) { vds.clear(); tn = nullptr; expr = new_Expr()->parse(); this->function = nullptr; if (currentToken().type != TT_SEMICOLON) { Parser::get()->parsingException(__FUNCTION__, __FILE__, __LINE__, currentToken() ); } nextToken(); }); } return pStatement(this); }
Token *TokenManager::next(void) { this->idx++; return currentToken(); }
void Parser::ensureToken(TokenKind token) { if (currentToken() != token) { error("'%s' expected, seen %s", tokenStr(token), tokenStr(currentToken())); } consumeToken(); }
Token *TokenManager::back(void) { this->idx--; return currentToken(); }
Token *nextToken(TokenIt &it, const TokenIt &end) { it++; return currentToken(it, end); }