|
前段时间做了一个简单的内存数据库,需要提供点简单的SQL支持,在参考了《Lex与Yacc》和网上的相关资料后,以《Lex与Yacc》中的SQL解释器为基础,做了修改,最后生成了一个简单的SQL解释器。 这个SQL解释器由于本身内存数据库提供的功能限制,提供的SQL也有很多的限制: 1、select不支持按字段取值,一次查询获取所有字段 2、查询条件之间的关系只支持AND 3、UPDATE一次只更新一个字段 4、不支持函数
在SQL解释器中,关键是我们要构建我们的语法结构,也就是最终通过SQL解释器要生成的一个程序能识别的结构。负责的SQL支持对应负责的负责的结构,因为我们支持的SQL简单,所以相对应的结构也就很简单了。
主题程序由3个文件组成,sql_plan.h parser_lex.l parser_yacc.y。其中,sql_plan.h是语法结构的定义,parser_lex.l是词法解释和主程序入口部分,parser_yacc.y是语法解释部分。 sql_plan.h | #ifndef __SQL_DEFINE__ #define __SQL_DEFINE__
#define NAME_SIZE 24 #define MAX_BUFFER_SIZE 8192
typedef enum { SQL_SELECT = 1, SQL_INSERT, SQL_UPDATE, SQL_DELETE, SQL_COUNT_ALL }sql_action;
typedef struct{ char field_name[NAME_SIZE]; char field_value[256]; }FieldNameValue;
typedef struct{ int list_len; FieldNameValue fnv_par[5]; }FieldNameValue_List;
typedef struct{ int action; char tab_name[256]; FieldNameValue_List w_fnv_list; char u_field_name[256]; char u_field_value[256]; int pos; char buffer[MAX_BUFFER_SIZE]; }SQL_PLAN;
extern SQL_PLAN sql_plan;
#endif |
parser_lex.l | %{
#include "parser_yacc.h" #include "sql_define.h" #include <stdio.h> #include <stdlib.h> #include <string.h>
int lineno = 1; void yyerror(char *s);
void TrimString(char *str) { char *copied, *tail = NULL; if ( str == NULL ) return;
for( copied = str; *str; str++ ) { if ( *str != ' ' && *str != '\t' && *str != '\n' && *str != '\r' && *str != '\'' && *str != ';') { *copied++ = *str; tail = copied; } else { if ( tail ) *copied++ = *str; } }
if ( tail ) *tail = 0; else *copied = 0;
return; }
char *_xInput; int _xLen = 0;
int myinput(char *buff,int max) { if(!_xLen) return 0; if(max > _xLen) max = _xLen ; memcpy(buff,_xInput,max); _xInput += max; _xLen -= max; return max; }
#define YY_INPUT(b, r, m) (r = myinput(b, m))
%}
%%
and { return AND; } CHAR(ACTER)? { return CHARACTER; } CLOSE { return CLOSE; } CREATE { return CREATE; } CURRENT { return CURRENT; } CURSOR { return CURSOR; } count { return COUNT; } DECLARE { return DECLARE; } delete { return DELETE; } DOUBLE { return DOUBLE; } FETCH { return FETCH; } FLOAT { return FLOAT; } FOR { return FOR; } from { return FROM; } insert { return INSERT; } INT(EGER)? { return INTEGER; } into { return INTO; } NOT { return NOT; } NULL { return NULLX; } NUMERIC { return NUMERIC; } OF { return OF; } OPEN { return OPEN; } OR { return OR; } REAL { return REAL; } select { return SELECT; } set { return SET; } table { return TABLE; } update { return UPDATE; } values { return VALUES; } where { return WHERE; }
/* punctuation */ "=" { return L_EQ; }
"<>" | "<" | ">" | "<=" | ">=" { return COMPARISON; }
[-+*/:(),.;] { return yytext[0]; }
/* names */
[A-Za-z][A-Za-z0-9_]* { strcpy(yylval.nameval,yytext); return NAME; }
/* numbers */
-?[0-9]+ { yylval.intval = atoi(yytext); return INTNUM; } [0-9]+"."[0-9]* { yylval.floatval = atof(yytext); return FLOATNUM; }
[0-9]+[eE][+-]?[0-9]+ | [0-9]+"."[0-9]*[eE][+-]?[0-9]+ | "."[0-9]*[eE][+-]?[0-9]+ { return APPROXNUM; }
/* strings */ '[^'\n]*' { TrimString(yytext); strcpy(yylval.strval,yytext); return STRING; } '[^\'\n]*$ { yyerror("Unterminated string"); }
\n lineno++;
[ \t\r]+ ; /* white space */
"--".*$ ; /* comment */
%%
void yyerror(char *s) { printf("%d: %s at %s\n", lineno, s, yytext); }
int yywrap() {return 1;}
main(int argc, char **argv) { int pos; int ret; char *buffer; char prompt_buffer[MAX_BUFFER_SIZE]; char last_prompt_buffer[MAX_BUFFER_SIZE]; int count; while(1) { printf("mdbsql>>"); fgets(prompt_buffer, sizeof(prompt_buffer), stdin); _xInput = prompt_buffer; _xLen = strlen(prompt_buffer); if (prompt_buffer[0]=='\\') { strcpy(prompt_buffer, last_prompt_buffer); _xLen = strlen(prompt_buffer); } if (strncmp(prompt_buffer, "quit", 4)==0 || strncmp(prompt_buffer, "exit", 4)==0 ) { break; } memset(&sql_plan, 0, sizeof(sql_plan) ); strcpy(last_prompt_buffer, prompt_buffer); if(!yyparse()) { if (sql_plan.action == SQL_SELECT) { printf("%d records are retrieved\n", count); } else if (sql_plan.action == SQL_DELETE) { printf("%d records are deleted\n", ret); } else if (sql_plan.action == SQL_UPDATE) { printf("%d records are updated\n", ret); } else if (sql_plan.action == SQL_INSERT) { if (ret>0) printf("1 record is inserted\n"); } else if (sql_plan.action == SQL_COUNT_ALL) { if (ret>0) printf("1 record is retrieved\n"); } } else { printf("SQL parse failed\n"); } } } | parser_yacc.h | %{
#include "parser_yacc.h" #include "sql_define.h" #include <stdio.h> #include <stdlib.h> #include <string.h>
int lineno = 1; void yyerror(char *s);
void TrimString(char *str) { char *copied, *tail = NULL; if ( str == NULL ) return;
for( copied = str; *str; str++ ) { if ( *str != ' ' && *str != '\t' && *str != '\n' && *str != '\r' && *str != '\'' && *str != ';') { *copied++ = *str; tail = copied; } else { if ( tail ) *copied++ = *str; } }
if ( tail ) *tail = 0; else *copied = 0;
return; }
char *_xInput; int _xLen = 0;
int myinput(char *buff,int max) { if(!_xLen) return 0; if(max > _xLen) max = _xLen ; memcpy(buff,_xInput,max); _xInput += max; _xLen -= max; return max; }
#define YY_INPUT(b, r, m) (r = myinput(b, m))
%}
%%
and { return AND; } CHAR(ACTER)? { return CHARACTER; } CLOSE { return CLOSE; } CREATE { return CREATE; } CURRENT { return CURRENT; } CURSOR { return CURSOR; } count { return COUNT; } DECLARE { return DECLARE; } delete { return DELETE; } DOUBLE { return DOUBLE; } FETCH { return FETCH; } FLOAT { return FLOAT; } FOR { return FOR; } from { return FROM; } insert { return INSERT; } INT(EGER)? { return INTEGER; } into { return INTO; } NOT { return NOT; } NULL { return NULLX; } NUMERIC { return NUMERIC; } OF { return OF; } OPEN { return OPEN; } OR { return OR; } REAL { return REAL; } select { return SELECT; } set { return SET; } table { return TABLE; } update { return UPDATE; } values { return VALUES; } where { return WHERE; }
/* punctuation */ "=" { return L_EQ; }
"<>" | "<" | ">" | "<=" | ">=" { return COMPARISON; }
[-+*/:(),.;] { return yytext[0]; }
/* names */
[A-Za-z][A-Za-z0-9_]* { strcpy(yylval.nameval,yytext); return NAME; }
/* numbers */
-?[0-9]+ { yylval.intval = atoi(yytext); return INTNUM; } [0-9]+"."[0-9]* { yylval.floatval = atof(yytext); return FLOATNUM; }
[0-9]+[eE][+-]?[0-9]+ | [0-9]+"."[0-9]*[eE][+-]?[0-9]+ | "."[0-9]*[eE][+-]?[0-9]+ { return APPROXNUM; }
/* strings */ '[^'\n]*' { TrimString(yytext); strcpy(yylval.strval,yytext); return STRING; } '[^\'\n]*$ { yyerror("Unterminated string"); }
\n lineno++;
[ \t\r]+ ; /* white space */
"--".*$ ; /* comment */
%%
void yyerror(char *s) { printf("%d: %s at %s\n", lineno, s, yytext); }
int yywrap() {return 1;}
main(int argc, char **argv) { int pos; int ret; char *buffer; char prompt_buffer[MAX_BUFFER_SIZE]; char last_prompt_buffer[MAX_BUFFER_SIZE]; int count; while(1) { printf("mdbsql>>"); fgets(prompt_buffer, sizeof(prompt_buffer), stdin); _xInput = prompt_buffer; _xLen = strlen(prompt_buffer); if (prompt_buffer[0]=='\\') { strcpy(prompt_buffer, last_prompt_buffer); _xLen = strlen(prompt_buffer); } if (strncmp(prompt_buffer, "quit", 4)==0 || strncmp(prompt_buffer, "exit", 4)==0 ) { break; } memset(&sql_plan, 0, sizeof(sql_plan) ); strcpy(last_prompt_buffer, prompt_buffer); if(!yyparse()) { if (sql_plan.action == SQL_SELECT) { printf("%d records are retrieved\n", count); } else if (sql_plan.action == SQL_DELETE) { printf("%d records are deleted\n", ret); } else if (sql_plan.action == SQL_UPDATE) { printf("%d records are updated\n", ret); } else if (sql_plan.action == SQL_INSERT) { if (ret>0) printf("1 record is inserted\n"); } else if (sql_plan.action == SQL_COUNT_ALL) { if (ret>0) printf("1 record is retrieved\n"); } } else { printf("SQL parse failed\n"); } } } | Makefile | mdbsql: parser_yacc.o parser_lex.o gcc -o $@ parser_yacc.o parser_lex.o -lfl
parser_yacc.c parser_yacc.h: parser_yacc.y yacc -vdt parser_yacc.y mv y.tab.h parser_yacc.h mv y.tab.c parser_yacc.c mv y.output parser_yacc.out
parser_lex.o: parser_yacc.h parser_lex.c
parser_lex.c : parser_lex.l flex $< mv lex.yy.c $*.c |
|