2009-03-28 9 views
4

문자열 집합과 패턴을 비교하는 라이브러리를 작성 했으므로 어휘 검색 프로그램을 C 프로그램에 쉽게 포함시킬 수 있습니다.C 스타일 : 매크로 또는 선처리 프로세서?

어휘 스캐너 (lex 및 re2c)를 만들 수있는 도구가 많이 있다는 것을 알고 있습니다. 이 질문은 lexers가 아니라 "확장"하는 가장 좋은 방법입니다. C 문법. 렉서 예제는 일반적인 문제의 구체적인 경우입니다.

나는 두 가지 솔루션을 볼 수 있습니다

  1. 가에서 사용되는 다른 파일 세트에, 아마도 일반 C 파일에 포함 된 렉서와 소스 파일을 변환하여 전처리 쓰기를 편집.
  2. 더 읽기 쉬운 형태로 렉서를 나타 내기 위해 C 매크로 세트를 작성하십시오.

저는 이미 두 가지를 모두 마쳤습니다. 질문은 다음과 같습니다. "다음 기준에 따라 더 나은 방법을 생각해보십시오."

  • 가독성. 렉서 논리는 명확하고 이해하기 쉬워야합니다.
  • 유지 관리 가능성. 버그를 찾아 고쳐서 악몽이되어서는 안됩니다!
  • 빌드 프로세스의 방해. 전처리 기는 빌드 프로세스에 추가 단계가 필요하며 전 처리기는 경로 등에 있어야합니다.

즉, 다음 중 하나를 사용하는 소프트웨어를 유지 관리하거나 작성해야하는 경우 하나는 당신을 덜 실망시킬 것입니까? 예를 들어

가 여기에 다음과 같은 문제에 대한 렉서입니다 :

  • 합계 모든 숫자
  • 건너 뛰기 문자열 (지수와 같은 1.3E-4.2를 포함하여 소수점 형태 일 수있다) (단일 및 이중 인용)
  • 스킵리스트 (리스트 LISP 비슷한 (3 4 (0 1)() 3) (케이스는 무관하다) 워드 단부를 발생 또는 버퍼의 끝)
  • 정지

두 가지 스타일.


/**** SCANNER STYLE 2 (macros) ****/ 
#include "pmx.h" 
/* There can be up to 128 tokens per scanner with id x80 to xFF */ 
#define TOK_STRING x81 
#define TOK_NUMBER x82 
#define TOK_LIST x83 
#define TOK_END x84 
#define TOK_CHAR x85 

pmxScanner( /* pmxScanner() is a pretty complex macro */ 
    buffer 
, 
    pmxTokSet("&q"   , TOK_STRING) 
    pmxTokSet("&f<?=eE>&F" , TOK_NUMBER) 
    pmxTokSet("&b()"  , TOK_LIST) 
    pmxTokSet("&iend"  , TOK_END) 
    pmxTokSet("<.>"  , TOK_CHAR) 
, 
    pmxTokCase(TOK_STRING) : /* skip strings */ 
    continue; 

    pmxTokCase(TOK_NUMBER) : /* sum numbers */ 
    sum += atof(pmxTokStart(0)); 
    continue; 

    pmxTokCase(TOK_LIST):  /* skip lists */ 
    continue; 

    pmxTokCase(TOK_END) :  /* stop processing */ 
    break; 

    pmxTokCase(TOK_CHAR) :  /* skip a char and proceed */ 
    continue; 
); 

/**** SCANNER STYLE 1 (preprocessor) ****/ 
#include "pmx.h" 

t = buffer 

while (*t) { 
    switch pmx(t) { /* the preprocessor will handle this */ 
    case "&q" :   /* skip strings */ 
     break; 

    case "&f<?=eE>&F" : /* sum numbers */ 
     sum += atof(pmx(Start,0)); 
     break; 

    case "&b()":  /* skip lists */ 
     break; 

    case "&iend" :  /* stop processing */ 
     t = ""; 
     break; 

    case "<.>":   /* skip a char and proceed */ 
     break; 
    } 
} 

