我已经有一个我一直在研究的语言的解析器。很难解释吗?我在想它很简单。解析和语法检查完成。我只有一棵对象树。每次创建对象时,我都会创建一个分支并存储其类型(字符串、整数、浮点数、类/obj)。然后每次将新成员添加到对象时,我都会创建一个分支并重复。
我试着让它听起来很简单。我仍然需要检查对象 A 是否可以添加到对象 B 等。
在完成 AST 和语法检查之后实际上是不是相当简单,还是还有很多工作要做?
我已经有一个我一直在研究的语言的解析器。很难解释吗?我在想它很简单。解析和语法检查完成。我只有一棵对象树。每次创建对象时,我都会创建一个分支并存储其类型(字符串、整数、浮点数、类/obj)。然后每次将新成员添加到对象时,我都会创建一个分支并重复。
我试着让它听起来很简单。我仍然需要检查对象 A 是否可以添加到对象 B 等。
在完成 AST 和语法检查之后实际上是不是相当简单,还是还有很多工作要做?
通常,您需要建立符号表并进行类型检查。对于某些语言,您可以即时执行此操作;对于其他人,我认为首先必须进行名称解析和类型检查,否则您将无法很好地解释它(想到 C++)。
一旦构建了符号表,您几乎可以通过按执行顺序遍历树并按照操作员所说的方式编写解释器。基本算术非常简单。字符串和动态存储管理更难;你要弄清楚你将如何处理存储分配和释放,对于管理存储的语言,你最终必须实现某种垃圾收集器。在这一点上,生活很快变得复杂。
您可能会发现您没有考虑过的语言需求功能。异常处理?多项任务?本地范围?拉姆达斯?关闭?您会很快发现有多少现代语言使它们变得有用。
当您开始编写更复杂的程序时,您将需要一个调试器。断点?一小步?变量检查?更新?从任意位置开始?读取-评估-打印循环?
您仍然需要将您的语言与外部库联系起来;大多数人想与控制台和文件交谈;您想要缓冲文件还是一次可以使用 1 个字符并相应的性能受到影响?您将与字符表示(7 位 ascii?8 位?带有非单位宽字符的 UTF8?全 Unicode?)和标准支持库(字符串连接、搜索、数字转换 [包括双向精确浮点转换] ,大数算术,浮点陷阱,....如果您想要一种有用的编程语言,问题列表会变得很长。
解释器核心可能会非常小。你会发现其他东西可能会花费一两个数量级的努力。在这里的某个地方,如果您希望任何人使用该语言,您需要记录您所做的所有选择。如果你在某人运行一个大型应用程序后稍微更改解释器,那么天堂会帮助你。
接下来,有人会抱怨性能。现在您开始调整您的实现,并开始后悔您编写了解释器而不是编译器这一事实。
享受。如果您有 AST,那么您几乎没有触及表面。如果您确实接受了这一点,您将学会真正欣赏现代语言提供的开箱即用的功能,以及提供它付出了多少努力。
这取决于您要为其编写解释器的语言有多复杂以及您选择的工具。简单的解释器很简单。
考虑以下作为 Haskell 中 AST 的定义,用于支持高阶函数和数字的语言:
data Exp = Lam String Exp
| App Exp Exp
| Var String
| Num Int
现在您可以为它编写一个解释器作为简单的“eval”函数:
eval (App e1 e2) env = (unF (eval e1 env)) (eval e2 env)
eval (Lam x e) env = F (\v -> (eval e ((x,v):env)))
eval (Num n) env = N n
eval (Var x) env = case (lookup x env) of
Just v -> v
Nothing -> error ("Unbound variable " ++ x)
就是这样。几个无聊的支持定义如下。
data Val = F (Val -> Val) | N Int
unF (F x) = x
instance Show Val where
show (F _) = "<procedure>"
show (N n) = show n
换句话说,如果您将上述三个代码块复制粘贴到 Haskell 源文件中,您将拥有一个工作解释器,您可以使用 ghci 进行测试,如下所示:
*Main> eval (App (Lam "x" (Var "x")) (Num 1)) []
1
*Main> eval (Var "x") []
*** Exception: Unbound variable x
您可以在经典的SICP或EOPL或这本小书中阅读有关创建语言的信息。如果您想构建一种类型化的语言,您可能需要阅读更多内容。
也就是说,如果您要构建语言,我是否强烈建议您先阅读大量内容。一方面,它非常有益。其次,不知道如何创造语言的人(其中许多由于各种历史原因变得非常流行)给世界带来了太多可怕的语言,我们被它们困住了。
我想说困难的部分(实际上也是最有趣的部分)在你完成 AST 之后开始。
看看LLVM,它有很多语言的绑定(我只使用了 C++ 和 Haskell,我不知道其他语言),它应该可以帮助你为你的语言编写一个即时编译器。实际上,LLVM 使编写编译器比编写解释器更容易!