我正在开发一个用户能够以特殊格式输入事实和规则的项目,但是我在检查该格式是否正确并获取信息方面遇到了一些麻烦.
当程序启动时,用户可以在文本区域中输入"命令",并将该文本发送到一个parseCommand方法,该方法根据用户编写的内容确定要执行的操作.例如,要添加事实或规则,您可以使用前缀+.或用于-删除事实或规则等.
我创建了处理前缀的系统,但我遇到了事实和规则格式的问题.
事实:这些由字母数字名称定义,并包含属性列表(每个属性都带有<>符号)和真值.属性也由字母数字名称定义,并包含2个字符串(称为参数),每个字符串都带有<>符号.通过!在列表中放置一个属性也可以为负.例如,用户可以键入以下内容将这3个事实添加到程序中:
+father(<parent(<John>,<Jake>)>, true)
+father(<parent(<Jammie>,<Jake>)>, false)
+father(!<parent(<Jammie>,<Jake>)>, true)
+familyTree(<parent(<John>,<Jake>)>, <parent(<Jammie>,<Jake>)> , true)
+fathers(<parent(<John>,<Jake>)>, !<parent(<Jammie>,<Jake>)> , true)
Run Code Online (Sandbox Code Playgroud)
我用来存储事实的类是这样的:
public class Fact implements Serializable{
private boolean truth;
private ArrayList<Property> properties;
private String name;
public Fact(boolean truth, ArrayList<Property> properties, String name){
this.truth = truth;
this.properties = properties;
this.name = name;
}
//getters and setters here...
}
Run Code Online (Sandbox Code Playgroud)
规则:这些是2个属性之间的链接,它们由=>符号标识.再次,它们的名称是字母数字.属性是有限的,因为它们只能包含由大写字母组成的参数,第二个属性的参数必须与第一个属性的参数相同.规则还有2个其他参数,可以通过输入名称来设置或不设置(这些参数中的每一个都对应于规则的属性,可以是Negative或Reversive).例如:
+son(<parent(<X>,<Y>)> => <child(<Y>,<X>)>)
+son(<parent(<X>,<Y>)> => <child(<Y>,<X>)>, Negative, Reversive)
+son(<parent(<X>,<Y>)> => <child(<Y>,<X>)>, Reversive)
+son(<parent(<X>,<Y>)> => <child(<Y>,<X>)>, Negative)
Run Code Online (Sandbox Code Playgroud)
规则属性
正常规则告诉我们,如果在下面的示例中,X它是父的,Y则暗示它Y是以下的子节点X:
son(<parent(<X>,<Y>)> => <child(<Y>,<X>)>)
Run Code Online (Sandbox Code Playgroud)
虽然Negative规则告诉我们,如果在下面的示例中,X它是父的,Y这意味着它Y不是以下的子代X:
son(<parent(<X>,<Y>)> => <child(<Y>,<X>)>, Negtive)
Run Code Online (Sandbox Code Playgroud)
一Reversive但规则告诉我们,如果在下面的例子中,Y是一个孩子X这意味着X是的父Y
son(<parent(<X>,<Y>)> => <child(<Y>,<X>)>, Reversive)
Run Code Online (Sandbox Code Playgroud)
最后一种情况是规则是Negative和Reversive.这告诉我们,如果在下面的例子中,Y不是这个的孩子X意味着它X是父的Y.
son(<parent(<X>,<Y>)> => <child(<Y>,<X>)>, Negative, Reversive)
Run Code Online (Sandbox Code Playgroud)
这是我用来存储规则的类:
public class Rule implements Serializable{
private Property derivative;
private Property impliant;
private boolean negative;
private boolean reversive;
private String name;
public Rule(Property derivative, Property impliant, boolean negative, boolean reversive) throws InvalidPropertyException{
if(!this.validRuleProperty(derivative) || !this.validRuleProperty(impliant))
throw new InvalidPropertyException("One or more properties are invalid");
this.derivative = derivative;
this.impliant = impliant;
this.negative = negative;
this.reversive = reversive;
}
//getters and setters here
}
Run Code Online (Sandbox Code Playgroud)
物业类:
public class Property implements Serializable{
private String name;
private String firstArgument;
private String secondArgument;
public Property(String name, String firstArgument, String secondArgument){
this.name = name;
this.firstArgument = firstArgument;
this.secondArgument = secondArgument;
}
Run Code Online (Sandbox Code Playgroud)
以上示例均为有效输入.这里只是澄清一些无效的输入示例:
事实:
参数没有提供真或假:
+father(<parent(<John>,<Jake>)>)
Run Code Online (Sandbox Code Playgroud)
没有给出的财产:
+father(false)
Run Code Online (Sandbox Code Playgroud)
提供的无效财产:
+father(<parent(<John>)>, true)
+father(<parent(John, Jake)>, true)
+father(<parent(John, Jake, Michel)>, true)
+father(parent(<John>,<Jake>), true)
Run Code Online (Sandbox Code Playgroud)
请注意最后一个中缺少的括号.
规则:
一个或多个属性无效:
+son(<parent(<X>,<Y>)> => child(<Y>,<X>))
+son(parent(<X>,<Y>) => child(<Y>,<X>))
+son(<parent(<X>,<Y>)> => <child(<Z>,<X>)>) (Note the Z in the child property)
+son(<parent(<Not Valid>,<Y>)> => child(<Y>,<X>)) (Invalid argument for first property)
+son(=> child(<Y>,<X>))
Run Code Online (Sandbox Code Playgroud)
我能够从用户那里获得输入,并且我还能够根据前缀看到用户想要执行哪种操作.
但是,我无法弄清楚如何处理字符串,如:
+familyTree(<parent(<John>,<Jake>)>, <parent(<Jammie>,<Jake>)> , true)
Run Code Online (Sandbox Code Playgroud)
这是由于多种原因:
()和<>符号分割输入字符串.Negative属性的位置.+familyTree(<parent(<John>,<Jake>)>,为这个事实设置属性我可以检查两者之间<>可能形成问题的任何东西,因为<在第一个之前 有2个开放>我的第一个想法是从字符串的开头开始(我从前缀中获取动作),然后从主字符串中删除该段字符串.
但是我不知道如何使这个系统适应上述问题(特别是问题1和2).
我试过使用像:String.split()和String.contains().
我该怎么做呢?如何理解并非所有字符串都包含相同的信息?(从某种意义上说,某些事实具有更多属性或某些规则具有比其他规则更多的属性.)
编辑:
我忘了说用于存储数据的所有方法都已完成并正常工作,可以通过调用例如:infoHandler.addRule()或infoHandler.removeFact().在这些功能中,我还可以验证输入数据是否更好.
例如,我可以从字符串中获取事实或规则的所有数据,并仅使用大写字母等来验证规则属性的参数.
编辑2:
在评论中有人建议使用像ANTLR或JavaCC这样的解析器生成器.我在最近3天内研究了这个选项,但我似乎找不到任何关于如何在其中定义自定义语言的好资料.大多数文档假设您正在尝试编译令人兴奋的语言,并建议从某个地方下载语言文件而不是自己编写.
我正在尝试理解ANTLR的基础知识(这似乎是最容易使用的.)然而,网上没有很多资源来帮助我.
如果这是一个可行的选择,任何人都可以帮助我理解如何在ANTLR中做这样的事情吗?
一旦我写了一个语法文件,我如何使用它?我已经阅读了一些关于从语言文件生成解析器的内容,但我似乎无法弄清楚它是如何完成的......
编辑3:
我很想做一个ANTLR的语法文件,看起来像这样:
/** Grammer used by communicate parser */
grammar communicate;
/*
* Parser Rules
*/
argument : '<' + NAMESTRING + '>' ;
ruleArgument : '<' + RULESTRING + '>' ;
property : NAMESTRING + '(' + argument + ',' + argument + ')' ;
propertyArgument : (NEGATIVITY | POSITIVITY) + property + '>' ;
propertyList : (propertyArgument + ',')+ ;
fact : NAMESTRING + '(' + propertyList + ':' + (TRUE | FALSE) + ')';
rule : NAMESTRING + '(' + ruleArgument + '=>' + ruleArgument + ':' + RULEOPTIONS + ')' ;
/*
* Lexer Rules
*/
fragment LOWERCASE : [a-z] ;
fragment UPPERCASE : [A-Z] ;
NAMESTRING : (LOWERCASE | UPPERCASE)+ ;
RULESTRING : (UPPERCASE)+ ;
TRUE : 'True';
FALSE : 'False';
POSITIVITY : '!<';
NEGATIVITY : '<' ;
NEWLINE : ('\r'? '\n' | '\r')+ ;
RULEOPTIONS : ('Negative' | 'Negative' + ',' + 'Reversive' | 'Reversive' );
WHITESPACE : ' ' -> skip ;
Run Code Online (Sandbox Code Playgroud)
我在这里走在正确的轨道上吗?如果这是一个好的语法文件,我怎样才能在以后测试和使用它?
我认为语法分析器对您的问题没有好处。无论如何,您可以通过使用正则表达式和一些字符串实用程序来更简单地处理它。
最好从小问题开始,然后转向更大的问题:首先解析属性本身似乎很容易,所以我们编写一个方法来做到这一点:
private static Property toProp(String propStr) {
String name = propStr.substring(1,propStr.indexOf("("));
String[] arguments = propStr.substring(propStr.indexOf('(')+1,propStr.indexOf(')')).split(",");
return new Property(name,
arguments[0].substring(1,arguments[0].length()-1),
arguments[1].substring(1,arguments[1].length()-1));
}
Run Code Online (Sandbox Code Playgroud)
要解析事实字符串,使用正则表达式使事情变得更容易,属性的正则表达式是 /<[\w\d] ([<>\w\d,] )>/并且通过我们已经编写的 toProp 方法的帮助,我们可以创建另一种解析事实的方法:
public static Fact handleFact(String factStr) {
Pattern propertyPattern = Pattern.compile("<[\\w\\d]*\\([<>\\w\\d,]*\\)>");
int s = factStr.indexOf("(") + 1;
int l = factStr.lastIndexOf(")");
String name = factStr.substring(0,s-1);
String params = factStr.substring(s, l);
Matcher matcher = propertyPattern.matcher(params);
List<Property> props = new ArrayList<>();
while(matcher.find()){
String propStr = matcher.group();
props.add(toProp(propStr));
}
String[] split = propertyPattern.split(params);
boolean truth = Boolean.valueOf(split[split.length-1].replaceAll(",","").trim());
return new Fact(truth,props,name);
}
Run Code Online (Sandbox Code Playgroud)
解析规则与事实非常相似:
private static Rule handleRule(String ruleStr) {
Pattern propertyPattern = Pattern.compile("<[\\w\\d]*\\([<>\\w\\d,]*\\)>");
String name = ruleStr.substring(0,ruleStr.indexOf('('));
String params = ruleStr.substring(ruleStr.indexOf('(') + 1, ruleStr.lastIndexOf(')'));
Matcher matcher = propertyPattern.matcher(params);
if(!matcher.find())
throw new IllegalArgumentException();
Property prop1 = toProp(matcher.group());
if(!matcher.find())
throw new IllegalArgumentException();
Property prop2 = toProp(matcher.group());
params = params.replaceAll("<[\\w\\d]*\\([<>\\w\\d,]*\\)>","").toLowerCase();
return new Rule(name,prop1,prop2,params.contains("negative"),params.contains("reversive"));
}
Run Code Online (Sandbox Code Playgroud)