C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 강좌/문서
C++Builder Programming Tutorial&Docments
[109] Parser Generator와 Borland C++ 6.0을 이용해서 SQL 파서를 만들어 보자
동그라미 [bluexmas] 20672 읽음    2006-08-09 18:14
Parser Generator와 Borland C++ 6.0을 이용해서 SQL 파서를 만들어 보자

준비물로는 Parser Generator 라는 Lex & Yacc 툴과 Borland C++ 6.0만 있으면 됩니다.

SQL 파서를 만들기 위해서 Delphi Lex & Yacc을 이용하다가 Delphi Lex & Yacc 에서 지원하는 것이
약해서 C++을 사용했던 것으로, Parser Generator 제품이 Borland C++ Builder을 지원하고 있지만
국내나 외국사이트를 보아도 자료를 찾기가 너무 힘들어 간단하게 제작한 소스를 올립니다.

Lex & Yacc과 C++ Builder에 대한 설명은 따로 하지 않겠습니다.
글로 써서 표현하는 능력이 떨어져 소스에 간단한 주석이 설명의 전부입니다.
▶ Parser Generator 다운로드 받기

http://www.bumblebeesoftware.com/

▶ Parser Generator 버전 정보



▶ Parser Generator - LibBuilder 메뉴로 Borland C++ Builder용 라이브러리 컴파일

메뉴) [Project] -> [LibBuilder...] 선택



Borland C++ Builder 체크 박스 선택



[LibBuilder 창]에서 [Properties...] 버튼 선택

Compiler Bin Directory,Compiler Include Directory, Compiler Library Directory 하나씩 선택하여
Borland C++ Builder의 경로를 지정



[LibBuilder 창]에서 [Directories...] 버튼 선택

Parser Generator 경로 지정



[LibBuilder 창]에서 [Build] 버튼 선택하면 아래와 같은 [Output 창]에서 컴파일 과정을 확인



▶ Parser Generator - Project 만들기

아래와 같은 과정을 거치면 sqllexer.l, sqlparser.y이 생성됨

메뉴) [Project] - [ParserWizard...] 선택









▶ sqllexer.l

%{
/****************************************************************************
sqllexer.l
ParserWizard generated Lex file.
****************************************************************************/

#include 
#pragma hdrstop
#include 

#include "sqlparser.h"

int lineno = 1;

%}

/////////////////////////////////////////////////////////////////////////////
// declarations section

%include {
#include 
#pragma hdrstop
#include 
}

// lexical analyser name
%name sqllexer

// class definition
{
	// place any extra class members here
	Classes::TStrings* FLines;
	void SetLog(Classes::TStrings* ALines);
}

// constructor
{
	// place any extra initialisation code here
}

// destructor
{
	// place any extra cleanup code here
}

// place any declarations here

%%

/////////////////////////////////////////////////////////////////////////////
// rules section

%{
	// extract yylval for use later on in actions
	YYSTYPE YYFAR& yylval = *(YYSTYPE YYFAR*)yyparserptr->yylvalptr;
%}

// place your Lex rules here

/* literal keyword tokens */

