packages feed

BNFC-2.5.0: src/formats/c/CFtoCPrinter.hs

{-
    BNF Converter: C Pretty Printer printer
    Copyright (C) 2004  Author:  Michael Pellauer

    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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-}

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

    Description   : This module generates the C Pretty Printer.
                    It also generates the "show" method for
                    printing an abstract syntax tree.
                    
                    The generated files use the Visitor design pattern.

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

    License       : GPL (GNU General Public License)

    Created       : 10 August, 2003                           

    Modified      : 3 September, 2003                          
                    * Added resizable buffers
   
   ************************************************************** 
-}

module CFtoCPrinter (cf2CPrinter) where

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

--Produces (.h file, .c file)
cf2CPrinter :: CF -> (String, String)
cf2CPrinter cf = (mkHFile cf groups, mkCFile cf groups)
 where
    groups = (fixCoercions (ruleGroupsInternals cf))

{- **** Header (.h) File Methods **** -}

--An extremely large function to make the Header File
mkHFile :: CF -> [(Cat,[Rule])] -> String
mkHFile cf groups = unlines
 [
  header,
  concatMap prPrints eps,
  concatMap prPrintDataH groups,
  concatMap prShows eps,
  concatMap prShowDataH groups,
  footer
 ]
 where
  eps = allEntryPoints cf
  prPrints s | normCat s == s = "char* print" ++ s' ++ "(" ++ s' ++ " p);\n"
    where
      s' = identCat s
  prPrints _ = ""
  prShows s | normCat s == s = "char* show" ++ s' ++ "(" ++ s' ++ " p);\n"
    where
      s' = identCat s
  prShows _ = ""
  header = unlines
   [
    "#ifndef PRINTER_HEADER",
    "#define PRINTER_HEADER",
    "",
    "#include \"Absyn.h\"",
    "",
    "/* Certain applications may improve performance by changing the buffer size */",
    "#define BUFFER_INITIAL 2000",
    "/* You may wish to change _L_PAREN or _R_PAREN */",
    "#define _L_PAREN '('",
    "#define _R_PAREN ')'",
    "",
    "/* The following are simple heuristics for rendering terminals */",
    "/* You may wish to change them */",
    "void renderCC(Char c);",
    "void renderCS(String s);",
    "void indent(void);",
    "void backup(void);",
    ""
   ]
  footer = unlines $
   ["void pp" ++ t ++ "(String s, int i);" | t <- tokenNames cf]
    ++
   ["void sh" ++ t ++ "(String s);" | t <- tokenNames cf]
    ++
   [
    "void ppInteger(Integer n, int i);",
    "void ppDouble(Double d, int i);",
    "void ppChar(Char c, int i);",
    "void ppString(String s, int i);",
    "void ppIdent(String s, int i);",
    "void shInteger(Integer n);",
    "void shDouble(Double d);",
    "void shChar(Char c);",
    "void shString(String s);",
    "void shIdent(String s);",
    "void bufAppendS(const char* s);",
    "void bufAppendC(const char c);",
    "void bufReset(void);",
    "void resizeBuffer(void);",
    "",
    "#endif"
   ]

--Prints all the required method names and their parameters.
prPrintDataH :: (Cat, [Rule]) -> String
prPrintDataH (cat, rules) = concat ["void pp", cl, "(", cl, " p, int i);\n"]
  where
   cl = identCat (normCat cat)
  
--Prints all the required method names and their parameters.
prShowDataH :: (Cat, [Rule]) -> String
prShowDataH (cat, rules) = concat ["void sh", cl, "(", cl, " p);\n"]
  where
   cl = identCat (normCat cat)

{- **** Implementation (.C) File Methods **** -}

