소 시 부 스 트 정신

6777 단어
텍스트 파일 을 설명 하 는 것 은 일상적인 프로 그래 밍 에서 너무 일반적인 일이 다. 일반적으로 거북이 점 의 방법 은 parser 를 직접 손 으로 써 서 맵 텍스트 의 키 워드 를 순환 적 이 고 폭력 적 으로 제거 하여 관련 정 보 를 추출 할 수 있다. 조금 만 아 끼 려 면 tokenizer 나 정규 표현 식 같은 도 구 를 사용 할 수 있다. 어쨌든 전체적으로 말 하면...손 으로 쓴 parser 가 텍스트 를 설명 하 는 것 은 기본적으로 힘 든 일이 다. 쓴 코드 는 다시 사용 하기 어렵 고 가 독성 도 떨 어 지 며 디자인 이 하마터면 변 할 뻔 하면 언제 텍스트 형식 이 바 뀌 었 는 지 예전 에 힘 들 게 썼 던 코드 를 바로 넘 어 뜨 려 다시 만 드 는 것 은 새로운 일이 아니다.
구출 방법 은 도 구 를 통 해 파 서 를 생 성 하 는 것 이다. 이 방면 에는 비교적 유명한 도구 가 많다. 예 를 들 어 Lex, Yacc, ANTLR 등 이다. 그들의 기능 은 대동소이 하 다. 모두 문법 (EBNF 로 설명) 을 바탕 으로 해당 하 는 파 서 로 전환 된다. 그러나 이런 도 구 는 사용 하기에 비교적 번거롭다. 먼저 생 성 된 코드 는 가 독성 이 좋 지 않다.또한 이 코드 를 프로젝트 에 넣 으 려 면 생 성 된 코드 를 수정 해 야 하기 때문에 문법 이 바 뀌 면 parser 가 다시 생 성 되 어야 합 니 다. 이런 수정 은 기본적으로 다시 해 야 하기 때문에 유지 하기 가 어렵 습 니 다.
그렇다면 이 도구 들 외 에 다른 해결 방안 은 없 을 까?아마도 너 는 boost spirit 을 시도 해 볼 수 있 을 것 이다.
다음 내용 은 boost 1.37.0 을 바탕 으로 spirit 의 버 전 은 1806 이 고 오래된 버 전 입 니 다. 최신 버 전에 비해 원 리 는 많 지 않 지만 라 이브 러 리 의 디 렉 터 리 구 조 는 많이 다 르 기 때문에 알 고 있 습 니 다.
Spirit 이 뭐 예요?
쉽게 말 하면 Spirit 은 parser generator 입 니 다. 기능 은 Yacc, ANTLR 과 유사 하고 EBNF 를 바탕 으로 문법 을 묘사 한 다음 에 문법 을 바탕 으로 parser 를 생 성 합 니 다. 그러나 앞의 도구 에 비해 가장 큰 차이 점 은 C + 코드 를 사용 하여 문법 을 묘사 하고 매우 잔인 한 템 플 릿 프로 그래 밍 기법 을 통 해컴 파일 단계 에서 해당 하 는 parser 가 생 성 되 었 습 니 다.사용자 의 측면 에서 볼 때 문법 은 코드 로 설명 되 기 때문에 현재 프로젝트 에 직접 가입 하여 기 존 코드 와 결합 할 수 있 습 니 다.
물론 Spirit 의 문법 은 형식적 으로 EBNF 에 약간의 차이 가 있다. 예 를 들 어 '>' 로 서로 다른 표현 식 을 연결 하고 중복 되 는 기 호 를 표현 식 의 앞 에 두 었 다 는 것 을 나타 내 는 등 은 모두 c + 문법 에 의 해 제 한 된 절충 이 고 문법 적 의 미 는 변 하지 않 았 다.
첫 체험
EBNF 로 설명 하면 다음 과 같은 전형 적 인 정수 사 칙 연산 을 쓸 수 있 습 니 다.
    group       ::= '(' expression ')'
    factor      ::= integer | group
    term        ::= factor (('*' factor) | ('/' factor))*
    expression  ::= term (('+' term) | ('-' term))*

그 중에서 group, factor, term, expression 은 각각 하나의 rule 이 라 고 부 르 는데 해당 하 는 텍스트 와 어떻게 일치 하 는 지 나타 내 는 데 사용 된다. 상기 EBNF 문법 은 Spirit 에서 다음 과 같은 형식 으로 쓸 수 있다.
    group       = '(' >> expression >> ')';
    factor      = integer | group;
    term        = factor >> *(('*' >> factor) | ('/' >> factor));
    expression  = term >> *(('+' >> term) | ('-' >> term));

