commit 5c7908ac4bae6e2f44433ca91df3171e7e3bce29 Author: VaclavT Date: Tue Jun 29 19:28:14 2021 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6be3b8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +cmake-build-debug diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..79b3c94 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..015bace --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/msql.iml b/.idea/msql.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/msql.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..625067d --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,30 @@ +{ + "configurations": [ + { + "name": "Mac", + "browse": { + "path": [ + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": true + }, + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/stdlib", + "/usr/local/opt/openssl/include", + "${workspaceFolder}" + ], + "defines": [], + "macFrameworkPath": [ + "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks" + ], + "compilerPath": "/usr/bin/clang", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "clang-x64", + "configurationProvider": "vector-of-bool.cmake-tools", + "compileCommands": "${workspaceFolder}/build/compile_commands.json" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..39b80b4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(lldb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/msql", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "lldb" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0c5fae8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,78 @@ +{ + "files.associations": { + "regex": "cpp", + "__config": "cpp", + "__nullptr": "cpp", + "exception": "cpp", + "initializer_list": "cpp", + "new": "cpp", + "stdexcept": "cpp", + "typeinfo": "cpp", + "__functional_03": "cpp", + "__string": "cpp", + "deque": "cpp", + "functional": "cpp", + "string": "cpp", + "system_error": "cpp", + "vector": "cpp", + "__bit_reference": "cpp", + "__functional_base": "cpp", + "algorithm": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "chrono": "cpp", + "iterator": "cpp", + "limits": "cpp", + "locale": "cpp", + "memory": "cpp", + "ratio": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "unordered_map": "cpp", + "__functional_base_03": "cpp", + "__hash_table": "cpp", + "__tuple": "cpp", + "utility": "cpp", + "__split_buffer": "cpp", + "ios": "cpp", + "string_view": "cpp", + "map": "cpp", + "__tree": "cpp", + "istream": "cpp", + "__locale": "cpp", + "iosfwd": "cpp", + "array": "cpp", + "hash_map": "cpp", + "list": "cpp", + "optional": "cpp", + "sstream": "cpp", + "cstddef": "cpp", + "hashtable": "cpp", + "variant": "cpp", + "__debug": "cpp", + "__errc": "cpp", + "__mutex_base": "cpp", + "__node_handle": "cpp", + "__threading_support": "cpp", + "cctype": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "fstream": "cpp", + "iomanip": "cpp", + "iostream": "cpp", + "mutex": "cpp", + "ostream": "cpp", + "streambuf": "cpp", + "thread": "cpp" + }, + "C_Cpp.intelliSenseEngineFallback": "Disabled", + "cmake.configureOnOpen": true, + "C_Cpp.configurationWarnings": "Disabled" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..0bda5b0 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,26 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "compile", + "type": "shell", + "command": "clang++ -o mi -g -mmacosx-version-min=10.14 -lstdc++ -std=c++1z -lssl -lcrypto -lpqxx -lpq -L/usr/local/opt/openssl/lib -I/usr/local/opt/openssl/include -L/usr/local/opt/libpqxx/lib -I/usr/local/opt/libpqxx/include -L/usr/local/opt/postgres/lib -I/usr/local/opt/postgres/include -I./ -I./stdlib *.cpp ./stdlib/*.cpp", + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "compile O3", + "type": "shell", + "command": "clang++ -o mi -O3 -mmacosx-version-min=10.14 -lstdc++ -std=c++1z -lssl -lcrypto -lpqxx -lpq -L/usr/local/opt/openssl/lib -I/usr/local/opt/openssl/include -L/usr/local/opt/libpqxx/lib -I/usr/local/opt/libpqxx/include -L/usr/local/opt/postgres/lib -I/usr/local/opt/postgres/include -I./ -I./stdlib *.cpp ./stdlib/*.cpp", + "group": { + "kind": "build", + "isDefault": false + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8303970 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.0) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") + + +project(msql) + +set(PROJECT_NAME msql) + +set(SOURCE + exception.cpp lexer.cpp parser.cpp executor.cpp main.cpp) + +add_executable(${PROJECT_NAME} ${SOURCE}) + +target_link_libraries(${PROJECT_NAME} stdc++ m) + +target_compile_options(msql PRIVATE -g) diff --git a/exception.cpp b/exception.cpp new file mode 100644 index 0000000..a5364a0 --- /dev/null +++ b/exception.cpp @@ -0,0 +1,9 @@ +#include "exception.h" + + +Exception::Exception(const std::string &msg) { + cause = msg; +} + + +const char* Exception::what() const noexcept { return cause.c_str(); } \ No newline at end of file diff --git a/exception.h b/exception.h new file mode 100644 index 0000000..c44a483 --- /dev/null +++ b/exception.h @@ -0,0 +1,15 @@ +#pragma once + +#include "lexer.h" + +#include + +class Exception : public std::exception { +private: + std::string cause; + +public: + Exception(const std::string &msg); + + const char* what() const noexcept; +}; diff --git a/executor.cpp b/executor.cpp new file mode 100644 index 0000000..5626482 --- /dev/null +++ b/executor.cpp @@ -0,0 +1,29 @@ +#include "executor.h" +#include "exception.h" + + + +Executor::Executor() { + // TODO init database +} + +bool Executor::execute(Node& node) { + switch (node.node_type) { + case NodeType::create_table: + return execute_create_table(static_cast(node)); + case NodeType::select_from: + return execute_select(node); + default: + // TODO error message + return false; + } + +} + +bool Executor::execute_create_table(CreateTableNode& node) { + return false; +} + +bool Executor::execute_select(Node& node) { + return false; +} diff --git a/executor.h b/executor.h new file mode 100644 index 0000000..03ee30a --- /dev/null +++ b/executor.h @@ -0,0 +1,21 @@ +#pragma once + +#include "parser.h" + +#include + +class Executor { +private: + +public: + Executor(); + + bool execute(Node& node); + +private: + bool execute_create_table(CreateTableNode& node); + bool execute_select(Node& node); + +private: + +}; diff --git a/lexer.cpp b/lexer.cpp new file mode 100644 index 0000000..4bb69cd --- /dev/null +++ b/lexer.cpp @@ -0,0 +1,376 @@ +#include "lexer.h" +#include "exception.h" + +#include + + +Token::Token(const std::string &token_str, TokenType typ) { + token_string = token_str; + type = typ; +} + +void Lexer::parse(const std::string &code) { + // TODO handle empty code + tokens.clear(); + + // PERF something like this to prealocate ?? + if (code.size() > 100) { + tokens.reserve(code.size() / 10); + } + code_str = code; + if (!code_str.empty() && code_str.back() != '\n') { + code_str.append("\n"); // TODO tempo solution to prevent possible situation when last line is a comment + } + + // TODO make it constant + std::regex words_regex("[0-9]+\\.[0-9]+|[0-9][0-9_]+[0-9]|[0-9]+|[A-Za-z]+[A-Za-z0-9_#]*|[\\(\\)\\[\\]\\{\\}]|[-\\+\\*/" + ",;:\?]|==|>=|<=|~=|>|<|=|;|~|\\|\\||&&|\n|\r|\r\n|'([^']|'')*'|\".*?\"|%.*?\n"); + + auto words_begin = std::sregex_iterator(code_str.begin(), code_str.end(), words_regex); + auto words_end = std::sregex_iterator(); + + for (std::sregex_iterator i = words_begin; i != words_end; ++i) { + std::smatch match = *i; + std::string match_str = match.str(); + TokenType token_type = type(match_str); + if (token_type == TokenType::string_literal) { + match_str = stringLiteral(match_str); + } else { + tokens.push_back(Token{match_str, token_type}); + } + } + + // DEBUG IT + // debugTokens(); + + index = 0; +} + +void Lexer::debugTokens() { + int i = 0; + for (std::vector::iterator it = tokens.begin(); it != tokens.end(); ++it) { + std::cerr << i << "\t" << it->token_string << std::endl; + i++; + } +} + +Token Lexer::currentToken() { return tokens[index]; } + +void Lexer::nextToken() { + if (index < tokens.size()) { + index++; + } +} + +void Lexer::skipToken(TokenType type) { + if (tokenType() == type) { + nextToken(); + } else { + throw Exception("ERROR unexpected token " + currentToken().token_string + ", instead of " + typeToString(type)); + } +} + +void Lexer::skipTokenOptional(TokenType type) { + if (tokenType() == type) { + nextToken(); + } +} + +TokenType Lexer::tokenType() { return index < tokens.size() ? currentToken().type : TokenType::eof; } + +TokenType Lexer::nextTokenType() { return index < tokens.size() - 1 ? tokens[index + 1].type : TokenType::eof; } + +TokenType Lexer::prevTokenType() { return index > 0 ? tokens[index - 1].type : TokenType::undef; } + +bool Lexer::isRelationalOperator(TokenType token_type) { + return (token_type == TokenType::equal || token_type == TokenType::not_equal || token_type == TokenType::greater || token_type == TokenType::greater_equal || + token_type == TokenType::lesser || token_type == TokenType::lesser_equal); +} + +TokenType Lexer::type(const std::string &token) { + // TODO move it to class level not to reinit it again and again + std::regex int_regex("[0-9]+"); + std::regex int_underscored_regex("[0-9][0-9_]+[0-9]"); + std::regex double_regex("[0-9]+\\.[0-9]+"); + std::regex identifier_regex("[A-Za-z]+[A-Za-z0-9_#]*"); + + if (token == ";") + return TokenType::semicolon; + + if (token == "+") + return TokenType::plus; + + if (token == "-") + return TokenType::minus; + + if (token == "*") + return TokenType::multiply; + + if (token == "/") + return TokenType::divide; + + if (token == "(") + return TokenType::open_paren; + + if (token == ")") + return TokenType::close_paren; + + if (token == "=") + return TokenType::equal; + + if (token == "!=") + return TokenType::not_equal; + + if (token == ">") + return TokenType::greater; + + if (token == ">=") + return TokenType::greater_equal; + + if (token == "<") + return TokenType::lesser; + + if (token == "<=") + return TokenType::lesser_equal; + + if (token == "create") + return TokenType::keyword_create; + + if (token == "where") + return TokenType::keyword_where; + + if (token == "from") + return TokenType::keyword_from; + + if (token == "table") + return TokenType::keyword_table; + + if (token == "insert") + return TokenType::keyword_insert; + + if (token == "into") + return TokenType::keyword_into; + + if (token == "values") + return TokenType::keyword_values; + + if (token == "select") + return TokenType::keyword_select; + + if (token == "set") + return TokenType::keyword_set; + + if (token == "copy") + return TokenType::keyword_copy; + + if (token == "not") + return TokenType::keyword_not; + + if (token == "null") + return TokenType::keyword_null; + + if (token == "integer") + return TokenType::keyword_int; + + if (token == "float") + return TokenType::keyword_float; + + if (token == "varchar") + return TokenType::keyword_varchar; + + if (token == "||") + return TokenType::logical_or; + + if (token == "&&") + return TokenType::logical_and; + + if (token == ",") + return TokenType::comma; + + if (token == "\n" || token == "\r\n" || token == "\r") + return TokenType::newline; + + if (token.length() > 1 && token.at(0) == '%' && (token.at(token.length() - 1) == '\n' || token.at(token.length() - 1) == '\r')) + return TokenType::comment; + + // if (token.length() >= 2 && token.at(0) == '"' && token.at(token.length() - 1) == '"') + // return TokenType::string_literal; + + if (token.length() >= 2 && token.at(0) == '\'' && token.at(token.length() - 1) == '\'') + return TokenType::string_literal; + + if (std::regex_match(token, int_regex)) + return TokenType::int_number; + + if (std::regex_match(token, int_underscored_regex)) + return TokenType::int_number; + + if (std::regex_match(token, double_regex)) + return TokenType::double_number; + + if (std::regex_match(token, identifier_regex)) + return TokenType::identifier; + + if (index + 1 >= tokens.size()) + return TokenType::eof; + + return TokenType::undef; +} + +std::string Lexer::stringLiteral(std::string token) { + // remove ' or " from the literal ends + bool replace = token[0]=='\'' && token[token.size()-1]=='\''; + + std::string str = token.substr(1, token.size() - 2); + if (!replace) { + return str; + } + std::string out = ""; + out.reserve(str.size()); + + + for(std::string::size_type i = 0; i < str.size(); ++i) { + if (str[i] == '\'' && i < str.size() - 1) { + if (str[i+1] == '\'') { + out.append(1, '\''); + i++; + } else { + out.append(1, str[i]); + } + } else if (str[i] == '\\' && i < str.size() - 1) { + if (str[i+1] == 'n') { + out.append(1, '\n'); + i++; + } else if (str[i+1] == 't') { + out.append(1, '\t'); + i++; + } else { + out.append(1, str[i]); + } + } else { + out.append(1, str[i]); + } + } + return out; +} + +std::string Lexer::typeToString(TokenType token_type) { + std::string txt; + switch (token_type) { + case TokenType::undef: + txt = "undef"; + break; + case TokenType::identifier: + txt = "identifier"; + break; + case TokenType::plus: + txt = "+"; + break; + case TokenType::minus: + txt = "-"; + break; + case TokenType::multiply: + txt = "*"; + break; + case TokenType::divide: + txt = "/"; + break; + case TokenType::equal: + txt = "=="; + break; + case TokenType::not_equal: + txt = "!="; + break; + case TokenType::greater: + txt = ">"; + break; + case TokenType::greater_equal: + txt = ">="; + break; + case TokenType::lesser: + txt = "<"; + break; + case TokenType::lesser_equal: + txt = "<="; + break; + case TokenType::keyword_create: + txt = "create"; + break; + case TokenType::keyword_where: + txt = "where"; + break; + case TokenType::keyword_table: + txt = "table"; + break; + case TokenType::keyword_into: + txt = "into"; + break; + case TokenType::keyword_values: + txt = "values"; + break; + case TokenType::keyword_select: + txt = "select"; + break; + case TokenType::keyword_set: + txt = "set"; + break; + case TokenType::keyword_copy: + txt = "copy"; + break; + case TokenType::keyword_not: + txt = "not"; + break; + case TokenType::keyword_null: + txt = "null"; + break; + case TokenType::keyword_int: + txt = "integer"; + break; + case TokenType::keyword_float: + txt = "float"; + break; + case TokenType::keyword_varchar: + txt = "varchar"; + break; + case TokenType::int_number: + txt = "int number"; + break; + case TokenType::double_number: + txt = "double number"; + break; + case TokenType::string_literal: + txt = "string literal"; + break; + case TokenType::open_paren: + txt = "("; + break; + case TokenType::close_paren: + txt = ")"; + break; + case TokenType::logical_and: + txt = "and"; + break; + case TokenType::logical_or: + txt = "or"; + break; + case TokenType::semicolon: + txt = ";"; + break; + case TokenType::comma: + txt = ","; + break; + case TokenType::newline: + txt = "newline"; + break; + case TokenType::comment: + txt = "comment"; + break; + case TokenType::eof: + txt = "eof"; + break; + default: + txt = "FIXME, unknown token type"; + break; + } + return txt; +} diff --git a/lexer.h b/lexer.h new file mode 100644 index 0000000..f6122a1 --- /dev/null +++ b/lexer.h @@ -0,0 +1,87 @@ +#pragma once + +#include +#include +#include +#include + +enum class TokenType { + undef, + identifier, + plus, + minus, + multiply, + divide, + equal, + not_equal, + greater, + greater_equal, + lesser, + lesser_equal, + keyword_create, + keyword_table, + keyword_where, + keyword_from, + keyword_insert, + keyword_into, + keyword_values, + keyword_select, + keyword_set, + keyword_copy, + keyword_not, + keyword_null, + keyword_int, + keyword_float, + keyword_varchar, + int_number, + double_number, + string_literal, + open_paren, + close_paren, + logical_and, + logical_or, + semicolon, + comma, + newline, + comment, + eof +}; + +struct Token { + std::string token_string; + TokenType type; + Token(const std::string &token_str, TokenType typ); +}; + +class Lexer { +private: + std::string code_str; + std::vector tokens; + int index = 0; + bool eof = false; + +public: + Lexer() {}; + + void parse(const std::string &code); + + void debugTokens(); + + Token currentToken(); + + void nextToken(); + + void skipToken(TokenType type); + void skipTokenOptional(TokenType type); + + TokenType tokenType(); + TokenType nextTokenType(); + TokenType prevTokenType(); + + static bool isRelationalOperator(TokenType token_type); + +private: + TokenType type(const std::string &token); + std::string stringLiteral(std::string token); + static std::string typeToString(TokenType token_type); +}; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..4c7f33b --- /dev/null +++ b/main.cpp @@ -0,0 +1,26 @@ +#include "parser.h" +#include "executor.h" + +// https://dev.to/joaoh82/what-would-sqlite-look-like-if-written-in-rust-part-1-2np4 + +// parser should get lexer as param and table executor to be able translate * or get types or so +// podporovat create as select +// drop table + +int main(int argc, char *argv[]) { + Parser parser{}; + Executor executor{}; + + std::string sql_create = "create table a (i integer not null, s varchar(64), f float)"; +// std::string sql_insert = "insert into a (i, s) values(1, 'one')"; +// std::string sql_inser2 = "insert into a (i, s) values(2, 'two')"; +// std::string sql_inser3 = "insert into a (i, s) values(3, 'two')"; +// std::string sql_update = "update a set s = 'three' where i = 3"; +// std::string sql_select = "select i, s from a where i > 0"; +// std::string sql_delete = "delete from a where i = 3"; + + auto node = parser.parse(sql_create); + + executor.execute(*node.get()); + return 0; +} diff --git a/parser.cpp b/parser.cpp new file mode 100644 index 0000000..e8473d7 --- /dev/null +++ b/parser.cpp @@ -0,0 +1,87 @@ +#include "parser.h" +#include "exception.h" + + + +Parser::Parser() { + lexer = Lexer{}; +} + +std::unique_ptr Parser::parse(const std::string &code) { + lexer.parse(code); + lexer.debugTokens(); + + if (lexer.tokenType() == TokenType::keyword_create && lexer.nextTokenType() == TokenType::keyword_table) { + return parse_create_table(); + } if (lexer.tokenType() == TokenType::keyword_select) { + return parse_select(); + } + + return std::make_unique(NodeType::error); +} + +std::unique_ptr Parser::parse_create_table() { + std::vector cols_def {}; + + lexer.skipToken(TokenType::keyword_create); + lexer.skipToken(TokenType::keyword_table); + + if (lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } + std::string table_name = lexer.currentToken().token_string; + lexer.nextToken(); + + lexer.skipToken(TokenType::open_paren); + do { + std::string column_name; + ColumnType column_type; + int column_len {1}; + bool column_nullable {true}; + + // column name + if (lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } + column_name = lexer.currentToken().token_string; + lexer.nextToken(); + + // column type and optionaly len + if (lexer.tokenType() == TokenType::keyword_int) { + column_type = ColumnType::integer_type; + lexer.nextToken(); + } else if (lexer.tokenType() == TokenType::keyword_float) { + column_type = ColumnType::float_type; + lexer.nextToken(); + } else if (lexer.tokenType() == TokenType::keyword_varchar) { + column_type = ColumnType::varchar_type; + lexer.nextToken(); + lexer.skipToken(TokenType::open_paren); + if (lexer.tokenType() == TokenType::int_number) { + column_len = std::stoi(lexer.currentToken().token_string); + lexer.nextToken(); + } else { /* TODO handle error */ } + lexer.skipToken(TokenType::close_paren); + } else { /* TODO handle error */ } + + if (lexer.tokenType() == TokenType::keyword_not) { + lexer.nextToken(); + lexer.skipToken(TokenType::keyword_null); + column_nullable = false; + } else if (lexer.tokenType() == TokenType::keyword_null) { + lexer.nextToken(); + } + + cols_def.push_back(ColDefNode(column_name, column_type, column_len, column_nullable)); + + if (lexer.tokenType() == TokenType::comma) lexer.nextToken(); + + // TODO in future constraints + + } while (lexer.tokenType() != TokenType::close_paren); + + + return std::make_unique(table_name, cols_def); +} + +std::unique_ptr Parser::parse_select() { + std::vector exec_code {}; + + return std::make_unique(NodeType::not_implemented_yet); +} \ No newline at end of file diff --git a/parser.h b/parser.h new file mode 100644 index 0000000..0cbca9e --- /dev/null +++ b/parser.h @@ -0,0 +1,63 @@ +#pragma once + +#include "lexer.h" +#include +#include + +#include + + +enum class ColumnType { + integer_type, + float_type, + varchar_type +}; + +enum class NodeType { + create_table, + select_from, + column_def, + not_implemented_yet, + error +}; + +struct Node { + NodeType node_type; + + Node(const NodeType type) : node_type(type) {} +}; + +struct ColDefNode : Node { + std::string name; + ColumnType type; + int length; + bool null; + + ColDefNode(const std::string col_name, const ColumnType col_type, int col_len, bool nullable) : + Node(NodeType::column_def), name(col_name), type(col_type), length(col_len), null(nullable) {} +}; + +struct CreateTableNode : Node { + std::string table_name; + std::vector cols_defs; + + CreateTableNode(const std::string name, std::vector defs) : + Node(NodeType::create_table), table_name(name), cols_defs(defs) {} +}; + + +class Parser { +private: + +public: + Parser(); + + std::unique_ptr parse(const std::string &code); + +private: + std::unique_ptr parse_create_table(); + std::unique_ptr parse_select(); + +private: + Lexer lexer; +};