我怎么能写一个快速而又脏的翻译?

时间:2010-01-09 17:29:38

标签: programming-languages parsing interpreter dynamic-languages

我接受采访时,我被告知的其中一个领域是“动态编程语言”。所以我想我可能会花这个周末写一个带样本代码。 : - )

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

7 个答案:

答案 0 :(得分:4)

如果你想写一个非常简单的解释性语言,你应该看看FORTH。词法分析器是微不足道的(令牌是空间分隔的),解释器也很简单。如果FORTH过于复古,请查看Scheme - 您可以非常快速地构建一个小型的Scheme解释器。

答案 1 :(得分:3)

用OCaml编写的简单解释器

我的示例解释器完整地描述了here

表达式和值的类型

表达式:

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

Lexing and parsing

使用Camlp4进行解析:

#load "camlp4o.cma"

定义词法分析器:

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 parse_atom = parser
  | [< 'Int n >] -> EInt n
  | [< 'Ident v >] -> EVar v
  | [< 'Kwd "("; e=parse_expr; 'Kwd ")" >] -> e
and parse_apply = parser
  | [< e1=parse_atom; stream >] ->
      (parser
       | [< e2=parse_atom >] -> EApply(e1, e2)
       | [< e2=parse_apply >] -> begin match e2 with
           | EApply(e2, e3) -> EApply(EApply(e1, e2), e3)
           | e2 -> EApply(e1, e2)
           end
     | [< >] -> e1) stream
and parse_arith = parser
  | [< e1=parse_apply; stream >] ->
      (parser
       | [< 'Kwd "+"; e2=parse_arith >] -> EAdd(e1, e2)
       | [< 'Kwd "-"; e2=parse_arith >] -> EAdd(e1, EMul(EInt(-1), e2))
       | [< >] -> e1) stream
and parse_expr : 'a Stream.t -> expr = parser
  | [< e1=parse_arith; stream >] ->
      (parser
       | [< 'Kwd "="; e2=parse_expr >] -> EEqual(e1, e2)
       | [< >] -> e1) stream
  | [< 'Kwd "if"; p=parse_expr; 'Kwd "then"; t=parse_expr;
       'Kwd "else"; f=parse_expr >] ->
      EIf(p, t, f)
  | [< 'Kwd "let"; 'Kwd "rec"; 'Ident f; 'Ident x; 'Kwd "="; body=parse_expr;
       'Kwd "in"; rest=parse_expr >] ->
      ELetRec(f, x, body, rest)

评价

取消装配int或bool:

let int = function VInt n -> n | _ -> invalid_arg "int"
let bool = function VBool b -> b | _ -> invalid_arg "bool"

评估表达式以在某个绑定vars

的上下文中给出值
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

答案 2 :(得分:1)

您可能希望查看Lex and Yacc以了解lexing和解析,以及python implementations

答案 3 :(得分:1)

我使用spark编写了一个功能齐全的DSL,用于在2或3天内为我的旧项目表达复杂的条件(包括单元测试)。

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

答案 4 :(得分:1)

解释语言!=动态语言,但相反的情况并非总是如此。

如果你非常精通Python(==动态),那么我认为你应该在面试中做得好,除非他们问解释语言和动态语言之间的区别。

答案 5 :(得分:0)

我建议将Haskell解析组合器一起使用。 为了解析组合器,使用Wikipedia article;这是非常理论化的,可能会让你感到困惑。而是使用paper by Graham Hutton,这是非常好的。

口译员和编译器是ML / Haskell语言系列的“杀手级应用程序”,我认为你会惊讶于你能够多快地构建一些有趣的东西。

为了开始,我建议你阅读Phil Wadler的论文The Essence of Functional Programming,其中包含许多使用monad组织的示例口译员。我认为示例解释器组织良好且易于遵循,尽管在该论文中对monad的解释可能会让你头疼。

还有一个非常好的blog entry更详细地介绍了一个例子;它描述了一个用Haskell编写的Lisp解释器。该文章还包含了Haskell和Java之间的一些比较,这可能让您了解为什么许多编译器编写者更喜欢使用OO语言编写函数式语言来编写编译器和解释器。

玩得开心!!!!

答案 6 :(得分:0)

翻译的典型玩具示例是Brainfuck语言。