gpt4 book ai didi

一个游戏的antlr语法

转载 作者:行者123 更新时间:2023-12-02 00:34:55 24 4
gpt4 key购买 nike

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

我想用 Antlr 来转换对话框文件,但我的语法有歧义,尽管格式很简单。

该格式允许 NPC 和 PC 的对话作为一系列的行:

 { TEXT } repeated (it varies, normally 13 but sometimes less)

其中一个标记(第 5 个,但在示例中为第 1 个)尤其重要,因为它定义了该行是属于 NPC 还是属于 PC。我上面有'#'字符。但是,其他标记可以具有相同的字符,并且我在一些我想删除的有效文件上收到警告。

ATM 的最大问题是语法歧义。为了解决 Token 数量可变的问题,我决定使用 '*' 将我不关心的那些通配到换行符。

所以我这样做了:

any* NL* 

期望它与任何一组换行符之前的其余标记相匹配。但是,Antlr 说语法有歧义,同时:

any NL* or any* NL is not.

编辑:删除旧语法,检查新语法和新问题。

编辑:我解决了歧义,感谢 Kiers 先生,我几乎可以肯定我的新语法将匹配输入,但是我现在有一个新问题:“错误(208):VampireDialog.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 ) }{ }{ }{ }{ }{ }{ }{ Not here. }

语法如下:

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');

最佳答案

我提出了一种略有不同的方法。你可以使用一个叫做 syntactic predicate 的东西.这看起来像 (some_parser_or_lexer_rules_here)=> parser_or_lexer_rules。一个小例子:

line
: (A B)=> A B
| A C
;

规则 line 中发生的事情是这样的:首先执行前瞻以查看流中的下一个标记是否为 AB。如果是这样,则匹配这些标记,如果不是,则匹配 AC

如果在行尾之前有一个 #,你可以在你的情况下应用相同的方法,首先在流中向前看,如果有,匹配一个 npc 行,如果不匹配,则匹配 pc 行。

演示语法:

grammar VampireDialog;

parse
: LineBreak* line (LineBreak+ line)* LineBreak* EOF
;

line
: (any_except_line_breaks_and_hash+ Hash)=> conditionals {System.out.println("> npc :: " + $conditionals.text);}
| conditionals {System.out.println("> pc :: " + $conditionals.text);}
;

conditionals
: Space* conditional (Space* conditional)* Space*
;

conditional
: Open conditional_text Close
;

conditional_text
: (Hash | Space | Other)*
;

any_except_line_breaks_and_hash
: (Space | Open | Close | Other)
;

LineBreak
: '\r'? '\n'
| '\r'
;

Space
: ' ' | '\t'
;

Hash : '#';
Open : '{';
Close : '}';

// Fall through rule: if the lexer does not match anything
// above this rule, this `Any` rule will match.
Other
: .
;

还有一个类来测试语法:

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. }

如您所见,它也会跳过空行。

(请注意,语法或语义谓词不适用于 ANTLRWorks,因此您需要在命令行上对其进行测试!)

关于一个游戏的antlr语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5051804/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com