Create procedure local symbol table
This commit is contained in:
parent
5f8d9abe76
commit
9cb9ab536f
9
TODO
9
TODO
@ -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.
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user