KiCS-0.8.9: src/lib/Curry/Module/CurrySyntax.curry
--- This module provides the necessary data structures to maintain the
--- parsed representation of a Curry program employed by the Curry frontend.
---
--- The representation differs from the one of the frontend in two respects:
--- 1. module names are repesented as strings and
--- 2. positions are replaced by polymorphic labels which are positions
--- without filename by default.
---
--- @author Wolfgang Lux, Martin Engelke, Sebastian Fischer
---
module CurrySyntax (
Module(..), Pos, Position(..), Ident(..), QualIdent(..), ModuleIdent,
ExportSpec(..), Export(..), ImportSpec(..), Import(..),
Decl(..), ConstrDecl(..), NewConstrDecl(..),
Infix(..), EvalAnnotation(..), CallConv(..),
TypeExpr(..), Equation(..), Lhs(..), Rhs(..), CondExpr(..),
Literal(..), ConstrTerm(..), Expression(..),
InfixOp(..), Statement(..), Alt(..), Field(..),
readCurry, readCurryWithParseOptions, readCurryFile,
writeCurryModule, writeCurryFile
) where
import Directory ( doesFileExist )
import ReadShowTerm ( readUnqualifiedTerm, showTerm )
import Distribution
( callFrontendWithParams , FrontendTarget(..)
, FrontendParams, defaultParams, setQuiet
, lookupFileInLoadPath )
import FileGoodies ( baseName )
--- Source positions: <code>(line,column)</code>
---
type Pos = (Int,Int)
--- The original type of source positions used by the frontend:
--- @cons Position <code>filepath line column</code>
---
data Position = Position String Int Int
--- An identifier is represented by its name and an additional number
--- to avoid name clashes. If the number is <code>0</code> the identifier
--- does not need to be renamed:
--- @cons Ident <code>name id</code>
---
data Ident = Ident String Int
--- Qualified identifiers have an attached module name, unqualified have not:
--- @cons UnqualIdent <code>ident</code>
--- @cons QualIdent <code>moduleIdent ident</code>
---
data QualIdent = UnqualIdent Ident | QualIdent ModuleIdent Ident
--- Module identifiers
---
type ModuleIdent = String
--- A module consists of a name, an optional export specification and a list of
--- declarations.
--- The type variable can be used to label some parts of the module.
--- Initially, a module is labeld with positions of type <code>Pos</code>.
--- @cons Module <code>moduleIdent optExportSpec decls</code>
---
data Module a = Module ModuleIdent (Maybe (ExportSpec a)) [Decl a]
--- An export specification consists of a label and a list of exports:
--- @cons ExportSpec: <code>(ExportSpec lab exports)</code>
---
data ExportSpec a = Exporting a [Export]
--- There are different kinds of exports:
--- @cons Export <code>qident</code>: a qualified identifier
--- @cons ExportTypeWith <code>type idents</code>: some identifiers of a type
--- @cons ExportTypeAll <code>type</code>: all identifiers of a type
--- @cons ExportModule <code>moduleIdent</code>: a complete module
---
data Export
= Export QualIdent
| ExportTypeWith QualIdent [Ident]
| ExportTypeAll QualIdent
| ExportModule ModuleIdent
--- An import specification either imports or hides names from other modules:
--- @cons Importing <code>lab imports</code>
--- @cons Hiding <code>lab imports</code>
---
data ImportSpec a
= Importing a [Import]
| Hiding a [Import]
--- There are different kinds of imports:
--- @cons Import <code>ident</code>: an identifier of the imported/hidden module
--- @cons ImportTypeWith <code>type idents</code>: some identifiers of a type
--- from the imported/hidden module
--- @cons ImportTypeAll <code>type</code>: all identifiers of a type from the
--- imported/hidden module
---
data Import
= Import Ident
| ImportTypeWith Ident [Ident]
| ImportTypeAll Ident
--- There are different kinds of declarations:
--- @cons ImportDecl <code>lab moduleIdent quali optAlias optImportSpec</code>:
--- An import declaration consists of a label, a module identifier, a flag
--- that determines whether the import is qualified, an optional alias and
--- an optional import specification.
--- @cons InfixDecl <code>lab assoc prec idents</code>:
--- An infix declaration consists of a label, an associativity declaration,
--- a precedence and a list of infix identifiers.
--- @cons DataDecl <code>lab name params consDecls</code>:
--- A data declaration consists of a label, the name of the datatype,
--- the names of the type parameters and a list of constructor declarations.
--- @cons NewtypeDecl <code>lab name params newConsDecl</code>:
--- A newtype declaration consists of a label, the name of the datatype,
--- the names of the type parameters and a "newconstructor declaration".
--- @cons TypeDecl <code>lab name params type</code>:
--- A type synonym declaration consists of a label, the name of the synonym,
--- the names of the type parameters and a type expression.
--- @cons TypeSig <code>lab idents type</code>:
--- A type signature consists of a label, a list of identifiers and the
--- specified type of these idetifiers.
--- @cons EvalAnnot <code>lab idents evalMode</code>:
--- An evaluation annotation consists of a label, a list of identifiers and
--- the specified evaluation mode of these identifiers.
--- @cons FunctionDecl <code>lab name eqs</code>:
--- A function declaration consists of a label, the name of the function
--- an a list of defining equations.
--- @cons ExternalDecl <code>lab callConvention optSpec name</code>:
--- The declaration of an external function consists of a label, a calling
--- convention, an optional specification and the name of the function.
--- @cons FlatExternalDecl <code>lab idents</code>:
--- A flat external declaration consists of a label and a list of
--- identifiers.
--- @cons PatternDecl <code>lab pat rhs</code>:
--- A pattern declaration consists of a label, a pattern and a
--- right-hand-side.
--- @cons ExtraVariables <code>lab idents</code>:
--- A declaration of free variables consists of a label and the list of
--- introduced names.
---
data Decl a
= ImportDecl a ModuleIdent Bool (Maybe ModuleIdent) (Maybe (ImportSpec a))
| InfixDecl a Infix Int [Ident]
| DataDecl a Ident [Ident] [ConstrDecl a]
| NewtypeDecl a Ident [Ident] (NewConstrDecl a) -- omit?
| TypeDecl a Ident [Ident] TypeExpr
| TypeSig a [Ident] TypeExpr
| EvalAnnot a [Ident] EvalAnnotation -- deprecated?
| FunctionDecl a Ident [Equation a]
| ExternalDecl a CallConv (Maybe String) Ident TypeExpr
| FlatExternalDecl a [Ident] -- purpose?
| PatternDecl a (ConstrTerm a) (Rhs a)
| ExtraVariables a [Ident]
--- Constructors can be defined in prefix or in infix notation:
--- @cons ConstrDecl <code>lab idents name argTypes</code>
--- @cons ConOpDecl <code>lab idents ltype name rtype</code>
---
data ConstrDecl a -- purpose of [Ident]?
= ConstrDecl a [Ident] Ident [TypeExpr]
| ConOpDecl a [Ident] TypeExpr Ident TypeExpr
--- A "newconstructor declaration" consists of a label, a list of identifiers,
--- the name of the new constructor and its argument type:
--- @cons NewConstrDecl <code>lab idents name type</code>
---
data NewConstrDecl a = NewConstrDecl a [Ident] Ident TypeExpr
--- An infix operator is either left-, right- or not associative.
---
data Infix = InfixL | InfixR | Infix
--- The evaluation mode of a function is either rigid or flexible.
---
data EvalAnnotation = EvalRigid | EvalChoice
--- There are two different kinds of calling conventions for external functions.
---
data CallConv = CallConvPrimitive | CallConvCCall
--- There are different types of type expressions:
--- @cons ConstructorType <code>name paramTypes</code>:
--- A constructed type consists of the name of the type constructor and the
--- list of parameter types.
--- @cons VariableType <code>name</code>:
--- A type variable is represented by its name.
--- @cons TupleType <code>paramTypes</code>:
--- A tuple type is represented by its parameter types.
--- @cons ListType <code>type</code>:
--- A list type is represented by the wrapped element type.
--- @cons ArrowType <code>dom ran</code>:
--- A functional type is represented by its domain and range types.
--- @cons RecordType <code>fieldTypes optType</code>:
--- A record type is represented by a list of type declarations for its
--- fields and an optional remaining type. A field type declaration specifies
--- the type of multiple field identifiers.
---
data TypeExpr
= ConstructorType QualIdent [TypeExpr]
| VariableType Ident
| TupleType [TypeExpr]
| ListType TypeExpr
| ArrowType TypeExpr TypeExpr
| RecordType [([Ident],TypeExpr)] (Maybe TypeExpr)
--- A defining equation consists of a label and the left- and right-hand-side
--- of the equation.
--- @cons Equation <code>lab lhs rhs</code>
---
data Equation a = Equation a (Lhs a) (Rhs a)
--- There are different kinds of left-hand-sides in equations:
--- @cons FunLhs <code>name patterns</code>:
--- The left-hand-side of a function rule consists of the name of the
--- function and a list of patterns.
--- @cons OpLhs <code>lpat opname rpat</code>:
--- The left-hand-side of an infix operator rule consists of the left
--- argument, the name and the right argument of the operator.
--- @cons ApLhs <code>lhs patterns</code>:
--- A left-hand-side can also be the application of another left-hand-side
--- to additional arguments. This is sometimes useful for operator declarations
--- like <pre>(f . g) x = f (g x)</pre>
---
data Lhs a
= FunLhs Ident [ConstrTerm a]
| OpLhs (ConstrTerm a) Ident (ConstrTerm a)
| ApLhs (Lhs a) [ConstrTerm a]
--- The right-hand-side of a defining equation may be quarded and can contain
--- local declarations:
--- @cons SimpleRhs <code>lab body localDecls</code>
--- @cons GuardedRhs <code>condEqs localDecls</code>
---
data Rhs a
= SimpleRhs a (Expression a) [Decl a]
| GuardedRhs [CondExpr a] [Decl a]
--- A conditional equation consists of a label, a condition and the conditional
--- right-hand-side:
--- @cons CondExpr <code>lab cond expr</code>
---
data CondExpr a = CondExpr a (Expression a) (Expression a)
--- The are character, integer, float and string literals. <br/>
--- The Ident argument of an Int literal is used for supporting
--- ad-hoc polymorphism on integer numbers.
--- An integer literal can be used either as an integer number or
--- as a floating-point number depending on its context. The compiler uses
--- the identifier of the Int literal for maintaining its type.
---
data Literal
= Char Char
| Int Ident Int
| Float Float
| String String
--- There are different kinds of patterns:
--- @cons LiteralPattern <code>literal</code>:
--- literal patterns declare the literal to be matched.
--- @cons NegativePattern <code>ident literal</code>:
--- negative patterns consist of an identifier and a literal.
--- @cons VariablePattern <code>name</code>:
--- a named variable pattern
--- @cons ConstructorPattern <code>name argPats</code>:
--- A constructor pattern consists of the name of the matched constructor
--- and a list of argument patterns.
--- @cons InfixPattern <code>lpat opname rpat</code>:
--- An infix pattern consists of the left argument pattern, the
--- name of the infix constructor and the right argument pattern.
--- @cons ParenPattern <code>pat</code>:
--- A pattern enclosed in brackets.
--- @cons TuplePattern <code>argPats</code>:
--- A tuple pattern references a list of argument patterns.
--- @cons ListPattern <code>argPats</code>:
--- A list pattern references a list of argument patterns.
--- @cons AsPattern <code>name pat</code>:
--- An as pattern introduces a name for the expression matched by the
--- enclosed pattern.
--- @cons LazyPattern <code>pat</code>:
--- A lazy pattern is syntactic sugar for a pattern variable that is
--- matched in a local declaration.
--- @cons FunctionPattern <code>name argPats</code>:
--- A function pattern consists of the name of the pattern function and
--- a list of argument patterns.
--- @cons InfixFuncPattern <code>lpat op rpat</code>
--- An infix function pattern consists of the left argument pattern, the
--- name of the infix operator and the right argument pattern.
--- @cons RecordPattern <code>fieldPats optPat</code>:
--- A record pattern consists of a list of field patterns and an optional
--- pattern for the remaining record.
---
data ConstrTerm a
= LiteralPattern Literal
| NegativePattern Ident Literal -- purpose? e.g. f (-3) = 4
| VariablePattern Ident
| ConstructorPattern QualIdent [ConstrTerm a]
| InfixPattern (ConstrTerm a) QualIdent (ConstrTerm a)
| ParenPattern (ConstrTerm a)
| TuplePattern [ConstrTerm a]
| ListPattern [ConstrTerm a]
| AsPattern Ident (ConstrTerm a)
| LazyPattern (ConstrTerm a) -- omit?
| FunctionPattern QualIdent [ConstrTerm a]
| InfixFuncPattern (ConstrTerm a) QualIdent (ConstrTerm a)
| RecordPattern [Field a (ConstrTerm a)] (Maybe (ConstrTerm a))
--- There are different kinds of expressions:
--- @cons Literal <code>literal</code>
--- @cons Variable <code>name</code>
--- @cons Constructor <code>name</code>
--- @cons Paren <code>exp</code>: bracketed expression
--- @cons Typed <code>exp type</code>:
--- expression with an annotated type (<code>exp :: type</code>)
--- @cons Tuple <code>exps</code>
--- @cons List <code>exps</code>
--- @cons ListCompr <code>exp stmts</code>:
--- <code>[ exp | stmts ]</code>
--- @cons EnumFrom <code>start</code>: <code>[start..]</code>
--- @cons EnumFromThen <code>start next</code>: <code>[start,next..]</code>
--- @cons EnumFromTo <code>start stop</code>: <code>[start..stop]</code>
--- @cons EnumFromThenTo <code>start next stop</code>:
--- <code>[start,next..stop]</code>
--- @cons UnaryMinus <code>ident exp</code>
--- @cons Apply <code>exp1 exp2</code>
--- @cons InfixApply <code>lexp op rexp</code>
--- @cons LeftSection <code>exp op</code>:
--- infix operator partially applied to left argument
--- @cons RightSection <code>op exp</code>:
--- infix operator partially applied to right argument.
--- @cons Lambda <code>pats exp</code>:
--- lambda abstraction with multiple arguments and pattern matching
--- @cons Let <code>decls exp</code>
--- @cons Do <code>stmts exp</code>:
--- do expression with multiple statements and a final expression
--- @cons IfThenElse <code>cond texp fexp</code>
--- @cons Case <code>exp alts</code>
--- case distinction with matched expression and list of alternatives:
--- @cons RecordConstr <code>fieldDecls</code>:
--- <code>{ key1 = value1, key2 = value2 }</code>
--- @cons RecordSelection <code>exp field</code>: <code>exp -> field</code>
--- @cons RecordUpdate <code>fieldDecls exp</code>:
--- <code>{ key := value | exp }</code>
---
data Expression a
= Literal Literal
| Variable QualIdent
| Constructor QualIdent
| Paren (Expression a)
| Typed (Expression a) TypeExpr
| Tuple [(Expression a)]
| List [(Expression a)]
| ListCompr (Expression a) [Statement a]
| EnumFrom (Expression a)
| EnumFromThen (Expression a) (Expression a)
| EnumFromTo (Expression a) (Expression a)
| EnumFromThenTo (Expression a) (Expression a) (Expression a)
| UnaryMinus Ident (Expression a) -- purpose of Ident? type Int/Float?
| Apply (Expression a) (Expression a)
| InfixApply (Expression a) InfixOp (Expression a)
| LeftSection (Expression a) InfixOp
| RightSection InfixOp (Expression a)
| Lambda [ConstrTerm a] (Expression a)
| Let [Decl a] (Expression a)
| Do [Statement a] (Expression a)
| IfThenElse (Expression a) (Expression a) (Expression a)
| Case (Expression a) [Alt a]
| RecordConstr [Field a (Expression a)]
| RecordSelection (Expression a) Ident
| RecordUpdate [Field a (Expression a)] (Expression a)
--- Infix operators can be function and constructor symbols.
---
data InfixOp = InfixOp QualIdent | InfixConstr QualIdent
--- There are different kinds of statements used in list comprehensions and
--- do expressions:
--- @cons StmtExpr <code>exp</code>
--- @cons StmtDecl <code>decls</code>: local declarations
--- @cons StmtBind <code>pat rhs</code>: statement bind with pattern matching
---
data Statement a
= StmtExpr (Expression a)
| StmtDecl [Decl a]
| StmtBind (ConstrTerm a) (Expression a)
--- A case alternative consists of a label, a pattern and a right-hand-side.
--- @cons Alt <code>lab pat rhs</code>
---
data Alt a = Alt a (ConstrTerm a) (Rhs a)
--- A field consists of a label, a field name and either a pattern or an
--- expression.
--- @cons Field <code>lab name pat</code>, <code>Field lab name exp</code>
---
data Field a b = Field a Ident b
--- Parses a Curry program into its source representation.
--- The argument is the name of a module or the name of its source file.
--- If necessary, a current .cy file is generated in the same place where the
--- .curry or .lcurry file is found.
--- The result is a representation of a module labeled with positions.
---
readCurry :: String -> IO (Module Pos)
readCurry name = readCurryWithParseOptions name (setQuiet True defaultParams)
--- Parses a Curry program into its source representation using the specified
--- parameters to the frontend.
--- The argument is the name of a module or the name of its source file.
--- If necessary, a current .cy file is generated in the same place where the
--- .curry or .lcurry file is found.
--- The result is a representation of a module labeled with positions.
---
readCurryWithParseOptions :: String -> FrontendParams -> IO (Module Pos)
readCurryWithParseOptions name options = do
parseCurry ".curry" `orElseDo` parseCurry ".lcurry"
readCurryFile progname
where
progname = stripSuffix name
parseCurry suffix
= absoluteFileName (progname++suffix) >>= maybe (return Nothing) parse
parse _ = callFrontendWithParams CY options progname >> return (Just ())
--- Parses a Curry program into its source representation.
--- The argument is the name of a module or the name of its source file.
--- The .cy file is neither generated if it does not exist nor updated if there
--- is a more recent source file!
---
readCurryFile :: String -> IO (Module Pos)
readCurryFile name = do
filename <- absoluteFileName (progname++".cy")
maybe (error $ progname ++ ".cy not found") read filename
where
progname = stripSuffix name
read filename = do
filecontents <- readFile filename
return (readUnqualifiedTerm ["CurrySyntax","Prelude"] filecontents)
--- Writes the source representation of a Curry module labeled with positions
--- into a file MOD.cy where MOD is the name of the module.
---
writeCurryModule :: Module Pos -> IO ()
writeCurryModule m@(Module name _ _) = writeCurryFile (name++".cy") m
--- Writes the source representation of a Curry module labeled with positions
--- into a file with the given name.
---
writeCurryFile :: String -> Module Pos -> IO ()
writeCurryFile file m = writeFile file (showTerm m)
-- private auxiliary functions ------------------------------------------------
absoluteFileName :: String -> IO (Maybe String)
absoluteFileName name
| name == baseName name = lookupFileInLoadPath name
| otherwise = do
exists <- doesFileExist name
return (if exists then Just name else Nothing)
orElseDo :: IO (Maybe a) -> IO (Maybe a) -> IO (Maybe a)
orElseDo ioma ioa = ioma >>= maybe ioa (return . Just)
stripSuffix :: String -> String
stripSuffix = takeWhile (/='.')