| 1 | -- | Generate declarations for optional patterns, ? and #?. |
|---|
| 2 | -- (Unfortunally we must place this function here since both variations |
|---|
| 3 | -- of transformations of optional patterns should be able to call it...) |
|---|
| 4 | mkOptDecl :: SrcLoc -> Bool -> MFunMetaInfo -> Tr MFunMetaInfo |
|---|
| 5 | mkOptDecl s greedy nvt@(_, vs, t) = do |
|---|
| 6 | -- Un nome, s'il vouz plaît. |
|---|
| 7 | n <- genMatchName |
|---|
| 8 | let -- Generate a generator for matching the subpattern |
|---|
| 9 | (g, val) = mkGenExp s nvt -- (harp_valX, (foo, bar, ...)) <- harp_matchY |
|---|
| 10 | -- ... and apply a Just to its value |
|---|
| 11 | ret1 = metaReturn $ tuple -- return (Just harp_val1, (foo, bar, ...)) |
|---|
| 12 | [app (var just_name) |
|---|
| 13 | (var val), varTuple vs] |
|---|
| 14 | -- ... and do those two steps in a do-expression |
|---|
| 15 | exp1 = doE [g, qualStmt ret1] -- do .... |
|---|
| 16 | -- For the non-matching branch, all the variables should be empty |
|---|
| 17 | ids = map (const idFun) vs -- (id, id, ...) |
|---|
| 18 | -- ... and the value should be Nothing. |
|---|
| 19 | ret2 = metaReturn $ tuple -- return (Nothing, (id, id, ...)) |
|---|
| 20 | [var nothing_name, tuple ids] -- i.e. no vars were bound |
|---|
| 21 | -- The order of the arguments to the choice (+++) operator |
|---|
| 22 | -- is determined by greed... |
|---|
| 23 | mc = if greedy |
|---|
| 24 | then metaChoice -- standard order |
|---|
| 25 | else (flip metaChoice) -- reversed order |
|---|
| 26 | -- ... and then apply it to the branches. |
|---|
| 27 | rhs = (paren exp1) `mc` -- (do ....) +++ |
|---|
| 28 | (paren ret2) -- (return (Nothing, .....)) |
|---|
| 29 | -- Finally we create a declaration for this function and |
|---|
| 30 | -- add it to the store. |
|---|
| 31 | pushDecl $ nameBind s n rhs -- harp_matchZ = (do ....) +++ (return ....) |
|---|
| 32 | -- The type of the returned value will be Maybe the type |
|---|
| 33 | -- of the value of the subpattern. |
|---|
| 34 | return (n, vs, M t) |
|---|
| 35 | |
|---|
| 36 | -- | Generate declarations for star patterns, * and #* |
|---|
| 37 | -- (Unfortunally we must place this function here since both variations |
|---|
| 38 | -- of transformations of repeating patterns should be able to call it...) |
|---|
| 39 | mkStarDecl :: SrcLoc -> Bool -> MFunMetaInfo -> Tr MFunMetaInfo |
|---|
| 40 | mkStarDecl s greedy (mname, vs, t) = do |
|---|
| 41 | -- Ett namn, tack! |
|---|
| 42 | n <- genMatchName |
|---|
| 43 | let -- Create a generator that matches the subpattern |
|---|
| 44 | -- many times, either greedily or non-greedily |
|---|
| 45 | g = mkManyGen s greedy mname |
|---|
| 46 | -- ... and unzip the result, choosing the proper unzip |
|---|
| 47 | -- function depending on the number of variables returned. |
|---|
| 48 | metaUnzipK = mkMetaUnzip s (length vs) |
|---|
| 49 | -- ... first unzip values from variables |
|---|
| 50 | dec1 = patBind s (pvarTuple [valname, varsname]) |
|---|
| 51 | (metaUnzip $ var valsvarsname) |
|---|
| 52 | -- ... and then unzip the variables |
|---|
| 53 | dec2 = patBind s (pvarTuple vs) |
|---|
| 54 | (metaUnzipK $ var varsname) |
|---|
| 55 | -- ... fold all the values for variables |
|---|
| 56 | retExps = map ((app foldCompFun) . var) vs |
|---|
| 57 | -- ... and return value and variables |
|---|
| 58 | ret = metaReturn $ tuple $ |
|---|
| 59 | [var valname, tuple retExps] |
|---|
| 60 | -- Finally we need to generate a function that does all this, |
|---|
| 61 | -- using a let-statement for the non-monadic stuff and a |
|---|
| 62 | -- do-expression to wrap it all in. |
|---|
| 63 | pushDecl $ nameBind s n $ |
|---|
| 64 | doE [g, letStmt [dec1, dec2], qualStmt ret] |
|---|
| 65 | -- The type of the returned value is a list ([]) of the |
|---|
| 66 | -- type of the subpattern. |
|---|
| 67 | return (n, vs, L t) |
|---|
| 68 | |
|---|
| 69 | -- | Generate declarations for plus patterns, + and #+ |
|---|
| 70 | -- (Unfortunally we must place this function here since both variations |
|---|
| 71 | -- of transformations of non-empty repeating patterns should be able to call it...) |
|---|
| 72 | mkPlusDecl :: SrcLoc -> Bool -> MFunMetaInfo -> Tr MFunMetaInfo |
|---|
| 73 | mkPlusDecl s greedy nvt@(mname, vs, t) = do |
|---|
| 74 | -- and now I've run out of languages... |
|---|
| 75 | n <- genMatchName |
|---|
| 76 | let k = length vs |
|---|
| 77 | -- First we want a generator to match the |
|---|
| 78 | -- subpattern exactly one time |
|---|
| 79 | (g1, val1) = mkGenExp s nvt -- (harp_valX, (foo, ...)) <- harpMatchY |
|---|
| 80 | -- ... and then one that matches it many times. |
|---|
| 81 | g2 = mkManyGen s greedy mname -- harp_vvs <- manyMatch harpMatchY |
|---|
| 82 | -- ... we want to unzip the result, using |
|---|
| 83 | -- the proper unzip function |
|---|
| 84 | metaUnzipK = mkMetaUnzip s k |
|---|
| 85 | -- ... first unzip values from variables |
|---|
| 86 | dec1 = patBind s -- (harp_vals, harp_vars) = unzip harp_vvs |
|---|
| 87 | (pvarTuple [valsname, varsname]) |
|---|
| 88 | (metaUnzip $ var valsvarsname) |
|---|
| 89 | -- .. now we need new fresh names for variables |
|---|
| 90 | -- since the ordinary ones are already taken. |
|---|
| 91 | vlvars = genNames "harp_vl" k |
|---|
| 92 | -- ... and then we can unzip the variables |
|---|
| 93 | dec2 = patBind s (pvarTuple vlvars) -- (harp_vl1, ...) = unzipK harp_vars |
|---|
| 94 | (metaUnzipK $ var varsname) |
|---|
| 95 | -- .. and do the unzipping in a let-statement |
|---|
| 96 | letSt = letStmt [dec1, dec2] |
|---|
| 97 | -- ... fold variables from the many-match, |
|---|
| 98 | -- prepending the variables from the single match |
|---|
| 99 | retExps = map mkRetFormat $ zip vs vlvars -- foo . (foldComp harp_vl1), ... |
|---|
| 100 | -- ... prepend values from the single match to |
|---|
| 101 | -- those of the many-match. |
|---|
| 102 | retVal = (var val1) `metaCons` |
|---|
| 103 | (var valsname) -- harp_valX : harp_vals |
|---|
| 104 | -- ... return all values and variables |
|---|
| 105 | ret = metaReturn $ tuple $ -- return (harp_valX:harpVals, |
|---|
| 106 | [retVal, tuple retExps] -- (foo . (...), ...)) |
|---|
| 107 | -- ... and wrap all of it in a do-expression. |
|---|
| 108 | rhs = doE [g1, g2, letSt, qualStmt ret] |
|---|
| 109 | -- Finally we create a declaration for this function and |
|---|
| 110 | -- add it to the store. |
|---|
| 111 | pushDecl $ nameBind s n rhs |
|---|
| 112 | -- The type of the returned value is a list ([]) of the |
|---|
| 113 | -- type of the subpattern. |
|---|
| 114 | return (n, vs, L t) |
|---|
| 115 | |
|---|
| 116 | where mkRetFormat :: (Name, Name) -> Exp |
|---|
| 117 | mkRetFormat (v, vl) = |
|---|
| 118 | -- Prepend variables using function composition. |
|---|
| 119 | (var v) `metaComp` |
|---|
| 120 | (paren $ (app foldCompFun) $ var vl) |
|---|
| 121 | |
|---|
| 122 | |
|---|
| 123 | -------------------------------------------------------------------------- |
|---|
| 124 | -- HaRP-specific functions and ids |
|---|
| 125 | |
|---|
| 126 | -- | Functions and ids from the @Match@ module, |
|---|
| 127 | -- used in the generated matching functions |
|---|
| 128 | runMatchFun, baseMatchFun, manyMatchFun, gManyMatchFun :: Exp |
|---|
| 129 | runMatchFun = match_qual runMatch_name |
|---|
| 130 | baseMatchFun = match_qual baseMatch_name |
|---|
| 131 | manyMatchFun = match_qual manyMatch_name |
|---|
| 132 | gManyMatchFun = match_qual gManyMatch_name |
|---|
| 133 | |
|---|
| 134 | runMatch_name, baseMatch_name, manyMatch_name, gManyMatch_name :: Name |
|---|
| 135 | runMatch_name = Ident "runMatch" |
|---|
| 136 | baseMatch_name = Ident "baseMatch" |
|---|
| 137 | manyMatch_name = Ident "manyMatch" |
|---|
| 138 | gManyMatch_name = Ident "gManyMatch" |
|---|
| 139 | |
|---|
| 140 | match_mod, match_qual_mod :: ModuleName |
|---|
| 141 | match_mod = ModuleName "Harp.Match" |
|---|
| 142 | match_qual_mod = ModuleName "HaRPMatch" |
|---|
| 143 | |
|---|
| 144 | match_qual :: Name -> Exp |
|---|
| 145 | match_qual = qvar match_qual_mod |
|---|
| 146 | |
|---|
| 147 | choiceOp :: QOp |
|---|
| 148 | choiceOp = QVarOp $ Qual match_qual_mod choice |
|---|
| 149 | |
|---|
| 150 | appendOp :: QOp |
|---|
| 151 | appendOp = QVarOp $ UnQual append |
|---|
| 152 | |
|---|
| 153 | -- foldComp = foldl (.) id, i.e. fold by composing |
|---|
| 154 | foldCompFun :: Exp |
|---|
| 155 | foldCompFun = match_qual $ Ident "foldComp" |
|---|
| 156 | |
|---|
| 157 | mkMetaUnzip :: SrcLoc -> Int -> Exp -> Exp |
|---|
| 158 | mkMetaUnzip s k | k <= 7 = let n = "unzip" ++ show k |
|---|
| 159 | in (\e -> matchFunction n [e]) |
|---|
| 160 | | otherwise = |
|---|
| 161 | let vs = genNames "x" k |
|---|
| 162 | lvs = genNames "xs" k |
|---|
| 163 | uz = name $ "unzip" ++ show k |
|---|
| 164 | ys = name "ys" |
|---|
| 165 | xs = name "xs" |
|---|
| 166 | alt1 = alt s peList $ tuple $ replicate k eList -- [] -> ([], [], ...) |
|---|
| 167 | pat2 = (pvarTuple vs) `metaPCons` (pvar xs) -- (x1, x2, ...) |
|---|
| 168 | ret2 = tuple $ map appCons $ zip vs lvs -- (x1:xs1, x2:xs2, ...) |
|---|
| 169 | rhs2 = app (var uz) (var xs) -- unzipK xs |
|---|
| 170 | dec2 = patBind s (pvarTuple lvs) rhs2 -- (xs1, xs2, ...) = unzipK xs |
|---|
| 171 | exp2 = letE [dec2] ret2 |
|---|
| 172 | alt2 = alt s pat2 exp2 |
|---|
| 173 | topexp = lamE s [pvar ys] $ caseE (var ys) [alt1, alt2] |
|---|
| 174 | topbind = nameBind s uz topexp |
|---|
| 175 | in app (paren $ letE [topbind] (var uz)) |
|---|
| 176 | where appCons :: (Name, Name) -> Exp |
|---|
| 177 | appCons (x, xs) = metaCons (var x) (var xs) |
|---|
| 178 | |
|---|
| 179 | matchFunction :: String -> [Exp] -> Exp |
|---|
| 180 | matchFunction s es = mf s (reverse es) |
|---|
| 181 | where mf s [] = match_qual $ Ident s |
|---|
| 182 | mf s (e:es) = app (mf s es) e |
|---|
| 183 | |
|---|
| 184 | -- | Some 'magic' gensym-like functions, and functions |
|---|
| 185 | -- with related functionality. |
|---|
| 186 | retname :: Name |
|---|
| 187 | retname = name "harp_ret" |
|---|
| 188 | |
|---|
| 189 | varsname :: Name |
|---|
| 190 | varsname = name "harp_vars" |
|---|
| 191 | |
|---|
| 192 | valname :: Name |
|---|
| 193 | valname = name "harp_val" |
|---|
| 194 | |
|---|
| 195 | valsname :: Name |
|---|
| 196 | valsname = name "harp_vals" |
|---|
| 197 | |
|---|
| 198 | valsvarsname :: Name |
|---|
| 199 | valsvarsname = name "harp_vvs" |
|---|
| 200 | |
|---|
| 201 | mkValName :: Int -> Name |
|---|
| 202 | mkValName k = name $ "harp_val" ++ show k |
|---|
| 203 | |
|---|
| 204 | extendVar :: Name -> String -> Name |
|---|
| 205 | extendVar (Ident n) s = Ident $ n ++ s |
|---|
| 206 | extendVar n _ = n |
|---|
| 207 | |
|---|
| 208 | xNameParts :: XName -> (Maybe String, String) |
|---|
| 209 | xNameParts n = case n of |
|---|
| 210 | XName s -> (Nothing, s) |
|---|
| 211 | XDomName d s -> (Just d, s) |
|---|
| 212 | |
|---|
| 213 | --------------------------------------------------------- |
|---|
| 214 | -- meta-level functions, i.e. functions that represent functions, |
|---|
| 215 | -- and that take arguments representing arguments... whew! |
|---|
| 216 | |
|---|
| 217 | metaReturn, metaConst, metaMap, metaUnzip :: Exp -> Exp |
|---|
| 218 | metaReturn e = metaFunction "return" [e] |
|---|
| 219 | metaConst e = metaFunction "const" [e] |
|---|
| 220 | metaMap e = metaFunction "map" [e] |
|---|
| 221 | metaUnzip e = metaFunction "unzip" [e] |
|---|
| 222 | |
|---|
| 223 | metaEither, metaMaybe :: Exp -> Exp -> Exp |
|---|
| 224 | metaEither e1 e2 = metaFunction "either" [e1,e2] |
|---|
| 225 | metaMaybe e1 e2 = metaFunction "maybe" [e1,e2] |
|---|
| 226 | |
|---|
| 227 | metaConcat :: [Exp] -> Exp |
|---|
| 228 | metaConcat es = metaFunction "concat" [listE es] |
|---|
| 229 | |
|---|
| 230 | metaAppend :: Exp -> Exp -> Exp |
|---|
| 231 | metaAppend l1 l2 = infixApp l1 appendOp l2 |
|---|
| 232 | |
|---|
| 233 | -- the +++ choice operator |
|---|
| 234 | metaChoice :: Exp -> Exp -> Exp |
|---|
| 235 | metaChoice e1 e2 = infixApp e1 choiceOp e2 |
|---|
| 236 | |
|---|
| 237 | metaPCons :: Pat -> Pat -> Pat |
|---|
| 238 | metaPCons p1 p2 = PInfixApp p1 cons p2 |
|---|
| 239 | |
|---|
| 240 | metaCons, metaComp :: Exp -> Exp -> Exp |
|---|
| 241 | metaCons e1 e2 = infixApp e1 (QConOp cons) e2 |
|---|
| 242 | metaComp e1 e2 = infixApp e1 (op fcomp) e2 |
|---|
| 243 | |
|---|
| 244 | metaPJust :: Pat -> Pat |
|---|
| 245 | metaPJust p = pApp just_name [p] |
|---|
| 246 | |
|---|
| 247 | metaPNothing :: Pat |
|---|
| 248 | metaPNothing = pvar nothing_name |
|---|
| 249 | |
|---|
| 250 | metaPMkMaybe :: Maybe Pat -> Pat |
|---|
| 251 | metaPMkMaybe mp = case mp of |
|---|
| 252 | Nothing -> metaPNothing |
|---|
| 253 | Just p -> pParen $ metaPJust p |
|---|
| 254 | |
|---|
| 255 | metaJust :: Exp -> Exp |
|---|
| 256 | metaJust e = app (var just_name) e |
|---|
| 257 | |
|---|
| 258 | metaNothing :: Exp |
|---|
| 259 | metaNothing = var nothing_name |
|---|
| 260 | |
|---|
| 261 | metaMkMaybe :: Maybe Exp -> Exp |
|---|
| 262 | metaMkMaybe me = case me of |
|---|
| 263 | Nothing -> metaNothing |
|---|
| 264 | Just e -> paren $ metaJust e |
|---|
| 265 | |
|---|
| 266 | --------------------------------------------------- |
|---|
| 267 | -- some other useful functions at abstract level |
|---|
| 268 | consFun, idFun :: Exp |
|---|
| 269 | consFun = Con cons |
|---|
| 270 | idFun = function "id" |
|---|
| 271 | |
|---|
| 272 | cons :: QName |
|---|
| 273 | cons = Special Cons |
|---|
| 274 | |
|---|
| 275 | fcomp, choice, append :: Name |
|---|
| 276 | fcomp = Symbol "." |
|---|
| 277 | choice = Symbol "+++" |
|---|
| 278 | append = Symbol "++" |
|---|
| 279 | |
|---|
| 280 | just_name, nothing_name, left_name, right_name :: Name |
|---|
| 281 | just_name = Ident "Just" |
|---|
| 282 | nothing_name = Ident "Nothing" |
|---|
| 283 | left_name = Ident "Left" |
|---|
| 284 | right_name = Ident "Right" |
|---|
| 285 | |
|---|
| 286 | ------------------------------------------------------------------------ |
|---|
| 287 | -- Help functions for meta programming xml |
|---|
| 288 | |
|---|
| 289 | {- No longer used. |
|---|
| 290 | hsx_data_mod :: ModuleName |
|---|
| 291 | hsx_data_mod = ModuleName "HSP.Data" |
|---|
| 292 | |
|---|
| 293 | -- Also no longer used, literal PCDATA should be considered a string. |
|---|
| 294 | -- | Create an xml PCDATA value |
|---|
| 295 | metaMkPcdata :: String -> Exp |
|---|
| 296 | metaMkPcdata s = metaFunction "pcdata" [strE s] |
|---|
| 297 | -} |
|---|
| 298 | |
|---|
| 299 | -- | Create an xml tag, given its domain, name, attributes and |
|---|
| 300 | -- children. |
|---|
| 301 | metaGenElement :: XName -> [Exp] -> Maybe Exp -> [Exp] -> Exp |
|---|
| 302 | metaGenElement name ats mat cs = |
|---|
| 303 | let (d,n) = xNameParts name |
|---|
| 304 | ne = tuple [metaMkMaybe $ fmap strE d, strE n] |
|---|
| 305 | m = maybe id (\x y -> paren $ y `metaAppend` (metaMap $ metaAsAttr x)) mat |
|---|
| 306 | attrs = m $ listE $ map metaAsAttr ats |
|---|
| 307 | in metaFunction "genElement" [ne, attrs, listE cs] |
|---|
| 308 | |
|---|
| 309 | -- | Create an empty xml tag, given its domain, name and attributes. |
|---|
| 310 | metaGenEElement :: XName -> [Exp] -> Maybe Exp -> Exp |
|---|
| 311 | metaGenEElement name ats mat = |
|---|
| 312 | let (d,n) = xNameParts name |
|---|
| 313 | ne = tuple [metaMkMaybe $ fmap strE d, strE n] |
|---|
| 314 | m = maybe id (\x y -> paren $ y `metaAppend` (metaMap $ metaAsAttr x)) mat |
|---|
| 315 | attrs = m $ listE $ map metaAsAttr ats |
|---|
| 316 | in metaFunction "genEElement" [ne, attrs] |
|---|
| 317 | |
|---|
| 318 | -- | Create an attribute by applying the overloaded @asAttr@ |
|---|
| 319 | metaAsAttr :: Exp -> Exp |
|---|
| 320 | metaAsAttr e = metaFunction "asAttr" [e] |
|---|
| 321 | |
|---|
| 322 | -- | Create a property from an attribute and a value. |
|---|
| 323 | metaAssign :: Exp -> Exp -> Exp |
|---|
| 324 | metaAssign e1 e2 = infixApp e1 assignOp e2 |
|---|
| 325 | where assignOp = QVarOp $ UnQual $ Symbol ":=" |
|---|
| 326 | |
|---|
| 327 | -- | Make xml out of some expression by applying the overloaded function |
|---|
| 328 | -- @asChild@. |
|---|
| 329 | metaAsChild :: Exp -> Exp |
|---|
| 330 | metaAsChild e = metaFunction "asChild" [paren e] |
|---|
| 331 | |
|---|
| 332 | |
|---|
| 333 | -- TODO: We need to fix the stuff below so pattern matching on XML could also be overloaded. |
|---|
| 334 | -- Right now it only works on HSP XML, or anything that is syntactically identical to it. |
|---|
| 335 | |
|---|
| 336 | -- | Lookup an attribute in the set of attributes. |
|---|
| 337 | metaExtract :: XName -> Name -> Exp |
|---|
| 338 | metaExtract name attrs = |
|---|
| 339 | let (d,n) = xNameParts name |
|---|
| 340 | np = tuple [metaMkMaybe $ fmap strE d, strE n] |
|---|
| 341 | in metaFunction "extract" [np, var attrs] |
|---|
| 342 | |
|---|
| 343 | -- | Generate a pattern under the Tag data constructor. |
|---|
| 344 | metaTag :: (Maybe String) -> String -> Pat -> Pat -> Pat |
|---|
| 345 | metaTag dom name ats cpat = |
|---|
| 346 | let d = metaPMkMaybe $ fmap strP dom |
|---|
| 347 | n = pTuple [d, strP name] |
|---|
| 348 | in metaConPat "Element" [n, ats, cpat] |
|---|
| 349 | |
|---|
| 350 | -- | Generate a pattern under the PCDATA data constructor. |
|---|
| 351 | metaPcdata :: String -> Pat |
|---|
| 352 | metaPcdata s = metaConPat "CDATA" [strP s] |
|---|
| 353 | |
|---|
| 354 | metaMkName :: XName -> Exp |
|---|
| 355 | metaMkName n = case n of |
|---|
| 356 | XName s -> strE s |
|---|
| 357 | XDomName d s -> tuple [strE d, strE s] |
|---|