Skip to content

Commit 0751109

Browse files
committed
First draft
1 parent 1084ee5 commit 0751109

8 files changed

Lines changed: 521 additions & 0 deletions

File tree

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,12 @@
3030
*.exe
3131
*.out
3232
*.app
33+
34+
35+
pgsql_example
36+
libpgsqlparser.a
37+
src/pgsql_parser/pgsql_parser.tab.c
38+
src/pgsql_parser/pgsql_parser.tab.h
39+
src/pgsql_parser/pgsql_lexer.yy.c
40+
src/pgsql_parser/pgsql_parser.output
41+
src/pgsql_parser/pgsql_parser.report

Makefile

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Makefile
2+
CXX = g++
3+
LINKER = g++
4+
5+
CXXFLAGS = -std=c++17 -Wall -g
6+
CPPFLAGS = -I$(PROJECT_ROOT)/include
7+
8+
PROJECT_ROOT = .
9+
INCLUDE_DIR = $(PROJECT_ROOT)/include
10+
SRC_DIR = $(PROJECT_ROOT)/src
11+
12+
PGSQL_PARSER_SRC_DIR = $(SRC_DIR)/pgsql_parser
13+
PGSQL_PARSER_INCLUDE_DIR = $(INCLUDE_DIR)/pgsql_parser
14+
15+
TARGET_LIB_NAME = pgsqlparser
16+
TARGET_LIB = $(PROJECT_ROOT)/lib$(TARGET_LIB_NAME).a
17+
EXAMPLE_EXE = $(PROJECT_ROOT)/pgsql_example
18+
19+
PGSQL_BISON_PARSER_C_FILE = pgsql_parser.tab.c
20+
PGSQL_BISON_PARSER_H_FILE = pgsql_parser.tab.h
21+
PGSQL_FLEX_LEXER_C_FILE = pgsql_lexer.yy.c
22+
23+
PGSQL_BISON_PARSER_C = $(PGSQL_PARSER_SRC_DIR)/$(PGSQL_BISON_PARSER_C_FILE)
24+
PGSQL_BISON_PARSER_H = $(PGSQL_PARSER_SRC_DIR)/$(PGSQL_BISON_PARSER_H_FILE)
25+
PGSQL_FLEX_LEXER_C = $(PGSQL_PARSER_SRC_DIR)/$(PGSQL_FLEX_LEXER_C_FILE)
26+
27+
PGSQL_LIB_OBJS = \
28+
$(PGSQL_BISON_PARSER_C:.c=.o) \
29+
$(PGSQL_FLEX_LEXER_C:.c=.o) \
30+
$(PGSQL_PARSER_SRC_DIR)/pgsql_parser.o
31+
32+
EXAMPLE_OBJS = $(PROJECT_ROOT)/examples/main_pgsql_example.o
33+
34+
.PHONY: all clean examples
35+
36+
all: $(TARGET_LIB) examples
37+
38+
examples: $(EXAMPLE_EXE)
39+
40+
$(TARGET_LIB): $(PGSQL_LIB_OBJS)
41+
ar rcs $@ $(PGSQL_LIB_OBJS)
42+
@echo "Created library $@"
43+
44+
$(EXAMPLE_EXE): $(EXAMPLE_OBJS) $(TARGET_LIB)
45+
$(LINKER) $(CXXFLAGS) -o $@ $(EXAMPLE_OBJS) -L$(PROJECT_ROOT) -l$(TARGET_LIB_NAME)
46+
@echo "Created example $@"
47+
48+
$(PGSQL_BISON_PARSER_H) $(PGSQL_BISON_PARSER_C): $(PGSQL_PARSER_SRC_DIR)/pgsql_parser.y $(PGSQL_PARSER_INCLUDE_DIR)/pgsql_ast.h $(PGSQL_PARSER_INCLUDE_DIR)/pgsql_parser.h
49+
cd $(PGSQL_PARSER_SRC_DIR) && bison -d -v --report=all pgsql_parser.y
50+
51+
$(PGSQL_FLEX_LEXER_C): $(PGSQL_PARSER_SRC_DIR)/pgsql_lexer.l $(PGSQL_BISON_PARSER_H)
52+
cd $(PGSQL_PARSER_SRC_DIR) && flex -o $(PGSQL_FLEX_LEXER_C_FILE) pgsql_lexer.l
53+
54+
# Rule to compile Bison-generated file (pgsql_parser.tab.c) AS C++
55+
$(PGSQL_BISON_PARSER_C:.c=.o): $(PGSQL_BISON_PARSER_C) $(PGSQL_BISON_PARSER_H) $(PGSQL_PARSER_INCLUDE_DIR)/pgsql_parser.h
56+
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
57+
58+
# Rule to compile Flex-generated file (pgsql_lexer.yy.c) AS C++
59+
$(PGSQL_FLEX_LEXER_C:.c=.o): $(PGSQL_FLEX_LEXER_C) $(PGSQL_BISON_PARSER_H) $(PGSQL_PARSER_INCLUDE_DIR)/pgsql_parser.h
60+
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
61+
62+
# Rule to compile pgsql_parser.cpp (C++ file)
63+
$(PGSQL_PARSER_SRC_DIR)/pgsql_parser.o: $(PGSQL_PARSER_SRC_DIR)/pgsql_parser.cpp $(PGSQL_PARSER_INCLUDE_DIR)/pgsql_parser.h $(PGSQL_PARSER_INCLUDE_DIR)/pgsql_ast.h $(PGSQL_BISON_PARSER_H)
64+
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
65+
66+
$(PROJECT_ROOT)/examples/main_pgsql_example.o: $(PROJECT_ROOT)/examples/main_pgsql_example.cpp $(PGSQL_PARSER_INCLUDE_DIR)/pgsql_parser.h $(PGSQL_PARSER_INCLUDE_DIR)/pgsql_ast.h
67+
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
68+
69+
clean:
70+
rm -f $(TARGET_LIB) $(EXAMPLE_EXE)
71+
rm -f $(PGSQL_LIB_OBJS) $(EXAMPLE_OBJS)
72+
rm -f $(PGSQL_BISON_PARSER_C) $(PGSQL_BISON_PARSER_H) $(PGSQL_FLEX_LEXER_C)
73+
rm -f $(PGSQL_PARSER_SRC_DIR)/pgsql_parser.output $(PGSQL_PARSER_SRC_DIR)/pgsql_parser.report
74+
rm -f $(PGSQL_PARSER_SRC_DIR)/lex.backup
75+
@echo "Cleaned up project."

