Collect function declaration symbols
This commit is contained in:
parent
c9ff4f0a2a
commit
8f5e3d755e
6
TODO
6
TODO
@ -9,3 +9,9 @@
|
|||||||
- Don't ignore relocations where the symbol is not defined in the symbol table.
|
- Don't ignore relocations where the symbol is not defined in the symbol table.
|
||||||
Report an error about an undefined symbol.
|
Report an error about an undefined symbol.
|
||||||
- Don't hardcode symbols in symbolEntry.
|
- Don't hardcode symbols in symbolEntry.
|
||||||
|
|
||||||
|
# Name analysis
|
||||||
|
|
||||||
|
- Format error messages.
|
||||||
|
- Name analyzer collects procedure names in the first run. Implement the second run,
|
||||||
|
where the analyzer goes into and checks procedures.
|
||||||
|
@ -1,39 +1,23 @@
|
|||||||
module Language.Elna.NameAnalysis
|
module Language.Elna.NameAnalysis
|
||||||
( nameAnalysis
|
( nameAnalysis
|
||||||
-- , Error(..)
|
, Error(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import qualified Data.List.NonEmpty as NonEmpty
|
||||||
|
import qualified Data.Vector as Vector
|
||||||
|
import qualified Language.Elna.SymbolTable as SymbolTable
|
||||||
import qualified Language.Elna.AST as AST
|
import qualified Language.Elna.AST as AST
|
||||||
import Language.Elna.SymbolTable
|
import Language.Elna.SymbolTable
|
||||||
( SymbolTable
|
( SymbolTable
|
||||||
, empty
|
, Info(..)
|
||||||
--, Info(..)
|
, ParameterInfo(..)
|
||||||
-- , ParameterInfo(..)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
nameAnalysis :: AST.Program -> SymbolTable -- Either Error SymbolTable
|
|
||||||
nameAnalysis = const empty {- runExcept
|
|
||||||
. flip runReaderT builtInSymbolTable
|
|
||||||
. runNameAnalysis
|
|
||||||
. program -}
|
|
||||||
{-
|
|
||||||
import Control.Monad.Trans.Except (Except, runExcept, throwE)
|
import Control.Monad.Trans.Except (Except, runExcept, throwE)
|
||||||
import Control.Monad.Trans.Reader
|
|
||||||
( ReaderT(..)
|
|
||||||
, ask
|
|
||||||
, asks
|
|
||||||
, runReaderT
|
|
||||||
, withReaderT
|
|
||||||
)
|
|
||||||
import Data.Functor ((<&>))
|
import Data.Functor ((<&>))
|
||||||
import Language.Elna.Location (Identifier(..))
|
import Language.Elna.Location (Identifier(..))
|
||||||
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 (foldM, unless)
|
|
||||||
import qualified Data.List.NonEmpty as NonEmpty
|
|
||||||
import Data.Foldable (traverse_)
|
import Data.Foldable (traverse_)
|
||||||
import qualified Data.Vector as Vector
|
import Control.Monad (foldM)
|
||||||
|
|
||||||
data Error
|
data Error
|
||||||
= UndefinedTypeError Identifier
|
= UndefinedTypeError Identifier
|
||||||
@ -44,7 +28,7 @@ data Error
|
|||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
newtype NameAnalysis a = NameAnalysis
|
newtype NameAnalysis a = NameAnalysis
|
||||||
{ runNameAnalysis :: ReaderT SymbolTable (Except Error) a
|
{ runNameAnalysis :: Except Error a
|
||||||
}
|
}
|
||||||
|
|
||||||
instance Functor NameAnalysis
|
instance Functor NameAnalysis
|
||||||
@ -60,30 +44,77 @@ instance Monad NameAnalysis
|
|||||||
where
|
where
|
||||||
(NameAnalysis x) >>= f = NameAnalysis $ x >>= (runNameAnalysis . f)
|
(NameAnalysis x) >>= f = NameAnalysis $ x >>= (runNameAnalysis . f)
|
||||||
|
|
||||||
program :: AST.Program -> NameAnalysis SymbolTable
|
nameAnalysis :: AST.Program -> Either Error SymbolTable
|
||||||
program (AST.Program declarations)
|
nameAnalysis = runExcept
|
||||||
= NameAnalysis ask
|
. runNameAnalysis
|
||||||
>>= flip (foldM declaration) declarations
|
. program SymbolTable.builtInSymbolTable
|
||||||
|
|
||||||
|
program :: SymbolTable -> AST.Program -> NameAnalysis SymbolTable
|
||||||
|
program symbolTable (AST.Program declarations) = do
|
||||||
|
globalTable <- foldM procedureDeclaration symbolTable declarations
|
||||||
|
foldM declaration globalTable declarations
|
||||||
|
|
||||||
|
procedureDeclaration :: SymbolTable -> AST.Declaration -> NameAnalysis SymbolTable
|
||||||
|
procedureDeclaration globalTable (AST.ProcedureDeclaration identifier parameters _ _) = do
|
||||||
|
parametersInfo <- mapM (parameter globalTable) parameters
|
||||||
|
let procedureInfo = ProcedureInfo SymbolTable.empty
|
||||||
|
$ Vector.fromList parametersInfo
|
||||||
|
maybe (NameAnalysis $ throwE $ IdentifierAlreadyDefinedError identifier) pure
|
||||||
|
$ SymbolTable.enter identifier procedureInfo globalTable
|
||||||
|
|
||||||
declaration :: SymbolTable -> AST.Declaration -> NameAnalysis SymbolTable
|
declaration :: SymbolTable -> AST.Declaration -> NameAnalysis SymbolTable
|
||||||
declaration globalTable (AST.TypeDefinition identifier typeExpression)
|
declaration globalTable (AST.ProcedureDeclaration identifier parameters variables body) = do
|
||||||
= withSymbolTable globalTable (dataType typeExpression)
|
variableInfo <- mapM (variableDeclaration globalTable) variables
|
||||||
>>= flip (enter identifier) globalTable . TypeInfo
|
parameterInfo <- mapM (parameterToVariableInfo globalTable) parameters
|
||||||
declaration globalTable (AST.ProcedureDefinition identifier parameters variables body) = do
|
procedureTable <- fmap (SymbolTable.scope globalTable)
|
||||||
parametersInfo <- mapM parameter parameters
|
$ either (NameAnalysis . throwE . IdentifierAlreadyDefinedError . NonEmpty.head) pure
|
||||||
variableInfo <- mapM variableDeclaration variables
|
|
||||||
newTable <- either (identifierAlreadyDefinedError . NonEmpty.head) pure
|
|
||||||
$ SymbolTable.fromList
|
$ SymbolTable.fromList
|
||||||
$ fmap parameterToVariableInfo parametersInfo
|
$ parameterInfo <> variableInfo
|
||||||
<> variableInfo
|
traverse_ (statement procedureTable) body
|
||||||
traverse_ (statement globalTable) body
|
pure $ SymbolTable.update (updater procedureTable) identifier globalTable
|
||||||
let procedureInfo = ProcedureInfo newTable
|
where
|
||||||
$ Vector.fromList parametersInfo
|
updater procedureTable (ProcedureInfo _ parameters') = Just
|
||||||
enter identifier procedureInfo globalTable
|
$ ProcedureInfo procedureTable parameters'
|
||||||
|
updater _ _ = Nothing
|
||||||
|
|
||||||
|
parameterToVariableInfo :: SymbolTable -> AST.Parameter -> NameAnalysis (Identifier, Info)
|
||||||
|
parameterToVariableInfo symbolTable (AST.Parameter identifier typeExpression isReferenceParameter')
|
||||||
|
= (identifier,) . VariableInfo isReferenceParameter'
|
||||||
|
<$> dataType symbolTable typeExpression
|
||||||
|
|
||||||
|
variableDeclaration :: SymbolTable -> AST.VariableDeclaration -> NameAnalysis (Identifier, Info)
|
||||||
|
variableDeclaration globalTable (AST.VariableDeclaration identifier typeExpression)
|
||||||
|
= (identifier,) . VariableInfo False
|
||||||
|
<$> dataType globalTable typeExpression
|
||||||
|
|
||||||
|
parameter :: SymbolTable -> AST.Parameter -> NameAnalysis ParameterInfo
|
||||||
|
parameter environmentSymbolTable (AST.Parameter identifier typeExpression isReferenceParameter') = do
|
||||||
|
parameterType <- dataType environmentSymbolTable typeExpression
|
||||||
|
case parameterType of
|
||||||
|
ArrayType _ _
|
||||||
|
| not isReferenceParameter' -> NameAnalysis
|
||||||
|
$ throwE $ UnexpectedArrayByValue identifier
|
||||||
|
_ ->
|
||||||
|
let parameterInfo = ParameterInfo
|
||||||
|
{ name = identifier
|
||||||
|
, type' = parameterType
|
||||||
|
, isReferenceParameter = isReferenceParameter'
|
||||||
|
}
|
||||||
|
in pure parameterInfo
|
||||||
|
|
||||||
|
dataType :: SymbolTable -> AST.TypeExpression -> NameAnalysis Type
|
||||||
|
dataType environmentSymbolTable (AST.NamedType baseType) = do
|
||||||
|
case SymbolTable.lookup baseType environmentSymbolTable of
|
||||||
|
Just baseInfo
|
||||||
|
| TypeInfo baseType' <- baseInfo -> pure baseType'
|
||||||
|
| otherwise -> NameAnalysis $ throwE $ UnexpectedTypeInfoError baseInfo
|
||||||
|
_ -> NameAnalysis $ throwE $ UndefinedTypeError baseType
|
||||||
|
dataType environmentSymbolTable (AST.ArrayType arraySize baseType) =
|
||||||
|
dataType environmentSymbolTable baseType <&> ArrayType arraySize
|
||||||
|
|
||||||
statement :: SymbolTable -> AST.Statement -> NameAnalysis ()
|
statement :: SymbolTable -> AST.Statement -> NameAnalysis ()
|
||||||
statement _ AST.EmptyStatement = pure ()
|
statement _ AST.EmptyStatement = pure ()
|
||||||
statement globalTable (AST.AssignmentStatement lvalue rvalue)
|
{- statement globalTable (AST.AssignmentStatement lvalue rvalue)
|
||||||
= variableAccess globalTable lvalue
|
= variableAccess globalTable lvalue
|
||||||
>> expression globalTable rvalue
|
>> expression globalTable rvalue
|
||||||
statement globalTable (AST.IfStatement ifCondition ifStatement elseStatement)
|
statement globalTable (AST.IfStatement ifCondition ifStatement elseStatement)
|
||||||
@ -170,41 +201,4 @@ variableDeclaration :: AST.VariableDeclaration -> NameAnalysis (Identifier, Info
|
|||||||
variableDeclaration (AST.VariableDeclaration identifier typeExpression)
|
variableDeclaration (AST.VariableDeclaration identifier typeExpression)
|
||||||
= (identifier,) . VariableInfo False
|
= (identifier,) . VariableInfo False
|
||||||
<$> dataType typeExpression
|
<$> dataType typeExpression
|
||||||
|
|
||||||
parameter :: AST.Parameter -> NameAnalysis ParameterInfo
|
|
||||||
parameter (AST.Parameter identifier typeExpression isReferenceParameter') = do
|
|
||||||
parameterType <- dataType typeExpression
|
|
||||||
case parameterType of
|
|
||||||
ArrayType _ _
|
|
||||||
| not isReferenceParameter' -> NameAnalysis
|
|
||||||
$ lift $ throwE $ UnexpectedArrayByValue identifier
|
|
||||||
_ ->
|
|
||||||
let parameterInfo = ParameterInfo
|
|
||||||
{ name = identifier
|
|
||||||
, type' = parameterType
|
|
||||||
, isReferenceParameter = isReferenceParameter'
|
|
||||||
}
|
|
||||||
in pure parameterInfo
|
|
||||||
|
|
||||||
parameterToVariableInfo :: ParameterInfo -> (Identifier, Info)
|
|
||||||
parameterToVariableInfo ParameterInfo{..} =
|
|
||||||
( name
|
|
||||||
, VariableInfo isReferenceParameter type'
|
|
||||||
)
|
|
||||||
|
|
||||||
withSymbolTable :: forall a. SymbolTable -> NameAnalysis a -> NameAnalysis a
|
|
||||||
withSymbolTable symbolTable' = NameAnalysis
|
|
||||||
. withReaderT (const symbolTable')
|
|
||||||
. runNameAnalysis
|
|
||||||
|
|
||||||
dataType :: AST.TypeExpression -> NameAnalysis Type
|
|
||||||
dataType (AST.NamedType baseType) = do
|
|
||||||
environmentSymbolTable <- NameAnalysis ask
|
|
||||||
case SymbolTable.lookup baseType environmentSymbolTable of
|
|
||||||
Just baseInfo
|
|
||||||
| TypeInfo baseType' <- baseInfo -> pure baseType'
|
|
||||||
| otherwise -> NameAnalysis $ lift $ throwE $ UnexpectedTypeInfoError baseInfo
|
|
||||||
_ -> NameAnalysis $ lift $ throwE $ UndefinedTypeError baseType
|
|
||||||
dataType (AST.ArrayType arraySize baseType) =
|
|
||||||
dataType baseType <&> ArrayType arraySize
|
|
||||||
-}
|
-}
|
||||||
|
@ -1,64 +1,72 @@
|
|||||||
module Language.Elna.SymbolTable
|
module Language.Elna.SymbolTable
|
||||||
( SymbolTable
|
( SymbolTable
|
||||||
, empty
|
, Info(..)
|
||||||
{-, Info(..)
|
|
||||||
, ParameterInfo(..)
|
, ParameterInfo(..)
|
||||||
, builtInSymbolTable
|
, builtInSymbolTable
|
||||||
|
, empty
|
||||||
, enter
|
, enter
|
||||||
, fromList
|
, fromList
|
||||||
, lookup
|
, lookup
|
||||||
, member -}
|
, member
|
||||||
|
, scope
|
||||||
|
, toMap
|
||||||
|
, update
|
||||||
) where
|
) where
|
||||||
|
|
||||||
data SymbolTable = SymbolTable -- (HashMap Identifier Info)
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
empty :: SymbolTable
|
|
||||||
empty = SymbolTable -- HashMap.empty
|
|
||||||
|
|
||||||
{-
|
|
||||||
import Data.HashMap.Strict (HashMap)
|
import Data.HashMap.Strict (HashMap)
|
||||||
import qualified Data.HashMap.Strict as HashMap
|
import qualified Data.HashMap.Strict as HashMap
|
||||||
import Data.List (sort)
|
import Data.List (sort)
|
||||||
import Data.List.NonEmpty (NonEmpty)
|
import Data.List.NonEmpty (NonEmpty)
|
||||||
import qualified Data.List.NonEmpty as NonEmpty
|
import qualified Data.List.NonEmpty as NonEmpty
|
||||||
|
import Data.Maybe (isJust)
|
||||||
import Data.Vector (Vector)
|
import Data.Vector (Vector)
|
||||||
import Language.Elna.Location (Identifier(..))
|
import Language.Elna.Location (Identifier(..))
|
||||||
import Language.Elna.Types (Type(..), intType, booleanType)
|
import Language.Elna.Types (Type(..), intType, booleanType)
|
||||||
import Prelude hiding (lookup)
|
import Prelude hiding (lookup)
|
||||||
|
|
||||||
instance Semigroup SymbolTable
|
data SymbolTable = SymbolTable (Maybe SymbolTable) (HashMap Identifier Info)
|
||||||
where
|
deriving (Eq, Show)
|
||||||
(SymbolTable lhs) <> (SymbolTable rhs) = SymbolTable $ rhs <> lhs
|
|
||||||
|
|
||||||
instance Monoid SymbolTable
|
empty :: SymbolTable
|
||||||
where
|
empty = SymbolTable Nothing HashMap.empty
|
||||||
mempty = empty
|
|
||||||
|
update :: (Info -> Maybe Info) -> Identifier -> SymbolTable -> SymbolTable
|
||||||
|
update updater key (SymbolTable parent mappings) = SymbolTable parent
|
||||||
|
$ HashMap.update updater key mappings
|
||||||
|
|
||||||
|
scope :: SymbolTable -> SymbolTable -> SymbolTable
|
||||||
|
scope parent (SymbolTable _ mappings) = SymbolTable (Just parent) mappings
|
||||||
|
|
||||||
builtInSymbolTable :: SymbolTable
|
builtInSymbolTable :: SymbolTable
|
||||||
builtInSymbolTable = SymbolTable $ HashMap.fromList
|
builtInSymbolTable = SymbolTable Nothing $ HashMap.fromList
|
||||||
[ ("boolean", TypeInfo booleanType)
|
[ ("boolean", TypeInfo booleanType)
|
||||||
, ("int", TypeInfo intType)
|
, ("int", TypeInfo intType)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
toMap :: SymbolTable -> HashMap Identifier Info
|
||||||
|
toMap (SymbolTable _ map') = map'
|
||||||
|
|
||||||
enter :: Identifier -> Info -> SymbolTable -> Maybe SymbolTable
|
enter :: Identifier -> Info -> SymbolTable -> Maybe SymbolTable
|
||||||
enter identifier info table@(SymbolTable hashTable)
|
enter identifier info table@(SymbolTable parent hashTable)
|
||||||
| member identifier table = Nothing
|
| member identifier table = Nothing
|
||||||
| otherwise = Just
|
| otherwise = Just
|
||||||
$ SymbolTable
|
$ SymbolTable parent (HashMap.insert identifier info hashTable)
|
||||||
$ HashMap.insert identifier info hashTable
|
|
||||||
|
|
||||||
lookup :: Identifier -> SymbolTable -> Maybe Info
|
lookup :: Identifier -> SymbolTable -> Maybe Info
|
||||||
lookup identifier (SymbolTable table) = HashMap.lookup identifier table
|
lookup identifier (SymbolTable parent table)
|
||||||
|
| Just found <- HashMap.lookup identifier table = Just found
|
||||||
|
| Just parent' <- parent = lookup identifier parent'
|
||||||
|
| otherwise = Nothing
|
||||||
|
|
||||||
member :: Identifier -> SymbolTable -> Bool
|
member :: Identifier -> SymbolTable -> Bool
|
||||||
member identifier (SymbolTable table) = HashMap.member identifier table
|
member identifier table =
|
||||||
|
isJust $ lookup identifier table
|
||||||
|
|
||||||
fromList :: [(Identifier, Info)] -> Either (NonEmpty Identifier) SymbolTable
|
fromList :: [(Identifier, Info)] -> Either (NonEmpty Identifier) SymbolTable
|
||||||
fromList elements
|
fromList elements
|
||||||
| Just identifierDuplicates' <- identifierDuplicates =
|
| Just identifierDuplicates' <- identifierDuplicates =
|
||||||
Left identifierDuplicates'
|
Left identifierDuplicates'
|
||||||
| otherwise = Right $ SymbolTable $ HashMap.fromList elements
|
| otherwise = Right $ SymbolTable Nothing $ HashMap.fromList elements
|
||||||
where
|
where
|
||||||
identifierDuplicates = NonEmpty.nonEmpty
|
identifierDuplicates = NonEmpty.nonEmpty
|
||||||
$ fmap NonEmpty.head
|
$ fmap NonEmpty.head
|
||||||
@ -77,4 +85,3 @@ data Info
|
|||||||
| VariableInfo Bool Type
|
| VariableInfo Bool Type
|
||||||
| ProcedureInfo SymbolTable (Vector ParameterInfo)
|
| ProcedureInfo SymbolTable (Vector ParameterInfo)
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
-}
|
|
||||||
|
14
src/Main.hs
14
src/Main.hs
@ -25,11 +25,13 @@ main = execParser commandLine >>= withCommandLine
|
|||||||
>>= withParsedInput defaultOutput
|
>>= withParsedInput defaultOutput
|
||||||
. runParser programP input
|
. runParser programP input
|
||||||
withParsedInput output (Right program) =
|
withParsedInput output (Right program) =
|
||||||
let symbolTable = nameAnalysis program
|
either print (withSymbolTable output program)
|
||||||
_ = typeAnalysis symbolTable program
|
$ nameAnalysis program
|
||||||
intermediate' = intermediate symbolTable program
|
|
||||||
in elfObject output
|
|
||||||
$ riscv32Elf
|
|
||||||
$ generateCode symbolTable intermediate'
|
|
||||||
withParsedInput _ (Left errorBundle) = putStrLn
|
withParsedInput _ (Left errorBundle) = putStrLn
|
||||||
$ errorBundlePretty errorBundle
|
$ errorBundlePretty errorBundle
|
||||||
|
withSymbolTable output program symbolTable =
|
||||||
|
let _ = typeAnalysis symbolTable program
|
||||||
|
intermediate' = intermediate symbolTable program
|
||||||
|
instructions = generateCode symbolTable intermediate'
|
||||||
|
in elfObject output
|
||||||
|
$ riscv32Elf instructions
|
||||||
|
Loading…
Reference in New Issue
Block a user