--This makes the .C file by a similar method.
mkCFile :: CF -> [(Cat,[Rule])] -> String
mkCFile cf groups = concat 
   [
    header,
    prRender,
    concatMap prPrintFun eps,
    concatMap prShowFun eps,
    concatMap (prPrintData user) groups,
    printBasics,
    printTokens,
    concatMap (prShowData user) groups,
    showBasics,
    showTokens,
    footer
   ]
  where
    eps = allEntryPoints cf
    user = fst (unzip (tokenPragmas cf))
    header = unlines 
     [
      "/*** BNFC-Generated Pretty Printer and Abstract Syntax Viewer ***/",
      "",
      "#include \"Printer.h\"",
      "#include <stdio.h>",
      "#include <string.h>",
      "#include <stdlib.h>",
      "",
      "int _n_;",
      "char* buf_;",
      "int cur_;",
      "int buf_size;",
      ""
     ]
    printBasics = unlines 
     [
      "void ppInteger(Integer n, int i)",
      "{",
      "  char tmp[16];",
      "  sprintf(tmp, \"%d\", n);",
      "  bufAppendS(tmp);",
      "}",
      "void ppDouble(Double d, int i)",
      "{",
      "  char tmp[16];",
      "  sprintf(tmp, \"%g\", d);",
      "  bufAppendS(tmp);",
      "}",
      "void ppChar(Char c, int i)",
      "{",
      "  bufAppendC('\\'');",
      "  bufAppendC(c);",
      "  bufAppendC('\\'');",
      "}",
      "void ppString(String s, int i)",
      "{",
      "  bufAppendC('\\\"');",
      "  bufAppendS(s);",
      "  bufAppendC('\\\"');",
      "}",
      "void ppIdent(String s, int i)",
      "{",
      "  renderS(s);",
      "}",
      ""
     ]
    printTokens = unlines 
     [unlines [
      "void pp" ++ t ++ "(String s, int i)",
      "{",
      "  renderS(s);",
      "}",
      ""
      ] | t <- tokenNames cf
     ]
    showBasics = unlines 
     [
      "void shInteger(Integer i)",
      "{",
      "  char tmp[16];",
      "  sprintf(tmp, \"%d\", i);",
      "  bufAppendS(tmp);",
      "}",
      "void shDouble(Double d)",
      "{",
      "  char tmp[16];",
      "  sprintf(tmp, \"%g\", d);",
      "  bufAppendS(tmp);",
      "}",
      "void shChar(Char c)",
      "{",
      "  bufAppendC('\\'');",
      "  bufAppendC(c);",
      "  bufAppendC('\\'');",
      "}",
      "void shString(String s)",
      "{",
      "  bufAppendC('\\\"');",
      "  bufAppendS(s);",
      "  bufAppendC('\\\"');",
      "}",
      "void shIdent(String s)",
      "{",
      "  bufAppendC('\\\"');",
      "  bufAppendS(s);",
      "  bufAppendC('\\\"');",
      "}",
      ""
     ]
    showTokens = unlines 
     [unlines [
      "void sh" ++ t ++ "(String s)",
      "{",
      "  bufAppendC('\\\"');",
      "  bufAppendS(s);",
      "  bufAppendC('\\\"');",
      "}",
      ""
      ] | t <- tokenNames cf
     ]
    footer = unlines
     [
      "void bufAppendS(const char* s)",
      "{",
      "  int len = strlen(s);",
      "  int n;",
      "  while (cur_ + len > buf_size)",
      "  {",
      "    buf_size *= 2; /* Double the buffer size */",
      "    resizeBuffer();",
      "  }",
      "  for(n = 0; n < len; n++)",
      "  {",
      "    buf_[cur_ + n] = s[n];",
      "  }",
      "  cur_ += len;",
      "  buf_[cur_] = 0;",
      "}",
      "void bufAppendC(const char c)",
      "{",
      "  if (cur_ == buf_size)",
      "  {",
      "    buf_size *= 2; /* Double the buffer size */",
      "    resizeBuffer();",
      "  }",
      "  buf_[cur_] = c;",
      "  cur_++;",
      "  buf_[cur_] = 0;",
      "}",
      "void bufReset(void)",
      "{",
      "  cur_ = 0;",
      "  buf_size = BUFFER_INITIAL;",
      "  resizeBuffer();",
      "  memset(buf_, 0, buf_size);",
      "}",
      "void resizeBuffer(void)",
      "{",
      "  char* temp = (char*) malloc(buf_size);",
      "  if (!temp)",
      "  {",
      "    fprintf(stderr, \"Error: Out of memory while attempting to grow buffer!\\n\");",
      "    exit(1);",
      "  }",
      "  if (buf_)",
      "  {",
      "    strncpy(temp, buf_, buf_size); /* peteg: strlcpy is safer, but not POSIX/ISO C. */",
      "    free(buf_);",
      "  }",
      "  buf_ = temp;",
      "}",
      "char *buf_;",
      "int cur_, buf_size;",
      ""
     ]
    

