Programming languages 我怎样才能写出一个又快又脏的解释器呢?

Programming languages 我怎样才能写出一个又快又脏的解释器呢?,programming-languages,parsing,interpreter,dynamic-languages,Programming Languages,Parsing,Interpreter,Dynamic Languages,我有一次采访,其中一个我被告知可能会复习的领域是“动态编程语言”。所以我想我可能会花这个周末写一个,作为示例代码带来。:-) 当然,考虑到时间的限制,我计划写一些非常基本的东西,最好使用一种语言和/或工具集,这样做会非常容易。我的大部分经验都是用Python编写的,但是我愿意花一点时间学习一些新的东西,如果它能使任务变得更简单(并且不会花费太长时间)。有没有人能给我提供一些工具或语言方面的建议,让这变得更简单?你可能会想看看词法分析和语法分析,他们的我曾经写过一个功能非常全面的DSL,在两三天内

我有一次采访,其中一个我被告知可能会复习的领域是“动态编程语言”。所以我想我可能会花这个周末写一个,作为示例代码带来。:-)


当然,考虑到时间的限制,我计划写一些非常基本的东西,最好使用一种语言和/或工具集,这样做会非常容易。我的大部分经验都是用Python编写的,但是我愿意花一点时间学习一些新的东西,如果它能使任务变得更简单(并且不会花费太长时间)。有没有人能给我提供一些工具或语言方面的建议,让这变得更简单?

你可能会想看看词法分析和语法分析,他们的

我曾经写过一个功能非常全面的DSL,在两三天内为我的一个老项目表达复杂的条件(包括单元测试)


它在Python中应该非常简单,因为spark(以及其他类似模块)将为您提供编写词法和语法分析器所需的工具。您可以使用python字典轻松地实现一个简单的符号表,并且可以将其转换为python和eval,或者将其移动到较低级别的语言中运行

如果你想写一个非常简单的解释性语言,你应该看看FORTH。lexer很简单(标记是空格分隔的),解释器也很简单。如果FORTH过于复古,请查看Scheme—您可以非常快速地构建一个小型Scheme解释器。

一种解释语言!=一种动态的语言,尽管并非总是相反

如果您非常精通Python(=动态),那么我认为您应该在面试中表现出色,除非他们询问解释语言和动态语言之间的区别。

我建议使用with parsing combinator。 要专注于解析组合符,不要使用;这是非常理论化的,可能会让你感到困惑。相反,使用的是非常好的

解释器和编译器是ML/Haskell语言家族的“杀手级应用”,我想你会惊讶于你能如此快速地构建出有趣的东西

对于入门,我建议您阅读Phil Wadler的论文,其中包含许多使用monads组织的示例口译员。我认为示例口译员组织良好,易于理解,尽管那篇论文中对单子的解释可能会让你头疼

还有一个非常好的例子,它更详细地介绍了一个例子;它描述了一个用Haskell编写的Lisp解释器。该writeup还包含Haskell和Java之间的一些比较,这可能会让您感觉到为什么许多编译器编写人员在编写编译器和解释器时更喜欢函数式语言而不是OO语言


玩得开心

解释器的典型示例是语言。

用OCaml编写的简单解释器 我的示例解释器完整地描述了

表达式和值的类型 表达方式:

type expr =
  | EAdd of expr * expr
  | EApply of expr * expr
  | EEqual of expr * expr
  | EIf of expr * expr * expr
  | EInt of int
  | ELetRec of string * string * expr * expr
  | EMul of expr * expr
  | EVar of string
价值观:

type value =
  | VInt of int
  | VBool of bool
  | VClosure of string * (string * value) list * expr
词法分析 使用Camlp4进行解析:

#load "camlp4o.cma"
定义lexer:

open Genlex
let keywords =
  ["("; ")"; "+"; "-"; "=";
   "if"; "then"; "else";
   "let"; "rec"; "in"]
let lex stream =
  let rec aux = parser
    | [< 'Int n when n<0; t=aux >] -> [< 'Kwd "-"; 'Int(-n); t >]
    | [< 'h; t=aux >] -> [< 'h; t >]
    | [< >] -> [< >] in
  aux(make_lexer keywords stream)
计算表达式以在某些绑定的
变量的上下文中给出值:

let rec eval vars = function
  | EApply(func, arg) ->
      begin
        match eval vars func, eval vars arg with
        | VClosure(var, vars, body), arg -> eval ((var, arg) :: vars) body
        | _ -> invalid_arg "Attempt to apply a non-function value"
      end
  | EAdd(e1, e2) -> VInt (int(eval vars e1) + int(eval vars e2))
  | EMul(e1, e2) -> VInt (int(eval vars e1) * int(eval vars e2))
  | EEqual(e1, e2) -> VBool (eval vars e1 = eval vars e2)
  | EIf(p, t, f) -> eval vars (if bool (eval vars p) then t else f)
  | EInt i -> VInt i
  | ELetRec(var, arg, body, rest) ->
      let rec vars = (var, VClosure(arg, vars, body)) :: vars in
      eval vars rest
  | EVar s -> List.assoc s vars
工作实例 将示例程序定义为字符串:

let program = "let rec fib n = if n=0 then 0 else if n=1 then 1 else fib(n - 1) + fib(n - 2) in fib 30"
Lex并将字符串解析为AST:

let ast = parse_expr(lex(Stream.of_string program))
评估AST:

eval [] ast

这是一个很好的建议,但我不打算使用C.:-)
lex
yacc
是一个有点“繁重”的工具,如果你想做一个“又快又脏”的解释器的话。lex和yacc是C工具。但是,除非你想特别了解它们,否则在没有它们的情况下编写解释器会更有趣。PLY是Python Lex/Yacc,使用起来并不难。学习Lex和Yacc工具会有一些开销,但一旦你了解了他们,制作一个快速而肮脏的解释器是一个标准,所以解释器和编译器的资源是非常棒的。这里有很多有用的东西。@Neil:我很想看到一个类似的页面,重点介绍口译员。它们很有趣,更专业,而且比本机代码编译器部署得更广泛。@Norman我不确定对解释器和编译器进行太多区分是否有用。两者必须执行基本相同的任务。以福斯(根据我的回答)为例,很难看出解释器从何处结束,编译器从何处开始(反之亦然)。@Neil:FORTH当然模糊了两者之间的区别,但构建一个高质量编译器所需的时间大约是构建一个高质量解释器所需时间的十倍。也许人们可以简单地忽略诸如代码生成、寄存器分配等主题,但我仍然认为关于口译员的单独页面将是一个有用的资源。这是真的,但编译语言在一个周末内要做起来就有点困难了。:-)“一种解释性语言!=一种动态语言,尽管事实并非总是相反”。这种交换关系的对立面是什么?:-)被解释并不是一种动态的语言。然而,动态语言是可以解释的;“很多是,一些不是,一些都是。”乔什·佩里,你是说他们不相关?
let ast = parse_expr(lex(Stream.of_string program))
eval [] ast