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
|
module Micro.Compiler
( version,
compile,
compileToAst,
)
where
import Control.Monad.State
import Data.Maybe (catMaybes, fromMaybe, isNothing)
import Micro.Asm.Sdcc (generate)
import qualified Micro.Ast as A
import Micro.Env
import Micro.Error
import Text.Parsec (SourcePos)
version :: String
version = "0.1.0"
data CompState = CompState
{ stEnv :: Env,
stErr :: [Error]
}
deriving (Show)
data CompResult = Result
{ -- last resolved type
crType :: Maybe A.Type,
crExpr :: A.Expr
}
typeResult :: Maybe A.Type -> A.Expr -> CompResult
typeResult t e = Result t e
startState :: CompState
startState = CompState emptyEnv []
-- | @foldlEither fn init xs@ folds left @xs@ applying a function @fn@ that
-- returns either, returning accumulated right and the collected lefts as a
-- list.
foldlEither :: (accr -> b -> Either accl accr) -> (accr, [accl]) -> [b] -> (accr, [accl])
foldlEither fn init xs =
foldl
( \(r, l) x -> case fn r x of
Left e -> (r, e : l)
Right x -> (x, l)
)
init
xs
-- | @addError error@ adds @error@ to the state and returns no type to allow
-- the compilation to continue.
addError :: Error -> State CompState CompResult
addError e = do
st <- get
put st {stErr = e : stErr st}
pure $ typeResult Nothing A.Nop
-- | @typecheckCall args params@ resolves @args@ and compares it with @params@,
-- returning a string describing an error or Nothing in case of type match.
typecheckCall :: [A.Expr] -> [A.Type] -> State CompState (Maybe String)
typecheckCall args params
| length args /= length params = return $ Just "invalid number of arguments in function call"
| length params == 0 = pure Nothing
| otherwise = do
-- resolve all args types
-- FIXME: this break constant folding
rargs <- traverse compileOne args
let targs = map crType rargs
case sequence targs of
Just t
-- after resolution, we could have less "rights"
| length t /= length params -> return $ Nothing
-- compare types
| all (\(a, b) -> a == b) $ zip t params ->
return $ Nothing -- all good!
| otherwise -> return $ Just ("type mismatch in function call\n found: " ++ A.showList t ++ "\n expected: " ++ A.showList params)
Nothing ->
-- there was an error in on argument
pure Nothing
showMaybet :: Maybe A.Type -> String
showMaybet Nothing = "()"
showMaybet (Just t) = show t
typecheck :: Maybe A.Type -> Maybe A.Type -> Maybe String
typecheck expected found
| expected == found = Nothing
| otherwise = Just $ "type mismatch\n found: " ++ showMaybet found ++ "\n expected: " ++ showMaybet expected
-- built-in types
types :: [String]
types = ["bool", "u8", "s8", "u16", "s16"]
definedType :: A.Type -> Bool
definedType (A.Type t) = t `elem` types
definedType (A.FuncType ts r) =
all definedType ts && fromMaybe False (fmap definedType r)
verifyFuncType :: String -> [A.FuncParam] -> Maybe A.Type -> SourcePos -> [Error]
verifyFuncType ident params ret pos = do
( catMaybes $
map
( \(_, t, _, pos) ->
if not (definedType t)
then Just $ Error UndefinedType ("undefined type in declaration of \"" ++ ident ++ "\"") pos
else Nothing
)
params
)
++ if not (fromMaybe True (fmap definedType ret))
then [Error UndefinedType ("undefined return type in \"" ++ ident ++ "\"") pos]
else []
compileOne :: A.Expr -> State CompState CompResult
compileOne x = do
case x of
(A.Module _ _) -> return $ typeResult Nothing x
(A.Num _ _) -> return $ typeResult (Just $ A.Type "u8") x -- TODO: placeholder
(A.Bool' _ _) -> return $ typeResult (Just $ A.Type "bool") x
(A.BinOp A.Plus pos (A.Num a _) (A.Num b _)) ->
-- TODO: overflow check
return $ typeResult (Just $ A.Type "u8") (A.Num (a + b) pos)
(A.BinOp op pos a b) -> do
ra <- compileOne a
let (ta, ea) = (crType ra, crExpr ra)
rb <- compileOne b
let (tb, eb) = (crType rb, crExpr rb)
case op of
A.Assign -> case a of
(A.Variable _ _) -> case typecheck ta tb of
Nothing -> return $ typeResult ta (A.BinOp A.Assign pos ea eb)
Just err -> addError $ Error TypeError err pos
_ -> addError $ Error InvalidTarget "invalid assignment target" pos
_ -> case typecheck ta tb of
Nothing -> compileOne (A.BinOp op pos ea eb)
Just err -> addError $ Error TypeError err pos
(A.Func ident params ret body priv anon pos) -> do
-- current env
st <- get
-- check for undefined types
(ev, errs) <- return $ (stEnv st, (verifyFuncType ident params ret pos) ++ stErr st)
-- updated with the function
(ev, errs) <-
return $ case addSymUniq ev (Sym ident ftype priv False pos) of
Left err -> (ev, err : errs)
Right ev -> (ev, errs)
-- lambdas can only access local variables (closures aren't supported)
fev <- return $ if anon then emptyEnv else ev
-- with parameters
(nev, errs) <- return $ foldlEither addSymUniq (addEnv fev, errs) $ map (toSym True) params
-- helper for return
nev <- return $ addSym nev $ Sym "$fn$" ftype True True pos
put st {stEnv = nev, stErr = errs}
r <- compileAll body
st <- get
-- store updated errors and the env with the function
put st {stEnv = ev}
rbody <- case r of
Right (_, xs) -> pure $ xs
Left _ -> pure $ body
return $ typeResult (Just ftype) (A.Func ident params ret rbody priv anon pos)
where
ftype = A.toFuncType params ret
(A.Call ident args pos) -> do
r <- compileOne ident
case crType r of
Just (A.FuncType params rtyp) -> do
r <- typecheckCall args params
case r of
Just err -> addError $ Error TypeError err pos
Nothing -> return $ typeResult rtyp x
_ -> addError $ Error NonCallable "non callable value in function call" pos
(A.Var ident typ val priv pos) -> do
st <- get
(ev, errs) <- return $ foldlEither addSymUniq (stEnv st, stErr st) [Sym ident typ priv True pos]
errs <-
return $
if not (definedType typ)
then Error UndefinedType ("undefined type in declaration \"" ++ ident ++ "\"") pos : errs
else errs
put st {stEnv = ev, stErr = errs}
r <- compileOne val
case typecheck (Just typ) (crType r) of
Just err -> addError $ Error TypeError err pos
Nothing -> return $ typeResult (Just typ) x
(A.Return value pos) -> do
st <- get
case getSyml (stEnv st) "$fn$" of
Just Sym {symType = A.FuncType _ rtyp} -> do
case value of
Nothing ->
if isNothing rtyp
then return (typeResult Nothing x)
else addError $ Error TypeError ("invalid return value\n found: ()\n expected: " ++ showMaybet rtyp) pos
Just v -> do
r <- compileOne v
case typecheck rtyp (crType r) of
Just err -> addError $ Error TypeError err pos
Nothing -> return $ typeResult rtyp (A.Return (Just (crExpr r)) pos)
_ -> addError $ Error UnexpectedReturn "return without function call" pos
(A.Variable ident pos) -> do
st <- get
case getSym (stEnv st) ident of
Just Sym {symType = t} -> return $ typeResult (Just t) x
Nothing -> addError $ Error Undefined ("undefined \"" ++ ident ++ "\"") pos
(A.Nop) -> return $ typeResult Nothing x
compileAll :: [A.Expr] -> State CompState (Either [Error] (SymMap, [A.Expr]))
compileAll ast = do
result <- traverse compileOne ast
st <- get
let (Env sym _) = stEnv st
let errs = stErr st
case errs of
[] -> pure $ Right $ (sym, (map (\r -> crExpr r) result))
_ -> return $ Left errs
compileToAst :: [A.Expr] -> Either [Error] [A.Expr]
compileToAst ast = do
(_, expr) <- evalState (compileAll ast) startState
pure $ expr
compile :: [A.Expr] -> Either [Error] String
compile ast = do
(sym, expr) <- evalState (compileAll ast) startState
return $ generate version sym expr
|