一种小型命令式语言的翻译

时间:2012-04-19 15:01:23

标签: haskell interpreter monads monad-transformers

您好,我正在Haskell中编写一个类似C语言的静态类型语言的解释器。我想在执行代码之前执行类型检查,但是我遇到了一些问题。首先,下面是我的抽象语法中的一些类型定义:

newtype Ident = Ident String deriving (Eq,Ord,Show)
data Exp = {-- some other value constructors --} | EFuncWithParams Ident [Exp]
data Type = TInt | TDouble | {-- some other value constructors --} | TFunction [Exp]
type TCM a = ErrorT String (Reader Env) a

TCM用于报告错误并传递环境,例如:

typeof (EVar v) = do
env <- ask
case M.lookup v env of
    Nothing -> throwError $ "undefined variable" ++ v ++ "\n"
    Just t - > return t

现在我想检查表达式的类型,所以我有以下执行检查的函数:

typeof Exp :: Exp -> TCM Type

它是针对所有情况定义的,只有一个:

typeof (EFuncWithParams f l)

我被困在这里。我认为我应该做的是检查f的类型(我的意思是首先检查它是否真的是一个函数)并查看在f的定义中记录的参数类型是否匹配实际传递的参数类型。不幸的是,我是一个haskell新手,并不知道如何以正确的方式表达它。任何建议将受到高度赞赏:)

编辑: 好吧,我之前写的内容可能并不暗示,但EFuncWithParams Ident [Exp]实际上是一个函数调用(是的,我知道这有点误导)我希望能够像f(2 + 3)一样调用函数,a,b [0])这就是我使用TFunction [Exp]的原因。函数声明和定义是一个声明并定义:

data Function_def =
   Func Type_specifier Declarator Compound_stm
   deriving (Eq,Ord,Show)

其中声明者是:

data Declarator =  FuncDec Ident Parameter_declarations

参数声明是Type_specifiers和Idents

的列表

我认为我应该做的是在检查声明时将函数类型保存在地图中,然后在此处获取它。我的意思是我也有:

typeof_stm :: Stm -> TCM Type -- Function_def is a statement

问题是我有一个单独的函数用于类型检查语句,我怀疑一个函数使用的映射(例如typeof_stm)是否自动传递给另一个函数(例如typeof)。我认为没有办法实现这一点,但也许我错了。

3 个答案:

答案 0 :(得分:3)

我认为你的功能类型是错误的。你有TFunction [Exp],它应该是TFunction [Type] Type(参数类型列表和返回类型)。

函数调用的Typechecking代码看起来像

case ... of ...
  EFuncWithParams ident args -> do
     t <- typeof (EVar ident)
     ts <- mapM typeof args
     case t of
         TFunction ps r -> if ts == ps
                               then return r
                               else throwError $ "parameter types do not match"
         _ -> throwError $ "called id " ++ ident ++ " which is not a function"

这个伪代码可能不正确地进出monad,请耐心等待我,我没有你所有的代码所以我不能真正地检查我所做的事情。但总体方案是这样的。如果参数类型不匹配(哪些不匹配,或者参数数量可能错误),您可能希望提供更详细的错误报告。

答案 1 :(得分:1)

我对Haskell不熟练,我只是在OCaml和C ++中做过,但你要做的是递归调用每个参数的类型检查函数并检查它们是否对应。

我的意思是你必须输入类似

的东西
FunCall ident, exp list

现在,您将在环境中拥有一个函数条目,其中包含相关参数类型,因此您需要确保的是:

  • 环境中存在名为ident的函数
  • 参数的数量等于定义(这可以通过参数检查功能隐式完成,见下文)
  • 为您调用typeof (exp1)的每个参数,并检查返回的TCM Type是否与相应参数相同

这是应该如何运作的。在OCaml(有点类似于Haskell)中,我会做类似的事情:

match exp with
    | FunCall ident, (param list) ->
      (* get fundecl from ident *)
      (* call check_params list_of_parameters, list_of_type_of_parameters *)
      (* if check params return true then type check value of the function is the return value *)


let check_params list decl_list =
  match list, decl_list with
    | [], [] -> true
    | p :: rp, d :: rd -> typeof(p) = d && check_params rp rd
    | _ -> false

答案 2 :(得分:0)

EFuncWithParams Ident [Exp]

像您这样的语言通常需要在输入上使用类型注释,并且可能还需要在输出上使用类型注释。因此,如果您将此信息添加到该构造函数

EFuncWithparams { inType, outType :: Type
                , funcInput :: Ident
                , funcBody :: [Expr] }

现在要进行类型检查,你只需:

  1. funcInputinType的绑定添加到您的类型环境
  2. 确定具有新类型环境的funcBody的类型
  3. 确保它与outType匹配。
  4. 您还应检查函数应用程序,以确保输入与函数inType匹配,并根据outType正确使用结果。