examples/main_pgsql_example.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include "pgsql_parser/pgsql_parser.h" // Changed include path and namespace
2+
#include <iostream>
3+
#include <vector>
4+
#include <string>
5+
6+
int main() {
7+
PgsqlParser::Parser parser; // Changed namespace
8+
9+
std::vector<std::string> queries = {
10+
"SELECT name FROM users;",
11+
"INSERT INTO products VALUES ('a new gadget');",
12+
"QUIT;",
13+
"SELECT * FROM;",
14+
"INSERT INTO logs VALUES (no_quotes_here);"
15+
};
16+
17+
for (const auto& query : queries) {
18+
std::cout << "------------------------------------------\n";
19+
std::cout << "Parsing query: " << query << std::endl;
20+
21+
parser.clearErrors();
22+
std::unique_ptr<PgsqlParser::AstNode> ast = parser.parse(query); // Changed namespace
23+
24+
if (ast) {
25+
std::cout << "Parsing successful!" << std::endl;
26+
PgsqlParser::print_ast(ast.get()); // Changed namespace
27+
} else {
28+
std::cout << "Parsing failed." << std::endl;
29+
const auto& errors = parser.getErrors();
30+
if (errors.empty()) {
31+
std::cout << " (No specific error messages, check parser logic or pgsql_yyerror)" << std::endl;
32+
} else {
33+
for (const auto& error : errors) {
34+
std::cout << " Error: " << error << std::endl;
35+
}
36+
}
37+
}
38+
}
39+
return 0;
40+
}

include/pgsql_parser/pgsql_ast.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#ifndef PGSQL_PARSER_AST_H
2+
#define PGSQL_PARSER_AST_H
3+
4+
#include <string>
5+
#include <vector>
6+
#include <iostream>
7+
#include <algorithm> // For potential string manipulations
8+
9+
namespace PgsqlParser { // Changed namespace
10+
11+
// Enum for different types of AST nodes
12+
enum class NodeType {
13+
NODE_UNKNOWN,
14+
NODE_COMMAND,
15+
NODE_SELECT_STATEMENT,
16+
NODE_INSERT_STATEMENT,
17+
NODE_IDENTIFIER,
18+
NODE_STRING_LITERAL
19+
};
20+
21+
// Basic AST Node
22+
struct AstNode {
23+
NodeType type;
24+
std::string value;
25+
std::vector<AstNode*> children;
26+
27+
AstNode(NodeType t, const std::string& val = "") : type(t), value(val) {}
28+
AstNode(NodeType t, std::string&& val) : type(t), value(std::move(val)) {}
29+
30+
~AstNode() {
31+
for (AstNode* child : children) {
32+
delete child;
33+
}
34+
}
35+
36+
AstNode(const AstNode&) = delete;
37+
AstNode& operator=(const AstNode&) = delete;
38+
AstNode(AstNode&&) = delete;
39+
AstNode& operator=(AstNode&&) = delete;
40+
41+
void addChild(AstNode* child) {
42+
if (child) {
43+
children.push_back(child);
44+
}
45+
}
46+
};
47+
48+
inline void print_ast(const AstNode* node, int indent = 0) {
49+
if (!node) return;
50+
for (int i = 0; i < indent; ++i) std::cout << " ";
51+
52+
std::string type_str;
53+
switch(node->type) {
54+
case NodeType::NODE_UNKNOWN: type_str = "UNKNOWN"; break;
55+
case NodeType::NODE_COMMAND: type_str = "COMMAND"; break;
56+
case NodeType::NODE_SELECT_STATEMENT: type_str = "SELECT_STMT"; break;
57+
case NodeType::NODE_INSERT_STATEMENT: type_str = "INSERT_STMT"; break;
58+
case NodeType::NODE_IDENTIFIER: type_str = "IDENTIFIER"; break;
59+
case NodeType::NODE_STRING_LITERAL: type_str = "STRING_LITERAL"; break;
60+
default: type_str = "UNHANDLED_TYPE(" + std::to_string(static_cast<int>(node->type)) + ")"; break;
61+
}
62+
std::cout << "Type: " << type_str << ", Value: '" << node->value << "'" << std::endl;
63+
for (const AstNode* child : node->children) {
64+
print_ast(child, indent + 1);
65+
}
66+
}
67+
68+
} // namespace PgsqlParser
69+
70+
#endif // PGSQL_PARSER_AST_H
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#ifndef PGSQL_PARSER_PARSER_H
2+
#define PGSQL_PARSER_PARSER_H
3+
4+
#include "pgsql_ast.h"
5+
#include <string>
6+
#include <vector>
7+
#include <memory>
8+
9+
typedef void* yyscan_t;
10+
11+
namespace PgsqlParser {
12+
13+
class Parser {
14+
public:
15+
Parser();
16+
~Parser();
17+
18+
std::unique_ptr<AstNode> parse(const std::string& sql_query);
19+
20+
const std::vector<std::string>& getErrors() const;
21+
void clearErrors();
22+
23+
void internal_set_ast(AstNode* root);
24+
void internal_add_error(const std::string& msg);
25+
void internal_add_error_at(const std::string& msg, int line, int column);
26+
27+
private:
28+
std::unique_ptr<AstNode> ast_root_;
29+
std::vector<std::string> errors_;
30+
yyscan_t scanner_state_;
31+
};
32+
33+
} // namespace PgsqlParser
34+
35+
// Declaration for pgsql_yyerror, which is called by Bison's pgsql_yyparse.
36+
// Since pgsql_yyparse is now compiled as C++, this can be a regular C++ declaration.
37+
void pgsql_yyerror(yyscan_t yyscanner, PgsqlParser::Parser* parser_context, const char* msg); // REMOVED extern "C"
38+
39+
#endif // PGSQL_PARSER_PARSER_H

