{- 
   **************************************************************
    BNF Converter Module

    Description   : This module generates the Skeleton Function
                    using Appel's method, not the Visitor Pattern.

    Author        : Michael Pellauer (pellauer@cs.chalmers.se)

    License       : GPL (GNU General Public License)

    Created       : 29 April, 2003                           

    Modified      : 2 September, 2003                          

   
   ************************************************************** 
-}
module CFtoJavaSkeleton (cf2JavaSkeleton) where

import CF
import Utils ((+++), (++++))
import NamedVariables
import Data.List
import Data.Char(toLower)


cf2JavaSkeleton :: String -> String -> CF -> String
cf2JavaSkeleton packageBase packageAbsyn cf = 
  unlines [
    header,
    unlines (map (prData packageAbsyn) groups),
    footer]
  where
    groups = (fixCoercions (ruleGroups cf))
    header = unlines [
      "package" +++ packageBase ++ ";",
      "import" +++ packageAbsyn ++ ".*;",
      "/*** BNFC-Generated Skeleton function. ***/",
      "/* You will probably want to save this in a new file.",
      "   Then do two search-and-replaces.",
      "   First replace \"skel\" with a real funciton name.",
      "   Then replace Object with a real return type. */",
      "",
      "public class Skeleton",
      "{"
      ]
    footer = unlines [ --later only include used categories
      "  public static Object skel(Integer i) { return null; }",
      "  public static Object skel(Double d) { return null; }",
      "  public static Object skel(String s) { return null; }",
      "}"
      ]

--Traverses a category based on its type.      
prData :: String -> (Cat, [Rule]) -> String
prData packageAbsyn (cat, rules) = 
 if isList cat
 then unlines
 [
  "  public static Object skel(" ++ packageAbsyn ++ "." ++ identCat (normCat cat) +++ "foo)",
  "  {",
  prList cat rules,
  "  }"
 ]
 else unlines --not a list
 [
  "  public static Object skel(" ++ packageAbsyn ++ "." ++ identCat (normCat cat) +++ "foo)",
  "  {",
  unlines (map (prRule packageAbsyn) rules),
  "    return null;",
  "  }"
 ]

--traverses a standard rule
prRule :: String -> Rule -> String
prRule packageAbsyn (Rule fun c cats) | not (isCoercion fun) = unlines
  [
   "    if (foo instanceof" +++ packageAbsyn ++ "." ++ fun ++ ")",
   "    {",
   "       " ++ packageAbsyn ++ "." ++ fun +++ fnm +++ "= ("
     ++ packageAbsyn ++ "." ++ fun ++ ") foo;",
   "",
   "       /* Code For " ++ fun ++ " Goes Here */",
   "",
   cats',
   "       return null;",
   "    }"
  ]
   where
    cats' = if allTerms cats
        then ""
    	else (unlines (map (prCat fnm) (fixOnes (numVars [] cats))))
    allTerms [] = True
    allTerms ((Left z):zs) = False
    allTerms (z:zs) = allTerms zs
    fnm = map toLower fun
prRule nm _ = ""

--This traverses list rules.
prList :: Cat -> [Rule] -> String
prList c rules = unlines
  [
   "    if (foo == null)",
   "    {",
   "       /* Optional End of List Code Goes Here */",
   "       return null;",
   "    }",
   "    else",
   "    {",
   "      /* Optional List Member Code Goes Here */",
   "      skel(foo." ++ c' ++ "_);",
   "      skel(foo." ++ c'' ++ "_);",
   "      return null;",
   "    }"
  ]
 where
    c' = map toLower (identCat (normCatOfList c))
    c'' = map toLower (identCat c)

--This traverses a class's instance variables.
prCat fnm c = case c of
		(Right t) -> ""
		(Left nt) -> "       skel(" ++ fnm ++ "." ++ nt ++ ");"
