mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2025-01-31 09:57:14 +01:00
1038 lines
25 KiB
C++
1038 lines
25 KiB
C++
//
|
|
// Parser.cpp
|
|
//
|
|
// Library: CppParser
|
|
// Package: CppParser
|
|
// Module: Parser
|
|
//
|
|
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
|
|
// and Contributors.
|
|
//
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
//
|
|
|
|
|
|
#include "Poco/CppParser/Parser.h"
|
|
#include "Poco/CppParser/CppToken.h"
|
|
#include "Poco/CppParser/Decl.h"
|
|
#include "Poco/CppParser/Enum.h"
|
|
#include "Poco/CppParser/EnumValue.h"
|
|
#include "Poco/CppParser/Function.h"
|
|
#include "Poco/CppParser/NameSpace.h"
|
|
#include "Poco/CppParser/Parameter.h"
|
|
#include "Poco/CppParser/Struct.h"
|
|
#include "Poco/CppParser/TypeDef.h"
|
|
#include "Poco/CppParser/Variable.h"
|
|
#include "Poco/CppParser/AttributesParser.h"
|
|
#include "Poco/Path.h"
|
|
#include "Poco/String.h"
|
|
#include "Poco/NumberFormatter.h"
|
|
#include "Poco/Exception.h"
|
|
#include <sstream>
|
|
#include <cctype>
|
|
|
|
|
|
using Poco::Token;
|
|
using Poco::WhitespaceToken;
|
|
using Poco::Path;
|
|
using Poco::NumberFormatter;
|
|
using Poco::SyntaxException;
|
|
using Poco::icompare;
|
|
using Poco::trimInPlace;
|
|
|
|
|
|
namespace Poco {
|
|
namespace CppParser {
|
|
|
|
|
|
Parser::Parser(NameSpace::SymbolTable& gst, const std::string& file, std::istream& istr):
|
|
_gst(gst),
|
|
_istr(istr),
|
|
_tokenizer(_istr),
|
|
_file(file),
|
|
_inFile(false),
|
|
_pCurrentSymbol(0),
|
|
_access(Symbol::ACC_PUBLIC)
|
|
{
|
|
Path p(file);
|
|
p.makeAbsolute();
|
|
_path = p.toString();
|
|
_currentPath = _path;
|
|
|
|
_nsStack.push_back(NameSpace::root());
|
|
}
|
|
|
|
|
|
Parser::~Parser()
|
|
{
|
|
}
|
|
|
|
|
|
inline bool Parser::isIdentifier(const Token* pToken)
|
|
{
|
|
return pToken->is(Token::IDENTIFIER_TOKEN) || isOperator(pToken, OperatorToken::OP_DBL_COLON);
|
|
}
|
|
|
|
|
|
inline bool Parser::isOperator(const Token* pToken, int kind)
|
|
{
|
|
return pToken->is(Token::OPERATOR_TOKEN) && pToken->asInteger() == kind;
|
|
}
|
|
|
|
|
|
inline bool Parser::isKeyword(const Token* pToken, int kind)
|
|
{
|
|
return pToken->is(Token::KEYWORD_TOKEN) && pToken->asInteger() == kind;
|
|
}
|
|
|
|
|
|
inline bool Parser::isEOF(const Token* pToken)
|
|
{
|
|
return pToken->is(Token::EOF_TOKEN);
|
|
}
|
|
|
|
|
|
void Parser::expectOperator(const Token* pToken, int kind, const std::string& msg)
|
|
{
|
|
if (!isOperator(pToken, kind))
|
|
syntaxError(msg + ", found " + pToken->tokenString());
|
|
}
|
|
|
|
|
|
void Parser::syntaxError(const std::string& msg)
|
|
{
|
|
throw SyntaxException("Expected", msg);
|
|
}
|
|
|
|
|
|
inline void Parser::append(std::string& decl, const std::string& token)
|
|
{
|
|
if (!decl.empty())
|
|
{
|
|
char last = decl[decl.length() - 1];
|
|
if (token != "::" &&
|
|
token != "." &&
|
|
token != ")" &&
|
|
token != "->" &&
|
|
token != "," &&
|
|
token != "[" &&
|
|
token != "]" &&
|
|
last != '~' &&
|
|
last != ':' &&
|
|
last != '(' &&
|
|
last != ')' &&
|
|
last != '[' &&
|
|
last != ']' &&
|
|
last != ' '
|
|
)
|
|
decl.append(" ");
|
|
}
|
|
decl.append(token);
|
|
if (token == "const"
|
|
|| token == "constexpr"
|
|
|| token == "static"
|
|
|| token == "mutable"
|
|
|| token == "inline"
|
|
|| token == "volatile"
|
|
|| token == "register"
|
|
|| token == "thread_local")
|
|
decl.append(" ");
|
|
}
|
|
|
|
|
|
inline void Parser::append(std::string& decl, const Token* pToken)
|
|
{
|
|
poco_check_ptr (pToken);
|
|
append(decl, pToken->tokenString());
|
|
}
|
|
|
|
|
|
void Parser::parse()
|
|
{
|
|
try
|
|
{
|
|
const Token* pNext = next();
|
|
pNext = parseFile(pNext);
|
|
if (!isEOF(pNext))
|
|
syntaxError("Additional tokens behind supposed EOF");
|
|
}
|
|
catch (SyntaxException& exc)
|
|
{
|
|
std::string m(exc.message());
|
|
std::string where(_currentPath);
|
|
where.append("(");
|
|
where.append(NumberFormatter::format(_istr.getCurrentLineNumber()));
|
|
where.append(")");
|
|
throw SyntaxException(m, where);
|
|
}
|
|
}
|
|
|
|
|
|
const Token* Parser::parseFile(const Token* pNext)
|
|
{
|
|
while (pNext->is(Token::IDENTIFIER_TOKEN) || pNext->is(Token::KEYWORD_TOKEN))
|
|
{
|
|
switch (pNext->asInteger())
|
|
{
|
|
case IdentifierToken::KW_NAMESPACE:
|
|
pNext = parseNameSpace(pNext);
|
|
break;
|
|
case IdentifierToken::KW_STRUCT:
|
|
case IdentifierToken::KW_CLASS:
|
|
case IdentifierToken::KW_UNION:
|
|
pNext = parseClass(pNext);
|
|
break;
|
|
case IdentifierToken::KW_TEMPLATE:
|
|
pNext = parseTemplate(pNext);
|
|
break;
|
|
case IdentifierToken::KW_TYPEDEF:
|
|
pNext = parseTypeDef(pNext);
|
|
break;
|
|
case IdentifierToken::KW_USING:
|
|
pNext = parseUsing(pNext);
|
|
break;
|
|
case IdentifierToken::KW_ENUM:
|
|
pNext = parseEnum(pNext);
|
|
break;
|
|
default:
|
|
pNext = parseVarFunc(pNext);
|
|
}
|
|
}
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseNameSpace(const Token* pNext)
|
|
{
|
|
poco_assert (isKeyword(pNext, IdentifierToken::KW_NAMESPACE));
|
|
|
|
pNext = next();
|
|
if (pNext->is(Token::IDENTIFIER_TOKEN))
|
|
{
|
|
_access = Symbol::ACC_PUBLIC;
|
|
std::string name = pNext->tokenString();
|
|
pNext = next();
|
|
expectOperator(pNext, OperatorToken::OP_OPENBRACE, "{");
|
|
|
|
std::string fullName = currentNameSpace()->fullName();
|
|
if (!fullName.empty()) fullName += "::";
|
|
fullName += name;
|
|
|
|
NameSpace* pNS = dynamic_cast<NameSpace*>(currentNameSpace()->lookup(fullName));
|
|
bool undefined = (pNS == 0);
|
|
if (undefined) pNS = new NameSpace(name, currentNameSpace());
|
|
pushNameSpace(pNS, -1, undefined);
|
|
pNext = next();
|
|
while (pNext->is(Token::IDENTIFIER_TOKEN) || pNext->is(Token::KEYWORD_TOKEN))
|
|
{
|
|
switch (pNext->asInteger())
|
|
{
|
|
case IdentifierToken::KW_NAMESPACE:
|
|
pNext = parseNameSpace(pNext);
|
|
break;
|
|
case IdentifierToken::KW_STRUCT:
|
|
case IdentifierToken::KW_CLASS:
|
|
case IdentifierToken::KW_UNION:
|
|
pNext = parseClass(pNext);
|
|
break;
|
|
case IdentifierToken::KW_TEMPLATE:
|
|
pNext = parseTemplate(pNext);
|
|
break;
|
|
case IdentifierToken::KW_TYPEDEF:
|
|
pNext = parseTypeDef(pNext);
|
|
break;
|
|
case IdentifierToken::KW_USING:
|
|
pNext = parseUsing(pNext);
|
|
break;
|
|
case IdentifierToken::KW_ENUM:
|
|
pNext = parseEnum(pNext);
|
|
break;
|
|
default:
|
|
pNext = parseVarFunc(pNext);
|
|
}
|
|
}
|
|
expectOperator(pNext, OperatorToken::OP_CLOSBRACE, "}");
|
|
pNext = next();
|
|
}
|
|
else syntaxError("namespace name");
|
|
popNameSpace();
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseClass(const Token* pNext)
|
|
{
|
|
std::string decl;
|
|
return parseClass(pNext, decl);
|
|
}
|
|
|
|
|
|
const Token* Parser::parseClass(const Token* pNext, std::string& decl)
|
|
{
|
|
poco_assert (isKeyword(pNext, IdentifierToken::KW_CLASS) || isKeyword(pNext, IdentifierToken::KW_STRUCT) || isKeyword(pNext, IdentifierToken::KW_UNION));
|
|
|
|
_pCurrentSymbol = 0;
|
|
bool isClass = isKeyword(pNext, IdentifierToken::KW_CLASS);
|
|
int line = static_cast<int>(_istr.getCurrentLineNumber());
|
|
Symbol::Access prevAccess = _access;
|
|
append(decl, pNext);
|
|
Symbol::Access access;
|
|
if (isKeyword(pNext, IdentifierToken::KW_CLASS))
|
|
access = Symbol::ACC_PRIVATE;
|
|
else
|
|
access = Symbol::ACC_PUBLIC;
|
|
pNext = next();
|
|
if (pNext->is(Token::IDENTIFIER_TOKEN))
|
|
append(decl, pNext);
|
|
else
|
|
syntaxError("class/struct name");
|
|
pNext = next();
|
|
|
|
bool isFinal = false;
|
|
if (isIdentifier(pNext) && pNext->asString() == "final")
|
|
{
|
|
pNext = next();
|
|
isFinal = true;
|
|
}
|
|
if (!isOperator(pNext, OperatorToken::OP_SEMICOLON))
|
|
{
|
|
// if we have a template specialization the next token will be a <
|
|
if (isOperator(pNext, OperatorToken::OP_LT))
|
|
{
|
|
// skip all template specializations
|
|
// skip until at { bracket, then parseBlock to ignore it
|
|
while (!isOperator(pNext, OperatorToken::OP_OPENBRACE))
|
|
pNext = next();
|
|
pNext = parseBlock(pNext); // skip after }
|
|
|
|
expectOperator(pNext, OperatorToken::OP_SEMICOLON, ";");
|
|
pNext = next();
|
|
_access = prevAccess;
|
|
_pCurrentSymbol = 0;
|
|
return pNext;
|
|
}
|
|
if (isOperator(pNext, OperatorToken::OP_COLON) || isOperator(pNext, OperatorToken::OP_OPENBRACE))
|
|
{
|
|
Struct* pClass = new Struct(decl, isClass, currentNameSpace());
|
|
if (isFinal) pClass->makeFinal();
|
|
pushNameSpace(pClass, line);
|
|
_access = access;
|
|
if (isOperator(pNext, OperatorToken::OP_COLON))
|
|
pNext = parseBaseClassList(next(), pClass);
|
|
expectOperator(pNext, OperatorToken::OP_OPENBRACE, "{");
|
|
pNext = parseClassMembers(pNext, pClass);
|
|
expectOperator(pNext, OperatorToken::OP_CLOSBRACE, "}");
|
|
pNext = next();
|
|
expectOperator(pNext, OperatorToken::OP_SEMICOLON, ";");
|
|
popNameSpace();
|
|
}
|
|
else return parseVarFunc(pNext, decl);
|
|
}
|
|
pNext = next();
|
|
_access = prevAccess;
|
|
_pCurrentSymbol = 0;
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseBaseClassList(const Token* pNext, Struct* pClass)
|
|
{
|
|
while (pNext->is(Token::IDENTIFIER_TOKEN) || pNext->is(Token::KEYWORD_TOKEN))
|
|
{
|
|
bool isVirtual = false;
|
|
Symbol::Access acc = _access;
|
|
while (pNext->is(Token::KEYWORD_TOKEN))
|
|
{
|
|
switch (pNext->asInteger())
|
|
{
|
|
case IdentifierToken::KW_PUBLIC:
|
|
acc = Symbol::ACC_PUBLIC;
|
|
break;
|
|
case IdentifierToken::KW_PROTECTED:
|
|
acc = Symbol::ACC_PROTECTED;
|
|
break;
|
|
case IdentifierToken::KW_PRIVATE:
|
|
acc = Symbol::ACC_PRIVATE;
|
|
break;
|
|
case IdentifierToken::KW_VIRTUAL:
|
|
isVirtual = true;
|
|
break;
|
|
default:
|
|
syntaxError("public, protected, private or virtual");
|
|
}
|
|
pNext = next();
|
|
}
|
|
std::string id;
|
|
pNext = parseIdentifier(pNext, id);
|
|
if (isOperator(pNext, OperatorToken::OP_LT))
|
|
pNext = parseTemplateArgs(pNext, id);
|
|
pClass->addBase(id, acc, isVirtual);
|
|
if (isOperator(pNext, OperatorToken::OP_COMMA))
|
|
pNext = next();
|
|
}
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseClassMembers(const Token* pNext, Struct* /*pClass*/)
|
|
{
|
|
poco_assert (isOperator(pNext, OperatorToken::OP_OPENBRACE));
|
|
|
|
pNext = next();
|
|
while (pNext->is(Token::IDENTIFIER_TOKEN) || pNext->is(Token::KEYWORD_TOKEN) || isOperator(pNext, OperatorToken::OP_COMPL))
|
|
{
|
|
switch (pNext->asInteger())
|
|
{
|
|
case IdentifierToken::KW_PRIVATE:
|
|
case IdentifierToken::KW_PROTECTED:
|
|
case IdentifierToken::KW_PUBLIC:
|
|
pNext = parseAccess(pNext);
|
|
break;
|
|
case IdentifierToken::KW_STRUCT:
|
|
case IdentifierToken::KW_CLASS:
|
|
case IdentifierToken::KW_UNION:
|
|
pNext = parseClass(pNext);
|
|
break;
|
|
case IdentifierToken::KW_TEMPLATE:
|
|
pNext = parseTemplate(pNext);
|
|
break;
|
|
case IdentifierToken::KW_TYPEDEF:
|
|
pNext = parseTypeDef(pNext);
|
|
break;
|
|
case IdentifierToken::KW_USING:
|
|
pNext = parseUsing(pNext);
|
|
break;
|
|
case IdentifierToken::KW_ENUM:
|
|
pNext = parseEnum(pNext);
|
|
break;
|
|
case IdentifierToken::KW_FRIEND:
|
|
pNext = parseFriend(pNext);
|
|
break;
|
|
case OperatorToken::OP_COMPL:
|
|
default:
|
|
pNext = parseVarFunc(pNext);
|
|
}
|
|
}
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseAccess(const Token* pNext)
|
|
{
|
|
switch (pNext->asInteger())
|
|
{
|
|
case IdentifierToken::KW_PRIVATE:
|
|
_access = Symbol::ACC_PRIVATE;
|
|
break;
|
|
case IdentifierToken::KW_PROTECTED:
|
|
_access = Symbol::ACC_PROTECTED;
|
|
break;
|
|
case IdentifierToken::KW_PUBLIC:
|
|
_access = Symbol::ACC_PUBLIC;
|
|
break;
|
|
}
|
|
pNext = next();
|
|
expectOperator(pNext, OperatorToken::OP_COLON, ":");
|
|
return next();
|
|
}
|
|
|
|
|
|
const Token* Parser::parseTemplate(const Token* pNext)
|
|
{
|
|
poco_assert (isKeyword(pNext, IdentifierToken::KW_TEMPLATE));
|
|
|
|
std::string decl;
|
|
append(decl, pNext);
|
|
pNext = next();
|
|
expectOperator(pNext, OperatorToken::OP_LT, "<");
|
|
pNext = parseTemplateArgs(pNext, decl);
|
|
if (isKeyword(pNext, IdentifierToken::KW_CLASS) || isKeyword(pNext, IdentifierToken::KW_STRUCT) || isKeyword(pNext, IdentifierToken::KW_UNION))
|
|
return parseClass(pNext, decl);
|
|
else
|
|
return parseVarFunc(pNext, decl);
|
|
}
|
|
|
|
|
|
const Token* Parser::parseTemplateArgs(const Token* pNext, std::string& decl)
|
|
{
|
|
poco_assert (isOperator(pNext, OperatorToken::OP_LT));
|
|
|
|
append(decl, pNext);
|
|
int depth = 1;
|
|
pNext = next();
|
|
while (depth > 0 && !isEOF(pNext))
|
|
{
|
|
append(decl, pNext);
|
|
if (isOperator(pNext, OperatorToken::OP_LT))
|
|
++depth;
|
|
else if (isOperator(pNext, OperatorToken::OP_GT))
|
|
--depth;
|
|
else if (isOperator(pNext, OperatorToken::OP_SHR))
|
|
depth -= 2;
|
|
pNext = next();
|
|
}
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseTypeDef(const Token* pNext)
|
|
{
|
|
poco_assert (isKeyword(pNext, IdentifierToken::KW_TYPEDEF));
|
|
|
|
_pCurrentSymbol = 0;
|
|
int line = static_cast<int>(_istr.getCurrentLineNumber());
|
|
std::string decl;
|
|
while (!isOperator(pNext, OperatorToken::OP_SEMICOLON) && !isEOF(pNext))
|
|
{
|
|
append(decl, pNext);
|
|
pNext = next();
|
|
}
|
|
TypeDef* pTypeDef = new TypeDef(decl, currentNameSpace());
|
|
addSymbol(pTypeDef, line);
|
|
|
|
pNext = next();
|
|
_pCurrentSymbol = 0;
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseUsing(const Token* pNext)
|
|
{
|
|
poco_assert (isKeyword(pNext, IdentifierToken::KW_USING));
|
|
|
|
_pCurrentSymbol = 0;
|
|
int line = static_cast<int>(_istr.getCurrentLineNumber());
|
|
pNext = next();
|
|
if (isKeyword(pNext, IdentifierToken::KW_NAMESPACE))
|
|
{
|
|
pNext = next();
|
|
if (isIdentifier(pNext))
|
|
{
|
|
std::string ns;
|
|
pNext = parseIdentifier(pNext, ns);
|
|
currentNameSpace()->importNameSpace(ns);
|
|
}
|
|
else syntaxError("identifier");
|
|
}
|
|
else
|
|
{
|
|
if (isIdentifier(pNext))
|
|
{
|
|
std::string id;
|
|
pNext = parseIdentifier(pNext, id);
|
|
if (isOperator(pNext, OperatorToken::OP_ASSIGN))
|
|
{
|
|
pNext = next();
|
|
std::string decl("using ");
|
|
decl += id;
|
|
decl += " = ";
|
|
while (!isOperator(pNext, OperatorToken::OP_SEMICOLON) && !isEOF(pNext))
|
|
{
|
|
append(decl, pNext);
|
|
pNext = next();
|
|
}
|
|
TypeAlias* pTypeAlias = new TypeAlias(decl, currentNameSpace());
|
|
addSymbol(pTypeAlias, line);
|
|
}
|
|
else
|
|
{
|
|
currentNameSpace()->importSymbol(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isOperator(pNext, OperatorToken::OP_SEMICOLON))
|
|
syntaxError("semicolon");
|
|
pNext = next();
|
|
_pCurrentSymbol = 0;
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseFriend(const Token* pNext)
|
|
{
|
|
poco_assert (isKeyword(pNext, IdentifierToken::KW_FRIEND));
|
|
|
|
pNext = next();
|
|
|
|
while (!isOperator(pNext, OperatorToken::OP_SEMICOLON) && !isEOF(pNext))
|
|
pNext = next();
|
|
|
|
if (isOperator(pNext, OperatorToken::OP_SEMICOLON))
|
|
pNext = next();
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseVarFunc(const Token* pNext)
|
|
{
|
|
std::string decl;
|
|
return parseVarFunc(pNext, decl);
|
|
}
|
|
|
|
|
|
const Token* Parser::parseVarFunc(const Token* pNext, std::string& decl)
|
|
{
|
|
_pCurrentSymbol = 0;
|
|
if (isKeyword(pNext, IdentifierToken::KW_EXTERN))
|
|
{
|
|
pNext = parseExtern(pNext);
|
|
}
|
|
else
|
|
{
|
|
append(decl, pNext);
|
|
pNext = next();
|
|
bool isOp = false;
|
|
while (!isOperator(pNext, OperatorToken::OP_SEMICOLON) && !isOperator(pNext, OperatorToken::OP_OPENPARENT) && !isEOF(pNext))
|
|
{
|
|
append(decl, pNext);
|
|
isOp = isKeyword(pNext, IdentifierToken::KW_OPERATOR);
|
|
pNext = next();
|
|
}
|
|
if (isOperator(pNext, OperatorToken::OP_SEMICOLON))
|
|
{
|
|
std::string name = Symbol::extractName(decl);
|
|
if (!currentNameSpace()->lookup(name))
|
|
{
|
|
Variable* pVar = new Variable(decl, currentNameSpace());
|
|
addSymbol(pVar, static_cast<int>(_istr.getCurrentLineNumber()));
|
|
}
|
|
pNext = next();
|
|
}
|
|
else if (isOperator(pNext, OperatorToken::OP_OPENPARENT))
|
|
{
|
|
if (isOp)
|
|
{
|
|
decl += " (";
|
|
pNext = next();
|
|
expectOperator(pNext, OperatorToken::OP_CLOSPARENT, ")");
|
|
append(decl, pNext);
|
|
pNext = next();
|
|
expectOperator(pNext, OperatorToken::OP_OPENPARENT, "(");
|
|
}
|
|
pNext = parseFunc(pNext, decl);
|
|
}
|
|
}
|
|
_pCurrentSymbol = 0;
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseExtern(const Token* pNext)
|
|
{
|
|
poco_assert (isKeyword(pNext, IdentifierToken::KW_EXTERN));
|
|
|
|
pNext = next();
|
|
if (pNext->is(Token::STRING_LITERAL_TOKEN))
|
|
pNext = next();
|
|
if (isOperator(pNext, OperatorToken::OP_OPENBRACE))
|
|
{
|
|
pNext = parseBlock(pNext);
|
|
}
|
|
else
|
|
{
|
|
while (!isOperator(pNext, OperatorToken::OP_SEMICOLON) && !isEOF(pNext))
|
|
pNext = next();
|
|
}
|
|
if (isOperator(pNext, OperatorToken::OP_SEMICOLON))
|
|
pNext = next();
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseFunc(const Token* pNext, std::string& decl)
|
|
{
|
|
poco_assert (isOperator(pNext, OperatorToken::OP_OPENPARENT));
|
|
|
|
int line = static_cast<int>(_istr.getCurrentLineNumber());
|
|
Function* pFunc = 0;
|
|
std::string name = Symbol::extractName(decl);
|
|
if (name.find(':') == std::string::npos)
|
|
{
|
|
pFunc = new Function(decl, currentNameSpace());
|
|
addSymbol(pFunc, line);
|
|
}
|
|
pNext = parseParameters(pNext, pFunc);
|
|
expectOperator(pNext, OperatorToken::OP_CLOSPARENT, ")");
|
|
pNext = next();
|
|
while (pNext->is(Poco::Token::IDENTIFIER_TOKEN) || pNext->is(Poco::Token::KEYWORD_TOKEN))
|
|
{
|
|
if (isKeyword(pNext, IdentifierToken::KW_CONST))
|
|
{
|
|
if (pFunc) pFunc->makeConst();
|
|
pNext = next();
|
|
}
|
|
if (isKeyword(pNext, IdentifierToken::KW_THROW))
|
|
{
|
|
while (!isOperator(pNext, OperatorToken::OP_ASSIGN) && !isOperator(pNext, OperatorToken::OP_SEMICOLON) &&
|
|
!isOperator(pNext, OperatorToken::OP_OPENBRACE) && !isEOF(pNext))
|
|
pNext = next();
|
|
}
|
|
else if (isKeyword(pNext, IdentifierToken::KW_NOEXCEPT))
|
|
{
|
|
if (pFunc) pFunc->makeNoexcept();
|
|
pNext = next();
|
|
}
|
|
else if (isIdentifier(pNext) && pNext->asString() == "override")
|
|
{
|
|
if (pFunc) pFunc->makeOverride();
|
|
pNext = next();
|
|
}
|
|
else if (isIdentifier(pNext) && pNext->asString() == "final")
|
|
{
|
|
if (pFunc) pFunc->makeFinal();
|
|
pNext = next();
|
|
}
|
|
else if (isKeyword(pNext, IdentifierToken::KW_TRY))
|
|
{
|
|
break; // handled below
|
|
}
|
|
}
|
|
if (isOperator(pNext, OperatorToken::OP_ASSIGN))
|
|
{
|
|
pNext = next();
|
|
if (!pNext->is(Token::INTEGER_LITERAL_TOKEN) && !isKeyword(pNext, IdentifierToken::KW_DEFAULT) && !isKeyword(pNext, IdentifierToken::KW_DELETE))
|
|
syntaxError("0, default or delete");
|
|
if (isKeyword(pNext, IdentifierToken::KW_DEFAULT))
|
|
pFunc->makeDefault();
|
|
else if (isKeyword(pNext, IdentifierToken::KW_DELETE))
|
|
pFunc->makeDelete();
|
|
pNext = next();
|
|
if (pFunc) pFunc->makePureVirtual();
|
|
expectOperator(pNext, OperatorToken::OP_SEMICOLON, ";");
|
|
}
|
|
else if (isOperator(pNext, OperatorToken::OP_OPENBRACE) || isOperator(pNext, OperatorToken::OP_COLON))
|
|
{
|
|
while (!isOperator(pNext, OperatorToken::OP_OPENBRACE) && !isEOF(pNext))
|
|
pNext = next();
|
|
|
|
pNext = parseBlock(pNext);
|
|
if (!pFunc)
|
|
pFunc = dynamic_cast<Function*>(currentNameSpace()->lookup(name));
|
|
if (pFunc)
|
|
pFunc->makeInline();
|
|
}
|
|
else if (isKeyword(pNext, IdentifierToken::KW_TRY))
|
|
{
|
|
pNext = next();
|
|
if (isOperator(pNext, OperatorToken::OP_OPENBRACE) || isOperator(pNext, OperatorToken::OP_COLON))
|
|
{
|
|
while (!isOperator(pNext, OperatorToken::OP_OPENBRACE) && !isEOF(pNext))
|
|
pNext = next();
|
|
|
|
pNext = parseBlock(pNext);
|
|
|
|
if (isKeyword(pNext, IdentifierToken::KW_CATCH))
|
|
{
|
|
while (!isOperator(pNext, OperatorToken::OP_OPENBRACE) && !isEOF(pNext))
|
|
pNext = next();
|
|
|
|
pNext = parseBlock(pNext);
|
|
}
|
|
else syntaxError("expected catch block");
|
|
|
|
if (!pFunc)
|
|
pFunc = dynamic_cast<Function*>(currentNameSpace()->lookup(name));
|
|
if (pFunc)
|
|
pFunc->makeInline();
|
|
}
|
|
else syntaxError("expected member initialization or block");
|
|
}
|
|
else
|
|
{
|
|
expectOperator(pNext, OperatorToken::OP_SEMICOLON, ";");
|
|
}
|
|
if (isOperator(pNext, OperatorToken::OP_SEMICOLON))
|
|
pNext = next();
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseParameters(const Token* pNext, Function* pFunc)
|
|
{
|
|
poco_assert (isOperator(pNext, OperatorToken::OP_OPENPARENT));
|
|
|
|
pNext = next();
|
|
while (!isOperator(pNext, OperatorToken::OP_CLOSPARENT) && !isEOF(pNext))
|
|
{
|
|
std::string decl;
|
|
int depth = 0;
|
|
int tdepth = 0;
|
|
while ((depth > 0 || tdepth > 0 || (!isOperator(pNext, OperatorToken::OP_CLOSPARENT) && !isOperator(pNext, OperatorToken::OP_COMMA))) && !isEOF(pNext))
|
|
{
|
|
append(decl, pNext);
|
|
if (isOperator(pNext, OperatorToken::OP_OPENPARENT))
|
|
++depth;
|
|
else if (isOperator(pNext, OperatorToken::OP_CLOSPARENT))
|
|
--depth;
|
|
else if (isOperator(pNext, OperatorToken::OP_LT))
|
|
++tdepth;
|
|
else if (isOperator(pNext, OperatorToken::OP_GT))
|
|
--tdepth;
|
|
else if (isOperator(pNext, OperatorToken::OP_SHR))
|
|
tdepth -= 2;
|
|
pNext = next();
|
|
}
|
|
if (isOperator(pNext, OperatorToken::OP_COMMA))
|
|
pNext = next();
|
|
if (pFunc && decl != "void") // don't add the void parameter, I simply check here to avoid throwing away void*
|
|
pFunc->addParameter(new Parameter(decl, pFunc));
|
|
}
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseBlock(const Token* pNext)
|
|
{
|
|
poco_assert (isOperator(pNext, OperatorToken::OP_OPENBRACE));
|
|
|
|
pNext = next();
|
|
int depth = 1;
|
|
while (depth > 0 && !isEOF(pNext))
|
|
{
|
|
if (isOperator(pNext, OperatorToken::OP_OPENBRACE))
|
|
++depth;
|
|
else if (isOperator(pNext, OperatorToken::OP_CLOSBRACE))
|
|
--depth;
|
|
pNext = next();
|
|
}
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseEnum(const Token* pNext)
|
|
{
|
|
poco_assert (isKeyword(pNext, IdentifierToken::KW_ENUM));
|
|
|
|
std::string baseType;
|
|
int flags = 0;
|
|
_pCurrentSymbol = 0;
|
|
int line = static_cast<int>(_istr.getCurrentLineNumber());
|
|
pNext = next();
|
|
|
|
if (isKeyword(pNext, IdentifierToken::KW_CLASS) || isKeyword(pNext, IdentifierToken::KW_STRUCT))
|
|
{
|
|
flags = Enum::ENUM_IS_CLASS;
|
|
pNext = next();
|
|
}
|
|
|
|
std::string name;
|
|
if (pNext->is(Token::IDENTIFIER_TOKEN))
|
|
{
|
|
name = pNext->tokenString();
|
|
pNext = next();
|
|
}
|
|
|
|
if (isOperator(pNext, OperatorToken::OP_COLON))
|
|
{
|
|
pNext = next();
|
|
if (pNext->is(Token::KEYWORD_TOKEN))
|
|
{
|
|
while (pNext->is(Token::KEYWORD_TOKEN)) // int, unsigned int, etc.
|
|
{
|
|
if (!baseType.empty()) baseType += ' ';
|
|
baseType += pNext->tokenString();
|
|
pNext = next();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pNext = parseIdentifier(pNext, baseType);
|
|
}
|
|
}
|
|
|
|
expectOperator(pNext, OperatorToken::OP_OPENBRACE, "{");
|
|
Enum* pEnum = new Enum(name, currentNameSpace(), baseType, flags);
|
|
addSymbol(pEnum, line);
|
|
pNext = next();
|
|
while (pNext->is(Token::IDENTIFIER_TOKEN))
|
|
{
|
|
pNext = parseEnumValue(pNext, pEnum);
|
|
}
|
|
expectOperator(pNext, OperatorToken::OP_CLOSBRACE, "}");
|
|
pNext = next();
|
|
expectOperator(pNext, OperatorToken::OP_SEMICOLON, ";");
|
|
pNext = next();
|
|
_pCurrentSymbol = 0;
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseEnumValue(const Token* pNext, Enum* pEnum)
|
|
{
|
|
_pCurrentSymbol = 0;
|
|
_doc.clear();
|
|
int line = static_cast<int>(_istr.getCurrentLineNumber());
|
|
std::string name = pNext->tokenString();
|
|
std::string value;
|
|
pNext = next();
|
|
if (isOperator(pNext, OperatorToken::OP_ASSIGN))
|
|
{
|
|
pNext = next();
|
|
while (!isOperator(pNext, OperatorToken::OP_COMMA) && !isOperator(pNext, OperatorToken::OP_CLOSBRACE) && !isEOF(pNext))
|
|
{
|
|
append(value, pNext);
|
|
pNext = next();
|
|
}
|
|
}
|
|
EnumValue* pValue = new EnumValue(name, value, pEnum);
|
|
addSymbol(pValue, line, true);
|
|
if (isOperator(pNext, OperatorToken::OP_COMMA))
|
|
pNext = next();
|
|
else if (!isOperator(pNext, OperatorToken::OP_CLOSBRACE))
|
|
syntaxError(", or }");
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::parseIdentifier(const Token* pNext, std::string& id)
|
|
{
|
|
poco_assert (pNext->is(Token::IDENTIFIER_TOKEN) || isOperator(pNext, OperatorToken::OP_DBL_COLON));
|
|
id.append(pNext->tokenString());
|
|
if (isOperator(pNext, OperatorToken::OP_DBL_COLON))
|
|
{
|
|
pNext = next();
|
|
if (!pNext->is(Token::IDENTIFIER_TOKEN)) syntaxError("identifier");
|
|
id.append(pNext->tokenString());
|
|
}
|
|
pNext = next();
|
|
while (isOperator(pNext, OperatorToken::OP_DBL_COLON))
|
|
{
|
|
id.append("::");
|
|
pNext = next();
|
|
if (!pNext->is(Token::IDENTIFIER_TOKEN)) syntaxError("identifier");
|
|
id.append(pNext->tokenString());
|
|
pNext = next();
|
|
}
|
|
return pNext;
|
|
}
|
|
|
|
|
|
void Parser::addSymbol(Symbol* pSymbol, int lineNumber, bool addGST)
|
|
{
|
|
pSymbol->setLineNumber(lineNumber);
|
|
pSymbol->setFile(_currentPath);
|
|
pSymbol->setPackage(_package);
|
|
pSymbol->setLibrary(_library);
|
|
pSymbol->setAccess(_access);
|
|
_pCurrentSymbol = pSymbol;
|
|
if (addGST)
|
|
_gst.insert(NameSpace::SymbolTable::value_type(pSymbol->name(), pSymbol));
|
|
if (!_doc.empty())
|
|
pSymbol->addDocumentation(_doc);
|
|
if (!_attrs.empty())
|
|
{
|
|
Attributes attrs;
|
|
std::istringstream istr(_attrs);
|
|
AttributesParser parser(attrs, istr);
|
|
parser.parse();
|
|
pSymbol->setAttributes(attrs);
|
|
_attrs.clear();
|
|
}
|
|
_doc.clear();
|
|
}
|
|
|
|
|
|
void Parser::pushNameSpace(NameSpace* pNameSpace, int lineNumber, bool addGST)
|
|
{
|
|
addSymbol(pNameSpace, lineNumber, addGST);
|
|
_nsStack.push_back(pNameSpace);
|
|
}
|
|
|
|
|
|
void Parser::popNameSpace()
|
|
{
|
|
_nsStack.pop_back();
|
|
}
|
|
|
|
|
|
NameSpace* Parser::currentNameSpace() const
|
|
{
|
|
return _nsStack.back();
|
|
}
|
|
|
|
|
|
const Token* Parser::next()
|
|
{
|
|
const Token* pNext = nextPreprocessed();
|
|
while (!_inFile && !isEOF(pNext))
|
|
pNext = nextPreprocessed();
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::nextPreprocessed()
|
|
{
|
|
const Token* pNext = nextToken();
|
|
while (pNext->is(Token::PREPROCESSOR_TOKEN))
|
|
{
|
|
std::istringstream pps(pNext->tokenString());
|
|
pps.get();
|
|
Tokenizer ppt(pps);
|
|
const Token* pPPT = ppt.next();
|
|
if (pPPT->tokenString() == "line" || pPPT->is(Token::INTEGER_LITERAL_TOKEN))
|
|
{
|
|
if (!pPPT->is(Token::INTEGER_LITERAL_TOKEN))
|
|
pPPT = ppt.next();
|
|
int line = pPPT->asInteger();
|
|
_istr.setCurrentLineNumber(line);
|
|
pPPT = ppt.next();
|
|
if (pPPT->is(Token::STRING_LITERAL_TOKEN))
|
|
{
|
|
std::string path = pPPT->asString();
|
|
Path p(path);
|
|
p.makeAbsolute();
|
|
_currentPath = p.toString();
|
|
_inFile = (icompare(_path, _currentPath) == 0);
|
|
}
|
|
}
|
|
pNext = nextToken();
|
|
}
|
|
return pNext;
|
|
}
|
|
|
|
|
|
const Token* Parser::nextToken()
|
|
{
|
|
const Token* pNext = _tokenizer.next();
|
|
while (pNext->is(Token::COMMENT_TOKEN) || pNext->is(Token::SPECIAL_COMMENT_TOKEN))
|
|
{
|
|
if (pNext->is(Token::SPECIAL_COMMENT_TOKEN))
|
|
{
|
|
if (_pCurrentSymbol)
|
|
{
|
|
_pCurrentSymbol->addDocumentation(pNext->asString());
|
|
_doc.clear();
|
|
}
|
|
else if (_inFile)
|
|
{
|
|
if (!_doc.empty()) _doc += "\n";
|
|
_doc += pNext->asString();
|
|
}
|
|
}
|
|
else if (pNext->is(Token::COMMENT_TOKEN) && _inFile)
|
|
{
|
|
const std::string& comment = pNext->tokenString();
|
|
if (comment.compare(0, 3, "//@") == 0)
|
|
{
|
|
_attrs.append(comment.substr(3));
|
|
}
|
|
else if (comment.compare(0, 11, "// Package:") == 0)
|
|
{
|
|
_package = comment.substr(11);
|
|
trimInPlace(_package);
|
|
}
|
|
else if (comment.compare(0, 11, "// Library:") == 0)
|
|
{
|
|
_library = comment.substr(11);
|
|
trimInPlace(_library);
|
|
}
|
|
}
|
|
pNext = _tokenizer.next();
|
|
}
|
|
return pNext;
|
|
}
|
|
|
|
|
|
} } // namespace Poco::CppParser
|