2 ------------------------------------------------------------------
3 -- A primop-table mangling program --
4 ------------------------------------------------------------------
12 import System ( getArgs )
13 import Maybe ( catMaybes )
15 main = getArgs >>= \args ->
16 if length args /= 1 || head args `notElem` known_args
17 then error ("usage: genprimopcode command < primops.txt > ...\n"
18 ++ " where command is one of\n"
19 ++ unlines (map (" "++) known_args)
23 let pres = parse pTop "" s
25 Left err -> do putStr "parse error at "
28 -> myseq (sanityTop p_o_specs) (
32 -> putStr (gen_data_decl p_o_specs)
35 -> putStr (gen_switch_from_attribs
37 "primOpHasSideEffects" p_o_specs)
40 -> putStr (gen_switch_from_attribs
42 "primOpOutOfLine" p_o_specs)
45 -> putStr (gen_switch_from_attribs
47 "commutableOp" p_o_specs)
50 -> putStr (gen_switch_from_attribs
52 "primOpNeedsWrapper" p_o_specs)
55 -> putStr (gen_switch_from_attribs
57 "primOpCanFail" p_o_specs)
60 -> putStr (gen_switch_from_attribs
62 "primOpStrictness" p_o_specs)
65 -> putStr (gen_switch_from_attribs
67 "primOpUsg" p_o_specs)
69 "--primop-primop-info"
70 -> putStr (gen_primop_info p_o_specs)
73 -> putStr (gen_primop_tag p_o_specs)
76 -> putStr (gen_primop_list p_o_specs)
78 "--make-haskell-wrappers"
79 -> putStr (gen_wrappers p_o_specs)
93 "--primop-primop-info",
96 "--make-haskell-wrappers"
99 ------------------------------------------------------------------
100 -- Code generators -----------------------------------------------
101 ------------------------------------------------------------------
103 gen_wrappers (Info defaults pos)
104 = "module PrelPrimopWrappers where\n"
105 ++ "import qualified PrelGHC\n"
106 ++ unlines (map f (filter (not.dodgy) pos))
108 f spec = let args = map (\n -> "a" ++ show n) [1 .. arity (ty spec)]
109 src_name = wrap (name spec)
110 in "{-# NOINLINE " ++ src_name ++ " #-}\n" ++
111 src_name ++ " " ++ unwords args
112 ++ " = (PrelGHC." ++ name spec ++ ") " ++ unwords args
113 wrap nm | isLower (head nm) = nm
114 | otherwise = "(" ++ nm ++ ")"
118 [-- C code generator can't handle these
121 -- not interested in parallel support
122 "par#", "parGlobal#", "parLocal#", "parAt#",
123 "parAtAbs#", "parAtRel#", "parAtForNow#"
127 gen_primop_list (Info defaults pos)
129 [ " [" ++ cons (head pos) ]
131 map (\pi -> " , " ++ cons pi) (tail pos)
136 gen_primop_tag (Info defaults pos)
137 = unlines (zipWith f pos [1..])
139 f i n = "tagOf_PrimOp " ++ cons i
140 ++ " = _ILIT(" ++ show n ++ ") :: FastInt"
142 gen_data_decl (Info defaults pos)
143 = let conss = map cons pos
144 in "data PrimOp\n = " ++ head conss ++ "\n"
145 ++ unlines (map (" | "++) (tail conss))
147 gen_switch_from_attribs :: String -> String -> Info -> String
148 gen_switch_from_attribs attrib_name fn_name (Info defaults pos)
149 = let defv = lookup_attrib attrib_name defaults
150 alts = catMaybes (map mkAlt pos)
152 getAltRhs (OptionFalse _) = "False"
153 getAltRhs (OptionTrue _) = "True"
154 getAltRhs (OptionString _ s) = s
157 = case lookup_attrib attrib_name (opts po) of
159 Just xx -> Just (fn_name ++ " " ++ cons po ++ " = " ++ getAltRhs xx)
161 lookup_attrib nm [] = Nothing
162 lookup_attrib nm (a:as)
163 = if get_attrib_name a == nm then Just a else lookup_attrib nm as
166 Nothing -> error ("gen_switch_from: " ++ attrib_name)
169 ++ fn_name ++ " other = " ++ getAltRhs xx ++ "\n"
171 ------------------------------------------------------------------
172 -- Create PrimOpInfo text from PrimOpSpecs -----------------------
173 ------------------------------------------------------------------
176 gen_primop_info (Info defaults pos)
177 = unlines (map mkPOItext pos)
179 mkPOItext i = mkPOI_LHS_text i ++ mkPOI_RHS_text i
182 = "primOpInfo " ++ cons i ++ " = "
189 -> "mkCompare " ++ sl_name i ++ ppType t1
193 -> "mkMonadic " ++ sl_name i ++ ppType t1
197 -> "mkDyadic " ++ sl_name i ++ ppType t1
199 -> let (argTys, resTy) = flatTys (ty i)
200 tvs = nub (tvsIn (ty i))
202 "mkGenPrimOp " ++ sl_name i ++ " "
203 ++ listify (map ppTyVar tvs) ++ " "
204 ++ listify (map ppType argTys) ++ " "
205 ++ "(" ++ ppType resTy ++ ")"
207 sl_name i = "SLIT(\"" ++ name i ++ "\") "
209 ppTyVar "a" = "alphaTyVar"
210 ppTyVar "b" = "betaTyVar"
211 ppTyVar "c" = "gammaTyVar"
212 ppTyVar "s" = "deltaTyVar"
213 ppTyVar "o" = "openAlphaTyVar"
216 ppType (TyApp "Bool" []) = "boolTy"
218 ppType (TyApp "Int#" []) = "intPrimTy"
219 ppType (TyApp "Int64#" []) = "int64PrimTy"
220 ppType (TyApp "Char#" []) = "charPrimTy"
221 ppType (TyApp "Word#" []) = "wordPrimTy"
222 ppType (TyApp "Word64#" []) = "word64PrimTy"
223 ppType (TyApp "Addr#" []) = "addrPrimTy"
224 ppType (TyApp "Float#" []) = "floatPrimTy"
225 ppType (TyApp "Double#" []) = "doublePrimTy"
226 ppType (TyApp "ByteArr#" []) = "byteArrayPrimTy"
227 ppType (TyApp "RealWorld" []) = "realWorldTy"
228 ppType (TyApp "ThreadId#" []) = "threadIdPrimTy"
229 ppType (TyApp "ForeignObj#" []) = "foreignObjPrimTy"
230 ppType (TyApp "BCO#" []) = "bcoPrimTy"
231 ppType (TyApp "Unit" []) = "unitTy" -- dodgy
234 ppType (TyVar "a") = "alphaTy"
235 ppType (TyVar "b") = "betaTy"
236 ppType (TyVar "c") = "gammaTy"
237 ppType (TyVar "s") = "deltaTy"
238 ppType (TyVar "o") = "openAlphaTy"
239 ppType (TyApp "State#" [x]) = "mkStatePrimTy " ++ ppType x
240 ppType (TyApp "MutVar#" [x,y]) = "mkMutVarPrimTy " ++ ppType x
242 ppType (TyApp "MutArr#" [x,y]) = "mkMutableArrayPrimTy " ++ ppType x
245 ppType (TyApp "MutByteArr#" [x]) = "mkMutableByteArrayPrimTy "
248 ppType (TyApp "Array#" [x]) = "mkArrayPrimTy " ++ ppType x
251 ppType (TyApp "Weak#" [x]) = "mkWeakPrimTy " ++ ppType x
252 ppType (TyApp "StablePtr#" [x]) = "mkStablePtrPrimTy " ++ ppType x
253 ppType (TyApp "StableName#" [x]) = "mkStableNamePrimTy " ++ ppType x
255 ppType (TyApp "MVar#" [x,y]) = "mkMVarPrimTy " ++ ppType x
257 ppType (TyUTup ts) = "(mkTupleTy Unboxed " ++ show (length ts)
259 ++ listify (map ppType ts) ++ ")"
261 ppType (TyF s d) = "(mkFunTy (" ++ ppType s ++ ") (" ++ ppType d ++ "))"
264 = error ("ppType: can't handle: " ++ show other ++ "\n")
266 listify :: [String] -> String
267 listify ss = "[" ++ concat (intersperse ", " ss) ++ "]"
269 flatTys (TyF t1 t2) = case flatTys t2 of (ts,t) -> (t1:ts,t)
270 flatTys other = ([],other)
272 tvsIn (TyF t1 t2) = tvsIn t1 ++ tvsIn t2
273 tvsIn (TyApp tc tys) = concatMap tvsIn tys
274 tvsIn (TyVar tv) = [tv]
275 tvsIn (TyUTup tys) = concatMap tvsIn tys
277 arity = length . fst . flatTys
280 ------------------------------------------------------------------
281 -- Abstract syntax -----------------------------------------------
282 ------------------------------------------------------------------
284 -- info for all primops; the totality of the info in primops.txt
286 = Info [Option] [PrimOpSpec] -- defaults, primops
289 -- info for one primop
291 = PrimOpSpec { cons :: String, -- PrimOp name
292 name :: String, -- name in prog text
294 cat :: Category, -- category
295 opts :: [Option] } -- default overrides
298 -- a binding of property to value
300 = OptionFalse String -- name = False
301 | OptionTrue String -- name = True
302 | OptionString String String -- name = { ... unparsed stuff ... }
305 -- categorises primops
307 = Dyadic | Monadic | Compare | GenPrimOp
315 | TyUTup [Ty] -- unboxed tuples; just a TyCon really,
316 -- but convenient like this
323 ------------------------------------------------------------------
324 -- Sanity checking -----------------------------------------------
325 ------------------------------------------------------------------
327 {- Do some simple sanity checks:
328 * all the default field names are unique
329 * for each PrimOpSpec, all override field names are unique
330 * for each PrimOpSpec, all overriden field names
331 have a corresponding default value
332 * that primop types correspond in certain ways to the
333 Category: eg if Comparison, the type must be of the form
335 Dies with "error" if there's a problem, else returns ().
338 myseqAll (():ys) x = myseqAll ys x
341 sanityTop :: Info -> ()
342 sanityTop (Info defs primops)
343 = let opt_names = map get_attrib_name defs
345 if length opt_names /= length (nub opt_names)
346 then error ("non-unique default attribute names: " ++ show opt_names ++ "\n")
347 else myseqAll (map (sanityPrimOp opt_names) primops) ()
349 sanityPrimOp def_names p
350 = let p_names = map get_attrib_name (opts p)
352 = length p_names == length (nub p_names)
353 && all (`elem` def_names) p_names
354 ty_ok = sane_ty (cat p) (ty p)
357 then error ("attribute names are non-unique or have no default in\n" ++
358 "info for primop " ++ cons p ++ "\n")
361 then error ("type of primop " ++ cons p ++ " doesn't make sense w.r.t" ++
362 " category " ++ show (cat p) ++ "\n")
365 sane_ty Compare (TyF t1 (TyF t2 td))
366 | t1 == t2 && td == TyApp "Bool" [] = True
367 sane_ty Monadic (TyF t1 td)
369 sane_ty Dyadic (TyF t1 (TyF t2 td))
370 | t1 == t2 && t2 == t2 = True
371 sane_ty GenPrimOp any_old_thing
376 get_attrib_name (OptionFalse nm) = nm
377 get_attrib_name (OptionTrue nm) = nm
378 get_attrib_name (OptionString nm _) = nm
380 ------------------------------------------------------------------
381 -- The parser ----------------------------------------------------
382 ------------------------------------------------------------------
384 -- Due to lack of proper lexing facilities, a hack to zap any
387 pTop = then4 (\_ ds ss _ -> Info ds ss)
388 pCommentAndWhitespace pDefaults (many pPrimOpSpec)
389 (lit "thats_all_folks")
391 pDefaults :: Parser [Option]
392 pDefaults = then2 sel22 (lit "defaults") (many pOption)
394 pOption :: Parser Option
397 then3 (\nm eq ff -> OptionFalse nm) pName (lit "=") (lit "False"),
398 then3 (\nm eq tt -> OptionTrue nm) pName (lit "=") (lit "True"),
399 then3 (\nm eq zz -> OptionString nm zz)
400 pName (lit "=") pStuffBetweenBraces
403 pPrimOpSpec :: Parser PrimOpSpec
405 = then6 (\_ c n k t o -> PrimOpSpec { cons = c, name = n, ty = t,
406 cat = k, opts = o } )
407 (lit "primop") pConstructor stringLiteral
408 pCategory pType pOptions
410 pOptions :: Parser [Option]
411 pOptions = optdef [] (then2 sel22 (lit "with") (many pOption))
413 pCategory :: Parser Category
416 apply (const Dyadic) (lit "Dyadic"),
417 apply (const Monadic) (lit "Monadic"),
418 apply (const Compare) (lit "Compare"),
419 apply (const GenPrimOp) (lit "GenPrimOp")
423 = lexeme (then3 sel23
424 (char '{') (many (satisfy (not . (== '}'))))
432 pType = then2 (\t maybe_tt -> case maybe_tt of
436 (opt (then2 sel22 (lit "->") pType))
439 paT = alts [ then2 TyApp pTycon (many ppT),
441 then3 sel23 (lit "(") pType (lit ")"),
445 -- the magic bit in the middle is: T (,T)* so to speak
447 = then3 (\ _ ts _ -> TyUTup ts)
449 (then2 (:) pType (many (then2 sel22 (lit ",") pType)))
453 ppT = alts [apply TyVar pTyvar,
454 apply (\tc -> TyApp tc []) pTycon
457 pTyvar = sat (`notElem` ["primop","with"]) pName
458 pTycon = pConstructor
459 pName = lexeme (then2 (:) lower (many isIdChar))
460 pConstructor = lexeme (then2 (:) upper (many isIdChar))
462 isIdChar = satisfy (`elem` idChars)
463 idChars = ['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9'] ++ "#_"
471 ------------------------------------------------------------------
472 -- Helpful additions to Daan's parser stuff ----------------------
473 ------------------------------------------------------------------
476 alts (p1:p2:ps) = (try p1) <|> alts (p2:ps)
479 = do x1 <- p1 ; x2 <- p2 ; return (f x1 x2)
481 = do x1 <- p1 ; x2 <- p2 ; x3 <- p3 ; return (f x1 x2 x3)
483 = do x1 <- p1 ; x2 <- p2 ; x3 <- p3 ; x4 <- p4 ; return (f x1 x2 x3 x4)
484 then5 f p1 p2 p3 p4 p5
485 = do x1 <- p1 ; x2 <- p2 ; x3 <- p3 ; x4 <- p4 ; x5 <- p5
486 return (f x1 x2 x3 x4 x5)
487 then6 f p1 p2 p3 p4 p5 p6
488 = do x1 <- p1 ; x2 <- p2 ; x3 <- p3 ; x4 <- p4 ; x5 <- p5 ; x6 <- p6
489 return (f x1 x2 x3 x4 x5 x6)
491 = (do x <- p; return (Just x)) <|> return Nothing
493 = (do x <- p; return x) <|> return d
500 -- Hacks for zapping whitespace and comments, unfortunately needed
501 -- because Daan won't let us have a lexer before the parser :-(
502 lexeme :: Parser p -> Parser p
503 lexeme p = then2 sel12 p pCommentAndWhitespace
505 lit :: String -> Parser ()
506 lit s = apply (const ()) (lexeme (string s))
508 pCommentAndWhitespace :: Parser ()
509 pCommentAndWhitespace
510 = apply (const ()) (many (alts [pLineComment,
511 apply (const ()) (satisfy isSpace)]))
515 pLineComment :: Parser ()
517 = try (then3 (\_ _ _ -> ()) (string "--") (many (satisfy (/= '\n'))) (char '\n'))
519 stringLiteral :: Parser String
520 stringLiteral = lexeme (
521 do { between (char '"')
522 (char '"' <?> "end of string")
525 <?> "literal string")
529 ------------------------------------------------------------------
531 ------------------------------------------------------------------