{- BNF Converter: C# Abstract Syntax Generator Copyright (C) 2006-2007 Author: Johan Broberg Modified from CFtoSTLAbs 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 -} {- ************************************************************** BNF Converter Module Description : This module generates the C# Abstract Syntax tree classes. It uses the Visitor design pattern. Author : Johan Broberg (johan@pontemonti.com) License : GPL (GNU General Public License) Created : 22 November, 2006 Modified : 21 January, 2007 by Johan Broberg ************************************************************** -} module BNFC.Backend.CSharp.CAbstoCSharpAbs (cabs2csharpabs) where import BNFC.Backend.Common.OOAbstract import BNFC.CF import BNFC.Utils((+++)) import Data.List import Data.Maybe import BNFC.Backend.CSharp.CSharpUtils --The result is one file (.cs) cabs2csharpabs :: Namespace -> CAbs -> Bool -> String cabs2csharpabs namespace cabs useWCF = unlinesInline [ "//C# Abstract Syntax Interface generated by the BNF Converter.", -- imports "using System;", if useWCF then "using System.Runtime.Serialization;" else "", "using System.Collections.Generic;", "namespace " ++ namespace ++ ".Absyn", "{", " #region Token Classes", prTokenBaseType useWCF, unlinesInlineMap (prToken namespace useWCF) (tokentypes cabs), " #endregion", " ", " #region Abstract Syntax Classes", unlinesInlineMap (prAbs namespace useWCF) abstractclasses, " ", unlinesInlineMap (prCon namespace useWCF) (flattenSignatures cabs), " ", " #region Lists", unlinesInlineMap (prList namespace) (listtypes cabs), " #endregion", " #endregion", "}" ] where -- an abstract class is a category which does not contain rules abstractclasses = [ (cat, map fst cabsrules) | (cat, cabsrules) <- signatures cabs, cat `notElem` map fst cabsrules ] -- auxiliaries prDataContract :: Bool -> [String] -> String prDataContract False _ = "" prDataContract True [] = " [DataContract]" prDataContract True funs = unlinesInline [ prDataContract True [], unlinesInline $ map prDataContract' funs ] where prDataContract' :: String -> String prDataContract' fun = " [KnownType(typeof(" ++ fun ++ "))]" prDataMember :: Bool -> String prDataMember False = "" prDataMember True = " [DataMember]" prTokenBaseType :: Bool -> String prTokenBaseType useWCF = unlinesInline [ prDataContract useWCF [], " public class TokenBaseType", " {", prDataMember useWCF, " private string str;", " ", " public TokenBaseType(string str)", " {", " this.str = str;", " }", " ", " public override string ToString()", " {", " return this.str;", " }", " }", " " ] prToken :: Namespace -> Bool -> String -> String prToken namespace useWCF name = unlinesInline [ prDataContract useWCF [], " public class " ++ name ++ " : " ++ identifier namespace "TokenBaseType", " {", " public " ++ name ++ "(string str) : base(str)", " {", " }", prAccept namespace name Nothing, prVisitor namespace [name], prEquals namespace name ["ToString()"], prHashCode namespace name ["ToString()"], " }" ] prAbs :: Namespace -> Bool -> (String, [String]) -> String prAbs namespace useWCF (cat, funs) = unlinesInline [ prDataContract useWCF funs, " public abstract class " ++ cat, " {", " public abstract R Accept(" ++ identifier namespace cat ++ ".Visitor v, A arg);", prVisitor namespace funs, " }" ] prVisitor :: Namespace -> [String] -> String prVisitor namespace funs = unlinesInline [ " " , " public interface Visitor" , " {" , unlinesInline (map prVisitFun funs) , " }" ] where prVisitFun f = " R Visit(" ++ identifier namespace f ++ " p, A arg);" prCon :: Namespace -> Bool -> (String,CSharpAbsRule) -> String prCon namespace useWCF (c,(f,cs)) = unlinesInline [ prDataContract useWCF [], " public class " ++ f ++ ext, " {", -- Instance variables unlines [prInstVar typ var | (typ,_,var,_) <- cs], prConstructor namespace (f,cs), unlinesInline [prProperty typ var prop | (typ,_,var,prop) <- cs], prEquals namespace f propnames, prHashCode namespace f propnames, -- print Accept method, override keyword needed for classes inheriting an abstract class prAccept namespace c (if isAlsoCategory f c then Nothing else Just " override"), -- if this label is also a category, we need to print the Visitor interface -- (if not, it was already printed in the abstract class) if isAlsoCategory f c then prVisitor namespace [c] else "", " }" ] where -- This handles the case where a LBNF label is the same as the category. ext = if isAlsoCategory f c then "" else " : " ++ identifier namespace (identCat $ strToCat c) propnames = [prop | (_, _, _, prop) <- cs] prInstVar typ var = unlinesInline [ " private " ++ identifier namespace (typename typ) +++ var ++ ";" ] prProperty typ var prop = unlinesInline [ " ", prDataMember useWCF, " public " ++ identifier namespace (typename typ) +++ prop, " {", " get", " {", " return this." ++ var ++ ";", " }", " set", " {", " this." ++ var ++ " = value;", " }", " }" ] -- Creates the Equals() methods prEquals :: Namespace -> Fun -> [String] -> String prEquals namespace c vars = unlinesInline [ " ", " public override bool Equals(Object obj)", " {", " if(this == obj)", " {", " return true;", " }", " if(obj is " ++ identifier namespace c ++ ")", " {", " return this.Equals((" ++ identifier namespace c ++ ")obj);", " }", " return base.Equals(obj);", " }", " ", " public bool Equals(" ++ identifier namespace c ++ " obj)", " {", " if(this == obj)", " {", " return true;", " }", " return " ++ prEqualsVars vars ++ ";", " }" ] where prEqualsVars [] = "true" prEqualsVars vs = intercalate " && " $ map equalVar vs equalVar v = "this." ++ v ++ ".Equals(obj." ++ v ++ ")" -- Creates the GetHashCode() method. prHashCode :: Namespace -> Fun -> [String] -> String prHashCode _ _ vars = unlinesInline [ " ", " public override int GetHashCode()", " {", " return " ++ prHashVars vars ++ ";", " }" ] where aPrime = 37 prHashVars [] = show aPrime prHashVars (v:vs) = foldl (\ r v -> show aPrime ++ "*" ++ "(" ++ r ++ ")+" ++ hashVar v) v vs hashVar var = "this." ++ var ++ ".GetHashCode()" prList :: Namespace -> (String,Bool) -> String prList namespace (c,_) = unlinesInline [ " public class " ++ c ++ " : List<" ++ identifier namespace (typename bas) ++ ">", " {", " }" ] where bas = drop 4 c -- drop List -- The standard Accept method for the Visitor pattern prAccept :: Namespace -> String -> Maybe String -> String prAccept namespace cat maybeOverride = unlinesInline [ " ", " public" ++ fromMaybe "" maybeOverride ++ " R Accept(" ++ identifier namespace cat ++ ".Visitor visitor, A arg)", " {", " return visitor.Visit(this, arg);", " }" ] -- The constructor assigns the parameters to the corresponding instance variables. prConstructor :: Namespace -> CSharpAbsRule -> String prConstructor namespace (f,cs) = unlinesInline [ " public " ++ f ++ "(" ++ conargs ++ ")", " {", unlinesInline [" " ++ c ++ " = " ++ p ++ ";" | (c,p) <- zip cvs pvs], " }" ] where cvs = [c | (_,_,c,_) <- cs] pvs = ["p" ++ show i | ((_,_,_,_),i) <- zip cs [1..]] conargs = intercalate ", " [identifier namespace (typename x) +++ v | ((x,_,_,_),v) <- zip cs pvs]