游戏的Antlr语法

游戏的Antlr语法,antlr,Antlr,我试图预处理一些旧游戏中的对话文件——吸血鬼化妆舞会:血统,如果你好奇的话——在一些数据文件的特定位置插入一些代码 我想使用Antlr来转换对话框文件,但是我的语法是不明确的,尽管格式非常简单 该格式允许NPC和PC以一系列行的形式进行对话: { TEXT } repeated (it varies, normally 13 but sometimes less) 其中一个标记尤其重要(示例中为第5个,但为第1个),因为它定义了该行是属于NPC还是属于PC。I上有“#”字符。但是,其他令牌可

我试图预处理一些旧游戏中的对话文件——吸血鬼化妆舞会:血统,如果你好奇的话——在一些数据文件的特定位置插入一些代码

我想使用Antlr来转换对话框文件,但是我的语法是不明确的,尽管格式非常简单

该格式允许NPC和PC以一系列行的形式进行对话:

 { TEXT } repeated (it varies, normally 13 but sometimes less)
其中一个标记尤其重要(示例中为第5个,但为第1个),因为它定义了该行是属于NPC还是属于PC。I上有“#”字符。但是,其他令牌可以具有相同的字符,并且我在一些有效文件上收到警告,我希望消除这些警告

我最大的问题是语法歧义。为了解决令牌数量可变的问题,我决定使用“*”对我在换行之前不关心的令牌进行全局化

所以我这样做了:

any* NL* 
期望这与任何换行符集之前的其余标记相匹配。然而,Antlr说语法是模棱两可的,而:

any NL* or any* NL is not.
编辑:删除旧语法,检查新语法和新问题

编辑:我解决了模棱两可的问题,多亏了基尔斯先生,我几乎可以肯定我的新语法将与输入相匹配,但我现在有一个新问题: “错误(208):错误对话框。g:99:1:无法匹配以下令牌定义,因为先前的令牌匹配相同的输入:NOT_SHARP” 如果我删除了他抱怨的NL输入,那么抱怨的是NL Lexer规则

正如Kiers先生告诉我在这里发布一个输入示例一样: npc行,注意#

