packages feed

curry-base-0.2.9: Curry/FlatCurry/Type.hs

{- |
    Library to support meta-programming in Curry.

    This library contains a definition for representing FlatCurry programs
    in Haskell (type "Prog").

    @author Michael Hanus
    @version September 2003

    Version for Haskell (slightly modified):
    December 2004, Martin Engelke (men@informatik.uni-kiel.de)

    Added part calls for constructors, Bernd Brassel, August 2005
-}

module Curry.FlatCurry.Type
  (
    -- * Data types for flat curry
    Prog (..), QName, Visibility (..), TVarIndex, TypeDecl (..), ConsDecl (..)
  , TypeExpr (..), OpDecl (..), Fixity (..), VarIndex, FuncDecl (..)
  , Rule (..), CaseType (..), CombType (..), Expr (..), BranchExpr (..)
  , Pattern (..), Literal (..)

    -- * Functions for reading and writing flat curry terms
  , readFlatCurry, readFlatInterface, readFlat, writeFlatCurry
  ) where

import Curry.Files.PathUtils (writeModule, maybeReadModule)

import Data.List (intercalate)
import Data.Char (isSpace)
import Control.Monad (liftM)

{- ---------------------------------------------------------------------------
   Definition of data types for representing FlatCurry programs
--------------------------------------------------------------------------- -}

{- |Data type for representing a Curry module in the intermediate form.
    A value of this data type has the form
    <CODE>
      (Prog modname imports typedecls functions opdecls translation_table)
    </CODE>
    where modname: name of this module,
          imports: list of modules names that are imported,
          typedecls, opdecls, functions, translation of type names
          and constructor/function names: see below
-}
data Prog = Prog String [String] [TypeDecl] [FuncDecl] [OpDecl]
            deriving (Read, Show, Eq)

{- |The data type for representing qualified names.
    In FlatCurry all names are qualified to avoid name clashes.
    The first component is the module name and the second component the
    unqualified name as it occurs in the source program.
-}
type QName = (String, String)

-- |Data type to specify the visibility of various entities.
data Visibility = Public    -- ^ public (exported) entity
                | Private   -- ^ private entity
                  deriving (Read, Show, Eq)

{- |The data type for representing type variables.
    They are represented by (TVar i) where i is a type variable index.
-}
type TVarIndex = Int

{- |Data type for representing definitions of algebraic data types.
    <PRE>
    A data type definition of the form

    data t x1...xn = ...| c t1....tkc |...

    is represented by the FlatCurry term

    (Type t [i1,...,in] [...(Cons c kc [t1,...,tkc])...])

    where each ij is the index of the type variable xj

    Note: the type variable indices are unique inside each type declaration
          and are usually numbered from 0

    Thus, a data type declaration consists of the name of the data type,
    a list of type parameters and a list of constructor declarations.
    </PRE>
-}
data TypeDecl = Type    QName Visibility [TVarIndex] [ConsDecl]
              | TypeSyn QName Visibility [TVarIndex] TypeExpr
                deriving (Read, Show, Eq)

{- |A constructor declaration consists of the name and arity of the
    constructor and a list of the argument types of the constructor.
-}
data ConsDecl = Cons QName Int Visibility [TypeExpr]
                deriving (Read, Show, Eq)

{- |Data type for type expressions.
    A type expression is either a type variable, a function type,
    or a type constructor application.

    Note: the names of the predefined type constructors are
          "Int", "Float", "Bool", "Char", "IO", "Success",
          "()" (unit type), "(,...,)" (tuple types), "[]" (list type)
-}
data TypeExpr
  = TVar TVarIndex             -- ^ type variable
  | FuncType TypeExpr TypeExpr -- ^ function type t1->t2
  | TCons QName [TypeExpr]     -- ^ type constructor application
  deriving (Read, Show, Eq)


{- |Data type for operator declarations.
    An operator declaration "fix p n" in Curry corresponds to the
    FlatCurry term (Op n fix p).
    Note: the constructor definition of 'Op' differs from the original
    PAKCS definition using Haskell type 'Integer' instead of 'Int'
    for representing the precedence.
-}
data OpDecl = Op QName Fixity Int deriving (Read, Show, Eq)

