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

时间:2011-09-24 05:41:01

标签: java parsing runtime anonymous-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,所以如果有一些地方可能会碰到砖墙,那么我也想知道。

感谢您的帮助。

3 个答案:

答案 0 :(得分:11)

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

这是因为:

  1. Python有一个非常好 IDE/REPL called IDLE。我不能说使用好Read-Eval-Print-Loop反馈周期非常适合学习/玩游戏。喜欢冒险的学生甚至可以直接进入!
  2. 图形支持是跨平台的,并且通过TkInter得到良好支持。
  3. 对于初学者和/或非程序员而言,我发现它比Java更好。 (Python实际上是不是我最喜欢的语言,但它非常适合初学者,并且还有非常很好的IDE / REPL。)
  4. 更少为你工作; - )
  5. 这是演示的Python代码的外观:

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

    由于所有这些只是常规Python语法,所以没有解析 - 只需创建几个类,或许实现__dir__协议,一切都准备好了。如果要求Java集成,还有Jython,尽管我从未尝试过使用IDLE(或者知道它是否受支持)。

    快乐的编码。

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


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

    1. 一个lexer,它将输入文本转换为令牌流,并且;
    2. 还有recursive descent parser(这是一种非常简单的解析方法)
      1. 构建AST (Abstract Syntax Tree)以后可以走路(读取:“运行”),或者;
      2. 或“现在做东西”(立即评估)
    3. A Simple Recursive Descent Parser是对上述概念的Java崩溃课程介绍。 Here is some code用于“中微子语法”的递归下降解析器,无论是什么 - 查看注释以及递归下降解析器能够匹配 EBNF语法的程度。

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


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

      CREATE Monster Bob
      

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

      1. Create the object with reflection,或;
      2. 如所讨论的工厂类(来自String - > FactoryObject)的地图,或者;
      3. 一个简单的静态if-else-branch。
      4. 结果将存储在“变量散列”中,映射名称 - > MonsterObject。

        Bob.jump()
        

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

        1. Use reflection to invoke a method,或;
        2. 具有名称的地图(通过MonsterObject的方法检索) - > MethodEvaluatorObject(例如,有eval(Object ... params)方法),或;
        3. 调用eval(String action, String[] ... parameters)形式的方法并让它使用if-else-branch来“do stuff”(请注意,在解析过程中,参数(如果有的话)已经分离出来。)
        4. LS BobLS Monster非常依赖前两者的实施方式。

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

          如果有关于其中一个主题(反射,递归下降,匿名类型,[模拟]闭包等)的任何特定问题,那么随时问另一个具有特定焦点的问题。 (并且,与往常一样,研究中的尽职调查得到回报; - )

答案 1 :(得分:2)

如果你真的不想构建一种新的编程语言,你可以将命令分成几部分(使用空格作为分隔符),然后对第一部分执行查找: 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(`...`)) {
  ...
}

答案 2 :(得分:1)

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

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

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