summaryrefslogtreecommitdiff
path: root/lib/Language/Elna/Frontend/SymbolTable.hs
blob: 4333acc53f551ba2bd94ab593f7513d095aac84a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
module Language.Elna.Frontend.SymbolTable
    ( SymbolTable
    , Info(..)
    , ParameterInfo(..)
    , builtInSymbolTable
    , empty
    , enter
    , fromList
    , lookup
    , member
    , scope
    , size
    , toMap
    , update
    ) where

import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.List (sort)
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NonEmpty
import Data.Maybe (isJust)
import Data.Vector (Vector)
import qualified Data.Vector as Vector
import Language.Elna.Location (Identifier(..))
import Language.Elna.Frontend.Types (Type(..), intType)
import Prelude hiding (lookup)

data SymbolTable = SymbolTable (Maybe SymbolTable) (HashMap Identifier Info)
    deriving (Eq, Show)

empty :: SymbolTable
empty = SymbolTable Nothing HashMap.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 Nothing $ HashMap.fromList
    [ ("printi", ProcedureInfo empty (Vector.singleton printiX))
    , ("printc", ProcedureInfo empty (Vector.singleton printcI))
    , ("exit", ProcedureInfo empty Vector.empty)
    , ("int", TypeInfo intType)
    ]
  where
    printiX = ParameterInfo
        { name = "x"
        , type' = intType
        , isReferenceParameter = False
        }
    printcI = ParameterInfo
        { name = "i"
        , type' = intType
        , isReferenceParameter = False
        }

toMap :: SymbolTable  -> HashMap Identifier Info
toMap (SymbolTable _ map') = map'

enter :: Identifier -> Info -> SymbolTable -> Maybe SymbolTable
enter identifier info table@(SymbolTable parent hashTable)
    | member identifier table = Nothing
    | otherwise = Just
        $ SymbolTable parent (HashMap.insert identifier info hashTable)

lookup :: Identifier -> SymbolTable -> Maybe Info
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 table =
    isJust $ lookup identifier table

size :: SymbolTable -> Int
size (SymbolTable _ map') = HashMap.size map'

fromList :: [(Identifier, Info)] -> Either (NonEmpty Identifier) SymbolTable
fromList elements
    | Just identifierDuplicates' <- identifierDuplicates =
        Left identifierDuplicates'
    | otherwise = Right $ SymbolTable Nothing $ HashMap.fromList elements
  where
    identifierDuplicates = NonEmpty.nonEmpty
        $ fmap NonEmpty.head
        $ filter ((> 1) . NonEmpty.length)
        $ NonEmpty.group . sort
        $ fst <$> elements

data ParameterInfo = ParameterInfo
    { name :: Identifier
    , type' :: Type
    , isReferenceParameter :: Bool
    } deriving (Eq, Show)

data Info
    = TypeInfo Type
    | VariableInfo Bool Type
    | ProcedureInfo SymbolTable (Vector ParameterInfo)
    deriving (Eq, Show)