-- |Data types for the different choices for the fixity of an operator.
data Fixity
  = InfixOp  -- ^ non-associative infix operator
  | InfixlOp -- ^ left-associative infix operator
  | InfixrOp -- ^ right-associative infix operator
    deriving (Read, Show, Eq)


{- |Data type for representing object variables.
    Object variables occurring in expressions are represented by (Var i)
    where i is a variable index.
-}
type VarIndex = Int

{- |Data type for representing function declarations.
    <PRE>
    A function declaration in FlatCurry is a term of the form

      (Func name arity type (Rule [i_1,...,i_arity] e))

    and represents the function "name" with definition

      name :: type
      name x_1...x_arity = e

    where each i_j is the index of the variable x_j

    Note: the variable indices are unique inside each function declaration
          and are usually numbered from 0

    External functions are represented as (Func name arity type (External s))
    where s is the external name associated to this function.

    Thus, a function declaration consists of the name, arity, type, and rule.
    </PRE>
-}
data FuncDecl = Func QName Int Visibility TypeExpr Rule
                deriving (Read, Show, Eq)


{- |A rule is either a list of formal parameters together with an expression
    or an "External" tag.
-}
data Rule = Rule [VarIndex] Expr
          | External String
            deriving (Read, Show, Eq)

{- |Data type for classifying case expressions.
    Case expressions can be either flexible or rigid in Curry.
-}
data CaseType = Rigid | Flex deriving (Read, Show, Eq)

{- |Data type for classifying combinations
    (i.e., a function/constructor applied to some arguments).
-}
data CombType
  -- |a call to a function where all arguments are provided
  = FuncCall
  -- |a call with a constructor at the top, all arguments are provided
  | ConsCall
  {- |a partial call to a function (i.e., not all arguments are provided)
      where the parameter is the number of missing arguments -}
  | FuncPartCall Int
  -- ^ a partial call to a constructor along with number of missing arguments
  | ConsPartCall Int
    deriving (Read, Show, Eq)

{- |Data type for representing expressions.

    Remarks:
    <PRE>
    1. if-then-else expressions are represented as function calls:
          (if e1 then e2 else e3)
        is represented as
          (Comb FuncCall ("Prelude","if_then_else") [e1,e2,e3])

    2. Higher order applications are represented as calls to the (external)
        function "apply". For instance, the rule
          app f x = f x
        is represented as
          (Rule  [0,1] (Comb FuncCall ("Prelude","apply") [Var 0, Var 1]))

    3. A conditional rule is represented as a call to an external function
        "cond" where the first argument is the condition (a constraint).
        For instance, the rule
          equal2 x | x=:=2 = success
        is represented as
          (Rule [0]
                (Comb FuncCall ("Prelude","cond")
                      [Comb FuncCall ("Prelude","=:=") [Var 0, Lit (Intc 2)],
                      Comb FuncCall ("Prelude","success") []]))

    4. Functions with evaluation annotation "choice" are represented
        by a rule whose right-hand side is enclosed in a call to the
        external function "Prelude.commit".
        Furthermore, all rules of the original definition must be
        represented by conditional expressions (i.e., (cond [c,e]))
        after pattern matching.
        Example:

          m eval choice
          m [] y = y
          m x [] = x

        is translated into (note that the conditional branches can be also
        wrapped with Free declarations in general):

          Rule [0,1]
                (Comb FuncCall ("Prelude","commit")
                  [Or (Case Rigid (Var 0)
                        [(Pattern ("Prelude","[]") []
                            (Comb FuncCall ("Prelude","cond")
                                  [Comb FuncCall ("Prelude","success") [],
                                    Var 1]))] )
                      (Case Rigid (Var 1)
                        [(Pattern ("Prelude","[]") []
                            (Comb FuncCall ("Prelude","cond")
                                  [Comb FuncCall ("Prelude","success") [],
                                    Var 0]))] )])

        Operational meaning of (Prelude.commit e):
        evaluate e with local search spaces and commit to the first
        (Comb FuncCall ("Prelude","cond") [c,ge]) in e whose constraint c
        is satisfied
    </PRE>
-}
data Expr
  -- |variable (represented by unique index)
  = Var VarIndex
  -- |literal (Integer/Float/Char constant)
  | Lit Literal
  -- |application (f e1 ... en) of function/constructor f with n<=arity(f)
  | Comb CombType QName [Expr]
  -- |introduction of free local variables
  | Free [VarIndex] Expr
  | Let [(VarIndex, Expr)] Expr
  {- |disjunction of two expressions (used to translate rules with overlapping
      left-hand sides) -}
  | Or Expr Expr
  -- |case distinction (rigid or flex)
  | Case CaseType Expr [BranchExpr]
    deriving (Read, Show, Eq)


