Ваш лексер токенизируется независимо от вашего парсера.Если ваш парсер пытается сопоставить токен BUFFER
, лексер не учитывает эту информацию.В вашем случае с вводом, подобным: "blah blah blah"
, лексер создает 3 IDENT
токенов, а не один BUFFER
токен.
Вам нужно «сказать» своему лексеру, что когда вы внутритег (т. е. вы столкнулись с тегом LD
), должен быть создан токен IDENT
, а когда вы находитесь вне тега (т. е. вы столкнулись с тегом RD
), вместо него должен быть создан токен BUFFER
IDENT
токена.
Чтобы реализовать это, вам необходимо:
- создать флаг
boolean
внутри лексера, который отслеживает тот факт, что вы 'внутри или вне тега.Это можно сделать в разделе @lexer::members { ... }
вашей грамматики; - после того, как лексер или создаст маркер
LD
- или RD
, переверните флаг boolean
из (1).Это можно сделать в разделе @after{ ... }
правил лексера; - перед созданием токена
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](https://i.stack.imgur.com/X341o.png)
(изображение, созданное с использованием graphviz-dev.appspot.com )