src/pgsql_parser/pgsql_lexer.l

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
%option reentrant noyywrap nounput noinput batch case-insensitive
2+
%option prefix="pgsql_yy"
3+
%option extra-type="PgsqlParser::Parser*"
4+
5+
%{
6+
#include "pgsql_parser/pgsql_parser.h" // Defines PgsqlParser::Parser, yyscan_t
7+
#include "pgsql_parser/pgsql_ast.h" // Defines PgsqlParser::AstNode, etc.
8+
#include "pgsql_parser.tab.h" // Defines tokens, AND the actual pgsql_yySTYPE/PGSQL_YYSTYPE union and YYSTYPE typedef
9+
#include <string>
10+
11+
// Forward declare the union with the name Bison seems to use internally (based on previous errors)
12+
// This might not be strictly necessary if pgsql_parser.tab.h defines it adequately before YY_DECL expansion,
13+
// but can help if there are subtle ordering issues.
14+
// union PGSQL_YYSTYPE; // Let's rely on pgsql_parser.tab.h to define this first.
15+
16+
#undef YY_DECL
17+
// Use the explicit union name for yylval_param's type.
18+
// This MUST match the extern declaration in pgsql_parser.y
19+
#define YY_DECL int pgsql_yylex (union PGSQL_YYSTYPE *yylval_param, yyscan_t yyscanner, PgsqlParser::Parser* parser_context)
20+
21+
#define YY_USER_DATA ((PgsqlParser::Parser*)yyget_extra(yyscanner))
22+
%}
23+
24+
%%
25+
26+
[ \t\n]+ { /* Ignore whitespace */ }
27+
28+
"SELECT" { return TOKEN_SELECT; }
29+
"FROM" { return TOKEN_FROM; }
30+
"INSERT" { return TOKEN_INSERT; }
31+
"INTO" { return TOKEN_INTO; }
32+
"VALUES" { return TOKEN_VALUES; }
33+
34+
"QUIT" {
35+
yylval_param->str_val = new std::string(yytext);
36+
return TOKEN_QUIT;
37+
}
38+
39+
[a-zA-Z_][a-zA-Z0-9_]* {
40+
yylval_param->str_val = new std::string(yytext);
41+
return TOKEN_IDENTIFIER;
42+
}
43+
44+
'[^'\n]+' {
45+
yylval_param->str_val = new std::string(yytext);
46+
return TOKEN_STRING_LITERAL;
47+
}
48+
49+
"(" { return TOKEN_LPAREN; }
50+
")" { return TOKEN_RPAREN; }
51+
";" { return TOKEN_SEMICOLON; }
52+
53+
. {
54+
char err_msg[100];
55+
snprintf(err_msg, sizeof(err_msg), "Lexer: Unknown character: '%s'", yytext);
56+
if (parser_context) { // parser_context is a direct parameter now
57+
parser_context->internal_add_error(err_msg);
58+
} else {
59+
fprintf(stderr, "%s\n", err_msg);
60+
}
61+
}
62+
%%

0 commit comments

Comments
 (0)