我提前就提出如此相似的问题道歉,但我很沮丧,我可能会在一个新问题上更好地解释。
我正在尝试重写结构化文件的一部分,并考虑使用antlr。 这些文件是{X}令牌的行。 有一个我正在展望的角色,所以如果我找到它,我可以重写不同文件的部分。 但是这个字符('#')可能出现在文件的许多部分。但是,如果它出现在第4 {#},它确定我是否需要以某种方式或以其他方式重写下一个{X}的一部分,或者根本不重写(如果没有任何内容)。
典型输入:
{1} {在哪里? # }{ 去哪儿? } {#} {} {G.Cabbie_Line = 1} {} {} {} {} {} {} {}
{2} {开车。 } {刚开车。 } {0} {} {npc.WorldMap(G.WorldMap_State)} {} {} {} {} {} {} {不在这里。 }
(我添加了第一个#所以你看它可以在那里。) 我的语法,antlr 3.3 - 它警告“第1:31行在输入'{#}'”没有可行的选择,而“第2:35行在输入'{0}'没有可行的选择”
grammar VampireDialog;
options
{
output=AST;
ASTLabelType=CommonTree;
language=Java;
}
tokens
{
REWRITE;
}
@parser::header {
import java.util.LinkedList;
import java.io.File;
}
@members {
//the lookahead type i'm using ( ()=> ) wraps everything after in a if, including the actions ( {} )
//that i need to use to prepare the arguments for the replace rules. Declare them global.
String condition, command, wrappedCommand; boolean isEmpty, alreadyProcessed;
public static void main(String[] args) throws Exception {
File vampireDir = new File(System.getProperty("user.home"), "Desktop/Vampire the Masquerade - Bloodlines/Vampire the Masquerade - Bloodlines/Vampire/dlg/dummy");
List<File> files = new LinkedList<File>();
getFiles(256, new File[]{vampireDir}, files, new LinkedList<File>());
for (File f : files) {
if (f.getName().endsWith(".dlg")) {
System.out.println(f.getName());
VampireDialogLexer lex = new VampireDialogLexer(new ANTLRFileStream(f.getAbsolutePath(), "Windows-1252"));
TokenRewriteStream tokens = new TokenRewriteStream(lex);
VampireDialogParser parser = new VampireDialogParser(tokens);
Tree t = (Tree) parser.dialog().getTree();
System.out.println(t.toStringTree());
}
}
}
public static void getFiles(int levels, File[] search, List<File> files, List<File> directories) {
for (File f : search) {
if (!f.exists()) {
throw new AssertionError("Search file array has non-existing files");
}
}
getFilesAux(levels, search, files, directories);
}
private static void getFilesAux(int levels, File[] startFiles, List<File> files, List<File> directories) {
List<File[]> subFilesList = new ArrayList<File[]>(50);
for (File f : startFiles) {
File[] subFiles = f.listFiles();
if (subFiles == null) {
files.add(f);
} else {
directories.add(f);
subFilesList.add(subFiles);
}
}
if (levels > 0) {
for (File[] subFiles : subFilesList) {
getFilesAux(levels - 1, subFiles, files, directories);
}
}
}
}
/*------------------------------------------------------------------
* PARSER RULES
*------------------------------------------------------------------*/
dialog : (ANY ANY ANY npc_or_pc ANY* NL*)*;
npc_or_pc : (ANY ANY) =>
pc_marker pc_condition
| npc_marker npc_condition;
pc_marker : t=ANY {!t.getText().trim().isEmpty() && !t.getText().contains("#")}?;
npc_marker : t=ANY {!t.getText().trim().isEmpty() && t.getText().contains("#")}?;
pc_condition : '{' condition_text '}'
{
condition = $condition_text.tree.toStringTree();
isEmpty = condition.trim().isEmpty();
command = "npc.Count()";
wrappedCommand = "("+condition+") and "+ command;
alreadyProcessed = condition.endsWith(command);
}
-> {alreadyProcessed}? '{' condition_text '}'
-> {isEmpty}? '{' REWRITE[command] '}'
-> '{' REWRITE[wrappedCommand] '}';
npc_condition : '{' condition_text '}'
{
condition = $condition_text.tree.toStringTree();
isEmpty = condition.trim().isEmpty();
command = "npc.Reset()";
wrappedCommand = "("+condition+") and "+ command;
alreadyProcessed = condition.endsWith(command);
}
-> {alreadyProcessed}? '{' condition_text '}'
-> {isEmpty}? '{' REWRITE[command] '}'
-> '{' REWRITE[wrappedCommand] '}';
marker_text : TEXT;
condition_text : TEXT;
/*------------------------------------------------------------------
* LEXER RULES
*------------------------------------------------------------------*/
//in the parser ~('#') means: "match any token except the token that matches '#'"
//and in lexer rules ~('#') means: "match any character except '#'"
TEXT : ~('{'|NL|'}')*;
ANY : '{' TEXT '}';
NL : ( '\r' | '\n'| '\u000C');
答案 0 :(得分:1)
你的语法有三个问题:
在npc_or_pc
规则中:
npc_or_pc
: (ANY ANY)=> pc_marker pc_condition
| npc_marker npc_condition
;
您不应该提前ANY ANY
,因为这会同时满足pc_marker
和 npc_marker
。您应该提前pc_marker
,然后ANY
(或pc_condition
)。
同时适用于pc_condition
和npc_condition
规则:
pc_condition
: '{' condition_text '}'
;
npc_condition
: '{' condition_text '}'
;
您正在使用令牌{
和}
,但词法分析器永远不会创建此类令牌。一旦词法分析器看到一个{
,它将始终跟在TEXT '}'
之后,因此词法分析器产生的唯一标记将是ANY
类型和{ {1}}:这些是唯一可用于解析器的令牌,它将我们带到问题3:
在您的规则NL
和marker_text
中:
condition_text
你正在使用令牌marker_text : TEXT;
condition_text : TEXT;
,它永远不会成为令牌流的一部分(见#2)。
更改前瞻以寻找TEXT
:
pc_marker
删除npc_or_pc
: (pc_marker ... )=> pc_marker ...
| npc_marker ...
;
和pc_condition
规则并将其替换为npc_condition
令牌:
ANY
删除npc_or_pc
: (pc_marker ANY)=> pc_marker ANY
| npc_marker ANY
;
和marker_text
规则,因为您已经删除了condition_text
和pc_condition
,所以不再需要它们。
这是你修改过的语法:
npc_condition
或甚至略短的等价物:
grammar VampireDialog;
dialog
: (line {System.out.print($line.text);})* EOF
;
line
: ANY ANY ANY npc_or_pc ANY* NL+
;
npc_or_pc
: (pc_marker ANY)=> pc_marker ANY {System.out.print("PC :: ");}
| npc_marker ANY {System.out.print("NPC :: ");}
;
pc_marker
: t=ANY {!t.getText().trim().isEmpty() && !t.getText().contains("#")}?
;
npc_marker
: t=ANY {!t.getText().trim().isEmpty() && t.getText().contains("#")}?
;
TEXT : ~('{'|NL|'}')*;
ANY : '{' TEXT '}';
NL : ( '\r' | '\n'| '\u000C');
可以用以下方法测试:
grammar VampireDialog;
dialog
: (line {System.out.print($line.text);})* EOF
;
line
: ANY ANY ANY npc_or_pc ANY+ NL+
;
npc_or_pc
: (pc_marker ANY)=> pc_marker {System.out.print("PC :: ");}
| ANY {System.out.print("NPC :: ");}
;
pc_marker
: t=ANY {!t.getText().trim().isEmpty() && !t.getText().contains("#")}?
;
ANY : '{' ~('{'|NL|'}')* '}';
NL : ( '\r' | '\n'| '\u000C');
将打印以下内容到控制台:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
String source =
"{ 1 }{ Where to? }{ Where to? }{ # }{ }{ G.Cabbie_Line = 1 }{ }{ }{ }{ }{ }{ }{ }\n" +
"{ 2 }{ Just drive. }{ Just drive. }{ 0 }{ }{ npc.WorldMap( G.WorldMap_State ) }{ }{ }{ }{ }{ }{ }{ Not here. }\n";
ANTLRStringStream in = new ANTLRStringStream(source);
VampireDialogLexer lexer = new VampireDialogLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
VampireDialogParser parser = new VampireDialogParser(tokens);
parser.dialog();
}
}