{-# Language FlexibleInstances, FlexibleContexts, GADTs, MultiParamTypeClasses, OverloadedStrings,
ScopedTypeVariables, StandaloneDeriving, TypeFamilies, TypeOperators, UndecidableInstances #-}
-- | The programming language Modula-2
module Language.Modula2 (parseModule, parseAndSimplifyModule, resolvePosition, resolvePositions,
Placed, Version(..), SomeVersion(..)) where
import qualified Language.Modula2.Abstract as Abstract
import qualified Language.Modula2.AST as Report (Language)
import qualified Language.Modula2.ISO.AST as ISO (Language)
import qualified Language.Modula2.Grammar as Grammar
import qualified Language.Modula2.ISO.Grammar as ISO.Grammar
import qualified Language.Modula2.ConstantFolder as ConstantFolder
import qualified Language.Modula2.ISO.ConstantFolder as ISO.ConstantFolder
import Language.Modula2.ConstantFolder (ConstantFold)
import Language.Modula2.Pretty ()
import Language.Modula2.ISO.Pretty ()
import qualified Language.Oberon.Reserializer as Reserializer
import qualified Rank2 as Rank2 (Functor, snd)
import Transformation.AG (Atts, Inherited, Synthesized, Knit)
import Transformation.AG.Generics (Auto)
import qualified Transformation.Rank2 as Rank2
import qualified Transformation.Full as Full
import qualified Transformation.Deep as Deep
import Control.Arrow (first)
import Control.Monad (when)
import Data.Functor.Compose (Compose(Compose, getCompose))
import Data.Functor.Identity (Identity)
import qualified Data.Map.Lazy as Map
import Data.Map.Lazy (Map)
import Data.Monoid ((<>))
import Data.Text (Text, unpack)
import Data.Text.IO (readFile)
import Text.Grampa (Ambiguous, Grammar, ParseResults, parseComplete, failureDescription)
import qualified Text.Parser.Input.Position as Position
import System.Directory (doesFileExist)
import System.FilePath (FilePath, addExtension, combine, takeDirectory)
import Prelude hiding (readFile)
-- | Every node in a parsed and resolved AST is wrapped with this functor
type Placed = (,) (Int, Grammar.ParsedLexemes, Int)
instance (Rank2.Functor (g Grammar.NodeWrap), Deep.Functor (Rank2.Map Grammar.NodeWrap Placed) g) =>
Full.Functor (Rank2.Map Grammar.NodeWrap Placed) g where
(<$>) = Full.mapUpDefault
instance (Rank2.Functor (g Placed), Deep.Functor (Rank2.Map Placed Identity) g) =>
Full.Functor (Rank2.Map Placed Identity) g where
(<$>) = Full.mapUpDefault
-- | The modes of operation
data Options = Options{
-- | whether to fold the constants in the parsed module
foldConstants :: Bool,
-- whether to check the types
-- checkTypes :: Bool,
-- | which version of the Modula-2 language?
version :: SomeVersion}
data Version l where
-- | the version specified by Niklaus Wirth's ''Report on the Programming Language Modula-2''
Report :: Version Report.Language
-- | the version specified by the ISO standard
ISO :: Version ISO.Language
-- | The language version in existential container
data SomeVersion where
SomeVersion :: Version l -> SomeVersion
deriving instance Show (Version l)
deriving instance Show SomeVersion
-- | Replace the stored positions in the entire tree with offsets from the start of the given source text
resolvePositions :: (p ~ Grammar.NodeWrap, q ~ Placed, Deep.Functor (Rank2.Map p q) g)
=> Text -> p (g p p) -> q (g q q)
resolvePositions src t = resolvePosition src ((resolvePosition src Rank2.<$>) <$> t)
-- | Replace the stored positions of the given node with offset from the start of the given source text
resolvePosition :: Text -> Grammar.NodeWrap a -> Placed a
resolvePosition src = \((start, ws, end), a)-> ((Position.offset src start, ws, Position.offset src end), a)
-- | Parse the given text of a single module and fold constants inside it.
parseAndSimplifyModule :: (Abstract.Modula2 l, Abstract.Nameable l,
Full.Functor (Knit (Auto ConstantFold)) (Abstract.Expression l l))
=> Version l -> Text -> ParseResults Text [Placed (Abstract.Module l l Placed Placed)]
parseAndSimplifyModule Report source =
(Reserializer.adjustPositions . (ConstantFolder.foldConstants (predefined Report) <$>) <$>)
<$> parseModule Report source
parseAndSimplifyModule ISO source =
(Reserializer.adjustPositions . (ISO.ConstantFolder.foldConstants (predefined ISO) <$>) <$>)
<$> parseModule ISO source
-- | The predefined environment of types, constants, and procedures for the given language version.
predefined :: (Abstract.Modula2 l, Ord (Abstract.QualIdent l)) => Version l -> ConstantFolder.Environment l
predefined Report = Map.fromList $ map (first Abstract.nonQualIdent) $
[("TRUE", Just Abstract.true),
("FALSE", Just Abstract.false)]
++ map builtin ["BITSET", "BOOLEAN", "CARDINAL", "CHAR", "INTEGER", "PROC", "REAL",
"ABS", "CAP", "CHR", "FLOAT", "HIGH", "MAX", "MIN", "ODD", "ORD", "TRUNC", "VAL"]
where builtin name = (name, Just $ Abstract.builtin name)
predefined ISO = Map.fromList $ map (first Abstract.nonQualIdent) $
[("TRUE", Just Abstract.true),
("FALSE", Just Abstract.false)]
++ map builtin ["BITSET", "BOOLEAN", "CARDINAL", "CHAR", "INTEGER", "PROC", "REAL",
"ABS", "CAP", "CHR", "FLOAT", "HIGH", "MAX", "MIN", "ODD", "ORD", "TRUNC", "VAL"]
where builtin name = (name, Just $ Abstract.builtin name)
-- | Parse the given text of a single module.
parseModule :: Version l -> Text -> ParseResults Text [Placed (Abstract.Module l l Placed Placed)]
parseModule Report source = resolve source (parseComplete Grammar.modula2grammar source)
parseModule ISO source = resolve source (Rank2.snd $ parseComplete ISO.Grammar.modula2ISOgrammar source)
resolve :: Deep.Functor (Rank2.Map Grammar.NodeWrap Placed) (Abstract.Module l l)
=> Text
-> Grammar.Modula2Grammar l Grammar.NodeWrap (Compose (Compose (ParseResults Text) []) ((,) [[Grammar.Lexeme]]))
-> ParseResults Text [Placed (Abstract.Module l l Placed Placed)]
resolve source results = getCompose (resolvePositions source . snd <$> getCompose (Grammar.compilationUnit results))
{-
parseNamedModule :: FilePath -> Text -> IO (ParseResults Text [Module Language Language Placed Placed])
parseNamedModule path name =
do let basePath = combine path (unpack name)
isDefn <- doesFileExist (addExtension basePath "Def")
src <- readFile (addExtension basePath $ if isDefn then "Def" else "Mod")
return (getCompose $ resolvePositions src <$> Grammar.compilationUnit (parseComplete Grammar.modula2grammar src))
parseImportsOf :: FilePath -> Map Text (Module Language Language Placed Placed)
-> IO (Map Text (Module Language Language Placed Placed))
parseImportsOf path modules =
case filter (`Map.notMember` modules) moduleImports
of [] -> return modules
newImports -> (((modules <>) . Map.fromList . map assertSuccess) <$>
(traverse . traverse) (parseNamedModule path) [(p, p) | p <- newImports])
>>= parseImportsOf path
where moduleImports = foldMap (fmap importedModule . importsOf) modules
importedModule (Import _ m) = m
importsOf (DefinitionModule _ imports _ _) = imports
importsOf (ImplementationModule _ _ imports _) = imports
importsOf (ProgramModule _ _ imports _) = imports
assertSuccess (m, Left err) = error ("Parse error in module " <> unpack m)
assertSuccess (m, Right [p]) = (m, p)
assertSuccess (m, Right _) = error ("Ambiguous parses of module " <> unpack m)
-}