elna/lib/Language/Elna/Frontend/NameAnalysis.hs

220 lines
8.9 KiB
Haskell
Raw Normal View History

2024-10-02 22:56:15 +02:00
module Language.Elna.Frontend.NameAnalysis
2024-09-08 02:08:13 +02:00
( nameAnalysis
2024-09-20 13:32:24 +02:00
, Error(..)
2024-07-26 12:22:07 +02:00
) where
2024-07-29 07:26:47 +02:00
2024-09-20 13:32:24 +02:00
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Vector as Vector
2024-10-02 22:56:15 +02:00
import qualified Language.Elna.Frontend.AST as AST
import qualified Language.Elna.Frontend.SymbolTable as SymbolTable
import Language.Elna.Frontend.SymbolTable
2024-09-08 02:08:13 +02:00
( SymbolTable
2024-09-20 13:32:24 +02:00
, Info(..)
, ParameterInfo(..)
2024-09-08 02:08:13 +02:00
)
import Control.Monad.Trans.Except (Except, runExcept, throwE)
2024-07-29 07:26:47 +02:00
import Data.Functor ((<&>))
import Language.Elna.Location (Identifier(..))
2024-10-02 22:56:15 +02:00
import Language.Elna.Frontend.Types (Type(..))
2024-08-06 17:02:18 +02:00
import Data.Foldable (traverse_)
2024-09-24 22:20:57 +02:00
import Control.Monad (foldM, unless)
2024-07-29 07:26:47 +02:00
data Error
= UndefinedTypeError Identifier
| UnexpectedTypeInfoError Info
2024-08-05 22:56:35 +02:00
| IdentifierAlreadyDefinedError Identifier
2024-08-06 17:02:18 +02:00
| UndefinedSymbolError Identifier
| UnexpectedArrayByValue Identifier
2024-09-21 23:35:32 +02:00
deriving Eq
instance Show Error
where
show (UndefinedTypeError identifier) =
concat ["Type \"", show identifier, "\" is not defined"]
show (UnexpectedTypeInfoError info) = show info
<> " expected to be a type"
show (IdentifierAlreadyDefinedError identifier) =
concat ["The identifier \"", show identifier, "\" is already defined"]
show (UndefinedSymbolError identifier) =
concat ["Symbol \"", show identifier, "\" is not defined"]
show (UnexpectedArrayByValue identifier) = concat
[ "Array \""
, show identifier
, "\" cannot be passed by value, only by reference"
]
2024-07-29 07:26:47 +02:00
newtype NameAnalysis a = NameAnalysis
2024-09-20 13:32:24 +02:00
{ runNameAnalysis :: Except Error a
2024-07-29 07:26:47 +02:00
}
instance Functor NameAnalysis
where
fmap f (NameAnalysis x) = NameAnalysis $ f <$> x
instance Applicative NameAnalysis
where
2024-08-05 22:56:35 +02:00
pure = NameAnalysis . pure
2024-07-29 07:26:47 +02:00
(NameAnalysis f) <*> (NameAnalysis x) = NameAnalysis $ f <*> x
instance Monad NameAnalysis
where
(NameAnalysis x) >>= f = NameAnalysis $ x >>= (runNameAnalysis . f)
2024-09-20 13:32:24 +02:00
nameAnalysis :: AST.Program -> Either Error SymbolTable
nameAnalysis = runExcept
. runNameAnalysis
. program SymbolTable.builtInSymbolTable
program :: SymbolTable -> AST.Program -> NameAnalysis SymbolTable
program symbolTable (AST.Program declarations) = do
globalTable <- foldM procedureDeclaration symbolTable declarations
foldM declaration globalTable declarations
procedureDeclaration :: SymbolTable -> AST.Declaration -> NameAnalysis SymbolTable
2024-10-17 00:37:42 +02:00
procedureDeclaration globalTable = \case
(AST.ProcedureDeclaration identifier parameters _ _)
-> mapM (parameter globalTable) parameters
>>= enterOrFail identifier
. ProcedureInfo SymbolTable.empty
. Vector.fromList
(AST.TypeDefinition identifier typeExpression)
-> dataType globalTable typeExpression
>>= enterOrFail identifier . SymbolTable.TypeInfo
where
enterOrFail identifier declarationInfo =
maybe (NameAnalysis $ throwE $ IdentifierAlreadyDefinedError identifier) pure
$ SymbolTable.enter identifier declarationInfo globalTable
2024-09-21 23:35:32 +02:00
declaration :: SymbolTable -> AST.Declaration -> NameAnalysis SymbolTable
2024-09-20 13:32:24 +02:00
declaration globalTable (AST.ProcedureDeclaration identifier parameters variables body) = do
variableInfo <- mapM (variableDeclaration globalTable) variables
parameterInfo <- mapM (parameterToVariableInfo globalTable) parameters
procedureTable <- fmap (SymbolTable.scope globalTable)
$ either (NameAnalysis . throwE . IdentifierAlreadyDefinedError . NonEmpty.head) pure
$ SymbolTable.fromList
$ parameterInfo <> variableInfo
traverse_ (statement procedureTable) body
pure $ SymbolTable.update (updater procedureTable) identifier globalTable
where
updater procedureTable (ProcedureInfo _ parameters') = Just
$ ProcedureInfo procedureTable parameters'
updater _ _ = Nothing
2024-10-17 00:37:42 +02:00
declaration globalTable (AST.TypeDefinition _ _) = pure globalTable
2024-09-20 13:32:24 +02:00
parameterToVariableInfo :: SymbolTable -> AST.Parameter -> NameAnalysis (Identifier, Info)
parameterToVariableInfo symbolTable (AST.Parameter identifier typeExpression isReferenceParameter')
= (identifier,) . VariableInfo isReferenceParameter'
<$> dataType symbolTable typeExpression
variableDeclaration :: SymbolTable -> AST.VariableDeclaration -> NameAnalysis (Identifier, Info)
variableDeclaration globalTable (AST.VariableDeclaration identifier typeExpression)
= (identifier,) . VariableInfo False
<$> dataType globalTable typeExpression
parameter :: SymbolTable -> AST.Parameter -> NameAnalysis ParameterInfo
parameter environmentSymbolTable (AST.Parameter identifier typeExpression isReferenceParameter') = do
parameterType <- dataType environmentSymbolTable typeExpression
case parameterType of
ArrayType _ _
| not isReferenceParameter' -> NameAnalysis
$ throwE $ UnexpectedArrayByValue identifier
_ ->
let parameterInfo = ParameterInfo
{ name = identifier
, type' = parameterType
, isReferenceParameter = isReferenceParameter'
}
in pure parameterInfo
dataType :: SymbolTable -> AST.TypeExpression -> NameAnalysis Type
dataType environmentSymbolTable (AST.NamedType baseType) = do
case SymbolTable.lookup baseType environmentSymbolTable of
Just baseInfo
| TypeInfo baseType' <- baseInfo -> pure baseType'
| otherwise -> NameAnalysis $ throwE $ UnexpectedTypeInfoError baseInfo
_ -> NameAnalysis $ throwE $ UndefinedTypeError baseType
dataType environmentSymbolTable (AST.ArrayType arraySize baseType) =
dataType environmentSymbolTable baseType <&> ArrayType (fromIntegral arraySize)
2024-08-05 22:56:35 +02:00
2024-08-15 20:13:56 +02:00
checkSymbol :: SymbolTable -> Identifier -> NameAnalysis ()
2024-09-24 22:20:57 +02:00
checkSymbol globalTable identifier
= unless (SymbolTable.member identifier globalTable)
$ NameAnalysis $ throwE
$ UndefinedSymbolError identifier
2024-08-06 17:02:18 +02:00
expression :: SymbolTable -> AST.Expression -> NameAnalysis ()
expression _ (AST.LiteralExpression _) = pure ()
expression globalTable (AST.SumExpression lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
expression globalTable (AST.SubtractionExpression lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
2024-09-29 19:50:55 +02:00
expression globalTable (AST.NegationExpression negation) =
expression globalTable negation
2024-08-06 17:02:18 +02:00
expression globalTable (AST.ProductExpression lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
expression globalTable (AST.DivisionExpression lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
2024-11-06 22:23:49 +01:00
expression globalTable (AST.VariableExpression variableExpression) =
2024-10-06 18:07:57 +02:00
variableAccess globalTable variableExpression
2024-11-06 22:23:49 +01:00
2024-09-24 22:20:57 +02:00
statement :: SymbolTable -> AST.Statement -> NameAnalysis ()
statement _ AST.EmptyStatement = pure ()
statement globalTable (AST.CallStatement name arguments)
= checkSymbol globalTable name
>> traverse_ (expression globalTable) arguments
2024-10-04 18:26:10 +02:00
statement globalTable (AST.CompoundStatement statements) =
traverse_ (statement globalTable) statements
2024-09-24 22:20:57 +02:00
statement globalTable (AST.IfStatement ifCondition ifStatement elseStatement)
= condition globalTable ifCondition
>> statement globalTable ifStatement
>> maybe (pure ()) (statement globalTable) elseStatement
2024-11-06 22:23:49 +01:00
statement globalTable (AST.AssignmentStatement lvalue rvalue)
= variableAccess globalTable lvalue
>> expression globalTable rvalue
2024-11-24 13:05:11 +01:00
statement globalTable (AST.WhileStatement whileCondition loop)
= condition globalTable whileCondition
>> statement globalTable loop
2024-08-15 20:13:56 +02:00
2024-10-11 16:14:01 +02:00
condition :: SymbolTable -> AST.Condition -> NameAnalysis ()
condition globalTable (AST.EqualCondition lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
2024-10-13 12:59:47 +02:00
condition globalTable (AST.NonEqualCondition lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
condition globalTable (AST.LessCondition lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
condition globalTable (AST.GreaterCondition lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
condition globalTable (AST.LessOrEqualCondition lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
condition globalTable (AST.GreaterOrEqualCondition lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
2024-11-06 22:23:49 +01:00
2024-08-15 20:13:56 +02:00
variableAccess :: SymbolTable -> AST.VariableAccess -> NameAnalysis ()
variableAccess globalTable (AST.VariableAccess identifier) =
checkSymbol globalTable identifier
2024-11-06 22:23:49 +01:00
{- variableAccess globalTable (AST.ArrayAccess arrayExpression indexExpression)
= variableAccess globalTable arrayExpression
>> expression globalTable indexExpression
2024-08-15 20:13:56 +02:00
2024-08-05 22:56:35 +02:00
enter :: Identifier -> Info -> SymbolTable -> NameAnalysis SymbolTable
enter identifier info table
= maybe (identifierAlreadyDefinedError identifier) pure
$ SymbolTable.enter identifier info table
identifierAlreadyDefinedError :: Identifier -> NameAnalysis a
identifierAlreadyDefinedError = NameAnalysis
. lift
. throwE
. IdentifierAlreadyDefinedError
2024-09-08 02:08:13 +02:00
-}