{- |Data type for representing branches in a case expression.
    <PRE>
    Branches "(m.c x1...xn) -> e" in case expressions are represented as

      (Branch (Pattern (m,c) [i1,...,in]) e)

    where each ij is the index of the pattern variable xj, or as

      (Branch (LPattern (Intc i)) e)

    for integers as branch patterns (similarly for other literals
    like float or character constants).
    </PRE>
-}
data BranchExpr = Branch Pattern Expr deriving (Read, Show, Eq)

-- |Data type for representing patterns in case expressions.
data Pattern = Pattern QName [VarIndex]
             | LPattern Literal
               deriving (Read, Show, Eq)

{- |Data type for representing literals occurring in an expression
    or case branch. It is either an integer, a float, or a character constant.
    Note: the constructor definition of 'Intc' differs from the original
    PAKCS definition. It uses Haskell type 'Integer' instead of 'Int'
    to provide an unlimited range of integer numbers. Furthermore
    float values are represented with Haskell type 'Double' instead of
    'Float'.
-}
data Literal = Intc   Integer
             | Floatc Double
             | Charc  Char
               deriving (Read, Show, Eq)


{- |Reads a FlatCurry file (extension ".fcy") and returns the corresponding
    FlatCurry program term (type 'Prog') as a value of type 'Maybe'.
-}
readFlatCurry :: FilePath -> IO (Maybe Prog)
readFlatCurry fn = readFlat $ genFlatFilename ".fcy" fn

{- |Reads a FlatInterface file (extension ".fint") and returns the
    corresponding term (type 'Prog') as a value of type 'Maybe'.
-}
readFlatInterface :: String -> IO (Maybe Prog)
readFlatInterface fn = readFlat $ genFlatFilename ".fint" fn

{- |Reads a Flat file and returns the corresponding term (type 'Prog') as
    a value of type 'Maybe'.
    Due to compatibility with PAKCS it is allowed to have a commentary
    at the beginning of the file enclosed in {- ... -}.
-}
readFlat :: FilePath -> IO (Maybe Prog)
readFlat = liftM (fmap (read . skipComment)) . maybeReadModule
  where
    skipComment s = case dropWhile isSpace s of
       '{':'-':s' -> dropComment s'
       s'         -> s'
    dropComment ('-':'}':xs) = xs
    dropComment (_:xs)       = dropComment xs
    dropComment []           = []

{- |Writes a FlatCurry program term into a file.
    If the flag is set, it will be in the hidden curry sub-directory.
-}
writeFlatCurry :: Bool -> String -> Prog -> IO ()
writeFlatCurry inHiddenSubdir filename prog
  = writeModule inHiddenSubdir filename (showFlatCurry prog)

-- |Shows FlatCurry program in a more nicely way.
showFlatCurry :: Prog -> String
showFlatCurry (Prog mname imps types funcs ops) =
  "Prog " ++ show mname ++ "\n " ++
  show imps ++ "\n [" ++
  intercalate ",\n  " (map show types) ++ "]\n [" ++
  intercalate ",\n  " (map show funcs) ++ "]\n " ++
  show ops ++ "\n"

-- TODO: Use replaceExtension instead?

-- |Add the extension 'ext' to the filename 'fn' if it doesn't already exist.
genFlatFilename :: String -> FilePath -> FilePath
genFlatFilename ext fn
   | drop (length fn - length ext) fn == ext
     = fn
   | otherwise
     = fn ++ ext