ADA				{ return ADA; }
ALL				{ return ALL; }
AND				{ return AND; }
AVG				{ return AMMSC; }
MIN				{ return AMMSC; }
MAX				{ return AMMSC; }
SUM				{ return AMMSC; }
COUNT			{ return AMMSC; }
ANY				{ return ANY; }
AS				{ return AS; }
ASC				{ return ASC; }
AUTHORIZATION	{ return AUTHORIZATION; }
BETWEEN			{ return BETWEEN; }
BY				{ return BY; }
C				{ return C; }
CHAR(ACTER)?	{ return CHARACTER; }
CHECK			{ return CHECK; }
CLOSE			{ return CLOSE; }
COBOL			{ return COBOL; }
COMMIT			{ return COMMIT; }
CONTINUE		{ return CONTINUE; }
CREATE			{ return CREATE; }
CURRENT			{ return CURRENT; }
CURSOR			{ return CURSOR; }
DECIMAL			{ return DECIMAL; }
DECLARE			{ return DECLARE; }
DEFAULT			{ return DEFAULT; }
DELETE			{ return DELETE; }
DESC			{ return DESC; }
DISTINCT		{ return DISTINCT; }
DOUBLE			{ return DOUBLE; }
ESCAPE			{ return ESCAPE; }
EXISTS			{ return EXISTS; }
FETCH			{ return FETCH; }
FLOAT			{ return FLOAT; }
FOR				{ return FOR; }
FOREIGN			{ return FOREIGN; }
FORTRAN			{ return FORTRAN; }
FOUND			{ return FOUND; }
FROM			{ printf("find from\n"); this->FLines->Add("select"); return FROM; }
GO[ \t]*TO		{ return GOTO; }
GRANT			{ return GRANT; }
GROUP			{ return GROUP; }
HAVING			{ return HAVING; }
IN				{ return IN; }
INDICATOR		{ return INDICATOR; }
INSERT			{ return INSERT; }
INT(EGER)?		{ return INTEGER; }
INTO			{ return INTO; }
IS				{ return IS; }
KEY				{ return KEY; }
LANGUAGE		{ return LANGUAGE; }
LIKE			{ return LIKE; }
MODULE			{ return MODULE; }
NOT				{ return NOT; }
NULL			{ return NULLX; }
NUMERIC			{ return NUMERIC; }
OF				{ return OF; }
ON				{ return ON; }
OPEN			{ return OPEN; }
OPTION			{ return OPTION; }
OR				{ return OR; }
ORDER			{ return ORDER; }
PASCAL			{ return PASCAL; }
PLI				{ return PLI; }
PRECISION		{ return PRECISION; }
PRIMARY			{ return PRIMARY; }
PRIVILEGES		{ return PRIVILEGES; }
PROCEDURE		{ return PROCEDURE; }
PUBLIC			{ return PUBLIC; }
REAL			{ return REAL; }
REFERENCES		{ return REFERENCES; }
ROLLBACK		{ return ROLLBACK; }
SCHEMA			{ return SCHEMA; }
SELECT			{ printf("find select\n"); this->FLines->Add("select"); return SELECT; }
SET				{ return SET; }
SMALLINT		{ return SMALLINT; }
SOME			{ return SOME; }
SQLCODE			{ return SQLCODE; }
TABLE			{ return TABLE; }
TO				{ return TO; }
UNION			{ return UNION; }
UNIQUE			{ return UNIQUE; }
UPDATE			{ return UPDATE; }
USER			{ return USER; }
VALUES			{ return VALUES; }
VIEW			{ return VIEW; }
WHENEVER		{ return WHENEVER; }
WHERE			{ return WHERE; }
WITH			{ return WITH; }
WORK			{ return WORK; }

/* punctuation */

"="		|
"<>" 	|
"<"		|
">"		|
"<="	|
">="	{ return COMPARISON; }

[-+*/:(),.;]	{ return yytext[0]; }

/* names */

[A-Za-z][A-Za-z0-9_]*	{ 
	yylval.strval = strdup(yytext);
	return NAME; 
}

/* numbers */

[0-9]+	|
[0-9]+"."[0-9]* |
"."[0-9]*	{ return INTNUM; }

[0-9]+[eE][+-]?[0-9]+	|
[0-9]+"."[0-9]*[eE][+-]?[0-9]+ |
"."[0-9]*[eE][+-]?[0-9]+	{ return APPROXNUM; }

/* strings */

'[^'\n]*'	{
	int c = input();

	unput(c);	/* just peeking */
	if(c != '\'') {
		return STRING;
	} else
		yymore();
}
		
'[^'\n]*$	{ yyerror("Unterminated string"); }

\n		{
  lineno++;
//  this->FLines->Add("aaa");
}

[ \t\r]+	;	/* white space */

"--".*$		;	/* comment */

%%

/////////////////////////////////////////////////////////////////////////////
// programs section

void sqllexer::SetLog(Classes::TStrings* ALines) {
  this->FLines = ALines;
}

▶ sqlparser.y
%{
/****************************************************************************
sqlparser.y
ParserWizard generated YACC file.
****************************************************************************/

#include 
#pragma hdrstop
#include 

#include "sqllexer.h"
%}

/////////////////////////////////////////////////////////////////////////////
// declarations section

/* symbolic tokens */

%union {
	int intval;
	double floatval;
	char *strval;
	int subtok;
}

%token NAME
%token STRING
%token INTNUM APPROXNUM

/* operators */

%left OR
%left AND
%left NOT
%left  COMPARISON /* = <> < > <= >= */
%left '+' '-'
%left '*' '/'
%nonassoc UMINUS

%include {
#include 
#pragma hdrstop
#include 
}

// parser name
%name sqlparser

// class definition
{
	// place any extra class members here
	Classes::TStrings* FLines;
	void SetLog(Classes::TStrings* ALines);
}

// constructor
{
	// place any extra initialisation code here
}

// destructor
{
	// place any extra cleanup code here
}

// attribute type
%include {
#ifndef YYSTYPE
#define YYSTYPE int
#endif
}

// place any declarations here

/* literal keyword tokens */

