diff --git a/include/parser/ast_node.hpp b/include/parser/ast_node.hpp index aa00fb778..7c0c7dc4f 100644 --- a/include/parser/ast_node.hpp +++ b/include/parser/ast_node.hpp @@ -3,6 +3,7 @@ #include "token.hpp" #include +#include #include namespace hex::lang { @@ -13,6 +14,7 @@ namespace hex::lang { VariableDecl, TypeDecl, Struct, + Enum, Scope, }; @@ -78,4 +80,19 @@ namespace hex::lang { std::string m_name, m_customTypeName; }; + class ASTNodeEnum : public ASTNode { + public: + explicit ASTNodeEnum(const Token::TypeToken::Type &type, const std::string &name) + : ASTNode(Type::Enum), m_type(type), m_name(name) { } + + const std::string& getName() const { return this->m_name; }; + + const Token::TypeToken::Type& getUnderlyingType() const { return this->m_type; } + std::vector>& getValues() { return this->m_values; } + private: + Token::TypeToken::Type m_type; + std::string m_name; + std::vector> m_values; + }; + } \ No newline at end of file diff --git a/include/parser/token.hpp b/include/parser/token.hpp index 661ae2685..79f526a94 100644 --- a/include/parser/token.hpp +++ b/include/parser/token.hpp @@ -25,7 +25,8 @@ namespace hex::lang { struct KeywordToken { enum class Keyword { Struct, - Using + Using, + Enum } keyword; } keywordToken; struct IdentifierToken { @@ -34,7 +35,8 @@ namespace hex::lang { struct OperatorToken { enum class Operator { AtDeclaration, - Assignment + Assignment, + Inherit } op; } operatorToken; struct IntegerToken { diff --git a/source/parser/lexer.cpp b/source/parser/lexer.cpp index 4faeffd3f..632ca6f1b 100644 --- a/source/parser/lexer.cpp +++ b/source/parser/lexer.cpp @@ -70,6 +70,7 @@ namespace hex::lang { u32 offset = 0; while (offset < code.length()) { + // Handle comments if (code[offset] == '/') { offset++; @@ -121,6 +122,9 @@ namespace hex::lang { } else if (c == '=') { tokens.push_back({.type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Assignment}}); offset += 1; + } else if (c == ':') { + tokens.push_back({.type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Inherit}}); + offset += 1; } else if (std::isalpha(c)) { std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; }); @@ -130,6 +134,8 @@ namespace hex::lang { tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Struct}}); else if (identifier == "using") tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Using}}); + else if (identifier == "enum") + tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Enum}}); // Check for built-in types else if (identifier == "u8") diff --git a/source/parser/parser.cpp b/source/parser/parser.cpp index cee5665ce..7e0f28736 100644 --- a/source/parser/parser.cpp +++ b/source/parser/parser.cpp @@ -51,7 +51,7 @@ namespace hex::lang { return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-5].identifierToken.identifier, curr[-2].integerToken.integer); } - std::optional parseStruct(TokenIter &curr) { + ASTNode* parseStruct(TokenIter &curr) { const std::string &structName = curr[-2].identifierToken.identifier; std::vector nodes; @@ -69,12 +69,51 @@ namespace hex::lang { if (!tryConsume(curr, {Token::Type::EndOfExpression})) { for(auto &node : nodes) delete node; - return { }; + return nullptr; } return new ASTNodeStruct(structName, nodes); } + ASTNode* parseEnum(TokenIter &curr) { + const std::string &enumName = curr[-4].identifierToken.identifier; + const Token::TypeToken::Type underlyingType = curr[-2].typeToken.type; + + if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) + return nullptr; + + if ((static_cast(underlyingType) & 0x0F) != 0x00) + return nullptr; + + auto enumNode = new ASTNodeEnum(underlyingType, enumName); + + while (!tryConsume(curr, {Token::Type::ScopeClose})) { + if (tryConsume(curr, { Token::Type::Identifier, Token::Type::Separator})) { + u64 value; + if (enumNode->getValues().empty()) + value = 0; + else + value = enumNode->getValues().back().first + 1; + + enumNode->getValues().push_back({ value, curr[-2].identifierToken.identifier }); + } + else if (tryConsume(curr, { Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::Separator})) { + enumNode->getValues().push_back({ curr[-2].integerToken.integer, curr[-4].identifierToken.identifier }); + } + else { + delete enumNode; + return nullptr; + } + } + + if (!tryConsume(curr, {Token::Type::EndOfExpression})) { + delete enumNode; + return nullptr; + } + + return enumNode; + } + ASTNode *parseScope(TokenIter &curr) { return new ASTNodeScope(parseTillToken(curr, Token::Type::ScopeClose)); } @@ -111,12 +150,27 @@ namespace hex::lang { if (curr[-3].keywordToken.keyword == Token::KeywordToken::Keyword::Struct) { auto structAst = parseStruct(curr); - if (!structAst.has_value()) { + if (structAst == nullptr) { for(auto &node : program) delete node; return { }; } - program.push_back(structAst.value()); + program.push_back(structAst); + } + + return program; + + } // Enum + if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::ScopeOpen })) { + if (curr[-5].keywordToken.keyword == Token::KeywordToken::Keyword::Enum) { + auto enumAst = parseEnum(curr); + + if (enumAst == nullptr) { + for(auto &node : program) delete node; + return { }; + } + + program.push_back(enumAst); } return program;