사람이 현재 구현에 관심이있을 경우, 코드는 여기에 있습니다 : http://sites.google.com/site/clibutl.

답변

6

전 처리기는보다 강력하고 일반적인 솔루션을 제공합니다. 반면에 매크로는 샘플 키워드/토큰 공간이 작을 때 채찍질을 빨리하고 개념 증명을 쉽게 제공하며 쉽게 사용할 수 있습니다. 새로운 기능을 확대/축소하는 것은 매크로가 끝나면 지루할 수 있습니다. 매크로를 시작하여 시작하고 전 처리기 명령으로 변환하는 매크로를 말합니다.

또한 가능한 경우 직접 작성하지 말고 일반적인 전 처리기를 사용할 수있게하십시오.

[...] 나는 (예를 들어, Windows 용 m4) 처리 할 또 다른 의존성이 있습니다.

예. 하지만 당신이 쓰는 해결책은 무엇입니까? - 을 유지해야합니다. 이름을 지정한 대부분의 프로그램에는 Windows 포트가 있습니다 (예 : m4 for windows 참조). 이러한 솔루션을 사용할 때의 이점은 시간이 많이 걸리는 인 을 절약한다는 것입니다. 물론, 단점은 이상한 버그가 나타나면 소스 코드로 속도를 높여야한다는 것입니다. 그러나 이들을 유지하는 사람들은 매우 도움이되고 모든 도움을 확실히 얻을 수 있습니다.

그리고 다시 말하지만, 저는 패키지 솔루션을 선호합니다.

+0

필자는 gema, m4 또는 awk 또는 perl과 같은 일반적인 전 처리기를 사용하여 특정 전처리기를 만들 수 있지만 다른 종속성 (예 : Windows 용 m4)을 처리해야합니다. 전처리기를 패키지 자체의 일부로 직접 제공하는 편이 낫지 않습니까? –

+0

btw, 전 처리기 솔루션에 대한 +1 답을 얻을 수 있습니다. –

+0

선택한 코드 생성기/전 처리기를 모든 대상 플랫폼에서 사용할 필요는 없습니다. * 개발 *에 사용하려는 플랫폼에만 있어야합니다. 생성 된 C 파일은 이식 가능하므로 (그렇게 가정 함) 소스 코드로 패키징하고 제공 할 수 있습니다. 예를 들어, Flex 나 Bison을 사용하는 프로젝트에서 생성 된 파일을 tarball과 함께 배포하는 것이 일반적이므로 패키지를 컴파일하려는 사용자는이 도구를 설치할 필요가 없습니다. – 5gon12eder

3

커스텀 프리 프로세서는 파서/인터프리터 제네레이터의 일반적인 접근 방식으로, 매크로 가능성이 매우 제한되어 있으며 확장 단계에서 잠재적 인 문제를 제공하여 디버깅을 엄청난 노력으로 만듭니다.

고전적인 Yacc/Lex Unix 프로그램과 같이 오랫동안 검증 된 도구를 사용하거나 C를 "확장"하려면 C++ 및 Boost :: spirit (광범위하게 템플릿을 사용하는 파서 생성기)을 사용하십시오.

+0

감사합니다. Hernan. 매크로 세트가 태스크 (예에서와 같이)에 적당하다고 가정하면 디버깅에 대한 귀하의 요점을 말합니다. 예를 들어 어디에서나 Lexers를 사용할 수 있으며 C 영역에 있어야합니다. –

+0

나는 boost가 C에서 사용 가능하다는 것을 몰랐다. 그리고 double :: operator. 나는 C로 부스트 구현을보기 위해 정말로 죽일 것이다.). –

+0

아니요, 아니요, Boost는 C++에서만 사용할 수 있습니다! –