diff --git a/include/occa/lang/parser.hpp b/include/occa/lang/parser.hpp index 29029b5da..9429e715a 100644 --- a/include/occa/lang/parser.hpp +++ b/include/occa/lang/parser.hpp @@ -143,21 +143,25 @@ namespace occa { void loadDeclarationBraceInitializer(variableDeclaration &decl); vartype_t loadType(); + struct_t* loadStruct(); + + qualifiers_t loadQualifiers(); void loadBaseType(vartype_t &vartype); - void loadQualifier(token_t *token, - const qualifier_t &qualifier, - vartype_t &vartype); + void loadVartypeQualifier(token_t *token, + const qualifier_t &qualifier, + vartype_t &vartype); - void setPointers(vartype_t &vartype); - void setPointer(vartype_t &vartype); + void setVartypePointers(vartype_t &vartype); + void setVartypePointer(vartype_t &vartype); - void setReference(vartype_t &vartype); + void setVartypeReference(vartype_t &vartype); bool isLoadingFunctionPointer(); bool isLoadingVariable(); bool isLoadingFunction(); + bool isLoadingStruct(); variable_t loadFunctionPointer(vartype_t &vartype); variable_t loadVariable(vartype_t &vartype); @@ -176,11 +180,6 @@ namespace occa { public: void getArgumentRanges(tokenRangeVector &argRanges); variable_t getArgument(); - - class_t loadClassType(); - struct_t loadStructType(); - enum_t loadEnumType(); - union_t loadUnionType(); //================================ //---[ Loader Helpers ]----------- @@ -199,9 +198,9 @@ namespace occa { statement_t* loadDeclarationStatement(attributeTokenMap &smntAttributes); - statement_t* loadNamespaceStatement(attributeTokenMap &smntAttributes); + statement_t* loadStructStatement(attributeTokenMap &smntAttributes); - statement_t* loadTypeDeclStatement(attributeTokenMap &smntAttributes); + statement_t* loadNamespaceStatement(attributeTokenMap &smntAttributes); statement_t* loadFunctionStatement(attributeTokenMap &smntAttributes); diff --git a/include/occa/lang/qualifier.hpp b/include/occa/lang/qualifier.hpp index 48bb3eb85..365fdf006 100644 --- a/include/occa/lang/qualifier.hpp +++ b/include/occa/lang/qualifier.hpp @@ -74,6 +74,8 @@ namespace occa { const udim_t qtype_); ~qualifier_t(); + bool operator == (const qualifier_t &other) const; + udim_t type() const; }; diff --git a/include/occa/lang/statement.hpp b/include/occa/lang/statement.hpp index 1c9a24df0..8a61fe90d 100644 --- a/include/occa/lang/statement.hpp +++ b/include/occa/lang/statement.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/include/occa/lang/statement/statement.hpp b/include/occa/lang/statement/statement.hpp index 304058dea..313eb9480 100644 --- a/include/occa/lang/statement/statement.hpp +++ b/include/occa/lang/statement/statement.hpp @@ -33,11 +33,16 @@ namespace occa { extern const int block; extern const int namespace_; - extern const int typeDecl; extern const int function; extern const int functionDecl; + + extern const int class_; + extern const int struct_; extern const int classAccess; + extern const int enum_; + extern const int union_; + extern const int expression; extern const int declaration; diff --git a/include/occa/lang/statement/structStatement.hpp b/include/occa/lang/statement/structStatement.hpp new file mode 100644 index 000000000..b958b4626 --- /dev/null +++ b/include/occa/lang/statement/structStatement.hpp @@ -0,0 +1,30 @@ +#ifndef OCCA_LANG_STATEMENT_STRUCTSTATEMENT_HEADER +#define OCCA_LANG_STATEMENT_STRUCTSTATEMENT_HEADER + +#include +#include + +namespace occa { + namespace lang { + class structStatement : public statement_t { + public: + struct_t &struct_; + + structStatement(blockStatement *up_, + struct_t &struct_); + structStatement(blockStatement *up_, + const structStatement& other); + + virtual statement_t& clone_(blockStatement *up_) const; + + virtual int type() const; + virtual std::string statementName() const; + + bool addStructToParentScope(); + + virtual void print(printer &pout) const; + }; + } +} + +#endif diff --git a/include/occa/lang/tokenContext.hpp b/include/occa/lang/tokenContext.hpp index 074077f65..023bf6408 100644 --- a/include/occa/lang/tokenContext.hpp +++ b/include/occa/lang/tokenContext.hpp @@ -36,6 +36,7 @@ namespace occa { intIntMap pairs; intVector semicolons; bool hasError; + bool supressErrors; tokenRangeList stack; tokenRange tp; @@ -69,7 +70,11 @@ namespace occa { int position() const; int size() const; + token_t* operator [] (const int index); + tokenContext& operator ++ (); + tokenContext& operator ++ (int); + void setToken(const int index, token_t *value); diff --git a/include/occa/lang/type/struct.hpp b/include/occa/lang/type/struct.hpp index 5264d2bc2..c614cccb0 100644 --- a/include/occa/lang/type/struct.hpp +++ b/include/occa/lang/type/struct.hpp @@ -1,19 +1,27 @@ #ifndef OCCA_LANG_TYPE_STRUCT_HEADER #define OCCA_LANG_TYPE_STRUCT_HEADER -#include +#include namespace occa { namespace lang { - class struct_t : public structure_t { + class struct_t : public type_t { public: + variableVector fields; + struct_t(); + struct_t(identifierToken &nameToken); + + struct_t(const struct_t &other); virtual int type() const; virtual type_t& clone() const; virtual dtype_t dtype() const; + void addField(variable_t &var); + void addFields(variableVector &fields_); + virtual void printDeclaration(printer &pout) const; }; } diff --git a/include/occa/lang/type/type.hpp b/include/occa/lang/type/type.hpp index b425e40da..790a361a4 100644 --- a/include/occa/lang/type/type.hpp +++ b/include/occa/lang/type/type.hpp @@ -20,8 +20,8 @@ namespace occa { class array_t; class variable_t; - typedef std::vector pointerVector; typedef std::vector arrayVector; + typedef std::vector pointerVector; typedef std::vector variableVector; typedef std::vector variablePtrVector; diff --git a/notes.cpp b/notes.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/lang/parser.cpp b/src/lang/parser.cpp index b1eb67404..4b5596341 100644 --- a/src/lang/parser.cpp +++ b/src/lang/parser.cpp @@ -56,7 +56,6 @@ namespace occa { statementLoaders[statementType::declaration] = &parser_t::loadDeclarationStatement; statementLoaders[statementType::block] = &parser_t::loadBlockStatement; statementLoaders[statementType::namespace_] = &parser_t::loadNamespaceStatement; - statementLoaders[statementType::typeDecl] = &parser_t::loadTypeDeclStatement; statementLoaders[statementType::if_] = &parser_t::loadIfStatement; statementLoaders[statementType::elif_] = &parser_t::loadElifStatement; statementLoaders[statementType::else_] = &parser_t::loadElseStatement; @@ -344,7 +343,7 @@ namespace occa { token_t *token = context[0]; if (!(token->type() & (tokenType::qualifier | tokenType::type))) { - context.set(1); + ++context; tokens.push_back(token->clone()); continue; } @@ -415,7 +414,7 @@ namespace occa { void parser_t::loadAttribute(attributeTokenMap &attrs) { // Skip [@] token - context.set(1); + ++context; if (!(context[0]->type() & tokenType::identifier)) { context.printError("Expected an attribute name"); @@ -424,7 +423,7 @@ namespace occa { } identifierToken &nameToken = *((identifierToken*) context[0]); - context.set(1); + ++context; attribute_t *attribute = getAttribute(nameToken.value); if (!attribute) { @@ -690,11 +689,11 @@ namespace occa { // If partially-defined type, finish parsing it vartype_t vartype = baseType.declarationType(); vartype.qualifiers = baseType.qualifiers; - setPointers(vartype); + setVartypePointers(vartype); if (!success) { return decl; } - setReference(vartype); + setVartypeReference(vartype); if (!success) { return decl; } @@ -839,25 +838,133 @@ namespace occa { return vartype; } - setPointers(vartype); + setVartypePointers(vartype); if (!success) { return vartype; } - setReference(vartype); + setVartypeReference(vartype); return vartype; } + struct_t* parser_t::loadStruct() { + // Store type declarations in temporary block statement + blockStatement *blockSmnt = new blockStatement(up, context[0]); + pushUp(*blockSmnt); + + // Skip 'struct' token + ++context; + + token_t *nameToken = context[0]; + if (!nameToken || + (!(nameToken->type() & tokenType::identifier))) { + context.printError("Expected struct name"); + success = false; + delete blockSmnt; + popUp(); + return NULL; + } + + ++context; + opType_t opType = getOperatorType(context[0]); + if (!(opType & (operatorType::braceStart | + operatorType::scope))) { + context.printError("Expected struct body {}"); + success = false; + delete blockSmnt; + popUp(); + return NULL; + } + + context.pushPairRange(0); + + // Load type declaration statements + statement_t *smnt = getNextStatement(); + variableVector fields; + while (smnt) { + const int sType = smnt->type(); + if (!(sType & statementType::declaration)) { + if (sType & (statementType::function | + statementType::functionDecl)) { + smnt->printError("Struct functions are not supported yet"); + } else if (sType & statementType::classAccess) { + smnt->printError("Access modifiers are not supported yet"); + } else { + smnt->printError("Expected variable declaration statements"); + } + success = false; + delete blockSmnt; + popUp(); + return NULL; + } + + variableDeclarationVector &declarations = (smnt + ->to() + .declarations); + const int varCount = (int) declarations.size(); + for (int i = 0; i < varCount; ++i) { + variableDeclaration &decl = declarations[i]; + if (decl.value) { + decl.value->printError("Struct fields cannot have default values"); + success = false; + delete blockSmnt; + popUp(); + return NULL; + } + fields.push_back(*(decl.variable)); + } + delete smnt; + smnt = getNextStatement(); + } + + delete blockSmnt; + popUp(); + + context.popAndSkip(); + + opType = getOperatorType(context[0]); + if (!(opType & operatorType::semicolon)) { + context.printError("Expected [;] after struct definition"); + success = false; + return NULL; + } + ++context; + + struct_t *type = new struct_t(*((identifierToken*) nameToken)); + type->addFields(fields); + + return type; + } + + qualifiers_t parser_t::loadQualifiers() { + qualifiers_t qualifiers; + + while (context.size()) { + token_t *token = context[0]; + keyword_t &keyword = getKeyword(token); + if (!(keyword.type() & keywordType::qualifier)) { + break; + } + qualifiers += keyword.to().qualifier; + ++context; + } + + return qualifiers; + } + void parser_t::loadBaseType(vartype_t &vartype) { // Type was already loaded if (vartype.type) { return; } - const int tokens = context.size(); + const int tokenCount = context.size(); int tokenPos; - for (tokenPos = 0; tokenPos < tokens; ++tokenPos) { - token_t *token = context[tokenPos]; + + bool typedefing = false; + for (tokenPos = 0; tokenPos < tokenCount; ++tokenPos) { + token_t *token = context[tokenPos]; + keyword_t &keyword = getKeyword(token); const int kType = keyword.type(); if (kType & keywordType::none) { @@ -865,9 +972,39 @@ namespace occa { } if (kType & keywordType::qualifier) { - loadQualifier(token, - keyword.to().qualifier, - vartype); + const qualifier_t &qualifier = keyword.to().qualifier; + type_t *type = NULL; + if (qualifier == typedef_) { + typedefing = true; + } else if (qualifier == struct_) { + if (typedefing) { + token->printError("Typedef'd structs are not supported yet"); + success = false; + } + } else if (qualifier == enum_) { + // TODO: type = loadEnum(); + token->printError("Enums are not supported yet"); + success = false; + } else if (qualifier == union_) { + // TODO: type = loadUnion(); + token->printError("Enums are not supported yet"); + success = false; + } else if (qualifier == class_) { + // TODO: type = loadClass(); + token->printError("Enums are not supported yet"); + success = false; + } + if (!success) { + return; + } + if (!type) { + loadVartypeQualifier(token, + keyword.to().qualifier, + vartype); + } else { + vartype.type = type; + vartype.typeToken = (identifierToken*) type->source->clone(); + } continue; } if ((kType & keywordType::type) && @@ -876,17 +1013,18 @@ namespace occa { vartype.typeToken = (identifierToken*) token->clone(); continue; } + break; } - if (tokenPos == 0) { + if (tokenPos) { + context.set(tokenPos); + } else { context.printError("Unable to load type"); success = false; return; } - context.set(tokenPos); - if (vartype.isValid()) { return; } @@ -901,9 +1039,9 @@ namespace occa { success = false; } - void parser_t::loadQualifier(token_t *token, - const qualifier_t &qualifier, - vartype_t &vartype) { + void parser_t::loadVartypeQualifier(token_t *token, + const qualifier_t &qualifier, + vartype_t &vartype) { // Handle long/long long case if (&qualifier == &long_) { if (vartype.has(long_)) { @@ -931,18 +1069,18 @@ namespace occa { } } - void parser_t::setPointers(vartype_t &vartype) { + void parser_t::setVartypePointers(vartype_t &vartype) { while (success && context.size()) { if (!(getOperatorType(context[0]) & operatorType::mult)) { break; } - context.set(1); - setPointer(vartype); + ++context; + setVartypePointer(vartype); } } - void parser_t::setPointer(vartype_t &vartype) { + void parser_t::setVartypePointer(vartype_t &vartype) { pointer_t pointer; const int tokens = context.size(); @@ -971,7 +1109,7 @@ namespace occa { } } - void parser_t::setReference(vartype_t &vartype) { + void parser_t::setVartypeReference(vartype_t &vartype) { if (!context.size()) { return; } @@ -979,7 +1117,7 @@ namespace occa { return; } vartype.setReferenceToken(context[0]); - context.set(1); + ++context; } bool parser_t::isLoadingFunctionPointer() { @@ -1024,14 +1162,18 @@ namespace occa { bool parser_t::isLoadingFunction() { context.push(); + context.supressErrors = true; vartype_t vartype = loadType(); + context.supressErrors = false; + if (!success) { - context.popAndSkip(); + success = true; + context.pop(); return false; } if (!(token_t::safeType(context[0]) & tokenType::identifier)) { - context.popAndSkip(); + context.pop(); return false; } @@ -1041,6 +1183,15 @@ namespace occa { return isFunction; } + bool parser_t::isLoadingStruct() { + context.push(); + qualifiers_t qualifiers = loadQualifiers(); + context.pop(); + + return (qualifiers.has(struct_) && + !qualifiers.has(typedef_)); + } + variable_t parser_t::loadFunctionPointer(vartype_t &vartype) { // TODO: Check for nested function pointers // Check for arrays @@ -1048,13 +1199,13 @@ namespace occa { functionPtr_t func(vartype); func.isBlock = (getOperatorType(context[0]) & operatorType::xor_); - context.set(1); + ++context; identifierToken *nameToken = NULL; if (context.size() && (context[0]->type() & tokenType::identifier)) { nameToken = (identifierToken*) context[0]; - context.set(1); + ++context; } // If we have arrays, we don't set them in the return type @@ -1088,7 +1239,7 @@ namespace occa { if (context.size() && (context[0]->type() & tokenType::identifier)) { nameToken = (identifierToken*) context[0]; - context.set(1); + ++context; } setArrays(vartype); @@ -1158,30 +1309,6 @@ namespace occa { } context.pop(); } - - class_t parser_t::loadClassType() { - context.printError("Cannot parse classes yet"); - success = false; - return class_t(); - } - - struct_t parser_t::loadStructType() { - context.printError("Cannot parse structs yet"); - success = false; - return struct_t(); - } - - enum_t parser_t::loadEnumType() { - context.printError("Cannot parse enum yet"); - success = false; - return enum_t(); - } - - union_t parser_t::loadUnionType() { - context.printError("Cannot parse union yet"); - success = false; - return union_t(); - } //================================== //---[ Loader Helpers ]------------- @@ -1266,7 +1393,7 @@ namespace occa { addAttributesTo(smntAttributes, smnt); // Skip [;] token - context.set(1); + ++context; return smnt; } @@ -1295,13 +1422,15 @@ namespace occa { } statement_t* parser_t::loadDeclarationStatement(attributeTokenMap &smntAttributes) { - bool isFunction = isLoadingFunction(); + if (isLoadingFunction()) { + return loadFunctionStatement(smntAttributes); + } + if (success && isLoadingStruct()) { + return loadStructStatement(smntAttributes); + } if (!success) { return NULL; } - if (isFunction) { - return loadFunctionStatement(smntAttributes); - } vartype_t baseType; loadBaseType(baseType); @@ -1325,14 +1454,14 @@ namespace occa { const opType_t opType = getOperatorType(context[0]); if (!(opType & operatorType::comma)) { if (opType & operatorType::semicolon) { - context.set(1); + ++context; } else if (checkSemicolon) { context.printError("[3] Expected a [;]"); success = false; } break; } - context.set(1); + ++context; } if (!success) { smnt.freeDeclarations(); @@ -1342,6 +1471,26 @@ namespace occa { return &smnt; } + statement_t* parser_t::loadStructStatement(attributeTokenMap &smntAttributes) { + struct_t *type = loadStruct(); + if (!success || !type) { + return NULL; + } + + structStatement *smnt = new structStatement(up, *type); + success = smnt->addStructToParentScope(); + if (!success) { + delete &smnt; + // Struct wasn't added to scope, free it manually + delete type; + return NULL; + } + + addAttributesTo(smntAttributes, smnt); + + return smnt; + } + statement_t* parser_t::loadNamespaceStatement(attributeTokenMap &smntAttributes) { if (context.size() == 1) { context.printError("Expected a namespace name"); @@ -1349,7 +1498,7 @@ namespace occa { } // Skip [namespace] token - context.set(1); + ++context; tokenVector names; while (true) { @@ -1367,7 +1516,7 @@ namespace occa { success = false; return NULL; } - context.set(1); + ++context; // Find { or :: const opType_t opType = getOperatorType(context[0]); @@ -1386,7 +1535,7 @@ namespace occa { success = false; return NULL; } - context.set(1); + ++context; } namespaceStatement *smnt = NULL; @@ -1420,10 +1569,6 @@ namespace occa { return smnt; } - statement_t* parser_t::loadTypeDeclStatement(attributeTokenMap &smntAttributes) { - return NULL; - } - statement_t* parser_t::loadFunctionStatement(attributeTokenMap &smntAttributes) { vartype_t returnType = loadType(); @@ -1468,7 +1613,7 @@ namespace occa { // func(); <-- function if (opType & operatorType::semicolon) { - context.set(1); + ++context; return new functionStatement(up, func); } @@ -1476,9 +1621,8 @@ namespace occa { functionDeclStatement &funcSmnt = *(new functionDeclStatement(up, func)); success = funcSmnt.addFunctionToParentScope(); if (!success) { - success = false; delete &funcSmnt; - // func wasn't added to scope, free it manually + // Function wasn't added to scope, free it manually delete &func; return NULL; } @@ -1506,7 +1650,7 @@ namespace occa { // Need to make sure we have another token (even if it's not '(') bool error = (context.size() == 1); if (!error) { - context.set(1); + ++context; error = !(getOperatorType(context[0]) & operatorType::parenthesesStart); } @@ -1679,7 +1823,7 @@ namespace occa { // This is basically the same code as loadIfStatement // but with an elif class token_t *elifToken = context[0]; - context.set(1); + ++context; checkIfConditionStatementExists(); if (!success) { return NULL; @@ -1716,7 +1860,7 @@ namespace occa { statement_t* parser_t::loadElseStatement(attributeTokenMap &smntAttributes) { token_t *elseToken = context[0]; - context.set(1); + ++context; elseStatement &elseSmnt = *(new elseStatement(up, elseToken)); pushUp(elseSmnt); @@ -1851,7 +1995,7 @@ namespace occa { statement_t* parser_t::loadDoWhileStatement(attributeTokenMap &smntAttributes) { token_t *doToken = context[0]; - context.set(1); + ++context; whileStatement &whileSmnt = *(new whileStatement(up, doToken, true)); pushUp(whileSmnt); @@ -1903,7 +2047,7 @@ namespace occa { delete &whileSmnt; return NULL; } - context.set(1); + ++context; return &whileSmnt; } @@ -1963,7 +2107,7 @@ namespace occa { statement_t* parser_t::loadCaseStatement(attributeTokenMap &smntAttributes) { token_t *caseToken = context[0]; - context.set(1); + ++context; const int pos = context.getNextOperator(operatorType::colon); // No : found @@ -1993,13 +2137,13 @@ namespace occa { statement_t* parser_t::loadDefaultStatement(attributeTokenMap &smntAttributes) { token_t *defaultToken = context[0]; - context.set(1); + ++context; if (!(getOperatorType(context[0]) & operatorType::colon)) { context.printError("Expected a [:]"); success = false; return NULL; } - context.set(1); + ++context; defaultStatement *smnt = new defaultStatement(up, defaultToken); addAttributesTo(smntAttributes, smnt); @@ -2008,13 +2152,13 @@ namespace occa { statement_t* parser_t::loadContinueStatement(attributeTokenMap &smntAttributes) { token_t *continueToken = context[0]; - context.set(1); + ++context; if (!(getOperatorType(context[0]) & operatorType::semicolon)) { context.printError("[6] Expected a [;]"); success = false; return NULL; } - context.set(1); + ++context; continueStatement *smnt = new continueStatement(up, continueToken); addAttributesTo(smntAttributes, smnt); @@ -2023,13 +2167,13 @@ namespace occa { statement_t* parser_t::loadBreakStatement(attributeTokenMap &smntAttributes) { token_t *breakToken = context[0]; - context.set(1); + ++context; if (!(getOperatorType(context[0]) & operatorType::semicolon)) { context.printError("[7] Expected a [;]"); success = false; return NULL; } - context.set(1); + ++context; breakStatement *smnt = new breakStatement(up, breakToken); addAttributesTo(smntAttributes, smnt); @@ -2038,7 +2182,7 @@ namespace occa { statement_t* parser_t::loadReturnStatement(attributeTokenMap &smntAttributes) { token_t *returnToken = context[0]; - context.set(1); + ++context; const int pos = context.getNextOperator(operatorType::semicolon); // No ; found @@ -2091,13 +2235,13 @@ namespace occa { *((pragmaToken*) context[0])); addAttributesTo(smntAttributes, smnt); - context.set(1); + ++context; return smnt; } statement_t* parser_t::loadGotoStatement(attributeTokenMap &smntAttributes) { - context.set(1); + ++context; if (!(token_t::safeType(context[0]) & tokenType::identifier)) { context.printError("Expected [goto label] identifier"); success = false; diff --git a/src/lang/qualifier.cpp b/src/lang/qualifier.cpp index 3e45452fd..d5a6f2836 100644 --- a/src/lang/qualifier.cpp +++ b/src/lang/qualifier.cpp @@ -102,6 +102,10 @@ namespace occa { qualifier_t::~qualifier_t() {} + bool qualifier_t::operator == (const qualifier_t &other) const { + return (this == &other); + } + udim_t qualifier_t::type() const { return qtype; } diff --git a/src/lang/statement/statement.cpp b/src/lang/statement/statement.cpp index a66f85e9a..7da201da1 100644 --- a/src/lang/statement/statement.cpp +++ b/src/lang/statement/statement.cpp @@ -16,31 +16,36 @@ namespace occa { const int block = (1 << 4); const int namespace_ = (1 << 5); - const int typeDecl = (1 << 6); - const int function = (1 << 7); - const int functionDecl = (1 << 8); - const int classAccess = (1 << 9); - - const int expression = (1 << 10); - const int declaration = (1 << 11); - - const int goto_ = (1 << 12); - const int gotoLabel = (1 << 13); - - const int if_ = (1 << 14); - const int elif_ = (1 << 15); - const int else_ = (1 << 16); - const int for_ = (1 << 17); - const int while_ = (1 << 18); - const int switch_ = (1 << 19); - const int case_ = (1 << 20); - const int default_ = (1 << 21); - const int continue_ = (1 << 22); - const int break_ = (1 << 23); - - const int return_ = (1 << 24); - - const int attribute = (1 << 25); + const int function = (1 << 6); + const int functionDecl = (1 << 7); + + const int class_ = (1 << 8); + const int struct_ = (1 << 9); + const int classAccess = (1 << 10); + + const int enum_ = (1 << 11); + const int union_ = (1 << 12); + + const int expression = (1 << 13); + const int declaration = (1 << 14); + + const int goto_ = (1 << 15); + const int gotoLabel = (1 << 16); + + const int if_ = (1 << 17); + const int elif_ = (1 << 18); + const int else_ = (1 << 19); + const int for_ = (1 << 20); + const int while_ = (1 << 21); + const int switch_ = (1 << 22); + const int case_ = (1 << 23); + const int default_ = (1 << 24); + const int continue_ = (1 << 25); + const int break_ = (1 << 26); + + const int return_ = (1 << 27); + + const int attribute = (1 << 28); } statement_t::statement_t(blockStatement *up_, diff --git a/src/lang/statement/structStatement.cpp b/src/lang/statement/structStatement.cpp new file mode 100644 index 000000000..39af1151f --- /dev/null +++ b/src/lang/statement/structStatement.cpp @@ -0,0 +1,41 @@ +#include +#include +#include + +namespace occa { + namespace lang { + structStatement::structStatement(blockStatement *up_, + struct_t &struct__) : + statement_t(up_, struct__.source), + struct_(struct__) {} + + structStatement::structStatement(blockStatement *up_, + const structStatement& other) : + statement_t(up_, other.source), + struct_((struct_t&) other.struct_.clone()) {} + + statement_t& structStatement::clone_(blockStatement *up_) const { + return *(new structStatement(up_, *this)); + } + + int structStatement::type() const { + return statementType::struct_; + } + + std::string structStatement::statementName() const { + return "struct"; + } + + bool structStatement::addStructToParentScope() { + if (up && !up->addToScope(struct_)) { + return false; + } + return true; + } + + void structStatement::print(printer &pout) const { + struct_.printDeclaration(pout); + pout << '\n'; + } + } +} diff --git a/src/lang/tokenContext.cpp b/src/lang/tokenContext.cpp index ccf5f6f55..50f0888cd 100644 --- a/src/lang/tokenContext.cpp +++ b/src/lang/tokenContext.cpp @@ -24,6 +24,7 @@ namespace occa { tp.end = 0; hasError = false; + supressErrors = false; const int tokenCount = (int) tokens.size(); for (int i = 0; i < tokenCount; ++i) { @@ -63,12 +64,14 @@ namespace occa { // Make sure we have a proper pair if (!pairStack.size()) { - std::stringstream ss; - ss << "Could not find an opening '" - << pairEndOp.pairStr - << '\''; - token->printError(ss.str()); - hasError = true; + if (!supressErrors) { + std::stringstream ss; + ss << "Could not find an opening '" + << pairEndOp.pairStr + << '\''; + token->printError(ss.str()); + hasError = true; + } return; } @@ -79,12 +82,14 @@ namespace occa { *((pairOperator_t*) tokens[pairIndex]->to().op); if (pairStartOp.opType != (pairEndOp.opType >> 1)) { - std::stringstream ss; - ss << "Could not find a closing '" - << pairStartOp.pairStr - << '\''; - tokens[pairIndex]->printError(ss.str()); - hasError = true; + if (!supressErrors) { + std::stringstream ss; + ss << "Could not find a closing '" + << pairStartOp.pairStr + << '\''; + tokens[pairIndex]->printError(ss.str()); + hasError = true; + } return; } @@ -98,12 +103,14 @@ namespace occa { pairOperator_t &pairStartOp = *((pairOperator_t*) tokens[pairIndex]->to().op); - std::stringstream ss; - ss << "Could not find a closing '" - << pairStartOp.pairStr - << '\''; - tokens[pairIndex]->printError(ss.str()); - hasError = true; + if (!supressErrors) { + std::stringstream ss; + ss << "Could not find a closing '" + << pairStartOp.pairStr + << '\''; + tokens[pairIndex]->printError(ss.str()); + hasError = true; + } } } @@ -207,6 +214,16 @@ namespace occa { return tokens[tp.start + index]; } + tokenContext& tokenContext::operator ++ () { + set(1); + return *this; + } + + tokenContext& tokenContext::operator ++ (int) { + set(1); + return *this; + } + void tokenContext::setToken(const int index, token_t *value) { if (!indexInRange(index)) { @@ -265,6 +282,9 @@ namespace occa { } void tokenContext::printError(const std::string &message) { + if (supressErrors) { + return; + } token_t *token = getPrintToken(false); if (!token) { occa::printError(io::stderr, "[No Token] " + message); @@ -274,6 +294,9 @@ namespace occa { } void tokenContext::printErrorAtEnd(const std::string &message) { + if (supressErrors) { + return; + } token_t *token = getPrintToken(true); if (!token) { occa::printError(io::stderr, "[No Token] " + message); diff --git a/src/lang/type/struct.cpp b/src/lang/type/struct.cpp index e121c8f27..d29287228 100644 --- a/src/lang/type/struct.cpp +++ b/src/lang/type/struct.cpp @@ -1,23 +1,95 @@ #include +#include +#include namespace occa { namespace lang { struct_t::struct_t() : - structure_t("") {} + type_t() {} + + struct_t::struct_t(identifierToken &nameToken) : + type_t(nameToken) {} + + struct_t::struct_t(const struct_t &other) : + type_t(other) { + + const int count = (int) other.fields.size(); + for (int i = 0; i < count; ++i) { + fields.push_back( + other.fields[i].clone() + ); + } + } int struct_t::type() const { return typeType::struct_; } type_t& struct_t::clone() const { - return *(new struct_t()); + return *(new struct_t(*this)); } dtype_t struct_t::dtype() const { - return dtype::byte; + dtype_t dtype_; + + const int fieldCount = (int) fields.size(); + for (int i = 0; i < fieldCount; ++i) { + const variable_t &var = fields[i]; + dtype_.addField(var.name(), + var.dtype()); + } + + return dtype_; + } + + void struct_t::addField(variable_t &field) { + fields.push_back(field.clone()); + } + + void struct_t::addFields(variableVector &fields_) { + const int fieldCount = (int) fields_.size(); + for (int i = 0; i < fieldCount; ++i) { + fields.push_back(fields_[i].clone()); + } } void struct_t::printDeclaration(printer &pout) const { + pout << "struct"; + const std::string name_ = name(); + if (name_.size()) { + pout << ' ' << name_; + } + + const int fieldCount = (int) fields.size(); + if (!fieldCount) { + pout << " {};"; + } else { + vartype_t prevVartype; + + pout << " {\n"; + pout.addIndentation(); + pout.printIndentation(); + + for (int i = 0; i < fieldCount; ++i) { + const variable_t &var = fields[i]; + if (prevVartype != var.vartype) { + if (i) { + pout << ";\n"; + pout.printIndentation(); + } + prevVartype = var.vartype; + var.printDeclaration(pout); + } else { + pout << ", "; + var.printExtraDeclaration(pout); + } + } + pout << ";\n"; + + pout.removeIndentation(); + pout.printIndentation(); + pout << "};"; + } } } } diff --git a/src/lang/type/vartype.cpp b/src/lang/type/vartype.cpp index 4e044b38f..ea37185b6 100644 --- a/src/lang/type/vartype.cpp +++ b/src/lang/type/vartype.cpp @@ -156,9 +156,8 @@ namespace occa { vartype_t flat = flatten(); vartype_t otherFlat = other.flatten(); - if (((*flat.type) != (*otherFlat.type)) || - (flat.isReference() != otherFlat.isReference()) || - (flat.qualifiers != otherFlat.qualifiers)) { + if (((*flat.type) != (*otherFlat.type)) || + (flat.qualifiers != otherFlat.qualifiers)) { return false; } @@ -180,11 +179,11 @@ namespace occa { ? otherFlat : flat); const int minPointerCount = ((pointerCount < otherPointerCount) - ? otherPointerCount - : pointerCount); - const int maxPointerCount = ((pointerCount < otherPointerCount) ? pointerCount : otherPointerCount); + const int maxPointerCount = ((pointerCount < otherPointerCount) + ? otherPointerCount + : pointerCount); for (int i = 0; i < minPointerCount; ++i) { if (flat.pointers[i].qualifiers diff --git a/tests/src/lang/parser.cpp b/tests/src/lang/parser.cpp deleted file mode 100644 index 78174afba..000000000 --- a/tests/src/lang/parser.cpp +++ /dev/null @@ -1,1415 +0,0 @@ -#include "parserUtils.hpp" - -void testTypeMethods(); -void testPeek(); -void testTypeLoading(); -void testTypeErrors(); -void testLoading(); -void testErrors(); -void testScope(); - -int main(const int argc, const char **argv) { - parser.addAttribute(); - parser.addAttribute(); - parser.addAttribute(); - parser.addAttribute(); - parser.addAttribute(); - parser.addAttribute(); - - testTypeMethods(); - testPeek(); - - testTypeLoading(); - testTypeErrors(); - - testLoading(); - testErrors(); - - testScope(); - - return 0; -} - -//---[ Utils ]-------------------------- -void testTypeMethods() { - setSource("int a = 0;"); - setSource("const int *a = 0;"); - - // Make sure we can handle [long] and [long long] - setSource("long a = 0;"); - setSource("const long a = 0;"); - - setSource("long long a = 0;"); - setSource("const long long *a = 0;"); -} -//====================================== - -//---[ Peek ]--------------------------- -void testPeek() { - testStatementPeek("", - statementType::none); - - testStatementPeek(";", - statementType::empty); - - testStatementPeek("#pragma", - statementType::pragma); - testStatementPeek("#pragma occa @dim(5)\n" - "int *x;", - statementType::declaration); - - testStatementPeek("1 + 2;", - statementType::expression); - testStatementPeek("\"a\";", - statementType::expression); - testStatementPeek("'a';", - statementType::expression); - testStatementPeek("sizeof 3;", - statementType::expression); - - testStatementPeek("int a = 0;", - statementType::declaration); - testStatementPeek("const int a = 0;", - statementType::declaration); - testStatementPeek("long long a, b = 0, *c = 0;", - statementType::declaration); - - testStatementPeek("goto foo;", - statementType::goto_); - - testStatementPeek("foo:", - statementType::gotoLabel); - - testStatementPeek("{}", - statementType::block); - - testStatementPeek("namespace foo {}", - statementType::namespace_); - - testStatementPeek("if (true) {}", - statementType::if_); - testStatementPeek("else if (true) {}", - statementType::elif_); - testStatementPeek("else {}", - statementType::else_); - - testStatementPeek("for () {}", - statementType::for_); - - testStatementPeek("while () {}", - statementType::while_); - testStatementPeek("do {} while ()", - statementType::while_); - - testStatementPeek("switch () {}", - statementType::switch_); - testStatementPeek("case foo:", - statementType::case_); - testStatementPeek("default:", - statementType::default_); - testStatementPeek("continue;", - statementType::continue_); - testStatementPeek("break;", - statementType::break_); - - testStatementPeek("return 0;", - statementType::return_); - - testStatementPeek("public:", - statementType::classAccess); - testStatementPeek("protected:", - statementType::classAccess); - testStatementPeek("private:", - statementType::classAccess); -} -//====================================== - -//---[ Type Loading ]------------------- -vartype_t loadType(const std::string &s) { - setSource(s); - return parser.loadType(); -} - -#define assertType(str_) \ - setSource(str_); \ - parser.loadType(); \ - ASSERT_FALSE(parser.isLoadingFunctionPointer()); \ - ASSERT_FALSE(parser.isLoadingVariable()) - -vartype_t loadVariableType(const std::string &s) { - setSource(s); - return parser.loadVariable().vartype; -} - -#define assertVariable(str_) \ - setSource(str_); \ - parser.loadType(); \ - ASSERT_FALSE(parser.isLoadingFunctionPointer()); \ - ASSERT_TRUE(parser.isLoadingVariable()) - -variable_t loadVariable(const std::string &s) { - setSource(s); - return parser.loadVariable(); -} - -#define assertFunctionPointer(str_) \ - setSource(str_); \ - parser.loadType(); \ - ASSERT_TRUE(parser.isLoadingFunctionPointer()) - -void testBaseTypeLoading(); -void testPointerTypeLoading(); -void testReferenceTypeLoading(); -void testArrayTypeLoading(); -void testVariableLoading(); -void testArgumentLoading(); -void testFunctionPointerLoading(); - -void testTypeLoading() { - testBaseTypeLoading(); - testPointerTypeLoading(); - testReferenceTypeLoading(); - testArrayTypeLoading(); - testVariableLoading(); - testArgumentLoading(); - testFunctionPointerLoading(); -} - -void testBaseTypeLoading() { - vartype_t type; - - // Test base type - type = loadType("int"); - ASSERT_EQ(0, - type.qualifiers.size()); - ASSERT_EQ(&int_, - type.type); - - type = loadType("const volatile float"); - ASSERT_EQ(2, - type.qualifiers.size()); - ASSERT_TRUE(type.has(volatile_)); - ASSERT_TRUE(type.has(const_)); - ASSERT_EQ(&float_, - type.type); - - type = loadType("const long long"); - ASSERT_EQ(2, - type.qualifiers.size()); - ASSERT_TRUE(type.has(const_)); - ASSERT_TRUE(type.has(longlong_)); - ASSERT_EQ(&int_, - type.type); - - // Test weird order declaration - type = loadType("double const long long"); - ASSERT_EQ(2, - type.qualifiers.size()); - ASSERT_TRUE(type.has(const_)); - ASSERT_TRUE(type.has(longlong_)); - ASSERT_EQ(&double_, - type.type); -} - -void testPointerTypeLoading() { - vartype_t type; - - type = loadType("int *"); - ASSERT_EQ(1, - (int) type.pointers.size()); - ASSERT_EQ(0, - type.pointers[0].qualifiers.size()); - - type = loadType("const volatile float * const"); - ASSERT_EQ(1, - (int) type.pointers.size()); - ASSERT_EQ(1, - type.pointers[0].qualifiers.size()); - ASSERT_TRUE(type.pointers[0].has(const_)); - - type = loadType("float * const * volatile ** const volatile"); - ASSERT_EQ(4, - (int) type.pointers.size()); - ASSERT_TRUE(type.pointers[0].has(const_)); - ASSERT_TRUE(type.pointers[1].has(volatile_)); - ASSERT_EQ(0, - type.pointers[2].qualifiers.size()); - ASSERT_TRUE(type.pointers[3].has(const_)); - ASSERT_TRUE(type.pointers[3].has(volatile_)); -} - -void testReferenceTypeLoading() { - vartype_t type; - - type = loadType("int"); - ASSERT_FALSE(type.isReference()); - type = loadType("int &"); - ASSERT_TRUE(type.isReference()); - - type = loadType("int *"); - ASSERT_FALSE(type.isReference()); - type = loadType("int *&"); - ASSERT_TRUE(type.isReference()); - - type = loadType("int ***"); - ASSERT_FALSE(type.isReference()); - type = loadType("int ***&"); - ASSERT_TRUE(type.isReference()); -} - -void testArrayTypeLoading() { - vartype_t type; - - assertType("int[]"); - type = loadVariableType("int[]"); - ASSERT_EQ(1, - (int) type.arrays.size()); - - assertType("int[][]"); - type = loadVariableType("int[][]"); - ASSERT_EQ(2, - (int) type.arrays.size()); - - assertType("int[1]"); - type = loadVariableType("int[1]"); - ASSERT_EQ(1, - (int) type.arrays.size()); - ASSERT_EQ(1, - (int) type.arrays[0].evaluateSize()); - - assertType("int[1 + 3][7]"); - type = loadVariableType("int[1 + 3][7]"); - ASSERT_EQ(2, - (int) type.arrays.size()); - ASSERT_EQ(4, - (int) type.arrays[0].evaluateSize()); - ASSERT_EQ(7, - (int) type.arrays[1].evaluateSize()); -} - -void testVariableLoading() { - variable_t var; - std::string varName; - - assertVariable("int varname[]"); - var = loadVariable("int varname[]"); - varName = var.name(); - ASSERT_EQ("varname", - varName); - ASSERT_EQ(1, - (int) var.vartype.arrays.size()); - - assertVariable("int varname[][]"); - var = loadVariable("int varname[][]"); - varName = var.name(); - ASSERT_EQ("varname", - varName); - ASSERT_EQ(2, - (int) var.vartype.arrays.size()); - - assertVariable("int varname[1]"); - var = loadVariable("int varname[1]"); - varName = var.name(); - ASSERT_EQ("varname", - varName); - ASSERT_EQ(1, - (int) var.vartype.arrays.size()); - ASSERT_EQ(1, - (int) var.vartype.arrays[0].evaluateSize()); - - assertVariable("int varname[1 + 3][7]"); - var = loadVariable("int varname[1 + 3][7]"); - varName = var.name(); - ASSERT_EQ("varname", - varName); - ASSERT_EQ(2, - (int) var.vartype.arrays.size()); - ASSERT_EQ(4, - (int) var.vartype.arrays[0].evaluateSize()); - ASSERT_EQ(7, - (int) var.vartype.arrays[1].evaluateSize()); -} - -void testArgumentLoading() { - // Test argument detection - tokenRangeVector argRanges; - - setSource(""); - parser.getArgumentRanges(argRanges); - ASSERT_EQ(0, - (int) argRanges.size()); - - setSource("a, b"); - parser.getArgumentRanges(argRanges); - ASSERT_EQ(2, - (int) argRanges.size()); - - setSource("(,,)"); - parser.getArgumentRanges(argRanges); - ASSERT_EQ(1, - (int) argRanges.size()); - - setSource("(,,), (,,), (,,)"); - parser.getArgumentRanges(argRanges); - ASSERT_EQ(3, - (int) argRanges.size()); - - // Removes trailing comma - setSource("a, b,"); - parser.getArgumentRanges(argRanges); - ASSERT_EQ(2, - (int) argRanges.size()); - - // Test arguments -} - -void testFunctionPointerLoading() { - variable_t var; - std::string varName; - -#define varFunc var.vartype.type->to() - - // Test pointer vs block - assertFunctionPointer("int (*varname)()"); - var = loadVariable("int (*varname)()"); - - ASSERT_EQ_BINARY(typeType::functionPtr, - var.vartype.type->type()); - varName = var.name(); - ASSERT_EQ("varname", - varName); - ASSERT_FALSE(varFunc.isBlock); - - assertFunctionPointer("int (^varname)()"); - var = loadVariable("int (^varname)()"); - varName = var.name(); - ASSERT_EQ("varname", - varName); - ASSERT_TRUE(varFunc.isBlock); - - // Test arguments - var = loadVariable("int (*varname)()"); - ASSERT_EQ(0, - (int) varFunc.args.size()); - - var = loadVariable("int (*varname)(const int i = 0,)"); - ASSERT_EQ(1, - (int) varFunc.args.size()); - ASSERT_EQ(&int_, - varFunc.args[0].vartype.type); - ASSERT_TRUE(varFunc.args[0].vartype.has(const_)); - ASSERT_EQ("i", - varFunc.args[0].name()); - - var = loadVariable("int (*varname)(int, double,)"); - ASSERT_EQ(2, - (int) varFunc.args.size()); - ASSERT_EQ(&int_, - varFunc.args[0].vartype.type); - ASSERT_EQ(&double_, - varFunc.args[1].vartype.type); - -#undef varFunc -} - -void testBaseTypeErrors(); -void testPointerTypeErrors(); -void testArrayTypeErrors(); -void testVariableErrors(); - -void testTypeErrors() { - std::cerr << "\n---[ Testing type errors ]----------------------\n\n"; - testBaseTypeErrors(); - testPointerTypeErrors(); - testArrayTypeErrors(); - testVariableErrors(); - std::cerr << "================================================\n\n"; -} - -void testBaseTypeErrors() { - vartype_t type; - type = loadType("const"); - type = loadType("const foo"); - type = loadType("const const"); - type = loadType("long long long"); -} - -void testPointerTypeErrors() { - vartype_t type; - type = loadType("const *"); - type = loadType("float * long"); -} - -void testArrayTypeErrors() { - assertType("int[-]"); - loadVariableType("int[-]"); -} - -void testVariableErrors() { - assertVariable("int varname[-]"); - loadVariable("int varname[-]"); -} -//====================================== - -//---[ Loading ]------------------------ -void testExpressionLoading(); -void testDeclarationLoading(); -void testNamespaceLoading(); -void testTypeDeclLoading(); -void testFunctionLoading(); -void testIfLoading(); -void testForLoading(); -void testWhileLoading(); -void testSwitchLoading(); -void testJumpsLoading(); -void testClassAccessLoading(); -void testAttributeLoading(); -void testPragmaLoading(); -void testGotoLoading(); -void testBlockLoading(); - -void testLoading() { - testExpressionLoading(); - testDeclarationLoading(); - testNamespaceLoading(); - // testTypeDeclLoading(); - testFunctionLoading(); - testIfLoading(); - testForLoading(); - testWhileLoading(); - testSwitchLoading(); - testJumpsLoading(); - testClassAccessLoading(); - testPragmaLoading(); - testGotoLoading(); - testBlockLoading(); - testAttributeLoading(); -} - -void testExpressionLoading() { - statement_t *statement; -#define expr (*(statement->to().expr)) - - setStatement("2 + 3;", - statementType::expression); - ASSERT_TRUE(expr.canEvaluate()); - ASSERT_EQ(5, - (int) expr.evaluate()); - - setStatement("-3;", - statementType::expression); - ASSERT_TRUE(expr.canEvaluate()); - ASSERT_EQ(-3, - (int) expr.evaluate()); - - setStatement("sizeof(4);", - statementType::expression); - ASSERT_TRUE(expr.canEvaluate()); - ASSERT_EQ((uint64_t) sizeof(4), - (uint64_t) expr.evaluate()); - - parseAndPrintSource("a[i] = b >= 0 ? c[i] : -d[-e - 1];"); - -#undef expr -} - -void testDeclarationLoading() { - statement_t *statement; - -#define decl statement->to() -#define decls decl.declarations -#define declVar(N) (*decls[N].variable) -#define declValue(N) (*(decls[N].value)) - - setStatement("int foo;", - statementType::declaration); - ASSERT_EQ(1, - (int) decls.size()); - - setStatement("int foo = 3;", - statementType::declaration); - ASSERT_EQ(1, - (int) decls.size()); - ASSERT_EQ("foo", - declVar(0).name()); - ASSERT_EQ(3, - (int) declValue(0).evaluate()); - - setStatement("int foo = 3, bar = 4;", - statementType::declaration); - ASSERT_EQ(2, - (int) decls.size()); - ASSERT_EQ("foo", - declVar(0).name()); - ASSERT_EQ(3, - (int) declValue(0).evaluate()); - ASSERT_EQ("bar", - declVar(1).name()); - ASSERT_EQ(4, - (int) declValue(1).evaluate()); - - setStatement("int foo = 3, *bar = 4;", - statementType::declaration); - ASSERT_EQ(2, - (int) decls.size()); - ASSERT_EQ("foo", - declVar(0).name()); - ASSERT_EQ(0, - (int) declVar(0).vartype.pointers.size()); - ASSERT_EQ(3, - (int) declValue(0).evaluate()); - ASSERT_EQ("bar", - declVar(1).name()); - ASSERT_EQ(1, - (int) declVar(1).vartype.pointers.size()); - ASSERT_EQ(4, - (int) declValue(1).evaluate()); - - setStatement("int *foo = 3, bar = 4;", - statementType::declaration); - ASSERT_EQ(2, - (int) decls.size()); - ASSERT_EQ("foo", - declVar(0).name()); - ASSERT_EQ(1, - (int) declVar(0).vartype.pointers.size()); - ASSERT_EQ(3, - (int) declValue(0).evaluate()); - ASSERT_EQ("bar", - declVar(1).name()); - ASSERT_EQ(0, - (int) declVar(1).vartype.pointers.size()); - ASSERT_EQ(4, - (int) declValue(1).evaluate()); - - setStatement("int foo { 3 }, *bar { 4 };", - statementType::declaration); - ASSERT_EQ(2, - (int) decls.size()); - ASSERT_EQ("foo", - declVar(0).name()); - ASSERT_EQ(3, - (int) declValue(0).evaluate()); - ASSERT_EQ("bar", - declVar(1).name()); - ASSERT_EQ(4, - (int) declValue(1).evaluate()); - - setStatement("int foo : 1 = 3, bar : 2 = 4;", - statementType::declaration); - ASSERT_EQ(2, - (int) decls.size()); - ASSERT_EQ("foo", - declVar(0).name()); - ASSERT_EQ(1, - declVar(0).vartype.bitfield); - ASSERT_EQ("bar", - declVar(1).name()); - ASSERT_EQ(2, - declVar(1).vartype.bitfield); - -#undef decl -#undef decls -#undef declVar -#undef declValue -} - -void testNamespaceLoading() { - statement_t *statement; - setStatement("namespace foo {}", - statementType::namespace_); - - ASSERT_EQ("foo", - statement->to().name()); - - setStatement("namespace A::B::C {}", - statementType::namespace_); - - namespaceStatement &A = statement->to(); - ASSERT_EQ("A", - A.name()); - - namespaceStatement &B = A[0]->to(); - ASSERT_EQ("B", - B.name()); - - namespaceStatement &C = B[0]->to(); - ASSERT_EQ("C", - C.name()); -} - -void testTypeDeclLoading() { - // statement_t *statement; - // TODO: typedef - // TODO: struct - // TODO: enum - // TODO: union - // TODO: class -} - -void testFunctionLoading() { - statement_t *statement; - -#define funcSmnt statement->to() -#define funcDeclSmnt statement->to() -#define func funcSmnt.function -#define funcDecl funcDeclSmnt.function - - setStatement("void foo();", - statementType::function); - ASSERT_EQ("foo", - func.name()); - ASSERT_EQ(&void_, - func.returnType.type); - - setStatement("int* bar();", - statementType::function); - ASSERT_EQ("bar", - func.name()); - ASSERT_EQ(&int_, - func.returnType.type); - ASSERT_EQ(1, - (int) func.returnType.pointers.size()); - - setStatement("void foo2(int a) {}", - statementType::functionDecl); - ASSERT_EQ("foo2", - funcDecl.name()); - ASSERT_EQ(&void_, - funcDecl.returnType.type); - ASSERT_EQ(1, - (int) funcDecl.args.size()); - ASSERT_EQ(0, - funcDeclSmnt.size()); - - setStatement("void foo3(int a, int b) { int x; int y; }", - statementType::functionDecl); - ASSERT_EQ("foo3", - funcDecl.name()); - ASSERT_EQ(&void_, - funcDecl.returnType.type); - ASSERT_EQ(2, - (int) funcDecl.args.size()); - ASSERT_EQ(2, - funcDeclSmnt.size()); - -#undef funcSmnt -#undef func -} - -void testIfLoading() { - statement_t *statement; - -#define ifSmnt statement->to() -#define condition (*ifSmnt.condition) -#define decl condition.to() -#define decls decl.declarations -#define declVar(N) (*decls[N].variable) -#define declValue(N) (*(decls[N].value)) - - setStatement("if (true) {}", - statementType::if_); - ASSERT_EQ_BINARY(statementType::expression, - condition.type()); - ASSERT_EQ(0, - (int) ifSmnt.elifSmnts.size()); - ASSERT_FALSE(!!ifSmnt.elseSmnt); - - setStatement("if (true) {}\n" - "else if (true) {}", - statementType::if_); - ASSERT_EQ_BINARY(statementType::expression, - condition.type()); - ASSERT_EQ(1, - (int) ifSmnt.elifSmnts.size()); - ASSERT_FALSE(!!ifSmnt.elseSmnt); - - setStatement("if (true) {}\n" - "else if (true) {}\n" - "else if (true) {}", - statementType::if_); - ASSERT_EQ_BINARY(statementType::expression, - condition.type()); - ASSERT_EQ(2, - (int) ifSmnt.elifSmnts.size()); - ASSERT_FALSE(!!ifSmnt.elseSmnt); - - setStatement("if (true) {}\n" - "else if (true) {}\n" - "else {}", - statementType::if_); - ASSERT_EQ_BINARY(statementType::expression, - condition.type()); - ASSERT_EQ(1, - (int) ifSmnt.elifSmnts.size()); - ASSERT_TRUE(!!ifSmnt.elseSmnt); - - // Test declaration in conditional - setStatement("if (const int i = 3) {}", - statementType::if_); - ASSERT_EQ_BINARY(statementType::declaration, - condition.type()); - ASSERT_EQ(1, - (int) decls.size()); - ASSERT_EQ("i", - declVar(0).name()); - ASSERT_EQ(3, - (int) declValue(0).evaluate()); - - // TODO: Test that 'i' exists in the if scope - -#undef ifSmnt -#undef condition -#undef decl -#undef decls -#undef declVar -#undef declValue -} - -void testForLoading() { - statement_t *statement; - -#define forSmnt statement->to() -#define init (*forSmnt.init) -#define check (*forSmnt.check) -#define update (*forSmnt.update) -#define initDecl init.to() - - setStatement("for (;;) {}", - statementType::for_); - ASSERT_EQ_BINARY(statementType::empty, - init.type()); - ASSERT_EQ_BINARY(statementType::empty, - check.type()); - ASSERT_EQ_BINARY(statementType::empty, - update.type()); - ASSERT_EQ(0, - (int) forSmnt.children.size()); - - setStatement("for (;;);", - statementType::for_); - ASSERT_EQ_BINARY(statementType::empty, - init.type()); - ASSERT_EQ_BINARY(statementType::empty, - check.type()); - ASSERT_EQ_BINARY(statementType::empty, - update.type()); - ASSERT_EQ(1, - (int) forSmnt.children.size()); - - // Test declaration in conditional - setStatement("for (int i = 0; i < 2; ++i) {}", - statementType::for_); - ASSERT_EQ_BINARY(statementType::declaration, - init.type()); - ASSERT_EQ_BINARY(statementType::expression, - check.type()); - ASSERT_EQ_BINARY(statementType::expression, - update.type()); - - // TODO: Test that 'i' exists in the if scope - -#undef forSmnt -#undef init -#undef check -#undef update -#undef initDecl -} - -void testWhileLoading() { - statement_t *statement; - (void) statement; - - setStatement("while (true) {}", - statementType::while_); - setStatement("while (true);", - statementType::while_); - - // Test declaration in conditional - setStatement("while (int i = 0) {}", - statementType::while_); - - // TODO: Test that 'i' exists in the if scope - - // Same tests for do-while - setStatement("do {} while (true);", - statementType::while_); - setStatement("do ; while (true);", - statementType::while_); - - setStatement("do {} while (int i = 0);", - statementType::while_); -} - -void testSwitchLoading() { - statement_t *statement; - -#define switchSmnt statement->to() -#define condition (*switchSmnt.condition) -#define decl condition.to() - - setStatement("switch (2) {}", - statementType::switch_); - ASSERT_EQ_BINARY(statementType::expression, - condition.type()); - ASSERT_EQ(0, - switchSmnt.size()); - - // Weird cases - setStatement("switch (2) ;", - statementType::switch_); - ASSERT_EQ_BINARY(statementType::expression, - condition.type()); - ASSERT_EQ(1, - switchSmnt.size()); - - // Weird cases - setStatement("switch (2) case 2:;", - statementType::switch_); - ASSERT_EQ_BINARY(statementType::expression, - condition.type()); - ASSERT_EQ(2, - switchSmnt.size()); - - setStatement("switch (2) default:;", - statementType::switch_); - ASSERT_EQ_BINARY(statementType::expression, - condition.type()); - ASSERT_EQ(2, - switchSmnt.size()); - - setStatement("switch (2) case 2: 2;", - statementType::switch_); - ASSERT_EQ_BINARY(statementType::expression, - condition.type()); - ASSERT_EQ(2, - switchSmnt.size()); - - setStatement("switch (2) default: 2;", - statementType::switch_); - ASSERT_EQ_BINARY(statementType::expression, - condition.type()); - ASSERT_EQ(2, - switchSmnt.size()); - - // Test declaration in conditional - setStatement("switch (int i = 2) {}", - statementType::switch_); - ASSERT_EQ_BINARY(statementType::declaration, - condition.type()); - ASSERT_EQ(0, - switchSmnt.size()); - ASSERT_EQ(1, - (int) decl.declarations.size()); - ASSERT_EQ(2, - (int) decl.declarations[0].value->evaluate()); - - // TODO: Test that 'i' exists in the if scope - -#undef switchSmnt -#undef condition -#undef decl -} - -void testJumpsLoading() { - statement_t *statement; - -#define returnValue statement->to().value - - setStatement("continue;", - statementType::continue_); - - setStatement("break;", - statementType::break_); - - setStatement("return;", - statementType::return_); - ASSERT_EQ((void*) NULL, - (void*) returnValue); - - setStatement("return 1 + (2 * 3);", - statementType::return_); - ASSERT_EQ(7, - (int) returnValue->evaluate()); - -#undef returnValue -} - -void testClassAccessLoading() { - statement_t *statement; - -#define access statement->to().access - - setStatement("public:", - statementType::classAccess); - ASSERT_EQ(classAccess::public_, - access); - - setStatement("protected:", - statementType::classAccess); - ASSERT_EQ(classAccess::protected_, - access); - - setStatement("private:", - statementType::classAccess); - ASSERT_EQ(classAccess::private_, - access); - -#undef access -} - -void testPragmaLoading() { - statement_t *statement; - -#define pragma_ statement->to() - - setStatement("#pragma", - statementType::pragma); - ASSERT_EQ("", - pragma_.token.value); - - setStatement("#pragma omp parallel for", - statementType::pragma); - ASSERT_EQ("omp parallel for", - pragma_.token.value); - -#undef pragma_ -} - -void testGotoLoading() { - statement_t *statement; - (void) statement; - - setStatement("label:", - statementType::gotoLabel); - setStatement("goto label;", - statementType::goto_); -} - -void testBlockLoading() { - statement_t *statement; - setStatement("{}", - statementType::block); - - ASSERT_EQ(0, - statement->to().size()); - - setStatement("{\n" - " const int i = 0;\n" - " ++i;\n" - " if (true) {}\n" - " if (true) {} else {}\n" - " while (true) {}\n" - " do {} while (true);\n" - " switch (1) default:;\n" - "}\n", - statementType::block); - - blockStatement &smnt = statement->to(); - ASSERT_EQ(7, - smnt.size()); - ASSERT_EQ_BINARY(statementType::declaration, - smnt[0]->type()); - ASSERT_EQ_BINARY(statementType::expression, - smnt[1]->type()); - ASSERT_EQ_BINARY(statementType::if_, - smnt[2]->type()); - ASSERT_EQ_BINARY(statementType::if_, - smnt[3]->type()); - ASSERT_EQ_BINARY(statementType::while_, - smnt[4]->type()); - ASSERT_EQ_BINARY(statementType::while_, - smnt[5]->type()); - ASSERT_EQ_BINARY(statementType::switch_, - smnt[6]->type()); -} - -void testAttributeLoading() { - statement_t *statement; - -#define smntAttr(N) statement->attributes[N]->name() -#define declSmnt statement->to() -#define decls declSmnt.declarations -#define declVar(N) (*decls[N].variable) -#define declVarAttr(N, A) declVar(N).attributes[A] - - setStatement("const int *x @dim(2, 3), *y;", - statementType::declaration); - ASSERT_EQ(0, - (int) statement->attributes.size()); - ASSERT_EQ(1, - (int) declVar(0).attributes.size()); - ASSERT_EQ("dim", - declVarAttr(0, "dim").name()); - ASSERT_EQ(0, - (int) declVar(1).attributes.size()); - - attributeToken_t &xDim1 = declVarAttr(0, "dim"); - ASSERT_EQ(2, - (int) xDim1.args.size()); - ASSERT_EQ(2, - (int) xDim1[0]->expr->evaluate()); - ASSERT_EQ(3, - (int) xDim1[1]->expr->evaluate()); - - setStatement("const int *x @dummy(x=2, y=3), *y;", - statementType::declaration); - ASSERT_EQ(0, - (int) statement->attributes.size()); - ASSERT_EQ(1, - (int) declVar(0).attributes.size()); - ASSERT_EQ("dummy", - declVarAttr(0, "dummy").name()); - ASSERT_EQ(0, - (int) declVar(1).attributes.size()); - - attributeToken_t &xDummy = declVarAttr(0, "dummy"); - ASSERT_EQ(2, - (int) xDummy.kwargs.size()); - ASSERT_EQ(2, - (int) xDummy["x"]->expr->evaluate()); - ASSERT_EQ(3, - (int) xDummy["y"]->expr->evaluate()); - - setStatement("@dim(2 + 2, 10 - 5) const int *x, *y;", - statementType::declaration); - ASSERT_EQ(1, - (int) statement->attributes.size()); - ASSERT_EQ(1, - (int) declVar(0).attributes.size()); - ASSERT_EQ("dim", - declVarAttr(0, "dim").name()); - ASSERT_EQ(1, - (int) declVar(1).attributes.size()); - ASSERT_EQ("dim", - declVarAttr(1, "dim").name()); - - attributeToken_t &xDim3 = declVarAttr(0, "dim"); - ASSERT_EQ(2, - (int) xDim3.args.size()); - ASSERT_EQ(4, - (int) xDim3[0]->expr->evaluate()); - ASSERT_EQ(5, - (int) xDim3[1]->expr->evaluate()); - - attributeToken_t &xDim4 = declVarAttr(1, "dim"); - ASSERT_EQ(2, - (int) xDim4.args.size()); - ASSERT_EQ(4, - (int) xDim4[0]->expr->evaluate()); - ASSERT_EQ(5, - (int) xDim4[1]->expr->evaluate()); - - std::cerr << "\n---[ @dim Transformations ]---------------------\n"; - parseAndPrintSource("@dim(1,2,3) int *x; x(1,2,3);"); - parseAndPrintSource("@dim(3,2,1) int *x; x(1,2,3);"); - parseAndPrintSource("@dim(1,2,3) @dimOrder(0,1,2) int *x; x(1,2,3);"); - parseAndPrintSource("@dim(1,2,3) @dimOrder(1,2,0) int *x; x(1,2,3);"); - parseAndPrintSource("@dim(1,2,3) @dimOrder(2,0,1) int *x; x(1,2,3);"); - parseAndPrintSource("@dim(1,2,3) @dimOrder(2,1,0) int *x; x(1,2,3);"); - std::cerr << "==============================================\n"; - - std::cerr << "\n---[ @tile Transformations ]--------------------\n"; - parseAndPrintSource("for (int i = 0; i < (1 + 2 + N + 6); ++i; @tile(16, @outer, @inner, check=false)) {" - " int x;" - "}"); - parseAndPrintSource("for (int i = 0; i > (1 + 2 + N + 6); --i; @tile(16, @outer, @inner, check=false)) {" - " int x;" - "}"); - parseAndPrintSource("for (int i = 0; i <= (1 + 2 + N + 6); i++; @tile(16, @outer, @inner, check=false)) {" - " int x;" - "}"); - parseAndPrintSource("for (int i = 0; i >= (1 + 2 + N + 6); i--; @tile(16, @outer, @inner, check=true)) {" - " int x;" - "}"); - parseAndPrintSource("for (int i = 0; (1 + 2 + N + 6) > i; i += 3; @tile(16, @outer, @inner, check=true)) {" - " int x;" - "}"); - parseAndPrintSource("for (int i = 0; (1 + 2 + N + 6) <= i; i -= 2 + 3; @tile(16, @outer, @inner, check=true)) {" - " int x;" - "}"); - std::cerr << "==============================================\n\n"; - -#undef smntAttr -#undef declSmnt -#undef decls -#undef declVar -#undef declVarAttr -} -//====================================== - -//---[ Errors ]------------------------ -void testExpressionErrors(); -void testDeclarationErrors(); -void testNamespaceErrors(); -void testTypeDeclErrors(); -void testFunctionErrors(); -void testIfErrors(); -void testForErrors(); -void testWhileErrors(); -void testSwitchErrors(); -void testJumpsErrors(); -void testClassAccessErrors(); -void testAttributeErrors(); -void testGotoErrors(); - -void testErrors() { - std::cerr << "\n---[ Testing parser errors ]--------------------\n\n"; - testExpressionErrors(); - testDeclarationErrors(); - testNamespaceErrors(); - // testTypeDeclErrors(); - testFunctionErrors(); - testIfErrors(); - testForErrors(); - testWhileErrors(); - testSwitchErrors(); - testJumpsErrors(); - testClassAccessErrors(); - testGotoErrors(); - testAttributeErrors(); - std::cerr << "==============================================\n\n"; -} - -void testExpressionErrors() { - parseBadSource("2 + 3"); - parseBadSource("-2"); - parseBadSource("2 = {}"); - parseBadSource("sizeof(4)"); -} - -void testDeclarationErrors() { - parseBadSource("int foo"); - parseBadSource("int foo = 3"); - parseBadSource("int foo = 3, bar = 4"); - parseBadSource("int foo = 3, *bar = 4"); -} - -void testNamespaceErrors() { - parseBadSource("namespace foo"); - parseBadSource("namespace foo::"); - parseBadSource("namespace foo::bar::"); - parseBadSource("namespace foo + {}"); -} - -void testTypeDeclErrors() { -} - -void testFunctionErrors() { - parseBadSource("int foo()"); -} - -void testIfErrors() { - parseBadSource("if (true)"); - parseBadSource("if () {}"); - parseBadSource("if (if (true) {}) {}"); - parseBadSource("if (;;) {}"); - - parseBadSource("if (;) @attr {}"); -} - -void testForErrors() { - // parseBadSource("for () {}"); - // parseBadSource("for (;) {}"); - // parseBadSource("for (;;)"); - // parseBadSource("for (;;;;) {}"); - - parseBadSource("for (;;) @attr {}"); -} - -void testWhileErrors() { - parseBadSource("while (;;) {}"); - parseBadSource("do {};"); - parseBadSource("do;"); - parseBadSource("do {} while (;;);"); - parseBadSource("do {} while (true)"); - parseBadSource("do ; while (true)"); - parseBadSource("do {} while (int i = 0)"); - - parseBadSource("while (;) @attr {}"); - parseBadSource("do {} while (int i = 0) @attr;"); -} - -void testSwitchErrors() { - parseBadSource("switch ()"); - parseBadSource("switch (true)"); - parseBadSource("switch (true) case 2:"); - parseBadSource("switch (true) default:"); - parseBadSource("switch (;;) {}"); - - parseBadSource("switch (true) @attr {}"); - parseBadSource("switch (true) @attr default:;"); -} - -void testJumpsErrors() { - parseBadSource("continue"); - parseBadSource("break"); - parseBadSource("return"); - parseBadSource("return 1 + 2"); -} - -void testClassAccessErrors() { - parseBadSource("public"); - parseBadSource("protected"); - parseBadSource("private"); -} - -void testGotoErrors() { - parseBadSource("goto"); - parseBadSource("goto;"); -} - -void testAttributeErrors() { - parseBadSource("@attr"); - parseBadSource("@attr()"); - - parseBadSource("@dim;"); - parseBadSource("@dim int x;"); - parseBadSource("@dim() int x;"); - parseBadSource("@dim(x=1) int x;"); - - parseBadSource("@tile(16);"); - parseBadSource("@tile(16, x=1);"); - parseBadSource("@tile(16, check='o');"); - parseBadSource("for (i = 0;;; @tile(16)) {}"); - parseBadSource("for (float i = 0;;; @tile(16)) {}"); - parseBadSource("for (int i = 0, j = 0;;; @tile(16)) {}"); - parseBadSource("for (int i = 0;;; @tile(16)) {}"); - parseBadSource("for (int i = 0; i + 2;; @tile(16)) {}"); - parseBadSource("for (int i = 0; j < 2;; @tile(16)) {}"); - parseBadSource("for (int i = 0; i < 2;; @tile(16)) {}"); - parseBadSource("for (int i = 0; i < 2; i *= 2; @tile(16)) {}"); - parseBadSource("for (int i = 0; i < 2; ++j; @tile(16)) {}"); - - parseBadSource("@dimOrder(1, 0);"); - parseBadSource("@dimOrder() int x;"); - parseBadSource("@dimOrder(,) int x;"); - parseBadSource("@dimOrder(1,x,0) int x;"); - parseBadSource("@dimOrder(0,1,2,4) int x;"); - parseBadSource("@dimOrder(-1,1,2,4) int x;"); - parseBadSource("@dimOrder(11) int x;"); -} -//====================================== - -//---[ Scope ]-------------------------- -const std::string scopeTestSource = ( - "int x;\n" - "typedef int myInt;\n" - "\n" - "void foo() {\n" - " int x;\n" - " {\n" - " int x;\n" - " }\n" - " typedef int myInt;\n" - "}\n" - "\n" - "int main(const int argc, const char **argv) {\n" - " int x = argc;\n" - " int a;\n" - " if (true) {\n" - " int x = 0;\n" - " int b;\n" - " if (true) {\n" - " int x = 1;\n" - " int c;\n" - " if (true) {\n" - " int x = 2;\n" - " int d;\n" - " }\n" - " }\n" - " }\n" - "}\n"); - -void testScopeUp(); -void testScopeKeywords(); -void testScopeErrors(); - -void testScope() { - testScopeUp(); - testScopeKeywords(); - - testScopeErrors(); -} - -void testScopeUp() { - parseSource(scopeTestSource); - - blockStatement &root = parser.root; - - statement_t *x = root[0]; - blockStatement &foo = root[2]->to(); - blockStatement &main = root[3]->to(); - blockStatement &fooBlock = foo[1]->to(); - - ASSERT_EQ(&root, - x->up); - ASSERT_EQ(&root, - foo.up); - ASSERT_EQ(&root, - main.up); - ASSERT_EQ(&foo, - fooBlock.up); -} - -void testScopeKeywords() { - parseSource(scopeTestSource); - - blockStatement &root = parser.root; - blockStatement &foo = root[2]->to(); - blockStatement &fooBlock = foo[1]->to(); - - // Make sure we can find variables 'x' - ASSERT_TRUE(root.hasInScope("x")); - ASSERT_TRUE(foo.hasInScope("x")); - ASSERT_TRUE(fooBlock.hasInScope("x")); - - // Make sure variables 'x' exist - ASSERT_EQ_BINARY(keywordType::variable, - root.getScopeKeyword("x").type()); - ASSERT_EQ_BINARY(keywordType::variable, - foo.getScopeKeyword("x").type()); - ASSERT_EQ_BINARY(keywordType::variable, - fooBlock.getScopeKeyword("x").type()); - - // Make sure all instances are different - ASSERT_NEQ(&root.getScopeKeyword("x").to().variable, - &foo.getScopeKeyword("x").to().variable); - - ASSERT_NEQ(&root.getScopeKeyword("x").to().variable, - &fooBlock.getScopeKeyword("x").to().variable); - - ASSERT_NEQ(&foo.getScopeKeyword("x").to().variable, - &fooBlock.getScopeKeyword("x").to().variable); - - // Test function - ASSERT_EQ_BINARY(keywordType::function, - root.getScopeKeyword("foo").type()); - ASSERT_EQ_BINARY(keywordType::function, - root.getScopeKeyword("main").type()); - - // Test types - ASSERT_EQ_BINARY(keywordType::type, - root.getScopeKeyword("myInt").type()); - ASSERT_EQ_BINARY(keywordType::type, - foo.getScopeKeyword("myInt").type()); -} - -void testScopeErrors() { - std::cerr << "\n---[ Testing scope errors ]---------------------\n\n"; - const std::string var = "int x;\n"; - const std::string type = "typedef int x;\n"; - const std::string func = "void x() {}\n"; - std::string sources[3] = { var, type, func }; - - for (int j = 0; j < 3; ++j) { - for (int i = 0; i < 3; ++i) { - parseBadSource(sources[j] + sources[i]); - std::cout << '\n'; - } - } - - parseBadSource("int x, x;\n"); - std::cerr << "==============================================\n\n"; -} -//====================================== diff --git a/tests/src/lang/parser/errors.cpp b/tests/src/lang/parser/errors.cpp new file mode 100644 index 000000000..adb6dc4a6 --- /dev/null +++ b/tests/src/lang/parser/errors.cpp @@ -0,0 +1,183 @@ +#include "utils.hpp" + +void testExpressionErrors(); +void testDeclarationErrors(); +void testNamespaceErrors(); +void testFunctionErrors(); +void testStructErrors(); +void testIfErrors(); +void testForErrors(); +void testWhileErrors(); +void testSwitchErrors(); +void testJumpsErrors(); +void testClassAccessErrors(); +void testAttributeErrors(); +void testGotoErrors(); + +void testErrors() { +} + +int main(const int argc, const char **argv) { + setupParser(); + + std::cerr << "\n---[ Testing parser errors ]--------------------\n\n"; + testExpressionErrors(); + testDeclarationErrors(); + testNamespaceErrors(); + testFunctionErrors(); + testStructErrors(); + testIfErrors(); + testForErrors(); + testWhileErrors(); + testSwitchErrors(); + testJumpsErrors(); + testClassAccessErrors(); + testGotoErrors(); + testAttributeErrors(); + std::cerr << "==============================================\n\n"; + + return 0; +} + +void testExpressionErrors() { + parseBadSource("2 + 3"); + parseBadSource("-2"); + parseBadSource("2 = {}"); + parseBadSource("sizeof(4)"); +} + +void testDeclarationErrors() { + parseBadSource("int foo"); + parseBadSource("int foo = 3"); + parseBadSource("int foo = 3, bar = 4"); + parseBadSource("int foo = 3, *bar = 4"); +} + +void testNamespaceErrors() { + parseBadSource("namespace foo"); + parseBadSource("namespace foo::"); + parseBadSource("namespace foo::bar::"); + parseBadSource("namespace foo + {}"); +} + +void testFunctionErrors() { + parseBadSource("int foo()"); +} + +void testStructErrors() { + parseBadSource("struct {}"); + parseBadSource("struct foo {}"); + parseBadSource("struct foo;"); + parseBadSource("struct foo {\n" + " 3;\n" + "};"); + parseBadSource("struct foo {\n" + " void bar();\n" + "};"); + parseBadSource("struct foo {\n" + " public:\n" + " int x, y, z;\n" + "};"); + parseBadSource("struct foo {\n" + " protected:\n" + " int x, y, z;\n" + "};"); + parseBadSource("struct foo {\n" + " private:\n" + " int x, y, z;\n" + "};"); + parseBadSource("struct foo {\n" + " int x = 3;\n" + "};"); +} + +void testIfErrors() { + parseBadSource("if (true)"); + parseBadSource("if () {}"); + parseBadSource("if (if (true) {}) {}"); + parseBadSource("if (;;) {}"); + + parseBadSource("if (;) @attr {}"); +} + +void testForErrors() { + // parseBadSource("for () {}"); + // parseBadSource("for (;) {}"); + // parseBadSource("for (;;)"); + // parseBadSource("for (;;;;) {}"); + + parseBadSource("for (;;) @attr {}"); +} + +void testWhileErrors() { + parseBadSource("while (;;) {}"); + parseBadSource("do {};"); + parseBadSource("do;"); + parseBadSource("do {} while (;;);"); + parseBadSource("do {} while (true)"); + parseBadSource("do ; while (true)"); + parseBadSource("do {} while (int i = 0)"); + + parseBadSource("while (;) @attr {}"); + parseBadSource("do {} while (int i = 0) @attr;"); +} + +void testSwitchErrors() { + parseBadSource("switch ()"); + parseBadSource("switch (true)"); + parseBadSource("switch (true) case 2:"); + parseBadSource("switch (true) default:"); + parseBadSource("switch (;;) {}"); + + parseBadSource("switch (true) @attr {}"); + parseBadSource("switch (true) @attr default:;"); +} + +void testJumpsErrors() { + parseBadSource("continue"); + parseBadSource("break"); + parseBadSource("return"); + parseBadSource("return 1 + 2"); +} + +void testClassAccessErrors() { + parseBadSource("public"); + parseBadSource("protected"); + parseBadSource("private"); +} + +void testGotoErrors() { + parseBadSource("goto"); + parseBadSource("goto;"); +} + +void testAttributeErrors() { + parseBadSource("@attr"); + parseBadSource("@attr()"); + + parseBadSource("@dim;"); + parseBadSource("@dim int x;"); + parseBadSource("@dim() int x;"); + parseBadSource("@dim(x=1) int x;"); + + parseBadSource("@tile(16);"); + parseBadSource("@tile(16, x=1);"); + parseBadSource("@tile(16, check='o');"); + parseBadSource("for (i = 0;;; @tile(16)) {}"); + parseBadSource("for (float i = 0;;; @tile(16)) {}"); + parseBadSource("for (int i = 0, j = 0;;; @tile(16)) {}"); + parseBadSource("for (int i = 0;;; @tile(16)) {}"); + parseBadSource("for (int i = 0; i + 2;; @tile(16)) {}"); + parseBadSource("for (int i = 0; j < 2;; @tile(16)) {}"); + parseBadSource("for (int i = 0; i < 2;; @tile(16)) {}"); + parseBadSource("for (int i = 0; i < 2; i *= 2; @tile(16)) {}"); + parseBadSource("for (int i = 0; i < 2; ++j; @tile(16)) {}"); + + parseBadSource("@dimOrder(1, 0);"); + parseBadSource("@dimOrder() int x;"); + parseBadSource("@dimOrder(,) int x;"); + parseBadSource("@dimOrder(1,x,0) int x;"); + parseBadSource("@dimOrder(0,1,2,4) int x;"); + parseBadSource("@dimOrder(-1,1,2,4) int x;"); + parseBadSource("@dimOrder(11) int x;"); +} diff --git a/tests/src/lang/parser/peek.cpp b/tests/src/lang/parser/peek.cpp new file mode 100644 index 000000000..f2d86122d --- /dev/null +++ b/tests/src/lang/parser/peek.cpp @@ -0,0 +1,89 @@ +#include "utils.hpp" + +void testPeek(); + +int main(const int argc, const char **argv) { + setupParser(); + + testPeek(); + + return 0; +} + +void testPeek() { + testStatementPeek("", + statementType::none); + + testStatementPeek(";", + statementType::empty); + + testStatementPeek("#pragma", + statementType::pragma); + testStatementPeek("#pragma occa @dim(5)\n" + "int *x;", + statementType::declaration); + + testStatementPeek("1 + 2;", + statementType::expression); + testStatementPeek("\"a\";", + statementType::expression); + testStatementPeek("'a';", + statementType::expression); + testStatementPeek("sizeof 3;", + statementType::expression); + + testStatementPeek("int a = 0;", + statementType::declaration); + testStatementPeek("const int a = 0;", + statementType::declaration); + testStatementPeek("long long a, b = 0, *c = 0;", + statementType::declaration); + + testStatementPeek("goto foo;", + statementType::goto_); + + testStatementPeek("foo:", + statementType::gotoLabel); + + testStatementPeek("{}", + statementType::block); + + testStatementPeek("namespace foo {}", + statementType::namespace_); + + testStatementPeek("if (true) {}", + statementType::if_); + testStatementPeek("else if (true) {}", + statementType::elif_); + testStatementPeek("else {}", + statementType::else_); + + testStatementPeek("for () {}", + statementType::for_); + + testStatementPeek("while () {}", + statementType::while_); + testStatementPeek("do {} while ()", + statementType::while_); + + testStatementPeek("switch () {}", + statementType::switch_); + testStatementPeek("case foo:", + statementType::case_); + testStatementPeek("default:", + statementType::default_); + testStatementPeek("continue;", + statementType::continue_); + testStatementPeek("break;", + statementType::break_); + + testStatementPeek("return 0;", + statementType::return_); + + testStatementPeek("public:", + statementType::classAccess); + testStatementPeek("protected:", + statementType::classAccess); + testStatementPeek("private:", + statementType::classAccess); +} diff --git a/tests/src/lang/parser/scope.cpp b/tests/src/lang/parser/scope.cpp new file mode 100644 index 000000000..c7b6edb5a --- /dev/null +++ b/tests/src/lang/parser/scope.cpp @@ -0,0 +1,126 @@ +#include "utils.hpp" + +void testScopeUp(); +void testScopeKeywords(); +void testScopeErrors(); + +int main(const int argc, const char **argv) { + setupParser(); + + testScopeUp(); + testScopeKeywords(); + testScopeErrors(); + + return 0; +} + +const std::string scopeTestSource = ( + "int x;\n" + "typedef int myInt;\n" + "\n" + "void foo() {\n" + " int x;\n" + " {\n" + " int x;\n" + " }\n" + " typedef int myInt;\n" + "}\n" + "\n" + "int main(const int argc, const char **argv) {\n" + " int x = argc;\n" + " int a;\n" + " if (true) {\n" + " int x = 0;\n" + " int b;\n" + " if (true) {\n" + " int x = 1;\n" + " int c;\n" + " if (true) {\n" + " int x = 2;\n" + " int d;\n" + " }\n" + " }\n" + " }\n" + "}\n" +); + +void testScopeUp() { + parseSource(scopeTestSource); + + blockStatement &root = parser.root; + + statement_t *x = root[0]; + blockStatement &foo = root[2]->to(); + blockStatement &main = root[3]->to(); + blockStatement &fooBlock = foo[1]->to(); + + ASSERT_EQ(&root, + x->up); + ASSERT_EQ(&root, + foo.up); + ASSERT_EQ(&root, + main.up); + ASSERT_EQ(&foo, + fooBlock.up); +} + +void testScopeKeywords() { + parseSource(scopeTestSource); + + blockStatement &root = parser.root; + blockStatement &foo = root[2]->to(); + blockStatement &fooBlock = foo[1]->to(); + + // Make sure we can find variables 'x' + ASSERT_TRUE(root.hasInScope("x")); + ASSERT_TRUE(foo.hasInScope("x")); + ASSERT_TRUE(fooBlock.hasInScope("x")); + + // Make sure variables 'x' exist + ASSERT_EQ_BINARY(keywordType::variable, + root.getScopeKeyword("x").type()); + ASSERT_EQ_BINARY(keywordType::variable, + foo.getScopeKeyword("x").type()); + ASSERT_EQ_BINARY(keywordType::variable, + fooBlock.getScopeKeyword("x").type()); + + // Make sure all instances are different + ASSERT_NEQ(&root.getScopeKeyword("x").to().variable, + &foo.getScopeKeyword("x").to().variable); + + ASSERT_NEQ(&root.getScopeKeyword("x").to().variable, + &fooBlock.getScopeKeyword("x").to().variable); + + ASSERT_NEQ(&foo.getScopeKeyword("x").to().variable, + &fooBlock.getScopeKeyword("x").to().variable); + + // Test function + ASSERT_EQ_BINARY(keywordType::function, + root.getScopeKeyword("foo").type()); + ASSERT_EQ_BINARY(keywordType::function, + root.getScopeKeyword("main").type()); + + // Test types + ASSERT_EQ_BINARY(keywordType::type, + root.getScopeKeyword("myInt").type()); + ASSERT_EQ_BINARY(keywordType::type, + foo.getScopeKeyword("myInt").type()); +} + +void testScopeErrors() { + std::cerr << "\n---[ Testing scope errors ]---------------------\n\n"; + const std::string var = "int x;\n"; + const std::string type = "typedef int x;\n"; + const std::string func = "void x() {}\n"; + std::string sources[3] = { var, type, func }; + + for (int j = 0; j < 3; ++j) { + for (int i = 0; i < 3; ++i) { + parseBadSource(sources[j] + sources[i]); + std::cout << '\n'; + } + } + + parseBadSource("int x, x;\n"); + std::cerr << "==============================================\n\n"; +} diff --git a/tests/src/lang/parser/statementLoading.cpp b/tests/src/lang/parser/statementLoading.cpp new file mode 100644 index 000000000..9d611d876 --- /dev/null +++ b/tests/src/lang/parser/statementLoading.cpp @@ -0,0 +1,735 @@ +#include "utils.hpp" + +void testExpressionLoading(); +void testDeclarationLoading(); +void testNamespaceLoading(); +void testStructLoading(); +void testClassLoading(); +void testUnionLoading(); +void testEnumLoading(); +void testFunctionLoading(); +void testIfLoading(); +void testForLoading(); +void testWhileLoading(); +void testSwitchLoading(); +void testJumpsLoading(); +void testClassAccessLoading(); +void testAttributeLoading(); +void testPragmaLoading(); +void testGotoLoading(); +void testBlockLoading(); + +int main(const int argc, const char **argv) { + setupParser(); + + testExpressionLoading(); + testDeclarationLoading(); + testNamespaceLoading(); + testStructLoading(); + // testClassLoading(); + // testUnionLoading(); + // testEnumLoading(); + testFunctionLoading(); + testIfLoading(); + testForLoading(); + testWhileLoading(); + testSwitchLoading(); + testJumpsLoading(); + testClassAccessLoading(); + testPragmaLoading(); + testGotoLoading(); + testBlockLoading(); + testAttributeLoading(); + + return 0; +} + +void testExpressionLoading() { + statement_t *statement; +#define expr (*(statement->to().expr)) + + setStatement("2 + 3;", + statementType::expression); + ASSERT_TRUE(expr.canEvaluate()); + ASSERT_EQ(5, + (int) expr.evaluate()); + + setStatement("-3;", + statementType::expression); + ASSERT_TRUE(expr.canEvaluate()); + ASSERT_EQ(-3, + (int) expr.evaluate()); + + setStatement("sizeof(4);", + statementType::expression); + ASSERT_TRUE(expr.canEvaluate()); + ASSERT_EQ((uint64_t) sizeof(4), + (uint64_t) expr.evaluate()); + + parseAndPrintSource("a[i] = b >= 0 ? c[i] : -d[-e - 1];"); + +#undef expr +} + +void testDeclarationLoading() { + statement_t *statement; + +#define decl statement->to() +#define decls decl.declarations +#define declVar(N) (*decls[N].variable) +#define declValue(N) (*(decls[N].value)) + + setStatement("int foo;", + statementType::declaration); + ASSERT_EQ(1, + (int) decls.size()); + + setStatement("int foo = 3;", + statementType::declaration); + ASSERT_EQ(1, + (int) decls.size()); + ASSERT_EQ("foo", + declVar(0).name()); + ASSERT_EQ(3, + (int) declValue(0).evaluate()); + + setStatement("int foo = 3, bar = 4;", + statementType::declaration); + ASSERT_EQ(2, + (int) decls.size()); + ASSERT_EQ("foo", + declVar(0).name()); + ASSERT_EQ(3, + (int) declValue(0).evaluate()); + ASSERT_EQ("bar", + declVar(1).name()); + ASSERT_EQ(4, + (int) declValue(1).evaluate()); + + setStatement("int foo = 3, *bar = 4;", + statementType::declaration); + ASSERT_EQ(2, + (int) decls.size()); + ASSERT_EQ("foo", + declVar(0).name()); + ASSERT_EQ(0, + (int) declVar(0).vartype.pointers.size()); + ASSERT_EQ(3, + (int) declValue(0).evaluate()); + ASSERT_EQ("bar", + declVar(1).name()); + ASSERT_EQ(1, + (int) declVar(1).vartype.pointers.size()); + ASSERT_EQ(4, + (int) declValue(1).evaluate()); + + setStatement("int *foo = 3, bar = 4;", + statementType::declaration); + ASSERT_EQ(2, + (int) decls.size()); + ASSERT_EQ("foo", + declVar(0).name()); + ASSERT_EQ(1, + (int) declVar(0).vartype.pointers.size()); + ASSERT_EQ(3, + (int) declValue(0).evaluate()); + ASSERT_EQ("bar", + declVar(1).name()); + ASSERT_EQ(0, + (int) declVar(1).vartype.pointers.size()); + ASSERT_EQ(4, + (int) declValue(1).evaluate()); + + setStatement("int foo { 3 }, *bar { 4 };", + statementType::declaration); + ASSERT_EQ(2, + (int) decls.size()); + ASSERT_EQ("foo", + declVar(0).name()); + ASSERT_EQ(3, + (int) declValue(0).evaluate()); + ASSERT_EQ("bar", + declVar(1).name()); + ASSERT_EQ(4, + (int) declValue(1).evaluate()); + + setStatement("int foo : 1 = 3, bar : 2 = 4;", + statementType::declaration); + ASSERT_EQ(2, + (int) decls.size()); + ASSERT_EQ("foo", + declVar(0).name()); + ASSERT_EQ(1, + declVar(0).vartype.bitfield); + ASSERT_EQ("bar", + declVar(1).name()); + ASSERT_EQ(2, + declVar(1).vartype.bitfield); + +#undef decl +#undef decls +#undef declVar +#undef declValue +} + +void testNamespaceLoading() { + statement_t *statement; + setStatement("namespace foo {}", + statementType::namespace_); + + ASSERT_EQ("foo", + statement->to().name()); + + setStatement("namespace A::B::C {}", + statementType::namespace_); + + namespaceStatement &A = statement->to(); + ASSERT_EQ("A", + A.name()); + + namespaceStatement &B = A[0]->to(); + ASSERT_EQ("B", + B.name()); + + namespaceStatement &C = B[0]->to(); + ASSERT_EQ("C", + C.name()); +} + +void testStructLoading() { + statement_t *statement; + +#define structSmnt statement->to() +#define structType structSmnt.struct_ + + setStatement( + "struct vec3 {\n" + " int x, *y, &z;\n" + "};", + statementType::struct_ + ); + + ASSERT_EQ(3, + (int) structType.fields.size()); + + ASSERT_EQ("x", + structType.fields[0].name()); + ASSERT_EQ(&int_, + structType.fields[0].vartype.type); + + ASSERT_EQ("y", + structType.fields[1].name()); + ASSERT_EQ(&int_, + structType.fields[1].vartype.type); + + ASSERT_EQ("z", + structType.fields[2].name()); + ASSERT_EQ(&int_, + structType.fields[2].vartype.type); + +#undef structSmnt +#undef structType +} + +void testClassLoading() { + // TODO: Add class tests +} + +void testUnionLoading() { + // TODO: Add union tests +} + +void testEnumLoading() { + // TODO: Add enum tests + +} + +void testFunctionLoading() { + statement_t *statement; + +#define funcSmnt statement->to() +#define funcDeclSmnt statement->to() +#define func funcSmnt.function +#define funcDecl funcDeclSmnt.function + + setStatement("void foo();", + statementType::function); + ASSERT_EQ("foo", + func.name()); + ASSERT_EQ(&void_, + func.returnType.type); + + setStatement("int* bar();", + statementType::function); + ASSERT_EQ("bar", + func.name()); + ASSERT_EQ(&int_, + func.returnType.type); + ASSERT_EQ(1, + (int) func.returnType.pointers.size()); + + setStatement("void foo2(int a) {}", + statementType::functionDecl); + ASSERT_EQ("foo2", + funcDecl.name()); + ASSERT_EQ(&void_, + funcDecl.returnType.type); + ASSERT_EQ(1, + (int) funcDecl.args.size()); + ASSERT_EQ(0, + funcDeclSmnt.size()); + + setStatement("void foo3(int a, int b) { int x; int y; }", + statementType::functionDecl); + ASSERT_EQ("foo3", + funcDecl.name()); + ASSERT_EQ(&void_, + funcDecl.returnType.type); + ASSERT_EQ(2, + (int) funcDecl.args.size()); + ASSERT_EQ(2, + funcDeclSmnt.size()); + +#undef funcSmnt +#undef func +} + +void testIfLoading() { + statement_t *statement; + +#define ifSmnt statement->to() +#define condition (*ifSmnt.condition) +#define decl condition.to() +#define decls decl.declarations +#define declVar(N) (*decls[N].variable) +#define declValue(N) (*(decls[N].value)) + + setStatement("if (true) {}", + statementType::if_); + ASSERT_EQ_BINARY(statementType::expression, + condition.type()); + ASSERT_EQ(0, + (int) ifSmnt.elifSmnts.size()); + ASSERT_FALSE(!!ifSmnt.elseSmnt); + + setStatement("if (true) {}\n" + "else if (true) {}", + statementType::if_); + ASSERT_EQ_BINARY(statementType::expression, + condition.type()); + ASSERT_EQ(1, + (int) ifSmnt.elifSmnts.size()); + ASSERT_FALSE(!!ifSmnt.elseSmnt); + + setStatement("if (true) {}\n" + "else if (true) {}\n" + "else if (true) {}", + statementType::if_); + ASSERT_EQ_BINARY(statementType::expression, + condition.type()); + ASSERT_EQ(2, + (int) ifSmnt.elifSmnts.size()); + ASSERT_FALSE(!!ifSmnt.elseSmnt); + + setStatement("if (true) {}\n" + "else if (true) {}\n" + "else {}", + statementType::if_); + ASSERT_EQ_BINARY(statementType::expression, + condition.type()); + ASSERT_EQ(1, + (int) ifSmnt.elifSmnts.size()); + ASSERT_TRUE(!!ifSmnt.elseSmnt); + + // Test declaration in conditional + setStatement("if (const int i = 3) {}", + statementType::if_); + ASSERT_EQ_BINARY(statementType::declaration, + condition.type()); + ASSERT_EQ(1, + (int) decls.size()); + ASSERT_EQ("i", + declVar(0).name()); + ASSERT_EQ(3, + (int) declValue(0).evaluate()); + + // TODO: Test that 'i' exists in the if scope + +#undef ifSmnt +#undef condition +#undef decl +#undef decls +#undef declVar +#undef declValue +} + +void testForLoading() { + statement_t *statement; + +#define forSmnt statement->to() +#define init (*forSmnt.init) +#define check (*forSmnt.check) +#define update (*forSmnt.update) +#define initDecl init.to() + + setStatement("for (;;) {}", + statementType::for_); + ASSERT_EQ_BINARY(statementType::empty, + init.type()); + ASSERT_EQ_BINARY(statementType::empty, + check.type()); + ASSERT_EQ_BINARY(statementType::empty, + update.type()); + ASSERT_EQ(0, + (int) forSmnt.children.size()); + + setStatement("for (;;);", + statementType::for_); + ASSERT_EQ_BINARY(statementType::empty, + init.type()); + ASSERT_EQ_BINARY(statementType::empty, + check.type()); + ASSERT_EQ_BINARY(statementType::empty, + update.type()); + ASSERT_EQ(1, + (int) forSmnt.children.size()); + + // Test declaration in conditional + setStatement("for (int i = 0; i < 2; ++i) {}", + statementType::for_); + ASSERT_EQ_BINARY(statementType::declaration, + init.type()); + ASSERT_EQ_BINARY(statementType::expression, + check.type()); + ASSERT_EQ_BINARY(statementType::expression, + update.type()); + + // TODO: Test that 'i' exists in the if scope + +#undef forSmnt +#undef init +#undef check +#undef update +#undef initDecl +} + +void testWhileLoading() { + statement_t *statement; + (void) statement; + + setStatement("while (true) {}", + statementType::while_); + setStatement("while (true);", + statementType::while_); + + // Test declaration in conditional + setStatement("while (int i = 0) {}", + statementType::while_); + + // TODO: Test that 'i' exists in the if scope + + // Same tests for do-while + setStatement("do {} while (true);", + statementType::while_); + setStatement("do ; while (true);", + statementType::while_); + + setStatement("do {} while (int i = 0);", + statementType::while_); +} + +void testSwitchLoading() { + statement_t *statement; + +#define switchSmnt statement->to() +#define condition (*switchSmnt.condition) +#define decl condition.to() + + setStatement("switch (2) {}", + statementType::switch_); + ASSERT_EQ_BINARY(statementType::expression, + condition.type()); + ASSERT_EQ(0, + switchSmnt.size()); + + // Weird cases + setStatement("switch (2) ;", + statementType::switch_); + ASSERT_EQ_BINARY(statementType::expression, + condition.type()); + ASSERT_EQ(1, + switchSmnt.size()); + + // Weird cases + setStatement("switch (2) case 2:;", + statementType::switch_); + ASSERT_EQ_BINARY(statementType::expression, + condition.type()); + ASSERT_EQ(2, + switchSmnt.size()); + + setStatement("switch (2) default:;", + statementType::switch_); + ASSERT_EQ_BINARY(statementType::expression, + condition.type()); + ASSERT_EQ(2, + switchSmnt.size()); + + setStatement("switch (2) case 2: 2;", + statementType::switch_); + ASSERT_EQ_BINARY(statementType::expression, + condition.type()); + ASSERT_EQ(2, + switchSmnt.size()); + + setStatement("switch (2) default: 2;", + statementType::switch_); + ASSERT_EQ_BINARY(statementType::expression, + condition.type()); + ASSERT_EQ(2, + switchSmnt.size()); + + // Test declaration in conditional + setStatement("switch (int i = 2) {}", + statementType::switch_); + ASSERT_EQ_BINARY(statementType::declaration, + condition.type()); + ASSERT_EQ(0, + switchSmnt.size()); + ASSERT_EQ(1, + (int) decl.declarations.size()); + ASSERT_EQ(2, + (int) decl.declarations[0].value->evaluate()); + + // TODO: Test that 'i' exists in the if scope + +#undef switchSmnt +#undef condition +#undef decl +} + +void testJumpsLoading() { + statement_t *statement; + +#define returnValue statement->to().value + + setStatement("continue;", + statementType::continue_); + + setStatement("break;", + statementType::break_); + + setStatement("return;", + statementType::return_); + ASSERT_EQ((void*) NULL, + (void*) returnValue); + + setStatement("return 1 + (2 * 3);", + statementType::return_); + ASSERT_EQ(7, + (int) returnValue->evaluate()); + +#undef returnValue +} + +void testClassAccessLoading() { + statement_t *statement; + +#define access statement->to().access + + setStatement("public:", + statementType::classAccess); + ASSERT_EQ(classAccess::public_, + access); + + setStatement("protected:", + statementType::classAccess); + ASSERT_EQ(classAccess::protected_, + access); + + setStatement("private:", + statementType::classAccess); + ASSERT_EQ(classAccess::private_, + access); + +#undef access +} + +void testPragmaLoading() { + statement_t *statement; + +#define pragma_ statement->to() + + setStatement("#pragma", + statementType::pragma); + ASSERT_EQ("", + pragma_.token.value); + + setStatement("#pragma omp parallel for", + statementType::pragma); + ASSERT_EQ("omp parallel for", + pragma_.token.value); + +#undef pragma_ +} + +void testGotoLoading() { + statement_t *statement; + (void) statement; + + setStatement("label:", + statementType::gotoLabel); + setStatement("goto label;", + statementType::goto_); +} + +void testBlockLoading() { + statement_t *statement; + setStatement("{}", + statementType::block); + + ASSERT_EQ(0, + statement->to().size()); + + setStatement("{\n" + " const int i = 0;\n" + " ++i;\n" + " if (true) {}\n" + " if (true) {} else {}\n" + " while (true) {}\n" + " do {} while (true);\n" + " switch (1) default:;\n" + "}\n", + statementType::block); + + blockStatement &smnt = statement->to(); + ASSERT_EQ(7, + smnt.size()); + ASSERT_EQ_BINARY(statementType::declaration, + smnt[0]->type()); + ASSERT_EQ_BINARY(statementType::expression, + smnt[1]->type()); + ASSERT_EQ_BINARY(statementType::if_, + smnt[2]->type()); + ASSERT_EQ_BINARY(statementType::if_, + smnt[3]->type()); + ASSERT_EQ_BINARY(statementType::while_, + smnt[4]->type()); + ASSERT_EQ_BINARY(statementType::while_, + smnt[5]->type()); + ASSERT_EQ_BINARY(statementType::switch_, + smnt[6]->type()); +} + +void testAttributeLoading() { + statement_t *statement; + +#define smntAttr(N) statement->attributes[N]->name() +#define declSmnt statement->to() +#define decls declSmnt.declarations +#define declVar(N) (*decls[N].variable) +#define declVarAttr(N, A) declVar(N).attributes[A] + + setStatement("const int *x @dim(2, 3), *y;", + statementType::declaration); + ASSERT_EQ(0, + (int) statement->attributes.size()); + ASSERT_EQ(1, + (int) declVar(0).attributes.size()); + ASSERT_EQ("dim", + declVarAttr(0, "dim").name()); + ASSERT_EQ(0, + (int) declVar(1).attributes.size()); + + attributeToken_t &xDim1 = declVarAttr(0, "dim"); + ASSERT_EQ(2, + (int) xDim1.args.size()); + ASSERT_EQ(2, + (int) xDim1[0]->expr->evaluate()); + ASSERT_EQ(3, + (int) xDim1[1]->expr->evaluate()); + + setStatement("const int *x @dummy(x=2, y=3), *y;", + statementType::declaration); + ASSERT_EQ(0, + (int) statement->attributes.size()); + ASSERT_EQ(1, + (int) declVar(0).attributes.size()); + ASSERT_EQ("dummy", + declVarAttr(0, "dummy").name()); + ASSERT_EQ(0, + (int) declVar(1).attributes.size()); + + attributeToken_t &xDummy = declVarAttr(0, "dummy"); + ASSERT_EQ(2, + (int) xDummy.kwargs.size()); + ASSERT_EQ(2, + (int) xDummy["x"]->expr->evaluate()); + ASSERT_EQ(3, + (int) xDummy["y"]->expr->evaluate()); + + setStatement("@dim(2 + 2, 10 - 5) const int *x, *y;", + statementType::declaration); + ASSERT_EQ(1, + (int) statement->attributes.size()); + ASSERT_EQ(1, + (int) declVar(0).attributes.size()); + ASSERT_EQ("dim", + declVarAttr(0, "dim").name()); + ASSERT_EQ(1, + (int) declVar(1).attributes.size()); + ASSERT_EQ("dim", + declVarAttr(1, "dim").name()); + + attributeToken_t &xDim3 = declVarAttr(0, "dim"); + ASSERT_EQ(2, + (int) xDim3.args.size()); + ASSERT_EQ(4, + (int) xDim3[0]->expr->evaluate()); + ASSERT_EQ(5, + (int) xDim3[1]->expr->evaluate()); + + attributeToken_t &xDim4 = declVarAttr(1, "dim"); + ASSERT_EQ(2, + (int) xDim4.args.size()); + ASSERT_EQ(4, + (int) xDim4[0]->expr->evaluate()); + ASSERT_EQ(5, + (int) xDim4[1]->expr->evaluate()); + + std::cerr << "\n---[ @dim Transformations ]---------------------\n"; + parseAndPrintSource("@dim(1,2,3) int *x; x(1,2,3);"); + parseAndPrintSource("@dim(3,2,1) int *x; x(1,2,3);"); + parseAndPrintSource("@dim(1,2,3) @dimOrder(0,1,2) int *x; x(1,2,3);"); + parseAndPrintSource("@dim(1,2,3) @dimOrder(1,2,0) int *x; x(1,2,3);"); + parseAndPrintSource("@dim(1,2,3) @dimOrder(2,0,1) int *x; x(1,2,3);"); + parseAndPrintSource("@dim(1,2,3) @dimOrder(2,1,0) int *x; x(1,2,3);"); + std::cerr << "==============================================\n"; + + std::cerr << "\n---[ @tile Transformations ]--------------------\n"; + parseAndPrintSource("for (int i = 0; i < (1 + 2 + N + 6); ++i; @tile(16, @outer, @inner, check=false)) {" + " int x;" + "}"); + parseAndPrintSource("for (int i = 0; i > (1 + 2 + N + 6); --i; @tile(16, @outer, @inner, check=false)) {" + " int x;" + "}"); + parseAndPrintSource("for (int i = 0; i <= (1 + 2 + N + 6); i++; @tile(16, @outer, @inner, check=false)) {" + " int x;" + "}"); + parseAndPrintSource("for (int i = 0; i >= (1 + 2 + N + 6); i--; @tile(16, @outer, @inner, check=true)) {" + " int x;" + "}"); + parseAndPrintSource("for (int i = 0; (1 + 2 + N + 6) > i; i += 3; @tile(16, @outer, @inner, check=true)) {" + " int x;" + "}"); + parseAndPrintSource("for (int i = 0; (1 + 2 + N + 6) <= i; i -= 2 + 3; @tile(16, @outer, @inner, check=true)) {" + " int x;" + "}"); + std::cerr << "==============================================\n\n"; + +#undef smntAttr +#undef declSmnt +#undef decls +#undef declVar +#undef declVarAttr +} diff --git a/tests/src/lang/parser/typeLoading.cpp b/tests/src/lang/parser/typeLoading.cpp new file mode 100644 index 000000000..4f6471fe8 --- /dev/null +++ b/tests/src/lang/parser/typeLoading.cpp @@ -0,0 +1,328 @@ +#include "utils.hpp" + +void testBaseTypeLoading(); +void testPointerTypeLoading(); +void testReferenceTypeLoading(); +void testArrayTypeLoading(); +void testVariableLoading(); +void testArgumentLoading(); +void testFunctionPointerLoading(); + +void testBaseTypeErrors(); +void testPointerTypeErrors(); +void testArrayTypeErrors(); +void testVariableErrors(); + +int main(const int argc, const char **argv) { + setupParser(); + + testBaseTypeLoading(); + testPointerTypeLoading(); + testReferenceTypeLoading(); + testArrayTypeLoading(); + testVariableLoading(); + testArgumentLoading(); + testFunctionPointerLoading(); + + std::cerr << "\n---[ Testing type errors ]----------------------\n\n"; + testBaseTypeErrors(); + testPointerTypeErrors(); + testArrayTypeErrors(); + testVariableErrors(); + std::cerr << "================================================\n\n"; + + return 0; +} + +vartype_t loadType(const std::string &s) { + setSource(s); + return parser.loadType(); +} + +#define assertType(str_) \ + setSource(str_); \ + parser.loadType(); \ + ASSERT_FALSE(parser.isLoadingFunctionPointer()); \ + ASSERT_FALSE(parser.isLoadingVariable()) + +vartype_t loadVariableType(const std::string &s) { + setSource(s); + return parser.loadVariable().vartype; +} + +#define assertVariable(str_) \ + setSource(str_); \ + parser.loadType(); \ + ASSERT_FALSE(parser.isLoadingFunctionPointer()); \ + ASSERT_TRUE(parser.isLoadingVariable()) + +variable_t loadVariable(const std::string &s) { + setSource(s); + return parser.loadVariable(); +} + +#define assertFunctionPointer(str_) \ + setSource(str_); \ + parser.loadType(); \ + ASSERT_TRUE(parser.isLoadingFunctionPointer()) + +void testBaseTypeLoading() { + vartype_t type; + + // Test base type + type = loadType("int"); + ASSERT_EQ(0, + type.qualifiers.size()); + ASSERT_EQ(&int_, + type.type); + + type = loadType("const volatile float"); + ASSERT_EQ(2, + type.qualifiers.size()); + ASSERT_TRUE(type.has(volatile_)); + ASSERT_TRUE(type.has(const_)); + ASSERT_EQ(&float_, + type.type); + + type = loadType("const long long"); + ASSERT_EQ(2, + type.qualifiers.size()); + ASSERT_TRUE(type.has(const_)); + ASSERT_TRUE(type.has(longlong_)); + ASSERT_EQ(&int_, + type.type); + + // Test weird order declaration + type = loadType("double const long long"); + ASSERT_EQ(2, + type.qualifiers.size()); + ASSERT_TRUE(type.has(const_)); + ASSERT_TRUE(type.has(longlong_)); + ASSERT_EQ(&double_, + type.type); +} + +void testPointerTypeLoading() { + vartype_t type; + + type = loadType("int *"); + ASSERT_EQ(1, + (int) type.pointers.size()); + ASSERT_EQ(0, + type.pointers[0].qualifiers.size()); + + type = loadType("const volatile float * const"); + ASSERT_EQ(1, + (int) type.pointers.size()); + ASSERT_EQ(1, + type.pointers[0].qualifiers.size()); + ASSERT_TRUE(type.pointers[0].has(const_)); + + type = loadType("float * const * volatile ** const volatile"); + ASSERT_EQ(4, + (int) type.pointers.size()); + ASSERT_TRUE(type.pointers[0].has(const_)); + ASSERT_TRUE(type.pointers[1].has(volatile_)); + ASSERT_EQ(0, + type.pointers[2].qualifiers.size()); + ASSERT_TRUE(type.pointers[3].has(const_)); + ASSERT_TRUE(type.pointers[3].has(volatile_)); +} + +void testReferenceTypeLoading() { + vartype_t type; + + type = loadType("int"); + ASSERT_FALSE(type.isReference()); + type = loadType("int &"); + ASSERT_TRUE(type.isReference()); + + type = loadType("int *"); + ASSERT_FALSE(type.isReference()); + type = loadType("int *&"); + ASSERT_TRUE(type.isReference()); + + type = loadType("int ***"); + ASSERT_FALSE(type.isReference()); + type = loadType("int ***&"); + ASSERT_TRUE(type.isReference()); +} + +void testArrayTypeLoading() { + vartype_t type; + + assertType("int[]"); + type = loadVariableType("int[]"); + ASSERT_EQ(1, + (int) type.arrays.size()); + + assertType("int[][]"); + type = loadVariableType("int[][]"); + ASSERT_EQ(2, + (int) type.arrays.size()); + + assertType("int[1]"); + type = loadVariableType("int[1]"); + ASSERT_EQ(1, + (int) type.arrays.size()); + ASSERT_EQ(1, + (int) type.arrays[0].evaluateSize()); + + assertType("int[1 + 3][7]"); + type = loadVariableType("int[1 + 3][7]"); + ASSERT_EQ(2, + (int) type.arrays.size()); + ASSERT_EQ(4, + (int) type.arrays[0].evaluateSize()); + ASSERT_EQ(7, + (int) type.arrays[1].evaluateSize()); +} + +void testVariableLoading() { + variable_t var; + std::string varName; + + assertVariable("int varname[]"); + var = loadVariable("int varname[]"); + varName = var.name(); + ASSERT_EQ("varname", + varName); + ASSERT_EQ(1, + (int) var.vartype.arrays.size()); + + assertVariable("int varname[][]"); + var = loadVariable("int varname[][]"); + varName = var.name(); + ASSERT_EQ("varname", + varName); + ASSERT_EQ(2, + (int) var.vartype.arrays.size()); + + assertVariable("int varname[1]"); + var = loadVariable("int varname[1]"); + varName = var.name(); + ASSERT_EQ("varname", + varName); + ASSERT_EQ(1, + (int) var.vartype.arrays.size()); + ASSERT_EQ(1, + (int) var.vartype.arrays[0].evaluateSize()); + + assertVariable("int varname[1 + 3][7]"); + var = loadVariable("int varname[1 + 3][7]"); + varName = var.name(); + ASSERT_EQ("varname", + varName); + ASSERT_EQ(2, + (int) var.vartype.arrays.size()); + ASSERT_EQ(4, + (int) var.vartype.arrays[0].evaluateSize()); + ASSERT_EQ(7, + (int) var.vartype.arrays[1].evaluateSize()); +} + +void testArgumentLoading() { + // Test argument detection + tokenRangeVector argRanges; + + setSource(""); + parser.getArgumentRanges(argRanges); + ASSERT_EQ(0, + (int) argRanges.size()); + + setSource("a, b"); + parser.getArgumentRanges(argRanges); + ASSERT_EQ(2, + (int) argRanges.size()); + + setSource("(,,)"); + parser.getArgumentRanges(argRanges); + ASSERT_EQ(1, + (int) argRanges.size()); + + setSource("(,,), (,,), (,,)"); + parser.getArgumentRanges(argRanges); + ASSERT_EQ(3, + (int) argRanges.size()); + + // Removes trailing comma + setSource("a, b,"); + parser.getArgumentRanges(argRanges); + ASSERT_EQ(2, + (int) argRanges.size()); + + // Test arguments +} + +void testFunctionPointerLoading() { + variable_t var; + std::string varName; + +#define varFunc var.vartype.type->to() + + // Test pointer vs block + assertFunctionPointer("int (*varname)()"); + var = loadVariable("int (*varname)()"); + + ASSERT_EQ_BINARY(typeType::functionPtr, + var.vartype.type->type()); + varName = var.name(); + ASSERT_EQ("varname", + varName); + ASSERT_FALSE(varFunc.isBlock); + + assertFunctionPointer("int (^varname)()"); + var = loadVariable("int (^varname)()"); + varName = var.name(); + ASSERT_EQ("varname", + varName); + ASSERT_TRUE(varFunc.isBlock); + + // Test arguments + var = loadVariable("int (*varname)()"); + ASSERT_EQ(0, + (int) varFunc.args.size()); + + var = loadVariable("int (*varname)(const int i = 0,)"); + ASSERT_EQ(1, + (int) varFunc.args.size()); + ASSERT_EQ(&int_, + varFunc.args[0].vartype.type); + ASSERT_TRUE(varFunc.args[0].vartype.has(const_)); + ASSERT_EQ("i", + varFunc.args[0].name()); + + var = loadVariable("int (*varname)(int, double,)"); + ASSERT_EQ(2, + (int) varFunc.args.size()); + ASSERT_EQ(&int_, + varFunc.args[0].vartype.type); + ASSERT_EQ(&double_, + varFunc.args[1].vartype.type); + +#undef varFunc +} + +void testBaseTypeErrors() { + vartype_t type; + type = loadType("const"); + type = loadType("const foo"); + type = loadType("const const"); + type = loadType("long long long"); +} + +void testPointerTypeErrors() { + vartype_t type; + type = loadType("const *"); + type = loadType("float * long"); +} + +void testArrayTypeErrors() { + assertType("int[-]"); + loadVariableType("int[-]"); +} + +void testVariableErrors() { + assertVariable("int varname[-]"); + loadVariable("int varname[-]"); +} diff --git a/tests/src/lang/parser/typeMethods.cpp b/tests/src/lang/parser/typeMethods.cpp new file mode 100644 index 000000000..545dc98e1 --- /dev/null +++ b/tests/src/lang/parser/typeMethods.cpp @@ -0,0 +1,23 @@ +#include "utils.hpp" + +void testTypeMethods(); + +int main(const int argc, const char **argv) { + setupParser(); + + testTypeMethods(); + + return 0; +} + +void testTypeMethods() { + setSource("int a = 0;"); + setSource("const int *a = 0;"); + + // Make sure we can handle [long] and [long long] + setSource("long a = 0;"); + setSource("const long a = 0;"); + + setSource("long long a = 0;"); + setSource("const long long *a = 0;"); +} diff --git a/tests/src/lang/parser/utils.hpp b/tests/src/lang/parser/utils.hpp new file mode 100644 index 000000000..844dda4da --- /dev/null +++ b/tests/src/lang/parser/utils.hpp @@ -0,0 +1,10 @@ +#include "../parserUtils.hpp" + +void setupParser() { + parser.addAttribute(); + parser.addAttribute(); + parser.addAttribute(); + parser.addAttribute(); + parser.addAttribute(); + parser.addAttribute(); +}