{- BNF Converter: Happy Generator Copyright (C) 2004 Author: Markus Forberg, Aarne Ranta This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA -} module BNFC.Backend.HaskellProfile.CFtoHappyProfile (cf2HappyProfileS) where import Data.List (intersperse) import BNFC.CF import BNFC.Utils (for) -- Type declarations type Rules = [(NonTerminal,[(Pattern,Action)])] type Pattern = String type Action = String type MetaVar = String -- default naming tokenName = "Token" -- The main function, that given a CF and a CFCat to parse according to, -- generates a happy module. cf2HappyProfileS :: String -> String -> String -> CFP -> String cf2HappyProfileS = cf2Happy cf2Happy :: String -> String -> String -> CFP -> String cf2Happy name absName lexName cf = unlines [header name absName lexName, declarations (allEntryPoints cf), tokens (cfgSymbols cf ++ reservedWords cf), specialToks cf, delimiter, specialRules cf, prRules (rulesForHappy cf), finalize cf] -- construct the header. header :: String -> String -> String -> String header modName _ lexName = unlines [ "-- This Happy file was machine-generated by the BNF converter" , "{" , "module " ++ modName ++ " where" , "import Trees" , "import " ++ lexName , "}" ] -- The declarations of a happy file. declarations :: [Cat] -> String declarations ns = unlines [ generateP ns , "%monad { Either String } { (>>=) } { return }" , "%tokentype { " ++ tokenName ++ " }" ] where generateP [] = [] generateP (n:ns) = concat [ "%name p", n', " ", n', "\n", generateP ns ] where n' = identCat n -- The useless delimiter symbol. delimiter :: String delimiter = "\n%%\n" -- Generate the list of tokens and their identifiers. tokens :: [String] -> String tokens toks = "%token \n" ++ prTokens toks where prTokens [] = [] prTokens (t:tk) = " " ++ convert t ++ " { " ++ oneTok t ++ " }\n" ++ prTokens tk oneTok t = "PT _ (TS " ++ show t ++ ")" -- Happy doesn't allow characters such as åäö to occur in the happy file. This -- is however not a restriction, just a naming paradigm in the happy source file. convert :: String -> String convert "\\" = "'" ++ "\\\\" ++ "'" convert xs = "'" ++ escape xs ++ "'" where escape [] = [] escape ('\'':xs) = '\\':'\'':escape xs escape (x:xs) = x:escape xs rulesForHappy :: CFP -> Rules rulesForHappy cf = for (ruleGroupsP cf) $ \ (cat,rules) -> (cat, constructRule cf rules cat) -- For every non-terminal, we construct a set of rules. A rule is a sequence of -- terminals and non-terminals, and an action to be performed. constructRule :: CFP -> [RuleP] -> NonTerminal -> [(Pattern,Action)] constructRule cf rules nt = [ (p, generateAction nt (funRule r) m) | r <- rules , let (p, m) = generatePatterns cf r ] -- Generates a string containing the semantic action. -- An action can for example be: Sum $1 $2, that is, construct an AST -- with the constructor Sum applied to the two metavariables $1 and $2. generateAction :: NonTerminal -> FunP -> [MetaVar] -> Action generateAction _ (_,(h,p)) ms = unwords (if isCoercion h then args else fun ++ mss) where fun = ["mkFunTree",show h,show p] mss = ["["] ++ intersperse "," ms ++ ["]"] args = intersperse "," ms -- Generate patterns and a set of metavariables indicating -- where in the pattern the non-terminal generatePatterns :: CFP -> RuleP -> (Pattern,[MetaVar]) generatePatterns _cf r = case rhsRule r of [] -> ("{- empty -}",[]) its -> (unwords (map mkIt its), metas its) where mkIt i = case i of Left c -> identCat c Right s -> convert s metas its = [ ('$': show i) | (i, Left _c) <- zip [1 ::Int ..] its ] -- We have now constructed the patterns and actions, -- so the only thing left is to merge them into one string. prRules :: Rules -> String prRules = unlines . map prOne where prOne (_,[]) = [] -- nt has only internal use prOne (nt,(p,a):ls) = unwords [nt', "::", "{", "CFTree", "}\n" ++ nt', ":" , p, "{", a, "}", '\n' : pr ls] ++ "\n" where nt' = identCat nt pr [] = [] pr ((p,a):ls) = unlines [unwords [" |", p, "{", a , "}"]] ++ pr ls -- Finally, some haskell code. finalize :: CFP -> String finalize _ = unlines [ "{", "\nhappyError :: [" ++ tokenName ++ "] -> Either String a", "happyError ts = Left $", " \"syntax error at \" ++ tokenPos ts ++ " ++ "if null ts then [] else " ++ "(\" before \" ++ " ++ "unwords (map prToken (take 4 ts)))", "\nmyLexer = tokens", "}" ] -- aarne's modifs 8/1/2002: -- Markus's modifs 11/02/2002 -- GF literals specialToks :: CFP -> String specialToks cf = unlines . (`map` literals cf) $ \case "Ident" -> "L_ident { PT _ (TV $$) }" "String" -> "L_quoted { PT _ (TL $$) }" "Integer" -> "L_integ { PT _ (TI $$) }" "Double" -> "L_doubl { PT _ (TD $$) }" "Char" -> "L_charac { PT _ (TC $$) }" own -> "L_" ++ own ++ " { PT _ (T_" ++ own ++ " " ++ posn ++ ") }" where posn = if isPositionCat cf own then "_" else "$$" specialRules :: CFP -> String specialRules cf = unlines . (`map` literals cf) $ \case "Ident" -> "Ident : L_ident { mkAtTree (AV (Ident $1)) }" "String" -> "String : L_quoted { mkAtTree (AS $1) }" "Integer" -> "Integer : L_integ { mkAtTree (AI ((read $1) :: Integer)) }" "Double" -> "Double : L_doubl { (read $1) :: Double }" "Char" -> "Char : L_charac { (read $1) :: Char }" own -> concat [ own , " : L_" , own , " { " , own , " (" , posn , "$1)}" ] where posn = if isPositionCat cf own then "mkPosToken " else ""