aboutsummaryrefslogtreecommitdiff
path: root/Sil/Check.icl
blob: dc9f6a7c4dbaf2a987301eec9a9105a5031322c6 (plain) (blame)
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
implementation module Sil.Check

import StdBool
import StdFile
from StdFunc import flip, o
import StdList
import StdOverloaded
import StdString
import StdTuple

import Data.Error
from Data.Func import $, mapSt, seqSt
import Data.List
import Data.Maybe
import Data.Tuple
from Text import <+

import Sil.Syntax
import Sil.Types

instance toString CheckError
where
	toString NoMainFunction
		= "Error: no main function."
	toString (MainFunctionInvalidType t)
		= "Error: function 'main' should not have arguments has type " <+ t <+ "."
	toString (DuplicateFunctionName n)
		= "Error: multiply defined: '" <+ n <+ "'."
	toString (DuplicateLocalName f arg)
		= "Error: multiply defined: '" <+ arg <+ "' in '" <+ f <+ "'."
	toString (ReturnExpressionFromVoid f)
		= "Type error: an expression was returned from void function '" <+ f <+ "'."
	toString (NoReturnFromNonVoid f)
		= "Type error: no return from non-void function '" <+ f <+ "'."
	toString (LocalVoid f l)
		= "Type error: local variable '" <+ l <+ "' in '" <+ f <+ "' cannot have type Void."

instance <<< CheckError where <<< f e = f <<< toString e <<< "\r\n"

checkProgram :: *(Maybe *File) Program -> *([CheckError], *Maybe *File)
checkProgram err prog
	= checkErrors
	[ checkFunctionNames
	, checkMainFunction
	] prog
	$ appFst flatten $ mapSt (flip checkFunction) prog.p_funs err
where
	checkMainFunction :: Program -> [CheckError]
	checkMainFunction p = case [f \\ f <- p.p_funs | f.f_name == "main"] of
		[] -> [NoMainFunction]
		_  -> []

	checkFunctionNames :: Program -> [CheckError]
	checkFunctionNames p =
		[ DuplicateFunctionName $ hd fs
		\\ fs <- tails [f.f_name \\ f <- p.p_funs]
		| not (isEmpty fs) && isMember (hd fs) (tl fs)]

checkFunction :: *(Maybe *File) Function -> *([CheckError], *Maybe *File)
checkFunction err f = checkErrors
	[ checkLocals
	, checkReturnAndVoid
	, checkMainFunctionType
	] f $ noErrors err
where
	checkReturnAndVoid :: Function -> [CheckError]
	checkReturnAndVoid f = case f.f_type of
		TVoid -> case [st \\ st=:(Return (Just _)) <- allStatements f] of
			[] -> []
			_  -> [ReturnExpressionFromVoid f.f_name]
		_     -> if (sureToReturn f.f_code) [] [NoReturnFromNonVoid f.f_name]
	where
		sureToReturn :: CodeBlock -> Bool
		sureToReturn cb = case cb.cb_content of
			[]  -> False
			sts -> case last sts of
				Return _       -> True
				While _ cb`    -> sureToReturn cb`
				If bs (Just e) -> all sureToReturn [e:map snd bs]
				If bs Nothing  -> all (sureToReturn o snd) bs
				_              -> False

	checkMainFunctionType :: Function -> [CheckError]
	checkMainFunctionType f=:{f_name="main",f_args=[]} = []
	checkMainFunctionType f=:{f_name="main"} = [MainFunctionInvalidType $ fromOk $ fromJust $ type zero f]
	checkMainFunctionType _ = []

	checkLocals :: Function -> [CheckError]
	checkLocals f =
		checkDupName [a.arg_name \\ a <- f.f_args] f.f_code ++
		concatMap checkVoid (allLocals f)
	where
		checkDupName :: [Name] CodeBlock -> [CheckError]
		checkDupName defined cb =
			[DuplicateLocalName f.f_name l \\ l <- defined | isMember l locals] ++
			concatMap (checkDupName (locals ++ defined)) (underlyingCBs cb)
		where locals = [i.init_name \\ i <- cb.cb_init]

		underlyingCBs :: CodeBlock -> [CodeBlock]
		underlyingCBs cb = concatMap findCBs cb.cb_content
		where
			findCBs (Declaration _ _) = []
			findCBs (Application _)   = []
			findCBs (Return _)        = []
			findCBs (If bs (Just e))  = [e:map snd bs]
			findCBs (If bs Nothing)   = map snd bs
			findCBs (While _ cb)      = [cb]
			findCBs (MachineStm _)    = []

		checkVoid :: (Type, Name) -> [CheckError]
		checkVoid (TVoid, n) = [LocalVoid f.f_name n]
		checkVoid _          = []

checkErrors :: [(a -> [CheckError])] a *([CheckError], Maybe *File) -> *([CheckError], *Maybe *File)
checkErrors cks x st = seqSt error (concatMap (flip ($) x) cks) st

error :: CheckError *([CheckError], *Maybe *File) -> *([CheckError], *Maybe *File)
error e (es, err) = ([e:es], err <?< e)

noErrors :: *(Maybe *File) -> *([CheckError], *Maybe *File)
noErrors f = ([], f)

(<?<) infixl :: !*(Maybe *File) !a -> *Maybe *File | <<< a
(<?<) (Just f) x = Just (f <<< x)
(<?<) Nothing  _ = Nothing