elna/lib/Language/Elna/NameAnalysis.hs

217 lines
8.8 KiB
Haskell
Raw Normal View History

2024-07-26 12:22:07 +02:00
module Language.Elna.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
import qualified Language.Elna.SymbolTable as SymbolTable
2024-09-08 02:08:13 +02:00
import qualified Language.Elna.AST as AST
import Language.Elna.SymbolTable
( 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(..))
import Language.Elna.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
procedureDeclaration globalTable (AST.ProcedureDeclaration identifier parameters _ _) = do
parametersInfo <- mapM (parameter globalTable) parameters
let procedureInfo = ProcedureInfo SymbolTable.empty
$ Vector.fromList parametersInfo
maybe (NameAnalysis $ throwE $ IdentifierAlreadyDefinedError identifier) pure
$ SymbolTable.enter identifier procedureInfo 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
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 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 ()
2024-09-24 22:20:57 +02:00
{- expression globalTable (AST.VariableExpression variableExpression) =
variableAccess globalTable variableExpression
2024-08-06 17:02:18 +02:00
expression globalTable (AST.NegationExpression negation) =
expression globalTable negation
expression globalTable (AST.SumExpression lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
expression globalTable (AST.SubtractionExpression lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
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-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
{- statement globalTable (AST.AssignmentStatement lvalue rvalue)
= variableAccess globalTable lvalue
>> expression globalTable rvalue
statement globalTable (AST.IfStatement ifCondition ifStatement elseStatement)
= condition globalTable ifCondition
>> statement globalTable ifStatement
>> maybe (pure ()) (statement globalTable) elseStatement
statement globalTable (AST.WhileStatement whileCondition loop)
= condition globalTable whileCondition
>> statement globalTable loop
statement globalTable (AST.CompoundStatement statements) =
traverse_ (statement globalTable) statements
2024-08-15 20:13:56 +02:00
variableAccess :: SymbolTable -> AST.VariableAccess -> NameAnalysis ()
variableAccess globalTable (AST.ArrayAccess arrayExpression indexExpression)
= variableAccess globalTable arrayExpression
>> expression globalTable indexExpression
variableAccess globalTable (AST.VariableAccess identifier) =
checkSymbol globalTable identifier
condition :: SymbolTable -> AST.Condition -> NameAnalysis ()
condition globalTable (AST.EqualCondition lhs rhs)
2024-08-06 17:02:18 +02:00
= expression globalTable lhs
>> expression globalTable rhs
2024-08-15 20:13:56 +02:00
condition globalTable (AST.NonEqualCondition lhs rhs)
2024-08-06 17:02:18 +02:00
= expression globalTable lhs
>> expression globalTable rhs
2024-08-15 20:13:56 +02:00
condition globalTable (AST.LessCondition lhs rhs)
2024-08-06 17:02:18 +02:00
= expression globalTable lhs
>> expression globalTable rhs
2024-08-15 20:13:56 +02:00
condition globalTable (AST.GreaterCondition lhs rhs)
2024-08-06 17:02:18 +02:00
= expression globalTable lhs
>> expression globalTable rhs
2024-08-15 20:13:56 +02:00
condition globalTable (AST.LessOrEqualCondition lhs rhs)
2024-08-06 17:02:18 +02:00
= expression globalTable lhs
>> expression globalTable rhs
2024-08-15 20:13:56 +02:00
condition globalTable (AST.GreaterOrEqualCondition lhs rhs)
2024-08-06 17:02:18 +02:00
= expression globalTable lhs
>> expression globalTable rhs
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-08-04 12:23:19 +02:00
variableDeclaration :: AST.VariableDeclaration -> NameAnalysis (Identifier, Info)
variableDeclaration (AST.VariableDeclaration identifier typeExpression)
= (identifier,) . VariableInfo False
2024-08-04 12:23:19 +02:00
<$> dataType typeExpression
2024-09-08 02:08:13 +02:00
-}