Create procedure local symbol table

This commit is contained in:
Eugen Wissner 2024-08-04 12:23:19 +02:00
parent 5f8d9abe76
commit 9cb9ab536f
4 changed files with 56 additions and 16 deletions

9
TODO
View File

@ -1,9 +1,4 @@
# Name analysis # Name analysis
- Collect all global type and procedure definitions. - Ensure type, procedure, variable and parameter names are unique.
Give errors if: - Check symbols used in procedures are defined.
- The type is already defined.
- Base type is not defined.
- Replace equivalent type with its base type.
- Check definitions inside procedures.

View File

@ -8,7 +8,7 @@ import Control.Monad.Trans.Reader (ReaderT(..), ask, 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(..))
import Language.Elna.SymbolTable (Info(..), SymbolTable, symbolTable) 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(..))
@ -38,7 +38,7 @@ instance Monad NameAnalysis
nameAnalysis :: AST.Program -> Either Error SymbolTable nameAnalysis :: AST.Program -> Either Error SymbolTable
nameAnalysis = runExcept nameAnalysis = runExcept
. flip runReaderT symbolTable . flip runReaderT builtInSymbolTable
. runNameAnalysis . runNameAnalysis
. program . program
@ -51,9 +51,21 @@ declaration :: SymbolTable -> AST.Declaration -> NameAnalysis SymbolTable
declaration globalTable (AST.TypeDefinition identifier typeExpression) declaration globalTable (AST.TypeDefinition identifier typeExpression)
= flip (SymbolTable.enter identifier) globalTable . TypeInfo = flip (SymbolTable.enter identifier) globalTable . TypeInfo
<$> withSymbolTable globalTable (dataType typeExpression) <$> withSymbolTable globalTable (dataType typeExpression)
declaration globalTable (AST.ProcedureDefinition identifier _parameters _variables _body) = declaration globalTable (AST.ProcedureDefinition identifier parameters variables _body) = do
let localTable = SymbolTable.empty parametersInfo <- mapM parameter parameters
in pure $ SymbolTable.enter identifier (ProcedureInfo localTable mempty) globalTable 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 :: forall a. SymbolTable -> NameAnalysis a -> NameAnalysis a
withSymbolTable symbolTable' = NameAnalysis withSymbolTable symbolTable' = NameAnalysis

View File

@ -4,8 +4,9 @@ module Language.Elna.SymbolTable
, SymbolTable , SymbolTable
, empty , empty
, enter , enter
, fromList
, lookup , lookup
, symbolTable , builtInSymbolTable
) where ) where
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
@ -26,8 +27,8 @@ instance Monoid SymbolTable
where where
mempty = SymbolTable HashMap.empty mempty = SymbolTable HashMap.empty
symbolTable :: SymbolTable builtInSymbolTable :: SymbolTable
symbolTable = SymbolTable $ HashMap.fromList builtInSymbolTable = SymbolTable $ HashMap.fromList
[ ("boolean", TypeInfo booleanType) [ ("boolean", TypeInfo booleanType)
, ("int", TypeInfo intType) , ("int", TypeInfo intType)
] ]
@ -42,6 +43,9 @@ enter identifier info (SymbolTable table) = SymbolTable
lookup :: Identifier -> SymbolTable -> Maybe Info lookup :: Identifier -> SymbolTable -> Maybe Info
lookup identifier (SymbolTable table) = HashMap.lookup identifier table lookup identifier (SymbolTable table) = HashMap.lookup identifier table
fromList :: [(Identifier, Info)] -> SymbolTable
fromList = SymbolTable . HashMap.fromList
data ParameterInfo = ParameterInfo data ParameterInfo = ParameterInfo
{ name :: Identifier { name :: Identifier
, type' :: Type , type' :: Type

View File

@ -4,7 +4,14 @@ module Language.Elna.NameAnalysisSpec
import Data.Text (Text) import Data.Text (Text)
import Text.Megaparsec (runParser) 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.NameAnalysis (Error(..), nameAnalysis)
import Language.Elna.SymbolTable (Info(..), SymbolTable) import Language.Elna.SymbolTable (Info(..), SymbolTable)
import qualified Language.Elna.SymbolTable as SymbolTable import qualified Language.Elna.SymbolTable as SymbolTable
@ -47,3 +54,25 @@ spec = describe "nameAnalysis" $ do
actual <- nameAnalysisOnText given actual <- nameAnalysisOnText given
actual `shouldSatisfy` (expected ==) . fmap (SymbolTable.lookup "C") 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"