在java中编写自定义语法解释器?

我即将开始编写一个演示程序来编写我即将给出的演讲。 我想让class上的每个学生都下载这个应用程序,然后能够通过命令行以交互方式创建对象实例(及其图形表示)。 我决定用java编写,不是因为它是我最熟悉的语言,而是因为它有简单的图形类,我可以非常肯定jar会在他们的计算机上运行。

简介。 现在的问题是:

为此程序实现某些自定义命令行语法的好方法是什么? 我想使用一个简单,随意的语法,如:

CREATE Monster Bob; Bob.jump(); LS Bob //to list Bob's methods or something. LS CREATE //to list all the classes 

首先,当我想到这个问题时,我会首先想到的是什么。

我可以想象我可以在树型链接中拥有一组地图。 我可以解析每个关键词作为下一个地图的关键。 因此,“CREATE Monster Bob”可以被评估为

1)搜索关键字“CREATE”的关键字映射。 返回值,该值是对类映射的引用。 2)搜索类映射关键“怪物”。 返回值,这是一个实现一些接口Leaf的工厂类,它让我知道它是一个叶子值(我将使用instanceof进行检查)。
3)也许Leaf接口将包含一个名为execute()的方法,它可以做任何想做的事情。 在这种情况下,它将创建一个Monster对象,将此对象添加到名为Objects的名为Bob的地图中。 (这个Leaf业务听起来很难看,但可以清理。)

凉。 但这句话对我来说有点困难:Bob.jump();

1)在“Bob”中搜索一些对象图。 返回一些使用类似“evaluate(String s)”的方法实现接口的对象,并将其传递给字符串“jump()”
2)Bob搜索方法的某些内部地图“jump()”,然后……? 在c ++中,我将把键作为指向将被执行的成员函数Monster.jump()的指针。 但是我不相信java中没有函数指针这样的东西。 我已经读过你可以使用匿名类来完成这个,虽然我没有尝试过。 看起来它会起作用。

所以,这会有效,但有更优雅的方式去做吗? 我以前从未写过任何类型的翻译。 如果有人提供一些提示,我想以一种很好的方式做一些事情并在这个过程中学到一些东西。 如果我不是很结构化,这似乎是一种可能容易出错的方法,特别是当Bob和其他所有对象开始解析自己的指令和使用匿名函数时。 此外,看起来除了普通代码之外,每个类都需要一个运行时就绪的接口。

我也不太了解Java,所以如果有一些地方我可能碰到砖墙,那么我也想知道。

我在这里先向您的帮助表示感谢。

我实际上建议使用Python – 除非有一个非常好的理由不这样做。

这是因为:

  1. Python有一个非常好的 IDE / REPL,叫做IDLE 。 我不能说使用良好的Read-Eval-Print-Loop : 反馈周期非常适合学习/播放。 喜欢冒险的学生甚至可以跳进去!
  2. 图形支持是跨平台的,并且通过TkInter得到良好支持。
  3. 对于初学者和/或非程序员而言,我发现它比Java更好。 (Python实际上不是我最喜欢的语言,但它非常适合初学者,并且有一个非常好的IDE / REPL。)
  4. 这对你来说要少得多 😉

这就是演示的Python代码的外观:

 Bob = BigMonster() Bob.jump() dir(Bob) dir(Monters) 

因为所有这些只是常规的Python语法,所以没有解析 – 只需创建几个类,也许实现__dir__协议,一切都准备好了。 如果Java集成是一个要求,那么也有Jython ,虽然我从来没有尝试过IDLE(或者知道它是否支持)。

快乐的编码。

基于图像的SmallTalk (如Sqeak )比Python更具交互性,因为代码持久运行环境的一部分。 但是,找到一个好的图像需要一些时间 – 吱吱声不是最好的实现,但它是免费的 – 并且学习特定的SmallTalk环境。 因此,虽然整合最终可以有很大的支出,但它确实需要更多的适应性:)


但是,唉,要在Java中使用一个简单的解析器,这些将是有趣的:

  1. 将输入文本转换为令牌流的词法分析器 ;
  2. 还有一个递归下降解析器 (这是一种非常简单的解析方法)
    1. 构建一个AST(抽象语法树) ,可以在以后行走(读取:“运行”),或者;
    2. 或者“现在做东西”(立即评估)

简单的递归下降解析器是上面概念的Java崩溃课程简介。 下面是一些用于“中微子语法”的递归下降解析器的代码 ,无论是什么 – 查看注释以及递归下降解析器 EBNF语法的匹配程度

现在,它只是“定义”这种伪/迷你语言的语义规则并实现它的问题;-)


更多地探索语义/ Java方法(部分只是原始post的简化/重新声明):

 CREATE Monster Bob 

会创建一个新的MonsterObject。 一些方法可能是:

  1. 用reflection创建对象 ,或;
  2. 如上所述的Factory类的映射(来自String – > FactoryObject),或者;
  3. 一个简单的静态if-else-branch。

结果将存储在映射Name – > MonsterObject的“变量散列”中。

 Bob.jump() 

将此解析为[object Bob] [method jump] [p1], [p2], ..., [pn] ,在“变量哈希”中查找对象,然后:

  1. 使用reflection来调用方法 ,或;
  2. 有一个地图(通过MonsterObject的方法检索)Name – > MethodEvaluatorObject(例如有eval(Object ... params)方法),或;
  3. 调用eval(String action, String[] ... parameters)forms的方法并让它使用if-else-branch来“do stuff”(注意,在解析过程中参数,如果有的话)已经分离出来了)。

LS BobLS Monster依赖前两者的实现方式。

虽然Java没有“函数指针”,但可以通过使用具有给定接口的对象来模拟它们(即,对象本身充当指针)。 Functional Java有F / F2 /…/F8类来尝试使用generics统一处理它。 但是,在Java中,通常会创建一个单独的一次性接口(或类),如Runnable ,它具有单个“action”方法,该方法被修改为接受适当的参数并返回适当的结果(例如MethodEvaluatorObjects或FactoryObjects)。

如果对于其中一个主题(reflection,递归下降,匿名类型,[模拟]闭包等)有任何具体问题,那么请随意提出具有特定焦点的另一个 SO问题。 (并且,一如既往,研究中的尽职调查得到回报;-)

如果你真的不想构建一种新的编程语言,你可以将命令分成几部分(使用空格作为分隔符),然后对第一部分执行查找: CREATE Monster Bob; => createmonsterbob

 String operation = parts[0]; if(operation.equals(`create`)) { String type = parts[1]; String name = parts[2]; // your logic here } else if(operation.equals(`...`)) { ... } 

您是否考虑使用像ANTLR这样的解析器生成器? 它可以为多种语言生成解析器,并以包括Java在内的各种语言输出解析器。 它可以大大加快你的任务,软件是免费的(虽然书籍是出售,但嘿,你的时间是值得的,对吧?)。

http://en.wikipedia.org/wiki/ANTLR

另一方面,您可能可以使用像PST这样的简单语言来编写自己的解析器,但我不会过度复杂化。 只需使自己成为将文件分解为字符串标记(词法分析器)的函数,以及另一个一次请求标记并确定如何处理它的函数。 如果您的语言很简单,那就足够了。