2010-07-26 3 views
9

나는 간단한 템플릿 언어를 파서로 작업 해왔다. 나는 Ragel을 사용하고있다.Ragel에서 템플릿 언어를 구문 분석하는 방법은 무엇입니까?

요구 사항은 적당하지 않습니다. 나는 [[tags]]를 발견하려고 노력하고 있는데, 그것은 입력 문자열의 어디에나 임베딩 될 수있다.

HTML 내에 {{foo}}와 같은 태그를 포함 할 수있는 간단한 템플릿 언어를 구문 분석하려고합니다. 이 방법을 분석하기 위해 여러 가지 방법을 시도했지만 Ragel 스캐너를 사용하고 단일 문자를 "모두 잡아라"와 일치시키는 비효율적 인 방법을 사용해야했습니다. 나는 이것이 이것이 잘못되는 길이라고 생각한다. 필자는 기본적으로 스캐너의 가장 긴 매치 바이어스를 악용하여 기본 규칙을 구현합니다 (길이가 1 자 밖에되지 않으므로 항상 최후의 수단이되어야합니다).

%%{ 

    machine parser; 

    action start  { tokstart = p; }   
    action on_tag  { results << [:tag, data[tokstart..p]] }    
    action on_static { results << [:static, data[p..p]] }    

    tag = ('[[' lower+ ']]') >start @on_tag; 

    main := |* 
    tag; 
    any  => on_static; 
    *|; 

}%% 

(루비로 작성된 작업이지만 이해하기 쉬워야 함).

간단한 언어로 파서를 작성하는 방법에 대해 알려주십시오. Ragel이 올바른 도구가 아닐 수도 있습니까? 문법이 예측 불가능한 경우 Ragel 치아와 손톱을 싸워야 할 것 같습니다.

답변

20

Ragel이 정상적으로 작동합니다. 당신이 뭘 일치하는지 조심해야합니다. 귀하의 질문은 [[tag]]{{tag}}을 모두 사용하지만 귀하의 예에서는 [[tag]]을 사용합니다. 그래서 나는 그것이 당신이 특별하다고 생각하는 것으로 생각합니다.

원하는 것은 열린 대괄호를 칠 때까지 텍스트를 먹는 것입니다. 그 괄호 뒤에 다른 괄호가 오면, 대괄호를 치기 전까지 소문자를 먹을 차례입니다. 태그의 텍스트에는 대괄호가 포함될 수 없으므로 대괄호 뒤에 올 수있는 오류가없는 문자 만 다른 대괄호로 사용됩니다. 그 시점에서, 당신은 당신이 시작한 곳으로 돌아 왔습니다.

음,이 기계의 축 어적 설명입니다 :

tag = '[[' lower+ ']]'; 

main := (
    (any - '[')* # eat text 
    ('[' ^'[' | tag) # try to eat a tag 
)*; 

까다로운 부분은 어디 당신이 당신의 행동을 부릅니까? 나는 그에게 가장 좋은 대답을 가지고 주장하지만, 여기에 내가 생각 해낸 무엇을하지 않습니다

static char *text_start; 

%%{ 
    machine parser; 

    action MarkStart { text_start = fpc; } 
    action PrintTextNode { 
    int text_len = fpc - text_start; 
    if (text_len > 0) { 
     printf("TEXT(%.*s)\n", text_len, text_start); 
    } 
    } 
    action PrintTagNode { 
    int text_len = fpc - text_start - 1; /* drop closing bracket */ 
    printf("TAG(%.*s)\n", text_len, text_start); 
    } 

    tag = '[[' (lower+ >MarkStart) ']]' @PrintTagNode; 

    main := (
    (any - '[')* >MarkStart %PrintTextNode 
    ('[' ^'[' %PrintTextNode | tag) >MarkStart 
)* @eof(PrintTextNode); 
}%% 

몇 비 명백한 가지가 있습니다

  • eof 조치가 %PrintTextNode 때문에 필요는 기계를 떠날 때만 호출됩니다. 입력이 일반 텍스트로 끝나면 그 상태를 벗어나는 입력이 없습니다. 입력이 태그로 끝나고 마지막으로 인쇄되지 않은 텍스트 노드가 없을 때도 호출되기 때문에 PrintTextNode은 인쇄 할 텍스트가 있는지 테스트합니다.
  • [에 도달했을 때 시작이라고 표시했으나 [이 아닌 값을 기록한 후에 다시 구문 분석을 시도하고 시작점을 말하기 시작하기 때문에 ^'[' 이후에 동작이 필요합니다. %PrintTextNode 동작이 필요합니다. 우리는 그 두 가지 문자가 발생하기 전에 플러시 할 필요가 있습니다. 따라서 액션 호출입니다.

전체 구문 분석기가 이어집니다.여기

/* ragel so_tag.rl && gcc so_tag.c -o so_tag */ 
#include <stdio.h> 
#include <string.h> 

static char *text_start; 

%%{ 
    machine parser; 

    action MarkStart { text_start = fpc; } 
    action PrintTextNode { 
    int text_len = fpc - text_start; 
    if (text_len > 0) { 
     printf("TEXT(%.*s)\n", text_len, text_start); 
    } 
    } 
    action PrintTagNode { 
    int text_len = fpc - text_start - 1; /* drop closing bracket */ 
    printf("TAG(%.*s)\n", text_len, text_start); 
    } 

    tag = '[[' (lower+ >MarkStart) ']]' @PrintTagNode; 

    main := (
    (any - '[')* >MarkStart %PrintTextNode 
    ('[' ^'[' %PrintTextNode | tag) >MarkStart 
)* @eof(PrintTextNode); 
}%% 

%% write data; 

int 
main(void) { 
    char buffer[4096]; 
    int cs; 
    char *p = NULL; 
    char *pe = NULL; 
    char *eof = NULL; 

    %% write init; 

    do { 
    size_t nread = fread(buffer, 1, sizeof(buffer), stdin); 
    p = buffer; 
    pe = p + nread; 
    if (nread < sizeof(buffer) && feof(stdin)) eof = pe; 

    %% write exec; 

    if (eof || cs == %%{ write error; }%%) break; 
    } while (1); 
    return 0; 
} 

몇 가지 테스트 입력입니다 :

[[header]] 
<html> 
<head><title>title</title></head> 
<body> 
<h1>[[headertext]]</h1> 
<p>I am feeling very [[emotion]].</p> 
<p>I like brackets: [ is cool. ] is cool. [] are cool. But [[tag]] is special.</p> 
</body> 
</html> 
[[footer]] 

그리고 여기에 출력의 그게 내가 무엇을 알고,하지만 당신은 아주 쉽게 필요한 어떤 언어 당신으로 바꿀 수 있어야하기 때문에 나는 C에서 그것을했다 구문 분석기에서 :

TAG(header) 
TEXT(
<html> 
<head><title>title</title></head> 
<body> 
<h1>) 
TAG(headertext) 
TEXT(</h1> 
<p>I am feeling very) 
TAG(emotion) 
TEXT(.</p> 
<p>I like brackets:) 
TEXT([) 
TEXT(is cool. ] is cool.) 
TEXT([]) 
TEXT(are cool. But) 
TAG(tag) 
TEXT(is special.</p> 
</body> 
</html> 
) 
TAG(footer) 
TEXT(
) 

마지막 텍스트 노드에는 파일 끝의 개행 문자 만 포함됩니다.

관련 문제