문제

저는 간단한 템플릿 언어에 대한 파서 작업을 해왔습니다.저는 라겔을 사용하고 있습니다.

요구 사항은 적당합니다.입력 문자열의 어느 곳에나 삽입할 수 있는 [[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과 싸워야 할 것 같습니다.

도움이 되었습니까?

해결책

레이겔은 잘 작동합니다.당신은 당신이 일치하는 것에주의해야합니다.귀하의 질문은 두 가지를 모두 사용합니다 [[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 그 뒤에 자리잡은 액션 ^'[' 왜냐하면 우리가 쳤을 때 시작을 표시했지만 [, 우리가 비-[, 우리는 무엇이든 다시 분석하고 시작점을 언급하기 시작합니다.그런 일이 발생하기 전에 해당 두 문자를 플러시해야 하므로 해당 작업이 호출됩니다.

전체 파서는 다음과 같습니다.저는 그것을 C로 했습니다. 왜냐하면 그것이 제가 아는 것이기 때문입니다. 그러나 당신은 그것을 당신이 필요로 하는 어떤 언어로든 매우 쉽게 바꿀 수 있을 것입니다:

/* 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]]

파서의 출력은 다음과 같습니다.

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(
)

최종 텍스트 노드에는 파일 끝에 줄 바꿈만 포함됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top