summaryrefslogtreecommitdiff
path: root/src/Language/Elna/AST.hs
blob: 0ac8eb59cc623a0bb7aab86431e28fc0b14d2826 (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
module Language.Elna.AST
    ( ConstantDefinition(..)
    , Declaration(..)
    , Expression(..)
    , Identifier(..)
    , Literal(..)
    , ProcedureDeclaration(..)
    , Program(..)
    , Statement(..)
    , VariableDeclaration(..)
    , TypeDefinition(..)
    , TypeName(..)
    ) where

import Data.Int (Int32)
import Data.List (intercalate)
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NonEmpty
import Data.Word (Word8)
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Char (chr)

newtype Identifier = Identifier { unIdentifier :: Text }
    deriving Eq

instance Show Identifier
  where
    show (Identifier identifier) = Text.unpack identifier

data TypeName
    = NamedType Identifier
    | PointerType TypeName
    | ArrayType TypeName Int32
    | ProcedureType [Parameter]
    deriving Eq

instance Show TypeName
  where
    show (NamedType typeName) = show typeName
    show (PointerType typeName) = '^' : show typeName
    show (ArrayType typeName elementCount) = concat
        [show typeName, "[", show elementCount, "]"]
    show (ProcedureType parameters) = "proc" <> showParameters parameters

data Literal
    = StringLiteral Text
    | IntegerLiteral Int32
    | ByteLiteral Word8
    | CharacterLiteral Word8
    | BooleanLiteral Bool
    | RecordLiteral (NonEmpty Argument)
    | VariantLiteral (NonEmpty Argument)
    | EnumLiteral Identifier
    deriving Eq

instance Show Literal
  where
    show (StringLiteral string) = Text.unpack
        $ "\"" <> string <> "\""
    show (IntegerLiteral integer) = show integer
    show (ByteLiteral word) = show word
    show (CharacterLiteral character) =
        '\'' : chr (fromEnum character) : ['\'']
    show (BooleanLiteral boolean)
        | boolean = "true"
        | otherwise = "false"
    show (RecordLiteral arguments) = showArguments arguments
    show (VariantLiteral arguments) = showArguments arguments
    show (EnumLiteral identifier) = show identifier

data Expression
    = VariableExpression Identifier
    | LiteralExpression Literal
    | NegationExpression Expression
    | NotExpression Expression
    | ReferenceExpression Expression
    | DereferenceExpression Expression
    | SumExpression Expression Expression
    | SubtractionExpression Expression Expression
    | ProductExpression Expression Expression
    | DivisionExpression Expression Expression
    | EqualExpression Expression Expression
    | NonEqualExpression Expression Expression
    | LessExpression Expression Expression
    | GreaterExpression Expression Expression
    | LessOrEqualExpression Expression Expression
    | GreaterOrEqualExpression Expression Expression
    | IfExpression Expression Statement Statement
    | LoopExpression Identifier Expression Statement
    | FieldExpression Expression Identifier
    deriving Eq

instance Show Expression
  where
    show (VariableExpression variable) = show variable
    show (LiteralExpression literal) = show literal
    show (NegationExpression negation) = '-' : show negation
    show (NotExpression negation) = "not " <> show negation
    show (ReferenceExpression reference) = '@' : show reference
    show (DereferenceExpression dereference) = show dereference <> "^"
    show (SumExpression lhs rhs) = concat [show lhs, " + ", show rhs]
    show (SubtractionExpression lhs rhs) = concat [show lhs, " - ", show rhs]
    show (ProductExpression lhs rhs) = concat [show lhs, " * ", show rhs]
    show (DivisionExpression lhs rhs) = concat [show lhs, " / ", show rhs]
    show (EqualExpression lhs rhs) = concat [show lhs, " = ", show rhs]
    show (NonEqualExpression lhs rhs) = concat [show lhs, " /= ", show rhs]
    show (LessExpression lhs rhs) = concat [show lhs, " < ", show rhs]
    show (GreaterExpression lhs rhs) = concat [show lhs, " > ", show rhs]
    show (LessOrEqualExpression lhs rhs) = concat [show lhs, " <= ", show rhs]
    show (GreaterOrEqualExpression lhs rhs) = concat [show lhs, " >= ", show rhs]
    show (IfExpression condition if' else') = concat
        [ "if ", show condition
        , " then ", show if'
        , " else ", show else'
        ]
    show (LoopExpression identifier accumulator iteration) = concat
        [ "loop ", show identifier
        , " := ", show accumulator
        , " do ", show iteration
        ]
    show (FieldExpression fieldExpression identifier) =
        show fieldExpression <> "." <> show identifier

data Statement
    = LetStatement Identifier TypeName Expression
    | CompoundStatement [Statement]
    | CallStatement Identifier [Expression]
    | ExpressionStatement Expression
    | BreakStatement Expression
    | ContinueStatement Expression
    deriving Eq

instance Show Statement
  where
    show (LetStatement identifier typeName definition) = concat
        ["let ", show identifier, ": ", show typeName, " := ", show definition]
    show (CompoundStatement statements) = "begin "
        <> intercalate "; " (show <$> statements) <> " end"
    show (CallStatement name parameters) = show name <> "("
        <> intercalate ", " (show <$> parameters) <> ")"
    show (ExpressionStatement expression) = show expression
    show (BreakStatement break') = "break " <> show break'
    show (ContinueStatement continue') = "continue " <> show continue'

data ConstantDefinition =
    ConstantDefinition Identifier TypeName Literal
    deriving Eq

instance Show ConstantDefinition
  where
    show (ConstantDefinition identifier typeName definition) = concat
        [show identifier, ": ", show typeName, " := ", show definition, ";"]

data Parameter = Parameter Identifier TypeName
    deriving Eq

instance Show Parameter
  where
    show (Parameter identifier typeName) =
        show identifier <> ": " <> show typeName

data Argument = Argument Identifier Literal
    deriving Eq

instance Show Argument
  where
    show (Argument identifier value) =
        concat [show identifier, ": ", show value]

data ProcedureDeclaration
    = ExternProcedureDeclaration Identifier [Parameter]
    | ProcedureDefinition Identifier [Parameter] Bool [ConstantDefinition] Statement
    deriving Eq

instance Show ProcedureDeclaration
  where
    show (ExternProcedureDeclaration procedureName parameters) =
        "proc " <> show procedureName <> showParameters parameters <> "; extern;"
    show (ProcedureDefinition procedureName parameters exports constants body)
        = "proc " <> show procedureName <> showParameters parameters <> ";"
        <> showAttributes exports
        <> showConstants constants
        <> show body <> ";"

data VariableDeclaration
    = ExternVariableDeclaration Identifier TypeName
    | VariableDefinition Identifier TypeName (Maybe Literal) Bool
    deriving Eq

instance Show VariableDeclaration
  where
    show (ExternVariableDeclaration identifier typeName)
        = show identifier <> ": " <> show typeName <> "; extern;"
    show (VariableDefinition identifier typeName initialValue exports)
        = show identifier <> ": " <> show typeName
        <> maybe "" ((" = " <>) . show) initialValue <> ";"
        <> showAttributes exports

data TypeDefinition
    = RecordDefinition Identifier (NonEmpty Parameter)
    | VariantDefinition Identifier (NonEmpty Parameter)
    | EnumerationDefinition Identifier (NonEmpty Identifier)
    deriving Eq

instance Show TypeDefinition
  where
    show (RecordDefinition identifier fields) = show identifier
        <> " = record " <> intercalate "; " (NonEmpty.toList $ show <$> fields)
        <> " end;"
    show (VariantDefinition identifier fields) = show identifier
        <> " = variant " <> intercalate "; " (NonEmpty.toList $ show <$> fields)
        <> " end;"
    show (EnumerationDefinition identifier members) = show identifier <> " = ("
        <> intercalate ", " (NonEmpty.toList $ show <$> members) <> ");"

data Declaration
    = TypeDeclaration TypeDefinition
    | ConstantDeclaration ConstantDefinition
    | VariableDeclaration VariableDeclaration
    | ProcedureDeclaration ProcedureDeclaration
    deriving Eq

instance Show Declaration
  where
    show (TypeDeclaration typeDefinition) = show typeDefinition
    show (ConstantDeclaration constantDefinition) = show constantDefinition
    show (VariableDeclaration variableDeclaration) = show variableDeclaration
    show (ProcedureDeclaration procedureDeclaration) = show procedureDeclaration

data Program = Program [Declaration] Statement
    deriving Eq

instance Show Program
  where
    show (Program declarations body) =
        let declarations' = foldr showDeclaration ("", []) declarations
         in unlines (snd declarations') <> show body <> "."
      where
        showDeclaration :: Declaration -> (String, [String]) -> (String, [String])
        showDeclaration (TypeDeclaration typeDeclaration) (previous, accumulator)
            | previous == "type" = ("type", show typeDeclaration : accumulator)
            | otherwise = ("type", "type " <> show typeDeclaration : accumulator)
        showDeclaration (ConstantDeclaration constantDeclaration) (previous, accumulator)
            | previous == "const" = ("const", show constantDeclaration : accumulator)
            | otherwise = ("const", "const " <> show constantDeclaration : accumulator)
        showDeclaration (VariableDeclaration variableDeclaration) (previous, accumulator)
            | previous == "var" = ("var", show variableDeclaration : accumulator)
            | otherwise = ("var", "var " <> show variableDeclaration : accumulator)
        showDeclaration (ProcedureDeclaration procedureDeclaration) (_previous, accumulator) =
            ("proc", show procedureDeclaration : accumulator)

showAttributes :: Bool -> String
showAttributes True = " export;"
showAttributes False = ""

showParameters :: [Parameter] -> String
showParameters parameters =
    "(" <> intercalate ", " (show <$> parameters) <> ")"

showArguments :: NonEmpty Argument -> String
showArguments arguments =
    "(" <> intercalate "; " (NonEmpty.toList $ show <$> arguments) <> ")"

showConstants :: [ConstantDefinition] -> String
showConstants constants
    | null constants = ""
    | otherwise = " const " <> unwords (show <$> constants) <> "\n"