haskell - Getting a <<loop>> when implementing block-visibility in a type-checker -
i'm writing simple type-checker simple imperative language, , i'm stuck kind of output:
testchecker: <<loop>>
i have alread read this question, know must doing wrong circular reference. i'm pretty sure problem in following function, responsible checking block of statements:
checkgroup :: environ -> [prog] -> (state, environ, [string]) checkgroup env progs = (finalstate, finalenv, messages) (finalstate, finalenv, messages) = foldl checksingleprog (ok, empty, []) progs checksingleprog (s, e, msg) prog = (ress, mergeenv e e', mess) (s', e', msg') = checkprog (mergeenv' env finalenv) prog mess = msg ++ msg' ress = if s == err || s' == err err else ok
note the:
checkprog (mergeenv' env finalenv) prog
where checkprog
uses environment merging of environment of father of group
plus environment generated whole group
.
(edit: yes know finalenv
part of output of call checkprog
. that's point of question. know can done, don't understand i'm doing wrong trick.)
the mergeenv'
function union between environments (it preferes right argument, opposed m.union
), keeping left-argument's variables. it's definition is:
-- variables, functions, labels [for goto] type environ = (m.map string type, m.map string type, s.set string) mergeenv' :: environ -> environ -> environ mergeenv' env1 env2 = (fst' env1, m.union (snd' env2) (snd' env1), s.union (thr' env2) (thr' env1))
(the mergeenv
(no apos @ end) 3 unions.)
the prog
type type of statements (e.g. if
, for
, group
etc.) state
type either ok
or err
, signal successful , unsuccessful checking.
what i'm trying achieve have block-visibility function definitions (and labels) , forward visibility variables, without doing 2 different runs.
if change:
(mergeenv' env finalenv)
to:
env
everything runs "fine", visibility forward everything.
i know it's possible achieve want in way similar i'm trying (i got idea professor of languages , compilers), seems i'm doing wrong merging of environments.
am doing wrong? or should work , problem hidden somewhere else in type-checker?
here's minimal working example demonstrates problem. it's still 180 lines:
module main import qualified data.map m data prog = group [prog] | fdecl type string [(type, string)] prog | simple simple deriving (eq, show) data simple = rexp rexp | vdecl type string rexp | return rexp deriving (eq, show) data rexp = call string [rexp] | lexp lexp | const const deriving(eq, show) data lexp = ident string deriving (eq, show) data const = integer integer deriving (eq, show) data type = func type [type] | int | error deriving (eq, show) compatible :: type -> type -> bool compatible _ error = true compatible x y | x == y = true compatible (func ty types) (func ty' types') = compatible ty ty' && , (zipwith compatible types types') compatible _ _ = false type environ = (m.map string type, m.map string type) empty :: environ empty = (m.empty, m.empty) hasvar :: environ -> string -> bool hasvar env var = m.member var $ fst env getvartype :: environ -> string -> type getvartype env var = fst env m.! var putvar :: environ -> string -> type -> environ putvar env var ty = (m.insert var ty $ fst env, snd env) hasfunc :: environ -> string -> bool hasfunc env func = m.member func $ snd env getfunctype :: environ -> string -> type getfunctype env func = snd env m.! func putfunc :: environ -> string -> type -> environ putfunc env func ty = (fst env, m.insert func ty $ snd env) vars :: environ -> m.map string type vars = fst funcs :: environ -> m.map string type funcs = snd mergeenv :: environ -> environ -> environ mergeenv env1 env2 = (m.union (fst env2) (fst env1), m.union (snd env2) (snd env1)) mergeenv' :: environ -> environ -> environ mergeenv' env1 env2 = (fst env1, m.union (snd env2) (snd env1)) data state = ok | err deriving (eq, show) checkprog :: environ -> prog -> (state, environ, [string]) checkprog env prog = case prog of group progs -> checkgroup env progs fdecl rettype name params body -> checkfdecl env rettype name params body simple simple -> checksimple env simple checksimple :: environ -> simple -> (state, environ, [string]) checksimple env simple = case simple of rexp expr -> checkexpr expr vdecl typ name expr -> checkvdecl env typ name expr return expr -> (ok, empty, []) checkexpr expr = let (t, msg) = checkrexpr env expr in if t == error (err, empty, msg) else (ok, empty, msg) checkgroup :: environ -> [prog] -> (state, environ, [string]) checkgroup env progs = (finalstate, finalenv, messages) (finalstate, finalenv, messages) = foldl checksingleprog (ok, empty, []) progs checksingleprog (s, e, msg) prog = (resstate, mergeenv e e', message) (s', e', msg') = checkprog (mergeenv' env finalenv) prog message = msg ++ msg' resstate = if s == err || s' == err err else ok checkfdecl :: environ -> type -> string -> [(type, string)] -> prog -> (state, environ, [string]) checkfdecl env rty name params body = (s, putfunc empty name funtype, msg) funtype = func rty [t | (t,_) <- params] paramenv = (m.fromlist [(x, ty) | (ty, x) <- params], m.empty) baseenv = mergeenv paramenv (putfunc env name funtype) (s, e', msg) = checkprog baseenv body checkvdecl :: environ -> type -> string -> rexp -> (state, environ, [string]) checkvdecl env ty name expr = if t == error (err, empty, msg) else if compatible t ty (ok, putvar empty name ty, msg) else (err, empty, msg ++ errmsg) (t, msg) = checkrexpr env expr errmsg = ["incompatible assignment of type: " ++ show t ++ " variable of type: " ++ show ty] checkrexpr env expr = case expr of const _-> (int, []) lexp lexp -> checklexpr env lexp call name params -> checkcall env name params checklexpr env lexp = if env `hasvar` name (getvartype env name, []) else (error, ["undefined identifier: " ++ name]) (ident name) = lexp checkcall env name params = if not $ env `hasfunc` name (error, ["undefined function: " ++ name]) else let (func retty paramsty) = getfunctype env name in if length params /= length paramsty (error, ["wrong number of arguments."]) else if , $ zipwith checkparam paramsty params (retty, []) else (error, ["wrong type argument."]) checkparam typ param = let (t, _) = checkrexpr env param in compatible t typ {- def f() -> int: return g() def g() -> int: return 1 f() -} testprog = group [fdecl int "f" [] $ group [simple $ return $ call "g" []], fdecl int "g" [] $ group [simple $ return $ const $ integer 1], simple $ rexp $ call "f" []] main = let (s,e,msg) = checkprog empty testprog if s == ok putstrln "correct!" else putstrln "error!" putstrln $ concatmap (++ "\n") msg
you're defining finalenv
in terms of foldl
, you're defining foldl
, via checkprog
, in terms of finalenv
seems algorithm wrong.
Comments
Post a Comment