Разбор шаблонного языка - PullRequest
       4

Разбор шаблонного языка

1 голос
/ 12 сентября 2011

Я пытаюсь разобрать язык шаблонов, и у меня возникают проблемы с правильным синтаксическим анализом произвольного HTML, который может появляться между тегами. Пока что у меня есть ниже, какие-либо предложения? Примером правильного ввода будет

{foo}{#bar}blah blah blah{zed}{/bar}{>foo2}{#bar2}This Should Be Parsed as a Buffer.{/bar2}

И грамматика:

grammar g;

options {
  language=Java;
  output=AST;
  ASTLabelType=CommonTree;
}

/* LEXER RULES */
tokens {

}

LD  :    '{';
RD  :    '}';
LOOP    :    '#';  
END_LOOP:   '/';
PARTIAL :   '>';
fragment DIGIT  : '0'..'9';
fragment LETTER : ('a'..'z' | 'A'..'Z');
IDENT : (LETTER | '_') (LETTER | '_' | DIGIT)*;
BUFFER options {greedy=false;} : ~(LD | RD)+ ;

/* PARSER RULES */
start   : body EOF
;

body    : (tag | loop | partial | BUFFER)*
;

tag     : LD! IDENT^ RD!
;

loop    : LD! LOOP^ IDENT RD!
  body
  LD! END_LOOP! IDENT RD!
;

 partial : LD! PARTIAL^ IDENT RD!
;

buffer  : BUFFER 
;

1 Ответ

3 голосов
/ 12 сентября 2011

Ваш лексер токенизируется независимо от вашего парсера.Если ваш парсер пытается сопоставить токен BUFFER, лексер не учитывает эту информацию.В вашем случае с вводом, подобным: "blah blah blah", лексер создает 3 IDENT токенов, а не один BUFFER токен.

Вам нужно «сказать» своему лексеру, что когда вы внутритег (т. е. вы столкнулись с тегом LD), должен быть создан токен IDENT, а когда вы находитесь вне тега (т. е. вы столкнулись с тегом RD), вместо него должен быть создан токен BUFFERIDENT токена.

Чтобы реализовать это, вам необходимо:

  1. создать флаг boolean внутри лексера, который отслеживает тот факт, что вы 'внутри или вне тега.Это можно сделать в разделе @lexer::members { ... } вашей грамматики;
  2. после того, как лексер или создаст маркер LD - или RD, переверните флаг boolean из (1).Это можно сделать в разделе @after{ ... } правил лексера;
  3. перед созданием токена BUFFER внутри лексера, проверьте, не находитесь ли вы в данный момент вне тега.Это можно сделать с помощью семантического предиката в начале вашего правила лексера.

Короткая демонстрация:

grammar g;

options { 
  output=AST;
  ASTLabelType=CommonTree; 
}

@lexer::members {
  private boolean insideTag = false;
}

start   
  :  body EOF -> body
  ;

body
  :  (tag | loop | partial | BUFFER)*
  ;

tag
  :  LD IDENT RD -> IDENT
  ;

loop    
  :  LD LOOP IDENT RD body LD END_LOOP IDENT RD -> ^(LOOP body IDENT IDENT)
  ;

partial 
  :  LD PARTIAL IDENT RD -> ^(PARTIAL IDENT)
  ;

LD @after{insideTag=true;}  : '{';
RD @after{insideTag=false;} : '}';

LOOP     : '#';  
END_LOOP : '/';
PARTIAL  : '>';
SPACE    : (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};
IDENT    :  (LETTER | '_') (LETTER | '_' | DIGIT)*;
BUFFER   : {!insideTag}?=> ~(LD | RD)+;

fragment DIGIT  : '0'..'9';
fragment LETTER : ('a'..'z' | 'A'..'Z');

(обратите внимание, что вы, вероятно, хотите отбросить пробелы между тегами, поэтому я добавил правило SPACE и отбросил эти пробелы)

Протестируйте его с помощью следующего класса:

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;

public class Main {
  public static void main(String[] args) throws Exception {
    String src = "{foo}{#bar}blah blah blah{zed}{/bar}{>foo2}{#bar2}" + 
                 "This Should Be Parsed as a Buffer.{/bar2}";
    gLexer lexer = new gLexer(new ANTLRStringStream(src));
    gParser parser = new gParser(new CommonTokenStream(lexer));
    CommonTree tree = (CommonTree)parser.start().getTree();
    DOTTreeGenerator gen = new DOTTreeGenerator();
    StringTemplate st = gen.toDOT(tree);
    System.out.println(st);
  }
}

и после запуска основного класса:

* nix / MacOS

java -cp antlr-3.3.jar org.antlr.Tool g.g 
javac -cp antlr-3.3.jar *.java
java -cp .:antlr-3.3.jar Main

Windows

java -cp antlr-3.3.jar org.antlr.Tool g.g 
javac -cp antlr-3.3.jar *.java
java -cp .;antlr-3.3.jar Main

Вы увидите, как печатается некоторый DOT-источникна консоль, которая соответствует следующему AST:

enter image description here

(изображение, созданное с использованием graphviz-dev.appspot.com )

...