{- **** Pretty Printer Methods **** -}

--An entry point to begin printing
prPrintFun :: Cat -> String
prPrintFun ep | normCat ep == ep = unlines
  [
   "char* print" ++ ep' ++ "(" ++ ep' ++ " p)",
   "{",
   "  _n_ = 0;",
   "  bufReset();",
   "  pp" ++ ep' ++ "(p, 0);",
   "  return buf_;",
   "}"
  ]
 where
  ep' = identCat ep
prPrintFun _ = ""

--Generates methods for the Pretty Printer
prPrintData :: [UserDef] -> (Cat, [Rule]) -> String
prPrintData user (cat, rules) = 
 if isList cat
 then unlines
 [
  "void pp" ++ cl ++ "("++ cl +++ vname ++ ", int i)",
  "{",
  "  while(" ++ vname ++ "!= 0)",
  "  {",
  "    if (" ++ vname ++ "->" ++ vname ++ "_ == 0)",
  "    {",
  visitMember,
  optsep,
  "      " ++ vname +++ "= 0;",
  "    }",
  "    else",
  "    {",
  visitMember,
  "      render" ++ sc ++ "(" ++ sep ++ ");",
  "      " ++ vname +++ "=" +++ vname ++ "->" ++ vname ++ "_;",
  "    }",
  "  }",
  "}",
  ""
 ] --Not a list:
 else unlines
 [
   "void pp" ++ cl ++ "(" ++ cl ++ " _p_, int _i_)",
   "{",
   "  switch(_p_->kind)",
   "  {",
   concatMap (prPrintRule user) rules,
   "  default:",
   "    fprintf(stderr, \"Error: bad kind field when printing " ++ cat ++ "!\\n\");",
   "    exit(1);",
   "  }",
   "}\n"
 ]
 where
   cl = identCat (normCat cat)
   ecl = identCat (normCatOfList cat)
   vname = map toLower cl
   member = map toLower ecl
   visitMember = "      pp" ++ ecl ++ "(" ++ vname ++ "->" ++ member ++ "_, 0);"
   (sc, sep) = if length sep' == 1
     then ("C", "'" ++ escapeChars sep' ++ "'")
     else ("S", "\"" ++ escapeChars sep' ++ "\"")
   sep' = getCons rules
   optsep = if hasOneFunc rules then "" else ("      render" ++ sc ++ "(" ++ sep ++ ");")
    
--Pretty Printer methods for a rule.
prPrintRule :: [UserDef] -> Rule -> String
prPrintRule user r@(Rule fun c cats) | not (isCoercion fun) = unlines
  [
   "  case is_" ++ fun ++ ":",
   lparen,
   cats',
   rparen,
   "    break;\n"
  ]
   where
    p = precRule r
    (lparen, rparen) = 
      ("    if (_i_ > " ++ (show p) ++ ") renderC(_L_PAREN);",
       "    if (_i_ > " ++ (show p) ++ ") renderC(_R_PAREN);")
    cats' = (concatMap (prPrintCat user fun) (zip3 (fixOnes (numVars [] cats)) cats (map getPrec cats)))
    getPrec (Right s) = (0 :: Int)
    getPrec (Left c) = precCat c
prPrintRule _ _ = ""

--This goes on to recurse to the instance variables.
prPrintCat :: [UserDef] -> String -> (Either Cat String, Either Cat String, Int) -> String
prPrintCat user fnm (c,o,p) = case c of
  (Right t) -> "    render" ++ sc ++ "(" ++ t' ++ ");\n"
    where
     (sc,t') = if length t == 1
       then ("C", "'" ++ (escapeChars t) ++ "'")
       else ("S", "\"" ++ (escapeChars t) ++ "\"")
  (Left nt) -> if isBasic user nt
       then "    pp" ++ (basicFunName nt) ++ "(_p_->u." ++ v ++ "_." ++ nt ++ ", " ++ (show p) ++ ");\n"
       else if nt == "#_" --Internal category
         then "    /* Internal Category */\n"
         else "    pp" ++ o' ++ "(_p_->u." ++ v ++ "_." ++ nt ++ ", " ++ (show p) ++ ");\n"
 where
  v = map toLower (identCat (normCat fnm))
  o' = case o of
    Right x -> x
    Left x -> normCat (identCat x)        

{- **** Abstract Syntax Tree Printer **** -}

--An entry point to begin printing
prShowFun :: Cat -> String
prShowFun ep | normCat ep == ep = unlines
  [
   "char* show" ++ ep' ++ "(" ++ ep' ++ " p)",
   "{",
   "  _n_ = 0;",
   "  bufReset();",
   "  sh" ++ ep' ++ "(p);",
   "  return buf_;",
   "}"
  ]
 where
  ep' = identCat ep
prShowFun _ = ""
  
--This prints the functions for Abstract Syntax tree printing.
prShowData :: [UserDef] -> (Cat, [Rule]) -> String
prShowData user (cat, rules) = 
 if isList cat
 then unlines
 [
  "void sh" ++ cl ++ "("++ cl +++ vname ++ ")",
  "{",
  "  while(" ++ vname ++ "!= 0)",
  "  {",
  "    if (" ++ vname ++ "->" ++ vname ++ "_)",
  "    {",
  visitMember,
  "      bufAppendS(\", \");",
  "      " ++ vname +++ "=" +++ vname ++ "->" ++ vname ++ "_;",
  "    }",
  "    else",
  "    {",
  visitMember,
  "      " ++ vname ++ " = 0;",
  "    }",
  "  }",
  "}",
  ""
 ] --Not a list:
 else unlines
 [
   "void sh" ++ cl ++ "(" ++ cl ++ " _p_)",
   "{",
   "  switch(_p_->kind)",
   "  {",
   concatMap (prShowRule user) rules,
   "  default:",
   "    fprintf(stderr, \"Error: bad kind field when showing " ++ cat ++ "!\\n\");",
   "    exit(1);",
   "  }",
   "}\n"
 ]
 where
   cl = identCat (normCat cat)
   ecl = identCat (normCatOfList cat)
   vname = map toLower cl
   member = map toLower ecl
   visitMember = "      sh" ++ ecl ++ "(" ++ vname ++ "->" ++ member ++ "_);"
    
--Pretty Printer methods for a rule.
prShowRule :: [UserDef] -> Rule -> String
prShowRule user r@(Rule fun c cats) | not (isCoercion fun) = unlines
  [
   "  case is_" ++ fun ++ ":",
   lparen,
   "    bufAppendS(\"" ++ fun ++ "\");\n",
   optspace,
   cats',
   rparen,
   "    break;\n"
  ]
   where
    (optspace, lparen, rparen) = if allTerms cats
      then ("","","")
      else ("  bufAppendC(' ');\n", "  bufAppendC('(');\n","  bufAppendC(')');\n")
    cats' = if allTerms cats
        then ""
    	else concat (insertSpaces (map (prShowCat user fun) (zip (fixOnes (numVars [] cats)) cats)))
    insertSpaces [] = []
    insertSpaces (x:[]) = [x]
    insertSpaces (x:xs) = if x == "" 
      then insertSpaces xs
      else (x : ["  bufAppendC(' ');\n"]) ++ (insertSpaces xs)
    allTerms [] = True
    allTerms ((Left z):zs) = False
    allTerms (z:zs) = allTerms zs
prShowRule _ _ = ""

--This goes on to recurse to the instance variables.
prShowCat :: [UserDef] -> String -> (Either Cat String, Either Cat String) -> String
prShowCat user fnm (c,o) = case c of
  (Right t) -> ""
  (Left nt) -> if isBasic user nt
       then "    sh" ++ (basicFunName nt) ++ "(_p_->u." ++ v ++ "_." ++ nt ++ ");\n"
       else if nt == "#_" --Internal category
         then "    /* Internal Category */\n"
         else if ((normCat nt) /= nt)
          then "    sh" ++ o' ++ "(_p_->u." ++ v ++ "_." ++ nt ++ ");\n"
          else concat
          [
	   "    bufAppendC('[');\n",
           "    sh" ++ o' ++ "(_p_->u." ++ v ++ "_." ++ nt ++ ");\n",
	   "    bufAppendC(']');\n"
          ]
 where
  v = map toLower (identCat (normCat fnm))
  o' = case o of
    Right x -> x
    Left x -> normCat (identCat x)    

{- **** Helper Functions Section **** -}


--Just checks if something is a basic or user-defined type.
--This is because you don't -> a basic non-pointer type.
isBasic :: [UserDef] -> String -> Bool
isBasic user v = 
  if elem (init v) user'
    then True
    else if "integer_" `isPrefixOf` v then True
    else if "char_" `isPrefixOf` v then True
    else if "string_" `isPrefixOf` v then True
    else if "double_" `isPrefixOf` v then True
    else if "ident_" `isPrefixOf` v then True
    else False
  where
   user' = map (map toLower) user

--The visit-function name of a basic type
basicFunName :: String -> String
basicFunName v = 
    if "integer_" `isPrefixOf` v then "Integer"
    else if "char_" `isPrefixOf` v then "Char"
    else if "string_" `isPrefixOf` v then "String"
    else if "double_" `isPrefixOf` v then "Double"
    else if "ident_" `isPrefixOf` v then "Ident"
    else "Ident" --User-defined type

--Just sets the coercion level for parentheses in the Pretty Printer.
setI :: Int -> String
setI n = "_i_ = " ++ (show n) ++ "; "

--Helper function that escapes characters in strings
escapeChars :: String -> String
escapeChars [] = []
escapeChars ('\\':xs) = '\\' : ('\\' : (escapeChars xs))
escapeChars ('\"':xs) = '\\' : ('\"' : (escapeChars xs))
escapeChars (x:xs) = x : (escapeChars xs)
    
--An extremely simple renderCer for terminals.
prRender :: String
prRender = unlines
  [
      "/* You may wish to change the renderC functions */",
      "void renderC(Char c)",
      "{",
      "  if (c == '{')",
      "  {",
      "     bufAppendC('\\n');",
      "     indent();",
      "     bufAppendC(c);",
      "     _n_ = _n_ + 2;",
      "     bufAppendC('\\n');",
      "     indent();",
      "  }",
      "  else if (c == '(' || c == '[')",
      "     bufAppendC(c);",
      "  else if (c == ')' || c == ']')",
      "  {",
      "     backup();",
      "     bufAppendC(c);",
      "     bufAppendC(' ');",
      "  }",
      "  else if (c == '}')",
      "  {",
      "     _n_ = _n_ - 2;",
      "     backup();",
      "     backup();",
      "     bufAppendC(c);",
      "     bufAppendC('\\n\');",
      "     indent();",
      "  }",
      "  else if (c == ',')",
      "  {",
      "     backup();",
      "     bufAppendC(c);",
      "     bufAppendC(' ');",
      "  }",
      "  else if (c == ';')",
      "  {",
      "     backup();",
      "     bufAppendC(c);",
      "     bufAppendC('\\n');",
      "     indent();",
      "  }",
      "  else if (c == 0) return;",
      "  else",
      "  {",
      "     bufAppendC(c);",
      "     bufAppendC(' ');",
      "  }",
      "}",
      "void renderS(String s)",
      "{",
      "  if(strlen(s) > 0)",
      "  {",
      "    bufAppendS(s);",
      "    bufAppendC(' ');",
      "  }",
      "}",
      "void indent(void)",
      "{",
      "  int n = _n_;",
      "  while (n > 0)",
      "  {",
      "    bufAppendC(' ');",
      "    n--;",
      "  }",
      "}",
      "void backup(void)",
      "{",
      "  if (buf_[cur_ - 1] == ' ')",
      "  {", 
      "    buf_[cur_ - 1] = 0;",
      "    cur_--;",
      "  }",
      "}"
  ]