From 9cb9ab536f51978b8251a651d77d955cb8b794e1 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 4 Aug 2024 12:23:19 +0200 Subject: [PATCH] Create procedure local symbol table --- TODO | 9 ++----- lib/Language/Elna/NameAnalysis.hs | 22 ++++++++++++++---- lib/Language/Elna/SymbolTable.hs | 10 +++++--- tests/Language/Elna/NameAnalysisSpec.hs | 31 ++++++++++++++++++++++++- 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/TODO b/TODO index ff1687a..b88eefb 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,4 @@ # Name analysis -- Collect all global type and procedure definitions. - Give errors if: - - The type is already defined. - - Base type is not defined. - - Replace equivalent type with its base type. - -- Check definitions inside procedures. +- Ensure type, procedure, variable and parameter names are unique. +- Check symbols used in procedures are defined. diff --git a/lib/Language/Elna/NameAnalysis.hs b/lib/Language/Elna/NameAnalysis.hs index 4acde39..bd1b854 100644 --- a/lib/Language/Elna/NameAnalysis.hs +++ b/lib/Language/Elna/NameAnalysis.hs @@ -8,7 +8,7 @@ import Control.Monad.Trans.Reader (ReaderT(..), ask, runReaderT, withReaderT) import Data.Functor ((<&>)) import qualified Language.Elna.AST as AST import Language.Elna.Location (Identifier(..)) -import Language.Elna.SymbolTable (Info(..), SymbolTable, symbolTable) +import Language.Elna.SymbolTable (Info(..), SymbolTable, builtInSymbolTable) import qualified Language.Elna.SymbolTable as SymbolTable import Language.Elna.Types (Type(..)) import Control.Monad.Trans.Class (MonadTrans(..)) @@ -38,7 +38,7 @@ instance Monad NameAnalysis nameAnalysis :: AST.Program -> Either Error SymbolTable nameAnalysis = runExcept - . flip runReaderT symbolTable + . flip runReaderT builtInSymbolTable . runNameAnalysis . program @@ -51,9 +51,21 @@ declaration :: SymbolTable -> AST.Declaration -> NameAnalysis SymbolTable declaration globalTable (AST.TypeDefinition identifier typeExpression) = flip (SymbolTable.enter identifier) globalTable . TypeInfo <$> withSymbolTable globalTable (dataType typeExpression) -declaration globalTable (AST.ProcedureDefinition identifier _parameters _variables _body) = - let localTable = SymbolTable.empty - in pure $ SymbolTable.enter identifier (ProcedureInfo localTable mempty) globalTable +declaration globalTable (AST.ProcedureDefinition identifier parameters variables _body) = do + parametersInfo <- mapM parameter parameters + variableInfo <- mapM variableDeclaration variables + let localTable = SymbolTable.fromList $ parametersInfo <> variableInfo + pure $ SymbolTable.enter identifier (ProcedureInfo localTable mempty) globalTable + +variableDeclaration :: AST.VariableDeclaration -> NameAnalysis (Identifier, Info) +variableDeclaration (AST.VariableDeclaration identifier typeExpression) + = (identifier,) . flip VariableInfo False + <$> dataType typeExpression + +parameter :: AST.Parameter -> NameAnalysis (Identifier, Info) +parameter (AST.Parameter identifier typeExpression isReferenceParameter') + = (identifier,) . flip VariableInfo isReferenceParameter' + <$> dataType typeExpression withSymbolTable :: forall a. SymbolTable -> NameAnalysis a -> NameAnalysis a withSymbolTable symbolTable' = NameAnalysis diff --git a/lib/Language/Elna/SymbolTable.hs b/lib/Language/Elna/SymbolTable.hs index f1c6534..b8892c9 100644 --- a/lib/Language/Elna/SymbolTable.hs +++ b/lib/Language/Elna/SymbolTable.hs @@ -4,8 +4,9 @@ module Language.Elna.SymbolTable , SymbolTable , empty , enter + , fromList , lookup - , symbolTable + , builtInSymbolTable ) where import Data.HashMap.Strict (HashMap) @@ -26,8 +27,8 @@ instance Monoid SymbolTable where mempty = SymbolTable HashMap.empty -symbolTable :: SymbolTable -symbolTable = SymbolTable $ HashMap.fromList +builtInSymbolTable :: SymbolTable +builtInSymbolTable = SymbolTable $ HashMap.fromList [ ("boolean", TypeInfo booleanType) , ("int", TypeInfo intType) ] @@ -42,6 +43,9 @@ enter identifier info (SymbolTable table) = SymbolTable lookup :: Identifier -> SymbolTable -> Maybe Info lookup identifier (SymbolTable table) = HashMap.lookup identifier table +fromList :: [(Identifier, Info)] -> SymbolTable +fromList = SymbolTable . HashMap.fromList + data ParameterInfo = ParameterInfo { name :: Identifier , type' :: Type diff --git a/tests/Language/Elna/NameAnalysisSpec.hs b/tests/Language/Elna/NameAnalysisSpec.hs index f0b133e..9d63e74 100644 --- a/tests/Language/Elna/NameAnalysisSpec.hs +++ b/tests/Language/Elna/NameAnalysisSpec.hs @@ -4,7 +4,14 @@ module Language.Elna.NameAnalysisSpec import Data.Text (Text) import Text.Megaparsec (runParser) -import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy) +import Test.Hspec + ( Spec + , describe + , expectationFailure + , it + , shouldBe + , shouldSatisfy + ) import Language.Elna.NameAnalysis (Error(..), nameAnalysis) import Language.Elna.SymbolTable (Info(..), SymbolTable) import qualified Language.Elna.SymbolTable as SymbolTable @@ -47,3 +54,25 @@ spec = describe "nameAnalysis" $ do actual <- nameAnalysisOnText given actual `shouldSatisfy` (expected ==) . fmap (SymbolTable.lookup "C") + + it "puts parameters into the local symbol table" $ do + let given = "proc main(ref param: int) {}" + expected = SymbolTable.enter "param" (VariableInfo intType True) SymbolTable.empty + actual <- nameAnalysisOnText given + + case SymbolTable.lookup "main" <$> actual of + Right lookupResult + | Just (ProcedureInfo localTable _) <- lookupResult -> + localTable `shouldBe` expected + _ -> expectationFailure "Procedure symbol not found" + + it "puts variables into the local symbol table" $ do + let given = "proc main() { var var1: int; }" + expected = SymbolTable.enter "var1" (VariableInfo intType False) SymbolTable.empty + actual <- nameAnalysisOnText given + + case SymbolTable.lookup "main" <$> actual of + Right lookupResult + | Just (ProcedureInfo localTable _) <- lookupResult -> + localTable `shouldBe` expected + _ -> expectationFailure "Procedure symbol not found"