summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO9
-rw-r--r--lib/Language/Elna/NameAnalysis.hs22
-rw-r--r--lib/Language/Elna/SymbolTable.hs10
-rw-r--r--tests/Language/Elna/NameAnalysisSpec.hs31
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"