문법 차이 가 많 지 않 은 것 같 습 니 다. 다만 연산 자 는 약간 다 릅 니 다. 지적 해 야 할 것 은 Spirit 에서 하나의 rule 은 parser 대상 입 니 다. 하나의 parser 대상 은 해당 하 는 문법 규칙 을 포함 하여 이 parser 는 이러한 규칙 에 부합 되 는 텍스트 만 parse 할 수 있 습 니 다. 예 를 들 어 우 리 는 지금 parse 에서 쉼표 로 구 분 된 정수 (CSV) 를 내 고 싶 습 니 다.다음 rule 을 정의 할 수 있 습 니 다.
boost::spirit::rule<> csv_int = int_p >> *(',' >> int_p);

상기 코드 중, intp 는 Spirit 내 에 만들어 진 rule 또는 parser 입 니 다. 이 parser 는 parse 의 전체 형 (int p 를 제외 하고 Spirit 은 parse 의 다른 기본 데이터 형식 에 사용 되 는 일련의 parser 를 만 들 었 습 니 다. 구체 적 인 목록 은 여 기 를 참고 하 십시오).parser 와 parser 는 ">" 연산 자 를 통 해 연결 한 후 새로운 parser 를 구성 하 였 습 니 다. 그러면 csv 를 어떻게 사용 합 니까?int 이 새로 생 성 된 parser 는?Spirit 내 에 함 수 를 정 의 했 습 니 다. 원형 은 다음 과 같 습 니 다.
boost::spirit::parse_info<> parse(const char* text, parser, separator);

parse () 함 수 를 호출 하여 parse 가 필요 한 텍스트 와 해당 하 는 parser 를 입력 하면 이 텍스트 에 대해 해당 하 는 설명 을 할 수 있 습 니 다. 결 과 를 되 돌려 주 는 과정 이 성 공 했 는 지, 그리고 오류 가 발생 하면 어느 위치 에서 오류 가 발생 했 는 지 알려 줍 니 다.
Semantic actions
앞에서 정의 한 csvint 라 는 parser 는 문법 을 정 의 했 지만 아무것도 하지 않 았 습 니 다. 특정한 텍스트 가 쉼표 로 구 분 된 정형 인지 확인 하 는 데 만 사용 할 수 있 습 니 다. 기능 이 너무 약 합 니 다. 일반적으로 우 리 는 텍스트 에서 데 이 터 를 추출 해 야 하기 때문에 parse 의 과정 은 특정한 동작 을 지원 해 야 합 니 다. 우 리 는 parser 가 parse 에서 어떤 내용 을 추출 해 야 할 때,사용자 가 지정 한 행동 동작 을 수행 할 수 있 습 니 다. Spirit 에서 이 동작 을 semantic action 이 라 고 합 니 다.
Semantic action 은 parser 에 속 합 니 다. 이 parser 가 성공 한 후에 어떤 작업 을 수행 해 야 하 는 지 를 가리 키 는 데 의미 가 있 습 니 다. 이 작업 들 은 사용자 가 지정 한 것 입 니 다.우 리 는 다음 과 같은 방식 으로 semantic action 을 parser 와 연결 할 수 있 습 니 다.
parser[func];

func 의 원형 에 대해 서 는 당연히 요구 가 있 습 니 다. 그리고 이것 은 구체 적 인 parser 를 봐 야 합 니 다. 예 를 들 어 intp 와 같은 parser 는 void func(const int val); 와 같은 함수 만 받 아들 일 수 있 고 간결 한 문법 입 니 다!이제 우 리 는 앞에서 정형 을 설명 하 는 코드 에 새로운 기능 을 추가 합 니 다. parse 가 모든 정형 을 완성 한 후에 우리 가 얻 은 데 이 터 를 저장 합 니 다.
#include 
#include 
#include 
#include 
#include 

std::vector g_output;

int on_parse_int(const int val)
{
   g_output.push_back(val);
}

int main()
{
   boost::spirit::rule<> int_csv_rule = int_p[on_parse_int] >> *(',' >> int_p[on_parse_int]);
   boost::spirit::parse("2,3,4", int_csv_rule);
   return 0;
}

