language-oberon-0.2.1: src/Language/Oberon/Resolver.hs
{-# LANGUAGE FlexibleContexts, FlexibleInstances, KindSignatures, MultiParamTypeClasses,
OverloadedStrings, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-}
-- | This module exports functions for resolving the syntactic ambiguities in a parsed module. For example, an Oberon
-- expression @foo(bar)@ may be a call to function @foo@ with a parameter @bar@, or it may be type guard on variable
-- @foo@ casting it to type @bar@.
module Language.Oberon.Resolver (Error(..),
Predefined, predefined, predefined2, resolveModule, resolveModules) where
import Control.Applicative (Alternative)
import Control.Monad ((>=>))
import Control.Monad.Trans.State (StateT(..), evalStateT)
import Data.Either (partitionEithers)
import Data.Either.Validation (Validation(..), validationToEither)
import Data.Foldable (toList)
import Data.Functor.Identity (Identity(..))
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.List as List
import Data.Monoid (Alt(..))
import Data.Map.Lazy (Map, traverseWithKey)
import qualified Data.Map.Lazy as Map
import Data.Semigroup (Semigroup(..), sconcat)
import qualified Rank2.TH
import qualified Transformation as Shallow
import qualified Transformation.Deep as Deep
import qualified Transformation.Deep.TH
import qualified Transformation.Rank2 as Rank2
import Text.Grampa (Ambiguous(..), ParseFailure)
import Language.Oberon.AST
data DeclarationRHS f' f = DeclaredConstant (f (ConstExpression f' f'))
| DeclaredType (f (Type f' f'))
| DeclaredVariable (f (Type f' f'))
| DeclaredProcedure Bool (Maybe (f (FormalParameters f' f')))
deriving instance Show (DeclarationRHS Identity Identity)
deriving instance Show (DeclarationRHS Ambiguous Ambiguous)
-- | All possible resolution errors
data Error = UnknownModule QualIdent
| UnknownLocal Ident
| UnknownImport QualIdent
| AmbiguousParses
| AmbiguousDeclaration [Declaration Ambiguous Ambiguous]
| AmbiguousDesignator [Designator Ambiguous Ambiguous]
| AmbiguousExpression [Expression Ambiguous Ambiguous]
| AmbiguousRecord [Designator Ambiguous Ambiguous]
| AmbiguousStatement [Statement Ambiguous Ambiguous]
| InvalidExpression (NonEmpty Error)
| InvalidFunctionParameters [Ambiguous (Expression Ambiguous Ambiguous)]
| InvalidRecord (NonEmpty Error)
| InvalidStatement (NonEmpty Error)
| NotARecord QualIdent
| NotAType QualIdent
| NotAValue QualIdent
| ClashingImports
| UnparseableModule ParseFailure
deriving (Show)
type Scope = Map Ident (Validation (NonEmpty Error) (DeclarationRHS Identity Identity))
-- | A set of predefined declarations.
type Predefined = Scope
data Resolution = Resolution{_modules :: Map Ident Scope}
type Resolved = StateT (Scope, ResolutionState) (Validation (NonEmpty Error))
data ResolutionState = ModuleState
| DeclarationState
| StatementState
| ExpressionState
| ExpressionOrTypeState
deriving (Eq, Show)
instance Monad (Validation (NonEmpty Error)) where
Success s >>= f = f s
Failure errors >>= _ = Failure errors
instance Shallow.Functor Resolution Ambiguous Resolved (Module Resolved Resolved) where
(<$>) = mapResolveDefault
instance {-# overlappable #-} Show (g Identity Identity) =>
Shallow.Traversable Resolution Ambiguous Identity Resolved (g Identity Identity) where
traverse = traverseResolveDefault
instance {-# overlappable #-} Show (g Ambiguous Ambiguous) =>
Shallow.Traversable Resolution Ambiguous Identity Resolved (g Ambiguous Ambiguous) where
traverse = traverseResolveDefault
instance {-# overlaps #-} Shallow.Traversable
Resolution Ambiguous Identity Resolved (Designator Ambiguous Ambiguous) where
traverse res (Ambiguous designators) = StateT $ \s@(scope, state)->
case partitionEithers (NonEmpty.toList (validationToEither . resolveDesignator res scope state <$> designators))
of (_, [x]) -> Success (Identity x, s)
(errors, []) -> Failure (sconcat $ NonEmpty.fromList errors)
(_, multi) -> Failure (AmbiguousDesignator multi :| [])
instance {-# overlaps #-} Shallow.Traversable
Resolution Ambiguous Identity Resolved (Expression Ambiguous Ambiguous) where
traverse res expressions = StateT $ \s@(scope, state)->
let resolveExpression :: Expression Ambiguous Ambiguous
-> Validation (NonEmpty Error) (Expression Ambiguous Ambiguous, ResolutionState)
resolveExpression e@(Read designators) =
case evalStateT (Shallow.traverse res designators) s
of Failure errors -> Failure errors
Success{} -> pure (e, state)
resolveExpression e@(FunctionCall functions parameters) =
case evalStateT (Shallow.traverse res functions) s
of Failure errors -> Failure errors
Success (Identity d)
| Variable q <- d, Success (DeclaredProcedure True _) <- resolveName res scope q
-> pure (e, ExpressionOrTypeState)
| Success{} <- evalStateT (traverse (Shallow.traverse res) parameters) (scope, ExpressionState)
-> pure (e, ExpressionState)
| otherwise -> Failure (pure $ InvalidFunctionParameters parameters)
resolveExpression e@(Relation Is lefts rights) = pure (e, ExpressionOrTypeState)
resolveExpression e = pure (e, state)
in (\(r, s)-> (Identity r, (scope, s)))
<$> unique InvalidExpression (AmbiguousExpression . (fst <$>)) (resolveExpression <$> expressions)
instance {-# overlaps #-} Shallow.Traversable
Resolution Ambiguous Identity Resolved (Declaration Ambiguous Ambiguous) where
traverse res (Ambiguous (proc@(ProcedureDeclaration heading body _) :| [])) =
StateT $ \s@(scope, state)->
let ProcedureHeading receiver _indirect _name parameters = heading
ProcedureBody declarations statements = body
innerScope = localScope res "" declarations (parameterScope `Map.union` receiverScope `Map.union` scope)
receiverScope = maybe mempty receiverBinding receiver
receiverBinding (_, name, ty) = Map.singleton name (Success $ DeclaredVariable $ pure $ TypeReference
$ NonQualIdent ty)
parameterScope = case parameters
of Nothing -> mempty
Just (Ambiguous (FormalParameters sections _ :| []))
-> Map.fromList (concatMap binding sections)
binding (Ambiguous (FPSection _ names types :| [])) =
[(v, evalStateT (Deep.traverseDown res $ DeclaredVariable types) s) | v <- NonEmpty.toList names]
in Success (Identity proc, (innerScope, state))
traverse res (Ambiguous (dec :| [])) = pure (Identity dec)
traverse _ declarations = StateT (const $ Failure $ pure $ AmbiguousDeclaration $ toList declarations)
instance {-# overlaps #-} Shallow.Traversable
Resolution Ambiguous Identity Resolved (ProcedureBody Ambiguous Ambiguous) where
traverse res (Ambiguous (body@(ProcedureBody declarations statements) :| [])) = StateT $ \(scope, state)->
Success (Identity body, (localScope res "" declarations scope, state))
traverse _ b = StateT (const $ Failure $ pure AmbiguousParses)
instance {-# overlaps #-} Shallow.Traversable
Resolution Ambiguous Identity Resolved (Statement Ambiguous Ambiguous) where
traverse res statements = StateT $ \s@(scope, state)->
let resolveStatement :: Statement Ambiguous Ambiguous
-> Validation (NonEmpty Error) (Statement Ambiguous Ambiguous, ResolutionState)
resolveStatement p@(ProcedureCall procedures parameters) =
case evalStateT (Shallow.traverse res procedures) s
of Failure errors -> Failure errors
Success{} -> pure (p, StatementState)
resolveStatement stat = pure (stat, StatementState)
in (\(r, s)-> (Identity r, (scope, s)))
<$> unique InvalidStatement (AmbiguousStatement . (fst <$>)) (resolveStatement <$> statements)
mapResolveDefault :: Resolution -> Ambiguous (g Resolved Resolved) -> Resolved (g Resolved Resolved)
mapResolveDefault Resolution{} (Ambiguous (x :| [])) = pure x
mapResolveDefault Resolution{} _ = StateT (const $ Failure $ pure AmbiguousParses)
traverseResolveDefault :: Show (g f f) => Resolution -> Ambiguous (g (f :: * -> *) f) -> Resolved (Identity (g f f))
traverseResolveDefault Resolution{} (Ambiguous (x :| [])) = StateT (\s-> Success (Identity x, s))
traverseResolveDefault Resolution{} x@(Ambiguous _) = StateT (const $ Failure $ pure AmbiguousParses)
resolveDesignator :: Resolution -> Scope -> ResolutionState -> Designator Ambiguous Ambiguous
-> Validation (NonEmpty Error) (Designator Ambiguous Ambiguous)
resolveDesignator res scope state = resolveDesignator'
where resolveTypeName :: QualIdent -> Validation (NonEmpty Error) QualIdent
resolveDesignator' (Variable q) =
case resolveName res scope q
of Failure err -> Failure err
Success DeclaredType{} | state /= ExpressionOrTypeState -> Failure (NotAValue q :| [])
Success _ -> Success (Variable q)
resolveDesignator' d@(Field records field) =
case evalStateT (Shallow.traverse res records) (scope, state)
of Failure errors -> Failure errors
Success{} -> pure d
resolveDesignator' (TypeGuard records subtypes) =
case unique InvalidRecord AmbiguousRecord (resolveRecord <$> records)
of Failure errors -> Failure errors
Success{} -> TypeGuard records <$> resolveTypeName subtypes
resolveDesignator' d@(Dereference pointers) =
case evalStateT (Shallow.traverse res pointers) (scope, state)
of Failure errors -> Failure errors
Success{} -> pure d
resolveDesignator' d = pure d
resolveRecord d@(Variable q) =
case resolveName res scope q
of Failure err -> Failure err
Success DeclaredType{} -> Failure (NotAValue q :| [])
Success DeclaredProcedure{} -> Failure (NotARecord q :| [])
Success (DeclaredVariable t) -> resolveDesignator' d
resolveRecord d = resolveDesignator' d
resolveTypeName q =
case resolveName res scope q
of Failure err -> Failure err
Success DeclaredType{} -> Success q
Success _ -> Failure (NotAType q :| [])
resolveName :: Resolution -> Scope -> QualIdent -> Validation (NonEmpty Error) (DeclarationRHS Identity Identity)
resolveName res scope q@(QualIdent moduleName name) =
case Map.lookup moduleName (_modules res)
of Nothing -> Failure (UnknownModule q :| [])
Just exports -> case Map.lookup name exports
of Just rhs -> rhs
Nothing -> Failure (UnknownImport q :| [])
resolveName res scope (NonQualIdent name) =
case Map.lookup name scope
of Just (Success rhs) -> Success rhs
_ -> Failure (UnknownLocal name :| [])
resolveModules :: Predefined -> Map Ident (Module Ambiguous Ambiguous)
-> Validation (NonEmpty (Ident, NonEmpty Error)) (Map Ident (Module Identity Identity))
resolveModules predefinedScope modules = traverseWithKey extractErrors modules'
where modules' = resolveModule predefinedScope modules' <$> modules
extractErrors moduleKey (Failure e) = Failure ((moduleKey, e) :| [])
extractErrors _ (Success mod) = Success mod
resolveModule :: Scope -> Map Ident (Validation (NonEmpty Error) (Module Identity Identity))
-> Module Ambiguous Ambiguous -> Validation (NonEmpty Error) (Module Identity Identity)
resolveModule predefined modules m@(Module moduleName imports declarations body _) =
evalStateT (Deep.traverseDown res m) (moduleGlobalScope, ModuleState)
where res = Resolution moduleExports
importedModules = Map.delete mempty (Map.mapKeysWith clashingRenames importedAs modules)
where importedAs moduleName = case List.find ((== moduleName) . snd) imports
of Just (Nothing, moduleKey) -> moduleKey
Just (Just innerKey, _) -> innerKey
Nothing -> mempty
clashingRenames _ _ = Failure (ClashingImports :| [])
resolveDeclaration :: Ambiguous (Declaration Ambiguous Ambiguous) -> Resolved (Declaration Identity Identity)
resolveDeclaration d = runIdentity <$> (traverse (Deep.traverseDown res) d >>= Shallow.traverse res)
moduleExports = foldMap exportsOfModule <$> importedModules
moduleGlobalScope = localScope res moduleName declarations predefined
localScope :: Resolution -> Ident -> [Ambiguous (Declaration Ambiguous Ambiguous)] -> Scope -> Scope
localScope res qual declarations outerScope = innerScope
where innerScope = Map.union (snd <$> scopeAdditions) outerScope
scopeAdditions = (resolveBinding res innerScope <$>)
<$> Map.fromList (concatMap (declarationBinding qual . unamb) declarations)
unamb (Ambiguous (x :| [])) = x
resolveBinding :: Resolution -> Scope -> DeclarationRHS Ambiguous Ambiguous
-> Validation (NonEmpty Error) (DeclarationRHS Identity Identity)
resolveBinding res scope dr = evalStateT (Deep.traverseDown res dr) (scope, DeclarationState)
declarationBinding :: Ident -> Declaration f f -> [(Ident, (AccessMode, DeclarationRHS f f))]
declarationBinding _ (ConstantDeclaration (IdentDef name export) expr) =
[(name, (export, DeclaredConstant expr))]
declarationBinding _ (TypeDeclaration (IdentDef name export) typeDef) =
[(name, (export, DeclaredType typeDef))]
declarationBinding _ (VariableDeclaration names typeDef) =
[(name, (export, DeclaredVariable typeDef)) | (IdentDef name export) <- NonEmpty.toList names]
declarationBinding moduleName (ProcedureDeclaration (ProcedureHeading _ _ (IdentDef name export) parameters) _ _) =
[(name, (export, DeclaredProcedure (moduleName == "SYSTEM") parameters))]
declarationBinding _ (ForwardDeclaration (IdentDef name export) parameters) =
[(name, (export, DeclaredProcedure False parameters))]
predefined, predefined2 :: Predefined
-- | The set of 'Predefined' types and procedures defined in the Oberon Language Report.
predefined = Success <$> Map.fromList
[("BOOLEAN", DeclaredType (Identity $ TypeReference $ NonQualIdent "BOOLEAN")),
("CHAR", DeclaredType (Identity $ TypeReference $ NonQualIdent "CHAR")),
("SHORTINT", DeclaredType (Identity $ TypeReference $ NonQualIdent "SHORTINT")),
("INTEGER", DeclaredType (Identity $ TypeReference $ NonQualIdent "INTEGER")),
("LONGINT", DeclaredType (Identity $ TypeReference $ NonQualIdent "LONGINT")),
("REAL", DeclaredType (Identity $ TypeReference $ NonQualIdent "REAL")),
("LONGREAL", DeclaredType (Identity $ TypeReference $ NonQualIdent "LONGREAL")),
("SET", DeclaredType (Identity $ TypeReference $ NonQualIdent "SET")),
("TRUE", DeclaredConstant (Identity $ Read $ Identity $ Variable $ NonQualIdent "TRUE")),
("FALSE", DeclaredConstant (Identity $ Read $ Identity $ Variable $ NonQualIdent "FALSE")),
("ABS", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "INTEGER"] $
Just $ NonQualIdent "INTEGER"),
("ASH", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "INTEGER"] $
Just $ NonQualIdent "INTEGER"),
("CAP", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "c") $ Identity $ TypeReference $ NonQualIdent "CHAR"] $
Just $ NonQualIdent "CHAR"),
("LEN", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "c") $ Identity $ TypeReference $ NonQualIdent "ARRAY"] $
Just $ NonQualIdent "LONGINT"),
("MAX", DeclaredProcedure True $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "c") $ Identity $ TypeReference $ NonQualIdent "SET"] $
Just $ NonQualIdent "INTEGER"),
("MIN", DeclaredProcedure True $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "c") $ Identity $ TypeReference $ NonQualIdent "SET"] $
Just $ NonQualIdent "INTEGER"),
("ODD", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "CHAR"] $
Just $ NonQualIdent "BOOLEAN"),
("SIZE", DeclaredProcedure True $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "CHAR"] $
Just $ NonQualIdent "INTEGER"),
("ORD", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "CHAR"] $
Just $ NonQualIdent "INTEGER"),
("CHR", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "INTEGER"] $
Just $ NonQualIdent "CHAR"),
("SHORT", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "INTEGER"] $
Just $ NonQualIdent "INTEGER"),
("LONG", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "INTEGER"] $
Just $ NonQualIdent "INTEGER"),
("ENTIER", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "REAL"] $
Just $ NonQualIdent "INTEGER"),
("INC", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "INTEGER"] Nothing),
("DEC", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "INTEGER"] Nothing),
("INCL", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "s") $ Identity $ TypeReference $ NonQualIdent "SET",
Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "INTEGER"] Nothing),
("EXCL", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "s") $ Identity $ TypeReference $ NonQualIdent "SET",
Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "INTEGER"] Nothing),
("COPY", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "s") $ Identity $ TypeReference $ NonQualIdent "ARRAY",
Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "ARRAY"] Nothing),
("NEW", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "POINTER"] Nothing),
("HALT", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "INTEGER"] Nothing)]
-- | The set of 'Predefined' types and procedures defined in the Oberon-2 Language Report.
predefined2 = predefined <>
(Success <$> Map.fromList
[("ASSERT", DeclaredProcedure False $ Just $ Identity $
FormalParameters [Identity $ FPSection False (pure "s") $ Identity $ TypeReference $ NonQualIdent "ARRAY",
Identity $ FPSection False (pure "n") $ Identity $ TypeReference $ NonQualIdent "ARRAY"] Nothing)])
exportsOfModule :: Module Identity Identity -> Scope
exportsOfModule = fmap Success . Map.mapMaybe isExported . globalsOfModule
where isExported (PrivateOnly, _) = Nothing
isExported (_, binding) = Just binding
globalsOfModule :: Module Identity Identity -> Map Ident (AccessMode, DeclarationRHS Identity Identity)
globalsOfModule (Module name imports declarations _ _) =
Map.fromList (concatMap (declarationBinding name . runIdentity) declarations)
unique :: (NonEmpty Error -> Error) -> ([a] -> Error) -> Ambiguous (Validation (NonEmpty Error) a)
-> Validation (NonEmpty Error) a
unique _ _ (Ambiguous (x :| [])) = x
unique inv amb (Ambiguous xs) =
case partitionEithers (validationToEither <$> NonEmpty.toList xs)
of (_, [x]) -> Success x
(errors, []) -> Failure (inv (sconcat $ NonEmpty.fromList errors) :| [])
(_, multi) -> Failure (amb multi :| [])
$(Rank2.TH.deriveFunctor ''DeclarationRHS)
$(Rank2.TH.deriveFoldable ''DeclarationRHS)
$(Rank2.TH.deriveTraversable ''DeclarationRHS)
$(Transformation.Deep.TH.deriveDownTraversable ''DeclarationRHS)