%token ALL AMMSC ANY AS ASC AUTHORIZATION BETWEEN BY
%token CHARACTER CHECK CLOSE COMMIT CONTINUE CREATE CURRENT
%token CURSOR DECIMAL DECLARE DEFAULT DELETE DESC DISTINCT DOUBLE
%token ESCAPE EXISTS FETCH FLOAT FOR FOREIGN FOUND FROM GOTO
%token GRANT GROUP HAVING IN INDICATOR INSERT INTEGER INTO
%token IS KEY LANGUAGE LIKE MODULE NULLX NUMERIC OF ON
%token OPEN OPTION ORDER PRECISION PRIMARY PRIVILEGES PROCEDURE
%token PUBLIC REAL REFERENCES ROLLBACK SCHEMA SELECT SET
%token SMALLINT SOME SQLCODE SQLERROR TABLE TO UNION
%token UNIQUE UPDATE USER VALUES VIEW WHENEVER WHERE WITH WORK
%token COBOL FORTRAN PASCAL PLI C ADA

%%

/////////////////////////////////////////////////////////////////////////////
// rules section

// place your YACC rules here (there must be at least one)

// place your YACC rules here (there must be at least one)

query_spec:
		SELECT selection table_exp ';'
	;
	
table_exp:
		from_clause
	;
		
from_clause:
		FROM table_ref_commalist 
	;
	
table_ref_commalist:
		table_ref
	|	table_ref_commalist ',' table_ref
	;
	
table_ref:
		table { this->FLines->Add($1.strval); }
	|	table range_variable
	;
	
range_variable:	NAME
	;
	
table:
		NAME 
	|	NAME '.' NAME
	;
	
selection:
		scalar_exp_commalist
	|	'*'
	;
	
scalar_exp_commalist:
		scalar_exp
	|	scalar_exp_commalist ',' scalar_exp
	;
	
scalar_exp:
		column_ref {
		  this->FLines->Add($1.strval);
		}
	;
	
column_ref:
		NAME 
	|	NAME '.' NAME	/* needs semantics */
	|	NAME '.' NAME '.' NAME 
	;

%%

/////////////////////////////////////////////////////////////////////////////
// programs section

/*

int main(void)
{
	int n = 1;
	sqllexer lexer;
	sqlparser parser;
	if (parser.yycreate(&lexer)) {
		if (lexer.yycreate(&parser)) {
			n = parser.yyparse();
		}
	}
	return n;
}

*/

void sqlparser::SetLog(Classes::TStrings* ALines) {
  this->FLines = ALines;
}

▶ Lex & Yacc 파일 Build 하기

메뉴) [Project] -> [Build] 선택

Build 하게 되면

sqllexer.l 파일은 sqllexer.h, sqllexer.cpp 파일이 생성되고,,
sqlparser.y 파일은 sqlparser.h, sqlparser.cpp 파일이 생성됨

▶ Borland C++ Builder

Parser Generator 의 Include, Source와 위에서 컴파일한 Library 파일을 Borland C++ Builder의
디렉토리 설정에서 지정하고,

Project을 생성하여, Parser Generator에서 생성된 파일 sqllexer.l, sqllexer.h, sqllexer.cpp,
sqlparser.y, sqlparser.h, sqlparser.cpp을 프로젝트에 추가

버튼과 메모 컴포넌트를 추가하여 아래와 같이 코딩

▶ Unit1.cpp 파일
//---------------------------------------------------------------------------

#include 
#pragma hdrstop

#include "Unit1.h"
#include 
#include 
#include "sqllexer.h"
#include "sqlparser.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  ifstream testfile ("D:\\source_sqlc\\sql_2\\TestSQL.txt");
  ofstream testfile2 ("D:\\source_sqlc\\sql_2\\TestSQL.out");
  ofstream testfile3 ("D:\\source_sqlc\\sql_2\\TestSQL.err");

  int n = 1;
  sqllexer lexer;
  sqlparser parser;
  if (parser.yycreate(&lexer)) {
    if (lexer.yycreate(&parser)) {
      lexer.SetLog(Memo1->Lines);
      parser.SetLog(Memo1->Lines);
      lexer.yyin = &testfile;
      lexer.yyout = &testfile2;
      lexer.yyerr = &testfile3;
      n = parser.yyparse();
    }
  }
  // return n;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  Memo1->Lines->Add("a");
}
//---------------------------------------------------------------------------

▶ TestSQL.txt 내용

SELECT aa1, bb1
  FROM bbaaaaaa;
 
▶ 실행 결과

captain [f7cap]   2006-11-07 17:04 X
이거 실행되는거 맞나요

+ -

관련 글 리스트
109 Parser Generator와 Borland C++ 6.0을 이용해서 SQL 파서를 만들어 보자 동그라미 20672 2006/08/09
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.