semantic action 의 실현 은 boost 에서 또 다른 명성 이 자자 한 함수 식 템 플 릿 라 이브 러 리 에 의존 합 니 다. phoenix, 위의 예 는 간단 한 시범 일 뿐 빙산 의 일각 에 미 치지 못 합 니 다. spirit 은 lambda 로 리 셋 함 수 를 쓰 는 것 을 지원 합 니 다. 그리고 서로 다른 rule 사이 에 사용자 가 정의 하 는 문맥 정 보 를 전달 하 는 등 기능 이 매우 강 합 니 다.관심 이 있 는 독 자 는 이곳 의 상대 적 인 완전 점 의 예 를 참고 하여 간단 한 네 가지 연산 과 기본 적 인 함수 호출 을 실현 할 수 있다.
생 성 된 Parser 의 종류
Parser 는 전체 Spirit 라 이브 러 리 의 핵심 기능 입 니 다. 그렇다면 Spirit 이 생 성 한 parser 는 어떻게 일 을 합 니까?결론 은 spirit 이 생 성 한 parser 는 이른바 LL recursive decent parser 이다. 따라서 parse 는 왼쪽 에서 오른쪽으로 스 캔 하여 입력 하고 parser 의 문법 에 대해 왼쪽, 오른쪽 순 서 를 매 칭 하기 때문에 왼쪽 재 귀 와 같은 문 제 는 사용자 가 스스로 제거 해 야 한다. 다음 과 같은 예 를 들 어:
rule<> rule1 = (int_p >> ',' >> int_p) | real_p;

rule 1 은 parse 텍스트 에 있 을 때 우선 일치 합 니 다 (int p > >, > int p). 실패 하면 real 와 일치 합 니 다.p。
장단 점
이 라 이브 러 리 를 사용 한 시간 이 그리 길지 않 기 때문에 초보 적 인 느낌 이 들 고 장점 에 있어 서 개인 적 으로 다음 과 같은 몇 가지 가 있다 고 생각 합 니 다.
  • 사용 이 매우 편리 하 다. 특히 parse 가 복잡 하지 않 은 텍스트 를 필요 로 할 때 코드 를 쓰 는 효율 이 높다.
  • 생 성 된 parser 의 집행 효율 도 좋다.

  • 결론 은 좋 고 강하 지만 단점 도 뚜렷 하 다.
  • 오류 알림 이 우호 적 이지 않 습 니 다.텍스트 형식 에 오류 가 있 을 때 spirit 은 잘못된 곳 에서 직접 돌아 오지 만 해당 하 는 오류 정 보 를 제공 하지 않 습 니 다. 상부 의 코드 는 어디서 잘못 되 었 는 지 만 무엇이 잘못 되 었 는 지 모 르 기 때문에 의미 있 는 오류 알림 을 만 들 기 어렵 습 니 다.
  • 이 라 이브 러 리 의 실현 은 템 플 릿 과 기 호 를 대량으로 사용 하여 다시 불 러 왔 습 니 다. 코드 는 매우 멋 지게 쓰 였 습 니 다. 그러나 오류 가 발생 하면 오류 알림 은 의미 있 는 정 보 를 많이 포함 하지 않 기 때문에 디 버 깅 하기 가 매우 고 통 스 럽 습 니 다. 특히 사용 이 숙련 되 지 않 은 상황 에서 개인 적 으로 Spirit 을 사용 할 때 임 무 를 적당 하 게 분해 하 는 것 이 좋 습 니 다.작은 임 무 를 수행 할 때마다 먼저 테스트 를 통 해 기능 이 정상 적 이 고 안정 적 인 지 를 확인 한 다음 에 다음 단 계 를 진행 합 니 다. 코드 를 한꺼번에 쓰 지 마 세 요. 기능 이 정상 적 이지 않 으 면 디 버 깅 하기 어렵 고 심지어 컴 파일 오류 가 발생 했 을 때 잘못된 곳 을 찾 는 것 도 어렵 습 니 다.
  • 이 라 이브 러 리 는 구현 에 있어 서 템 플 릿 메타 프로 그래 밍 에 심각하게 의존 하고 expression template 와 같은 coding idiom 을 많이 사 용 했 습 니 다. parser 의 생 성 은 실제 컴 파일 단계 에서 이 루어 졌 기 때문에 rule 이 복잡 할 때 컴 파일 시간 이 길 고 정말 깁 니 다.

  • [참고]
    http://boost-spirit.com/distrib/spirit_1_8_3/libs/spirit/doc/quick_start.html http://en.highscore.de/cpp/boost/

    좋은 웹페이지 즐겨찾기