From 5c7908ac4bae6e2f44433ca91df3171e7e3bce29 Mon Sep 17 00:00:00 2001 From: VaclavT Date: Tue, 29 Jun 2021 19:28:14 +0200 Subject: [PATCH] initial commit --- .gitignore | 2 + .idea/.gitignore | 3 + .idea/misc.xml | 4 + .idea/modules.xml | 8 + .idea/msql.iml | 2 + .idea/vcs.xml | 6 + .vscode/c_cpp_properties.json | 30 +++ .vscode/launch.json | 20 ++ .vscode/settings.json | 78 +++++++ .vscode/tasks.json | 26 +++ CMakeLists.txt | 21 ++ exception.cpp | 9 + exception.h | 15 ++ executor.cpp | 29 +++ executor.h | 21 ++ lexer.cpp | 376 ++++++++++++++++++++++++++++++++++ lexer.h | 87 ++++++++ main.cpp | 26 +++ parser.cpp | 87 ++++++++ parser.h | 63 ++++++ 20 files changed, 913 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/msql.iml create mode 100644 .idea/vcs.xml create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 CMakeLists.txt create mode 100644 exception.cpp create mode 100644 exception.h create mode 100644 executor.cpp create mode 100644 executor.h create mode 100644 lexer.cpp create mode 100644 lexer.h create mode 100644 main.cpp create mode 100644 parser.cpp create mode 100644 parser.h 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; +};