{-# OPTIONS -fglasgow-exts #-}
module FreeNames (tests) where
{-
This example illustrates the kind of traversals that naturally show up
in language processing. That is, the free names (say, variables) are
derived for a given program fragment. To this end, we need several
worker functions that extract declaring and referencing occurrences
from given program fragments; see "decsExpr", "decsEqua",
etc. below. Then, we need a traversal "freeNames" that traverses over
the program fragment in a bottom-up manner so that free names from
subterms do not escape to the top when corresponding declarations are
provided. The "freeNames" algorithm uses set operations "union" and
"//" to compute sets of free names from the declared and referenced
names of the root term and free names of the immediate subterms.
Contributed by Ralf Laemmel, ralf@cwi.nl
-}
import Test.HUnit
import Data.Generics
import Data.List
data System = S [Function] deriving (Typeable, Data)
data Function = FName [Equation] deriving (Typeable, Data)
data Equation = E [Pattern] ExpressionSystemderiving (Typeable, Data)
data Pattern = PVarName
| PTermName [Pattern] deriving (Typeable, Data)
data Expression = VarName
| AppExpressionExpression
| LambdaNameExpressionderiving (Typeable, Data)type Name = String-- A little sample programsys1 = S [f1,f2]
f1 = F"f1" [e11]
f2 = F"f2" [e21,e22]
e11 = E [] (Var"id") (S [])
e21 = E [ PTerm"C" [ PVar"x" ] ] (Var"x") (S [])
e22 = E [] (Var"id") (S [])
-- Names declared in an expressiondecsExpr :: Expression -> [Name]
decsExpr (Lambdan _) = [n]
decsExpr _ = []
-- Names declared in an equationdecsEqua :: Equation -> [Name]
decsEqua (Eps _ _) = everythingunion ([] `mkQ`pvar) ps
where
pvar (PVarn) = [n]
pvar _ = []
-- Names declared in a systemdecsSyst :: System -> [Name]
decsSyst (Sl) = nub$map (\(Fn _) -> n) l-- Names referenced in an expressionrefsExpr :: Expression -> [Name]
refsExpr (Varn) = [n]
-- Names referenced in an equationrefsEqua :: Equation -> [Name]
refsEqua (Eps _ _) = everythingunion ([] `mkQ`pterm) ps
where
pterm (PTermn _) = [n]
pterm _ = []
-- Combine the above type-specific cases to obtain-- generic functions that find declared and referenced names--decsFun :: Data a =>a -> [Name]
decsFun = const [] `extQ`decsExpr`extQ`decsEqua`extQ`decsSystrefsFun :: Data a =>a -> [Name]
refsFun = const [] `extQ`refsExpr`extQ`refsEqua{-
Free name analysis: Take the union of free names obtained from the
immediate subterms (via gmapQ) and the names being referred to at the
root of the present term, but subtract all the names that are declared
at the root.
-}freeNames :: Data a =>a -> [Name]
freeNamesx = ( (refsFunx)
`union`
(nub.concat.gmapQfreeNames) x
) \\decsFunx{-
Print the free names for the sample program sys1; see module
FunDatatypes.hs. This should print the list ["id","C"] because the
"Prelude" function "id" is used in the sample program, and also the
term constructor "C" occurs in a pattern; we assume a language without
explicit datatype declarations ;-)
-}tests = freeNamessys1~=?outputoutput = ["id","C"]