From 9af2f7bb454d7dd48cf738eb0854328b4b8d79e8 Mon Sep 17 00:00:00 2001 From: Sam Stevens Date: Sun, 10 Jun 2018 12:21:13 +0100 Subject: [PATCH] Implemented conversion from parse tree to SQL structure. Added debug printing for SQL statements. --- Language.txt | 4 +- src/SQL.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/SQL.h | 41 +++++++++++--- src/main.c | 5 ++ src/parser.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++- src/parser.h | 12 +++- src/scanner.c | 6 +- src/scanner.h | 2 +- 8 files changed, 352 insertions(+), 25 deletions(-) diff --git a/Language.txt b/Language.txt index 37feb1f..4f8204c 100644 --- a/Language.txt +++ b/Language.txt @@ -10,8 +10,8 @@ Statement = SelectStmt | InsertStmt | UpdateStmt | DeleteStmt | CreateStmt | Dro SelectStmt = 'Select', FieldList, 'From', Identifier, ['Where', ComparisonGroup] ; InsertStmt = 'Insert Into', Identifier, 'Set', AssignmentList ; -UpdateStmt = 'Update', Identifier, 'Set', AssignmentList, 'Where', AssignmentList ; -DeleteStmt = 'Delete From', Identifier, 'Where', AssignmentList ; +UpdateStmt = 'Update', Identifier, 'Set', AssignmentList, 'Where', ComparisonGroup ; +DeleteStmt = 'Delete From', Identifier, 'Where', ComparisonGroup ; CreateStmt = 'Create Table', Identifier, '(', ColumnSpecList, ')' ; DropStmt = 'Drop Table', Identifier ; diff --git a/src/SQL.c b/src/SQL.c index 8789967..0efda5f 100644 --- a/src/SQL.c +++ b/src/SQL.c @@ -3,10 +3,13 @@ // #include +#include +#include #include "SQL.h" Value *new_value() { Value *value = malloc(sizeof(Value)); + value->type = VALUE_NONE; value->number = 0; value->string = NULL; return value; @@ -177,7 +180,7 @@ SelectStmt *new_select_stmt() { void free_select_stmt(SelectStmt *stmt) { if (stmt->where != NULL) { - free_assignment_list(stmt->where); + free_comparison_group(stmt->where); } if (stmt->tableName != NULL) { free(stmt->tableName); @@ -218,7 +221,7 @@ void free_update_stmt(UpdateStmt *stmt) { free(stmt->tableName); } if (stmt->where != NULL) { - free_assignment_list(stmt->where); + free_comparison_group(stmt->where); } if (stmt->values != NULL) { free_assignment_list(stmt->values); @@ -237,7 +240,7 @@ void free_delete_stmt(DeleteStmt *stmt) { free(stmt->tableName); } if (stmt->where != NULL) { - free_assignment_list(stmt->where); + free_comparison_group(stmt->where); } free(stmt); } @@ -272,10 +275,10 @@ void free_drop_stmt(DropStmt *stmt) { free(stmt); } -Statement *new_statement() { +Statement *new_statement(StatementType type, void *statement) { Statement *stmt = malloc(sizeof(Statement)); - stmt->type = STMT_NONE; - stmt->stmt = NULL; + stmt->type = type; + stmt->stmt = statement; return stmt; } @@ -330,4 +333,143 @@ void append_statement_list(StatementList *list, Statement *statement) { list->length++; list->statements = realloc(list->statements, sizeof(Statement *) * list->length); list->statements[list->length - 1] = statement; +} + +void print_statement_list(StatementList *list) { + + for (size_t i = 0; i < list->length; i++) { + Statement *stmt = list->statements[i]; + switch (stmt->type) { + case STMT_SELECT: { + SelectStmt *select = stmt->stmt; + printf("SELECT "); + print_field_list(select->fields); + printf(" FROM %s", select->tableName); + if (select->where->length > 0) { + printf(" WHERE "); + print_comparison_group(select->where); + } + } + break; + case STMT_INSERT: { + InsertStmt *insert = stmt->stmt; + printf("INSERT INTO %s SET ", insert->tableName); + print_assignment_list(insert->values); + } + break; + case STMT_UPDATE: { + UpdateStmt *update = stmt->stmt; + printf("UPDATE %s SET ", update->tableName); + print_assignment_list(update->values); + if (update->where->length > 0) { + printf(" WHERE "); + print_comparison_group(update->where); + } + } + break; + case STMT_DELETE: { + DeleteStmt *delete = stmt->stmt; + printf("DELETE FROM %s", delete->tableName); + if (delete->where->length > 0) { + printf(" "); + print_comparison_group(delete->where); + } + } + break; + case STMT_CREATE: { + CreateStmt *create = stmt->stmt; + printf("CREATE TABLE %s (", create->tableName); + print_column_spec_list(create->columns); + printf(")"); + } + break; + case STMT_DROP: { + DropStmt *drop = stmt->stmt; + printf("DELETE TABLE %s", drop->tableName); + } + break; + default: + break; + } + printf(";\n"); + } + +} + +void print_field_list(FieldList *list) { + for (size_t i = 0; i < list->length; i++) { + printf("%s%s", list->fields[i], i + 1 < list->length ? ", " : ""); + } +} + +void print_assignment_list(AssignmentList *list) { + for (size_t i = 0; i < list->length; i++) { + Assignment *assignment = list->assignments[i]; + printf("%s=", assignment->identifier); + print_value(assignment->value); + if (i + 1 < list->length) { + printf(", "); + } + } +} + +void print_comparison_group(ComparisonGroup *group) { + for (size_t i = 0; i < group->length; i++) { + Comparison *comparison = group->comparisons[i]; + printf("%s=", comparison->identifier); + print_value(comparison->value); + if (i + 1 < group->length) { + printf(" AND "); + } + } +} + +void print_column_spec_list(ColumnSpecList *list) { + for (size_t i = 0; i < list->length; i++) { + ColumnSpec *spec = list->columns[i]; + if (spec->type == COLTYPE_STRING) { + printf("%s STRING(%d)", spec->identifier, spec->size); + } else if (spec->type == COLTYPE_INT) { + printf("%s INTEGER", spec->identifier); + } + if (spec->option == COLOPT_INDEX) { + printf(" INDEX"); + } + if (i + 1 < list->length) { + printf(", "); + } + } +} + +void print_value(Value *value) { + if (value->type == VALUE_STRING) { + printf("'"); + size_t len = strlen(value->string); + for (size_t i = 0; i < len; i++) { + char next = value->string[i]; + switch (next) { + case '\'': + printf("\\'"); + break; + case '\n': + printf("\\n"); + break; + case '\r': + printf("\\r"); + break; + case '\t': + printf("\\t"); + break; + case '\\': + printf("\\\\"); + break; + default: + printf("%c", next); + break; + } + } + printf("'"); + } else if (value->type == VALUE_NUMBER) { + printf("%lld", value->number); + } } \ No newline at end of file diff --git a/src/SQL.h b/src/SQL.h index 0da19a6..1b1ee85 100644 --- a/src/SQL.h +++ b/src/SQL.h @@ -8,11 +8,19 @@ #include #include -union Value_t { - char *string; - uint64_t number; +enum ValueType_t { + VALUE_NONE, + VALUE_STRING, + VALUE_NUMBER }; -typedef union Value_t Value; +typedef enum ValueType_t ValueType; + +struct Value_t { + ValueType type; + char *string; + int64_t number; +}; +typedef struct Value_t Value; Value *new_value(); @@ -33,6 +41,7 @@ struct Comparison_t { typedef struct Comparison_t Comparison; Comparison *new_comparison(); + void free_comparison(Comparison *comparison); struct ComparisonGroup_t { @@ -42,7 +51,9 @@ struct ComparisonGroup_t { typedef struct ComparisonGroup_t ComparisonGroup; ComparisonGroup *new_comparision_group(); -void free_comparison_group(ComparisonGroup* group); + +void free_comparison_group(ComparisonGroup *group); + void append_comparison_group(ComparisonGroup *group, Comparison *comparison); struct Assignment_t { @@ -119,7 +130,7 @@ void append_column_spec_list(ColumnSpecList *list, ColumnSpec *spec); struct SelectStmt_t { FieldList *fields; char *tableName; - AssignmentList *where; + ComparisonGroup *where; }; typedef struct SelectStmt_t SelectStmt; @@ -140,7 +151,7 @@ void free_insert_stmt(InsertStmt *stmt); struct UpdateStmt_t { char *tableName; AssignmentList *values; - AssignmentList *where; + ComparisonGroup *where; }; typedef struct UpdateStmt_t UpdateStmt; @@ -150,7 +161,7 @@ void free_update_stmt(UpdateStmt *stmt); struct DeleteStmt_t { char *tableName; - AssignmentList *where; + ComparisonGroup *where; }; typedef struct DeleteStmt_t DeleteStmt; @@ -194,7 +205,7 @@ struct Statement_t { }; typedef struct Statement_t Statement; -Statement *new_statement(); +Statement *new_statement(StatementType type, void *statement); void free_statement(Statement *stmt); @@ -210,4 +221,16 @@ void free_statement_list(StatementList *list); void append_statement_list(StatementList *list, Statement *statement); +void print_statement_list(StatementList *list); + +void print_field_list(FieldList *list); + +void print_assignment_list(AssignmentList *list); + +void print_comparison_group(ComparisonGroup *group); + +void print_column_spec_list(ColumnSpecList *list); + +void print_value(Value *value); + #endif //SDB_SQL_H diff --git a/src/main.c b/src/main.c index bd99557..c691021 100644 --- a/src/main.c +++ b/src/main.c @@ -36,6 +36,11 @@ void parse_input(char *input) { if (node != NULL) { printf("%s\n", input); parser_print_node_tree(node, 0); + + StatementList* list = parser_node_convert(node); + print_statement_list(list); + free_statement_list(list); + free_parser_node(node); } diff --git a/src/parser.c b/src/parser.c index c83fa3a..f6d9659 100644 --- a/src/parser.c +++ b/src/parser.c @@ -76,6 +76,149 @@ void free_parser(Parser *parser) { free(parser); } +StatementList *parser_node_convert(ParserNode *node) { + if (node->type != NODE_STATEMENT_LIST) { + return NULL; + } + + StatementList *list = new_statement_list(); + + size_t i, k; + + for (i = 0; i < node->childrenLength; i++) { + ParserNode *child = node->children[i]; + switch (child->type) { + case NODE_SELECT_STMT: { + SelectStmt *select = new_select_stmt(); + append_statement_list(list, new_statement(STMT_SELECT, select)); + select->fields = new_field_list(); + select->tableName = strdup(child->children[1]->token->valueStr); + for (k = 0; k < child->children[0]->childrenLength; k++) { + ParserNode *fieldNode = child->children[0]->children[k]; + append_field_list(select->fields, strdup(fieldNode->token->valueStr)); + } + if (child->childrenLength == 3) { + select->where = parser_node_convert_comparison_group(child->children[2]); + } else { + select->where = new_comparision_group(); + } + } + break; + case NODE_INSERT_STMT: { + InsertStmt *insert = new_insert_stmt(); + append_statement_list(list, new_statement(STMT_INSERT, insert)); + insert->tableName = strdup(child->children[0]->token->valueStr); + insert->values = parser_node_convert_assignment_list(child->children[1]); + } + break; + case NODE_UPDATE_STMT: { + UpdateStmt *update = new_update_stmt(); + append_statement_list(list, new_statement(STMT_UPDATE, update)); + update->tableName = strdup(child->children[0]->token->valueStr); + update->values = parser_node_convert_assignment_list(child->children[1]); + if (child->childrenLength == 3) { + update->where = parser_node_convert_comparison_group(child->children[2]); + } else { + update->where = new_comparision_group(); + } + } + break; + case NODE_DELETE_STMT: { + DeleteStmt *delete = new_delete_stmt(); + append_statement_list(list, new_statement(STMT_DELETE, delete)); + delete->tableName = strdup(child->children[0]->token->valueStr); + if (child->childrenLength == 2) { + delete->where = parser_node_convert_comparison_group(child->children[1]); + } else { + delete->where = new_comparision_group(); + } + } + break; + case NODE_CREATE_STMT: { + CreateStmt *create = new_create_stmt(); + append_statement_list(list, new_statement(STMT_CREATE, create)); + create->tableName = strdup(child->children[0]->token->valueStr); + create->columns = parser_node_convert_column_spec_list(child->children[1]); + } + break; + case NODE_DROP_STMT: { + DropStmt *drop= new_drop_stmt(); + append_statement_list(list, new_statement(STMT_DROP, drop)); + drop->tableName = (child->children[0]->token->valueStr); + } + break; + default: + break; + } + } + + return list; +} + +ColumnSpecList *parser_node_convert_column_spec_list(ParserNode *node) { + ColumnSpecList *list = new_column_spec_list(); + for (size_t i = 0; i < node->childrenLength; i++) { + ParserNode* specNode = node->children[i]; + ColumnSpec *spec = new_column_spec(); + append_column_spec_list(list, spec); + spec->identifier = strdup(specNode->token->valueStr); + ParserNode *colTypeNode = specNode->children[0]; + if (colTypeNode->token->type == T_KW_STRING) { + spec->type = COLTYPE_STRING; + spec->size = (size_t)colTypeNode->children[0]->token->valueInt; + } else if (colTypeNode->token->type == T_KW_INT) { + spec->type = COLTYPE_INT; + } + if (specNode->childrenLength == 2) { + if (specNode->children[1]->token->type == T_KW_INDEX) { + spec->option = COLOPT_INDEX; + } + } + } + return list; +} + +ComparisonGroup *parser_node_convert_comparison_group(ParserNode *node) { + ComparisonGroup *group = new_comparision_group(); + for (size_t i = 0; i < node->childrenLength; i++) { + ParserNode *compNode = node->children[i]; + Comparison *comp = new_comparison(); + append_comparison_group(group, comp); + comp->identifier = strdup(compNode->children[0]->token->valueStr); + if (compNode->children[1]->token->type == T_COMP_EQ) { + comp->comp = COMP_EQ; + } else if (compNode->children[1]->token->type == T_COMP_NEQ) { + comp->comp = COMP_NEQ; + } + comp->value = parser_node_convert_value(compNode->children[2]); + } + return group; +} + +AssignmentList *parser_node_convert_assignment_list(ParserNode *node) { + AssignmentList *list = new_assignment_list(); + for (size_t i = 0; i < node->childrenLength; i++) { + ParserNode *assignmentNode = node->children[i]; + Assignment *assignment = new_assignment(); + append_assignment_list(list, assignment); + assignment->identifier = strdup(assignmentNode->token->valueStr); + assignment->value = parser_node_convert_value(assignmentNode->children[0]); + } + return list; +} + +Value *parser_node_convert_value(ParserNode *node) { + Value *value = new_value(); + if (node->token->type == T_STRING) { + value->type = VALUE_STRING; + value->string = strdup(node->token->valueStr); + } else if (node->token->type == T_NUMBER) { + value->type = VALUE_NUMBER; + value->number = node->token->valueInt; + } + return value; +} + #define NEXT_TOKEN() {\ token = scanner_next_token(scanner, token);\ } @@ -286,9 +429,9 @@ ParserNode *parser_parse(Parser *parser, Scanner *scanner) { ASCEND_NODE(); } else if (token->type == T_KW_WHERE) { node->phase++; - ParserNode *assignments = new_parser_node(NODE_ASSIGNMENT_LIST, NULL); - append_parser_node(node, assignments); - node = assignments; + ParserNode *comparisons = new_parser_node(NODE_COMPARISON_GROUP, NULL); + append_parser_node(node, comparisons); + node = comparisons; } } break; @@ -407,6 +550,10 @@ ParserNode *parser_parse(Parser *parser, Scanner *scanner) { parser_set_error(parser, "Expected number", token); break; } + if (token->valueInt < 1) { + parser_set_error(parser, "Expected positive number", token); + break; + } append_parser_node(columnType, new_parser_node(NODE_COLUMN_TYPE_SPECIFIER, token)); token = NULL; EXPECT(T_PAREN_CLOSE); diff --git a/src/parser.h b/src/parser.h index 57c9fcd..2a665af 100644 --- a/src/parser.h +++ b/src/parser.h @@ -55,7 +55,7 @@ enum ParserNodeType_t { typedef enum ParserNodeType_t ParserNodeType; #undef X -char* parser_node_type_to_str(ParserNodeType nodeType); +char *parser_node_type_to_str(ParserNodeType nodeType); struct ParserNode_t { struct ParserNode_t *parent; @@ -73,6 +73,16 @@ void free_parser_node(ParserNode *node); void append_parser_node(ParserNode *node, ParserNode *child); +StatementList *parser_node_convert(ParserNode *node); + +ColumnSpecList *parser_node_convert_column_spec_list(ParserNode *node); + +ComparisonGroup *parser_node_convert_comparison_group(ParserNode *node); + +AssignmentList *parser_node_convert_assignment_list(ParserNode *node); + +Value *parser_node_convert_value(ParserNode *node); + struct Parser_t { ParseStatus status; char *errMsg; diff --git a/src/scanner.c b/src/scanner.c index e99f6dc..ae64f87 100644 --- a/src/scanner.c +++ b/src/scanner.c @@ -203,11 +203,11 @@ ScannerToken *scanner_next_token(Scanner *scanner, ScannerToken *token) { } //Numbers - if (isdigit(scanner_peek_char(scanner))) { + if (isdigit(scanner_peek_char(scanner)) || scanner_peek_char(scanner) == '-') { char intInput[32] = {0}; size_t intIndex = 0; while (scanner_peek_char(scanner) != 0 - && isdigit(scanner_peek_char(scanner)) + && (isdigit(scanner_peek_char(scanner)) || scanner_peek_char(scanner) == '-') && intIndex + 1 < 32) { intInput[intIndex++] = scanner_next_char(scanner); } @@ -219,7 +219,7 @@ ScannerToken *scanner_next_token(Scanner *scanner, ScannerToken *token) { } token->type = T_NUMBER; //convert number - token->valueInt = (uint64_t) strtol(intInput, NULL, 10); + token->valueInt = (int64_t) strtol(intInput, NULL, 10); return token; } diff --git a/src/scanner.h b/src/scanner.h index 3990ba8..5cc64a0 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -48,7 +48,7 @@ char* scanner_token_type_to_str(ScannerTokenType tokenType); struct ScannerToken_t { ScannerTokenType type; char *valueStr; - uint64_t valueInt; + int64_t valueInt; size_t lineNo; size_t linePos; };