commit of current state
This commit is contained in:
parent
b5e65a7821
commit
6ea2c0e318
|
@ -0,0 +1,5 @@
|
|||
lex_lexer.c
|
||||
lex_lexer.h
|
||||
lex_parser.c
|
||||
lex_parser.h
|
||||
*.o
|
|
@ -0,0 +1,33 @@
|
|||
# Makefile
|
||||
|
||||
LIB_FILES = lex_lexer.c lex_parser.c lib/Statement.cpp lib/SQLParser.cpp
|
||||
|
||||
|
||||
TESTS_MAIN = sql_tests.cpp
|
||||
TESTS_BIN = bin/tests
|
||||
|
||||
PARSER_MAIN = sql_parser.cpp
|
||||
PARSER_BIN = bin/parser
|
||||
|
||||
CC = g++
|
||||
CFLAGS = -g -O3 -Ilib/ -I./
|
||||
|
||||
|
||||
tests: $(LIB_FILES) $(TESTS_MAIN)
|
||||
$(CC) $(CFLAGS) $(LIB_FILES) $(TESTS_MAIN) -o $(TESTS_BIN)
|
||||
|
||||
|
||||
parser: $(LIB_FILES) $(PARSER_MAIN)
|
||||
$(CC) $(CFLAGS) $(LIB_FILES) $(PARSER_MAIN) -o $(PARSER_BIN)
|
||||
|
||||
|
||||
lex_lexer.c: lex_lexer.l
|
||||
flex lex_lexer.l
|
||||
|
||||
|
||||
lex_parser.c: lex_parser.y lex_lexer.c
|
||||
bison lex_parser.y
|
||||
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ lex_lexer.c lex_lexer.h lex_parser.c lex_parser.h $(PARSER_BIN) $(TESTS_BIN)
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
make clean
|
||||
make tests
|
||||
|
||||
./bin/tests
|
|
@ -0,0 +1,51 @@
|
|||
%{
|
||||
|
||||
/*
|
||||
* lexer.l file
|
||||
* To generate the lexical analyzer run: "flex lexer.l"
|
||||
*/
|
||||
|
||||
#include "Statement.h"
|
||||
#include "List.h"
|
||||
#include "lex_parser.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
#define TOK(name) { return name; }
|
||||
|
||||
%}
|
||||
|
||||
|
||||
%option outfile="lex_lexer.c" header-file="lex_lexer.h"
|
||||
%option warn nodefault
|
||||
%option reentrant noyywrap never-interactive nounistd
|
||||
%option bison-bridge
|
||||
%option case-insensitive
|
||||
|
||||
%%
|
||||
|
||||
[ \t\n]+ ;
|
||||
|
||||
SELECT TOK(SELECT)
|
||||
FROM TOK(FROM)
|
||||
GROUP TOK(GROUP)
|
||||
BY TOK(BY)
|
||||
|
||||
[-+*/(),.;] TOK(yytext[0])
|
||||
|
||||
[0-9]+ |
|
||||
[0-9]+"."[0-9]* |
|
||||
"."[0-9]* TOK(INTNUM)
|
||||
|
||||
[A-Za-z][A-Za-z0-9_]* {
|
||||
yylval->sval = strdup(yytext);
|
||||
return STRING;
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
int yyerror(const char *msg) {
|
||||
fprintf(stderr,"Error:%s\n",msg); return 0;
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
%{
|
||||
|
||||
/*
|
||||
* parser.y file
|
||||
* To generate the parser run: "bison parser.y"
|
||||
*/
|
||||
|
||||
#include "Statement.h"
|
||||
#include "List.h"
|
||||
#include "lex_parser.h"
|
||||
#include "lex_lexer.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int yyerror(Statement **expression, yyscan_t scanner, const char *msg) {
|
||||
// Add error handling routine as needed
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%code requires {
|
||||
|
||||
#ifndef YY_TYPEDEF_YY_SCANNER_T
|
||||
#define YY_TYPEDEF_YY_SCANNER_T
|
||||
typedef void* yyscan_t;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
%output "lex_parser.c"
|
||||
%defines "lex_parser.h"
|
||||
|
||||
%define api.pure
|
||||
%lex-param { yyscan_t scanner }
|
||||
%parse-param { Statement **statement }
|
||||
%parse-param { yyscan_t scanner }
|
||||
|
||||
%union {
|
||||
int value;
|
||||
char* sval;
|
||||
|
||||
Statement* statement;
|
||||
SelectStatement* select_statement;
|
||||
TableRef* table;
|
||||
Expression* expr;
|
||||
|
||||
List<char*>* slist;
|
||||
List<Expression*>* explist;
|
||||
}
|
||||
|
||||
%token SELECT FROM GROUP BY INTNUM
|
||||
%token <sval> STRING
|
||||
|
||||
%type <statement> statement
|
||||
%type <select_statement> select_statement
|
||||
%type <table> from_clause
|
||||
%type <slist> string_list
|
||||
%type <explist> expr_list group_clause
|
||||
%type <expr> expr;
|
||||
%%
|
||||
|
||||
|
||||
input:
|
||||
statement opt_semicolon { *statement = $1; };
|
||||
|
||||
|
||||
opt_semicolon:
|
||||
';' | ;
|
||||
|
||||
|
||||
statement:
|
||||
select_statement { $$ = $1; }
|
||||
| { $$ = NULL; };
|
||||
|
||||
|
||||
select_statement:
|
||||
SELECT expr_list from_clause group_clause
|
||||
{
|
||||
SelectStatement* s = new SelectStatement();
|
||||
s->_select_list = $2;
|
||||
s->_from_table = $3;
|
||||
s->_group_by = $4;
|
||||
$$ = s;
|
||||
};
|
||||
|
||||
|
||||
expr_list:
|
||||
expr { $$ = new List<Expression*>($1); }
|
||||
| expr_list ',' expr { $1->push_back($3); $$ = $1; };
|
||||
|
||||
|
||||
expr:
|
||||
STRING { $$ = new Expression($1); }
|
||||
| STRING '(' STRING ')' { $$ = new Expression($3, $1); };
|
||||
|
||||
|
||||
from_clause:
|
||||
FROM string_list
|
||||
{
|
||||
TableRef* t = new TableRef(eTableName);
|
||||
t->_table_names = $2;
|
||||
$$ = t;
|
||||
}
|
||||
| FROM '(' select_statement ')'
|
||||
{
|
||||
TableRef* t = new TableRef(eTableSelect);
|
||||
t->_stmt = $3;
|
||||
$$ = t;
|
||||
};
|
||||
|
||||
|
||||
group_clause:
|
||||
GROUP BY expr_list { $$ = $3; }
|
||||
| { $$ = NULL; };
|
||||
|
||||
string_list:
|
||||
STRING { $$ = new List<char*>($1); }
|
||||
| string_list ',' STRING { $1->push_back($3); $$ = $1; }
|
||||
|
||||
%%
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef __EXPRESSION_H__
|
||||
#define __EXPRESSION_H__
|
||||
|
||||
|
||||
|
||||
class Expression {
|
||||
public:
|
||||
Expression(char* name) : name(name) {};
|
||||
Expression(char* name, char* func_name) : name(name), func_name(func_name) {};
|
||||
|
||||
char* name;
|
||||
char* func_name;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef __LIST_H__
|
||||
#define __LIST_H__
|
||||
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
|
||||
template <typename _T>
|
||||
class List {
|
||||
public:
|
||||
std::vector<_T> _vector;
|
||||
|
||||
List() {}
|
||||
|
||||
List(_T first_value) {
|
||||
_vector.push_back(first_value);
|
||||
}
|
||||
|
||||
inline size_t size() { return _vector.size(); };
|
||||
|
||||
inline _T at(int i) { return _vector[i]; }
|
||||
inline _T &operator[](int i) { return _vector[i]; }
|
||||
inline void push_back(_T value) { _vector.push_back(value); }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,37 @@
|
|||
#include "SQLParser.h"
|
||||
#include "lex_parser.h"
|
||||
#include "lex_lexer.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int yyparse(Statement **expression, yyscan_t scanner);
|
||||
|
||||
|
||||
SQLParser::SQLParser() {
|
||||
fprintf(stderr, "SQLParser only has static methods atm! Do not initialize!\n");
|
||||
}
|
||||
|
||||
|
||||
Statement* SQLParser::parseSQL(const char *text) {
|
||||
Statement* stmt;
|
||||
yyscan_t scanner;
|
||||
YY_BUFFER_STATE state;
|
||||
|
||||
if (yylex_init(&scanner)) {
|
||||
// couldn't initialize
|
||||
fprintf(stderr, "Error when initializing!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
state = yy_scan_string(text, scanner);
|
||||
|
||||
if (yyparse(&stmt, scanner)) {
|
||||
// error parsing
|
||||
fprintf(stderr, "Error when parsing!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
yy_delete_buffer(state, scanner);
|
||||
|
||||
yylex_destroy(scanner);
|
||||
return stmt;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef __SQLPARSER_H_
|
||||
#define __SQLPARSER_H_
|
||||
|
||||
#include "Statement.h"
|
||||
|
||||
class SQLParser {
|
||||
public:
|
||||
static Statement* parseSQL(const char* sql);
|
||||
|
||||
private:
|
||||
SQLParser();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Statement.c
|
||||
* Implementation of functions used to build the syntax tree.
|
||||
*/
|
||||
|
||||
#include "Statement.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
Statement::Statement(EStatementType type) : _type(type) {};
|
||||
|
||||
SelectStatement::SelectStatement() : Statement(eSelect) {};
|
||||
|
||||
TableRef::TableRef(ETableRefType type) : _type(type) {};
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Statement.h
|
||||
* Definition of the structure used to build the syntax tree.
|
||||
*/
|
||||
#ifndef __STATEMENT_H__
|
||||
#define __STATEMENT_H__
|
||||
|
||||
#include "Expression.h"
|
||||
#include "List.h"
|
||||
|
||||
class TableRef;
|
||||
|
||||
typedef enum {
|
||||
eSelect,
|
||||
eDelete,
|
||||
eInsert,
|
||||
eCreate
|
||||
} EStatementType;
|
||||
|
||||
|
||||
|
||||
class Statement {
|
||||
public:
|
||||
Statement(EStatementType type);
|
||||
|
||||
EStatementType _type;
|
||||
};
|
||||
|
||||
|
||||
class SelectStatement : public Statement {
|
||||
public:
|
||||
SelectStatement();
|
||||
|
||||
TableRef* _from_table;
|
||||
List<Expression*>* _select_list;
|
||||
List<Expression*>* _group_by;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* TableRef
|
||||
* Holds reference to tables. Can be either table names or a select statement.
|
||||
*/
|
||||
typedef enum {
|
||||
eTableName,
|
||||
eTableSelect
|
||||
} ETableRefType;
|
||||
|
||||
class TableRef {
|
||||
public:
|
||||
TableRef(ETableRefType type);
|
||||
|
||||
ETableRefType _type;
|
||||
|
||||
SelectStatement* _stmt;
|
||||
List<char*>* _table_names;
|
||||
|
||||
};
|
||||
|
||||
#endif // __STATEMENT_H__
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* sql_parser.cpp
|
||||
*/
|
||||
|
||||
#include "sql_interface.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void evaluate_statement(Statement* stmt);
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc <= 1) {
|
||||
fprintf(stderr, "No SQL-Statement given!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Statement *stmt = NULL;
|
||||
|
||||
for (int n = 1; n < argc; ++n) {
|
||||
char* sql = argv[n];
|
||||
|
||||
printf("\nEvaluating Statement \"%s\"\n", sql);
|
||||
Statement* stmt = parse_sql(sql);
|
||||
evaluate_statement(stmt);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void evaluate_select_statement(SelectStatement* stmt) {
|
||||
// printf("Selecting %s from %s\n", stmt->_targets->toString(), stmt->_from_clause);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void evaluate_statement(Statement* stmt) {
|
||||
printf("Statement at %p\n", stmt);
|
||||
if (stmt == NULL) return;
|
||||
|
||||
switch (stmt->_type) {
|
||||
case eSelect:
|
||||
evaluate_select_statement((SelectStatement*) stmt);
|
||||
break;
|
||||
case eInsert:
|
||||
printf("Insert Statment found!\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* sql_tests.cpp
|
||||
*/
|
||||
|
||||
#include "lib/SQLParser.h"
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
|
||||
#define STREQUALS(str1, str2) std::string(str1).compare(std::string(str2)) == 0
|
||||
|
||||
#define ASSERT(cond) if (!(cond)) { fprintf(stderr, "failed! Assertion (" #cond ")\n"); return; }
|
||||
#define ASSERT_STR(STR1, STR2) ASSERT(STREQUALS(STR1, STR2));
|
||||
|
||||
|
||||
void SelectTest1() {
|
||||
printf("Test: SelectTest1... ");
|
||||
fflush(stdout);
|
||||
|
||||
const char* sql = "SELECT age, name, address from table;";
|
||||
Statement* stmt = SQLParser::parseSQL(sql);
|
||||
ASSERT(stmt != NULL);
|
||||
ASSERT(stmt->_type == eSelect);
|
||||
|
||||
SelectStatement* select = (SelectStatement*) stmt;
|
||||
|
||||
ASSERT(select->_select_list->size() == 3);
|
||||
ASSERT_STR(select->_select_list->at(0)->name, "age");
|
||||
ASSERT_STR(select->_select_list->at(1)->name, "name");
|
||||
ASSERT_STR(select->_select_list->at(2)->name, "address");
|
||||
|
||||
ASSERT(select->_from_table != NULL);
|
||||
ASSERT(select->_from_table->_type == eTableName);
|
||||
ASSERT_STR(select->_from_table->_table_names->at(0), "table");
|
||||
|
||||
printf("passed!\n");
|
||||
}
|
||||
|
||||
void SelectTest2() {
|
||||
printf("Test: SelectTest2... ");
|
||||
fflush(stdout);
|
||||
|
||||
const char* sql = "SELECT age, name, address FROM (SELECT age FROM table, table2);";
|
||||
Statement* stmt = SQLParser::parseSQL(sql);
|
||||
ASSERT(stmt != NULL);
|
||||
ASSERT(stmt->_type == eSelect);
|
||||
|
||||
SelectStatement* select = (SelectStatement*) stmt;
|
||||
|
||||
ASSERT(select->_select_list->size() == 3);
|
||||
ASSERT_STR(select->_select_list->at(0)->name, "age");
|
||||
ASSERT_STR(select->_select_list->at(1)->name, "name");
|
||||
ASSERT_STR(select->_select_list->at(2)->name, "address");
|
||||
|
||||
ASSERT(select->_from_table != NULL);
|
||||
ASSERT(select->_from_table->_type == eTableSelect);
|
||||
ASSERT(select->_from_table->_stmt != NULL);
|
||||
ASSERT(select->_from_table->_stmt->_select_list->size() == 1);
|
||||
ASSERT_STR(select->_from_table->_stmt->_from_table->_table_names->at(0), "table");
|
||||
ASSERT_STR(select->_from_table->_stmt->_from_table->_table_names->at(1), "table2");
|
||||
|
||||
printf("passed!\n");
|
||||
}
|
||||
|
||||
void SelectTest3() {
|
||||
printf("Test: SelectTest3... ");
|
||||
fflush(stdout);
|
||||
|
||||
const char* sql = "SELECT name, AVG(age) FROM table GROUP BY name";
|
||||
Statement* stmt = SQLParser::parseSQL(sql);
|
||||
ASSERT(stmt != NULL);
|
||||
ASSERT(stmt->_type == eSelect);
|
||||
|
||||
SelectStatement* select = (SelectStatement*) stmt;
|
||||
|
||||
ASSERT(select->_select_list->size() == 2);
|
||||
|
||||
ASSERT(select->_select_list->at(0)->func_name == NULL);
|
||||
ASSERT_STR("name", select->_select_list->at(0)->name);
|
||||
|
||||
ASSERT(select->_select_list->at(1)->func_name != NULL);
|
||||
ASSERT_STR("age", select->_select_list->at(1)->name);
|
||||
ASSERT_STR("AVG", select->_select_list->at(1)->func_name);
|
||||
|
||||
ASSERT(select->_group_by != NULL);
|
||||
ASSERT(select->_group_by->size() == 1);
|
||||
ASSERT_STR("name", select->_group_by->at(0)->name);
|
||||
|
||||
printf("passed!\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
printf("\n######################################\n");
|
||||
printf("## Running all tests...\n\n");
|
||||
|
||||
SelectTest1();
|
||||
SelectTest2();
|
||||
SelectTest3();
|
||||
|
||||
printf("\n## Finished running all tests...\n");
|
||||
printf("######################################\n");
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue