Look for undefined symbols

This commit is contained in:
Eugen Wissner 2024-08-06 17:02:18 +02:00
parent a1863147f8
commit 385322235d
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
4 changed files with 121 additions and 7 deletions

4
TODO
View File

@ -1,3 +1 @@
# Name analysis # Type analysis
- Ensure type, procedure, variable and parameter names are unique.

View File

@ -33,6 +33,7 @@ library elna-internal
Language.Elna.NameAnalysis Language.Elna.NameAnalysis
Language.Elna.Parser Language.Elna.Parser
Language.Elna.SymbolTable Language.Elna.SymbolTable
Language.Elna.TypeAnalysis
Language.Elna.Types Language.Elna.Types
build-depends: build-depends:
exceptions ^>= 0.10, exceptions ^>= 0.10,

View File

@ -4,7 +4,13 @@ module Language.Elna.NameAnalysis
) where ) where
import Control.Monad.Trans.Except (Except, runExcept, throwE) import Control.Monad.Trans.Except (Except, runExcept, throwE)
import Control.Monad.Trans.Reader (ReaderT(..), ask, runReaderT, withReaderT) import Control.Monad.Trans.Reader
( ReaderT(..)
, ask
, asks
, runReaderT
, withReaderT
)
import Data.Functor ((<&>)) import Data.Functor ((<&>))
import qualified Language.Elna.AST as AST import qualified Language.Elna.AST as AST
import Language.Elna.Location (Identifier(..)) import Language.Elna.Location (Identifier(..))
@ -12,13 +18,15 @@ import Language.Elna.SymbolTable (Info(..), SymbolTable, builtInSymbolTable)
import qualified Language.Elna.SymbolTable as SymbolTable import qualified Language.Elna.SymbolTable as SymbolTable
import Language.Elna.Types (Type(..)) import Language.Elna.Types (Type(..))
import Control.Monad.Trans.Class (MonadTrans(..)) import Control.Monad.Trans.Class (MonadTrans(..))
import Control.Monad (foldM) import Control.Monad (foldM, unless)
import qualified Data.List.NonEmpty as NonEmpty import qualified Data.List.NonEmpty as NonEmpty
import Data.Foldable (traverse_)
data Error data Error
= UndefinedTypeError Identifier = UndefinedTypeError Identifier
| UnexpectedTypeInfoError Info | UnexpectedTypeInfoError Info
| IdentifierAlreadyDefinedError Identifier | IdentifierAlreadyDefinedError Identifier
| UndefinedSymbolError Identifier
deriving (Eq, Show) deriving (Eq, Show)
newtype NameAnalysis a = NameAnalysis newtype NameAnalysis a = NameAnalysis
@ -53,15 +61,83 @@ declaration :: SymbolTable -> AST.Declaration -> NameAnalysis SymbolTable
declaration globalTable (AST.TypeDefinition identifier typeExpression) declaration globalTable (AST.TypeDefinition identifier typeExpression)
= withSymbolTable globalTable (dataType typeExpression) = withSymbolTable globalTable (dataType typeExpression)
>>= flip (enter identifier) globalTable . TypeInfo >>= flip (enter identifier) globalTable . TypeInfo
declaration globalTable (AST.ProcedureDefinition identifier parameters variables body) = do
declaration globalTable (AST.ProcedureDefinition identifier parameters variables _body) = do
parametersInfo <- mapM parameter parameters parametersInfo <- mapM parameter parameters
variableInfo <- mapM variableDeclaration variables variableInfo <- mapM variableDeclaration variables
newTable <- either (identifierAlreadyDefinedError . NonEmpty.head) pure newTable <- either (identifierAlreadyDefinedError . NonEmpty.head) pure
$ SymbolTable.fromList $ SymbolTable.fromList
$ parametersInfo <> variableInfo $ parametersInfo <> variableInfo
traverse_ (statement globalTable) body
enter identifier (ProcedureInfo newTable mempty) globalTable enter identifier (ProcedureInfo newTable mempty) globalTable
statement :: SymbolTable -> AST.Statement -> NameAnalysis ()
statement _ AST.EmptyStatement = pure ()
statement globalTable (AST.AssignmentStatement lvalue rvalue)
= expression globalTable lvalue
>> expression globalTable rvalue
statement globalTable (AST.IfStatement condition ifStatement elseStatement)
= expression globalTable condition
>> statement globalTable ifStatement
>> maybe (pure ()) (statement globalTable) elseStatement
statement globalTable (AST.WhileStatement condition loop)
= expression globalTable condition
>> statement globalTable loop
statement globalTable (AST.CompoundStatement statements) =
traverse_ (statement globalTable) statements
statement globalTable (AST.CallStatement name arguments)
= checkSymbol name globalTable
>> traverse_ (expression globalTable) arguments
checkSymbol :: Identifier -> SymbolTable -> NameAnalysis ()
checkSymbol identifier globalTable =
let undefinedSymbolError = NameAnalysis
$ lift
$ throwE
$ UndefinedSymbolError identifier
isDefined = SymbolTable.member identifier globalTable
in NameAnalysis (asks (SymbolTable.member identifier))
>>= (flip unless undefinedSymbolError . (isDefined ||))
expression :: SymbolTable -> AST.Expression -> NameAnalysis ()
expression globalTable (AST.VariableExpression identifier) =
checkSymbol identifier globalTable
expression _ (AST.LiteralExpression _) = pure ()
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
expression globalTable (AST.EqualExpression lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
expression globalTable (AST.NonEqualExpression lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
expression globalTable (AST.LessExpression lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
expression globalTable (AST.GreaterExpression lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
expression globalTable (AST.LessOrEqualExpression lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
expression globalTable (AST.GreaterOrEqualExpression lhs rhs)
= expression globalTable lhs
>> expression globalTable rhs
expression globalTable (AST.ArrayExpression arrayExpression indexExpression)
= expression globalTable arrayExpression
>> expression globalTable indexExpression
enter :: Identifier -> Info -> SymbolTable -> NameAnalysis SymbolTable enter :: Identifier -> Info -> SymbolTable -> NameAnalysis SymbolTable
enter identifier info table enter identifier info table
= maybe (identifierAlreadyDefinedError identifier) pure = maybe (identifierAlreadyDefinedError identifier) pure

View File

@ -0,0 +1,39 @@
module Language.Elna.TypeAnalysis
( Error(..)
, typeAnalysis
) where
import Control.Monad.Trans.Except (Except, runExcept)
import Control.Monad.Trans.Reader (ReaderT, runReaderT)
import qualified Language.Elna.AST as AST
import Language.Elna.SymbolTable (SymbolTable)
data Error = Error
deriving (Eq, Show)
newtype TypeAnalysis a = TypeAnalysis
{ runTypeAnalysis :: ReaderT SymbolTable (Except Error) a
}
instance Functor TypeAnalysis
where
fmap f (TypeAnalysis x) = TypeAnalysis $ f <$> x
instance Applicative TypeAnalysis
where
pure = TypeAnalysis . pure
(TypeAnalysis f) <*> (TypeAnalysis x) = TypeAnalysis $ f <*> x
instance Monad TypeAnalysis
where
(TypeAnalysis x) >>= f = TypeAnalysis $ x >>= (runTypeAnalysis . f)
typeAnalysis :: SymbolTable -> AST.Program -> Maybe Error
typeAnalysis globalTable = either Just (const Nothing)
. runExcept
. flip runReaderT globalTable
. runTypeAnalysis
. program
program :: AST.Program -> TypeAnalysis ()
program (AST.Program _declarations) = pure ()