{1}{Where to?}{Where to?}{{{{}{{{G.Cabbie_Line=1}{{}{}{{}{}{}{}}

pc行,注意没有#

{2}{Just drive.}{Just drive.}{0}{{{}{npc.WorldMap(G.WorldMap_State)}{{}{{}{}{}{{}{{}{{{}{{{}{不在这里。}

以下是语法:

grammar VampireDialog;

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

@parser::header {
import java.util.LinkedList;
import java.io.File;
}

@members {
    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");
        List<File> files = new LinkedList<File>();
        getFiles(256, new File[]{vampireDir}, files, new LinkedList<File>());
        for (File f : files) {
            if (f.getName().endsWith(".dlg")) {
                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_line | player_line) ANY* NL*)*;
npc_line :  npc_marker npc_conditional;
player_line : pc_marker conditional;
npc_conditional : '{' condiction '}'
            {   String cond = $condiction.tree.toStringTree(), partial = "npc.Reset()", full = "("+cond+") and npc.Reset()";
                boolean empty = cond.trim().isEmpty(); 
                boolean alreadyProcessed = cond.endsWith("npc.Reset()");}   
                ->   {empty}? '{' REWRITE[partial] '}'
                ->   {alreadyProcessed}? '{' REWRITE[cond] '}'
                ->   '{' REWRITE[full] '}';
conditional : '{' condiction '}'
            {   String cond = $condiction.tree.toStringTree(), full = "("+cond+") and npc.Count()";
                boolean empty = cond.trim().isEmpty(); 
                boolean alreadyProcessed = cond.endsWith("npc.Count()");}   
                ->   {empty}? '{' REWRITE[cond] '}'
                ->   {alreadyProcessed}? '{' REWRITE[cond] '}'
                ->   '{' REWRITE[full] '}';
condiction : TEXT*;
//in the parser ~('#') means: "match any token except the token that matches '#'" 
//and in lexer rules ~('#') means: "match any character except '#'"
pc_marker : '{' NOT_SHARP* '}';
npc_marker : '{' NOT_SHARP* '#' NOT_SHARP* '}';


/*------------------------------------------------------------------
 * LEXER RULES
 *------------------------------------------------------------------*/
ANY : '{' TEXT* '}';
TEXT : ~(NL|'}');
NOT_SHARP : ~(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" + 
                "\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.parse();
    }
}
语法对话框;
选择权
{
输出=AST;
ASTLabelType=CommonTree;
语言=Java;
} 
代币
{
重写;
}
@解析器::头{
导入java.util.LinkedList;
导入java.io.File;
}
@成员{
公共静态void main(字符串[]args)引发异常{
File vampairdir=new文件(System.getProperty(“user.home”),“桌面/吸血鬼化妆舞会-血统/吸血鬼化妆舞会-血统/吸血鬼/dlg”);
列表文件=新建LinkedList();
getFiles(256,新文件[]{vampairdir},文件,新LinkedList());
用于(文件f:文件){
if(f.getName().endsWith(“.dlg”)){
吸血鬼对话lexer lex=新的吸血鬼对话lexer(新的ANTLRFileStream(f.getAbsolutePath(),“Windows-1252”);
TokenRewriteStream令牌=新的TokenRewriteStream(lex);
吸血鬼对话框解析器=新的吸血鬼对话框解析器(令牌);
Tree t=(Tree)parser.dialog().getTree();
//System.out.println(t.toStringTree());
}
}
}
公共静态void getFiles(int级别、文件[]搜索、列表文件、列表目录){
用于(文件f:搜索){
如果(!f.exists()){
抛出新的断言错误(“搜索文件数组有不存在的文件”);
}
}
GetFileSux(级别、搜索、文件、目录);
}
私有静态void getFilesAux(int级别、文件[]开始文件、列表文件、列表目录){
列表子文件列表=新的ArrayList(50);
对于(文件f:startFiles){
File[]子文件=f.listFiles();
如果(子文件==null){
添加(f);
}否则{
目录。添加(f);
子文件列表。添加(子文件);
}
}
如果(级别>0){
对于(文件[]子文件:子文件列表){
GetFileSax(级别-1,子文件,文件,目录);
}
}
}
}
/*------------------------------------------------------------------
*解析器规则
*------------------------------------------------------------------*/
对话:(任意任意任意(npc|线|玩家|线)任意*NL*)*;
npc_线:npc_标记npc_条件;
播放器线:pc线;
npc_条件:'{'条件'}'
{String cond=$condiction.tree.toStringTree()、partial=“npc.Reset()”、full=“(“+cond+”)和npc.Reset()”;
布尔空=cond.trim().isEmpty();
布尔值alreadyProcessed=cond.endsWith(“npc.Reset()”);}
->{空}?“{“重写[部分]”}
->{alreadyProcessed}?“{“重写[cond]”
->“{”重写[完整]”};
条件:“{”条件“}”
{String cond=$condiction.tree.toStringTree(),full=“(“+cond+”)和npc.Count()”;
布尔空=cond.trim().isEmpty();
布尔值alreadyProcessed=cond.endsWith(“npc.Count()”);}
->{空}?“{“重写[cond]”
->{alreadyProcessed}?“{“重写[cond]”
->“{”重写[完整]”};
条件:文本*;
//在解析器中,(“#”)的意思是:“匹配除匹配“#”的标记之外的任何标记”
//在lexer规则中,(“#”)的意思是:“匹配除“#”以外的任何字符”
pc_标记:“{”不_SHARP*“}”;
npc_标记:“{”NOT_SHARP*“#”NOT_SHARP*“}”;
/*------------------------------------------------------------------
*LEXER规则
*------------------------------------------------------------------*/
任何:“{”文本*“}”;
正文:~(NL |'});
不尖锐:~(NL |'#'|'});
NL:(“\r”|“\n”|“\u000C”);

我提出了一种稍微不同的方法。你可以用一种叫a的东西。这看起来像
(这里有些语法分析器或词法分析器规则)=>语法分析器或词法分析器规则。一个小例子:

line
  :  (A B)=> A B
  |          A C
  ;
规则
中发生的情况是:首先执行一个前瞻,以查看流中的下一个令牌是否是
a
B
。如果是这种情况,则这些令牌
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" + 
                "\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.parse();
    }
}
> npc :: { 1 }{ Where to? }{ Where to? }{ # }{ }{ G.Cabbie_Line = 1 }{ }{ }{ }{ }{ }{ }{ }
> pc  :: { 2 }{ Just drive. }{ Just drive. }{ 0 }{ }{ npc.WorldMap( G.WorldMap_State ) }{ }{ }{ }{ }{ }{ }{ Not here. }