aboutsummaryrefslogtreecommitdiff
path: root/Sil/Compile.icl
blob: 495f277106a3f2f185c489cfcb3da9be8ac3c337 (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
implementation module Sil.Compile

import StdList
import StdString

import Control.Applicative
import Control.Monad
import Control.Monad.RWST
import Control.Monad.Trans
import Data.Error
from Data.Func import $
import qualified Data.Map as M
import Data.Maybe
import Data.Monoid
from Text import <+

import qualified ABC.Assembler as ABC

import Sil.Syntax
import Sil.Util

instance toString CompileError
where
	toString (UndefinedName n) = "Undefined name '" <+ n <+ "'."
	toString VariableLabel     = "Variable stored at label."
	toString FunctionOnStack   = "Function stored on the stack."
	toString UnknownError      = "Unknown error."

compile :: Program -> MaybeError CompileError 'ABC'.Assembler
compile prog = case evalRWST start () zero of
	Error e  -> Error e
	Ok (_,p) -> Ok p
where
	start = mapM_ gen prog.p_funs

:: Address
	= LabelAddr String
	| StackAddr Int

:: CompileState =
	{ labels    :: ['ABC'.Label]
	, addresses :: 'M'.Map Name Address
	}

instance zero CompileState
where
	zero =
		{ labels    = ["_l" <+ i \\ i <- [0..]]
		, addresses = 'M'.newMap
		}

labels :: CompileState -> ['ABC'.Label]
labels cs = cs.labels

addresses :: CompileState -> 'M'.Map Name Address
addresses cs = cs.addresses

:: Gen a :== RWST () 'ABC'.Assembler CompileState (MaybeError CompileError) a

fresh :: Gen 'ABC'.Label
fresh = gets labels
	>>= \labs -> modify (\cs -> {cs & labels=tl labs})
	*> pure (hd labs)

reserveVar :: Int Name -> Gen Int
reserveVar i n = modify (\cs -> {cs & addresses='M'.put n (StackAddr i) cs.addresses}) *> pure (i+1)

class gen a :: a -> Gen ()

instance gen Function
where
	gen f = tell ['ABC'.Label f.f_name] *> gen f.f_code

instance gen CodeBlock
where
	gen cb = foldM reserveVar 0 [i.init_name \\ i <- cb.cb_init] *>
		mapM_ gen cb.cb_init *>
		mapM_ gen cb.cb_content

instance gen Initialisation
where
	gen init = comment ("Initialise " <+ init.init_name) *> tell ['ABC'.Create]

instance gen Statement
where
	gen st=:(Declaration n app) = gets addresses >>= \addrs -> case 'M'.get n addrs of
		Just (StackAddr i) -> comment (toString st) *> gen app *>
		                      tell ['ABC'.Update_a 0 $ i+1, 'ABC'.Pop_a 1]
		Just (LabelAddr _) -> liftT $ Error VariableLabel
		_                  -> liftT $ Error $ UndefinedName n
	gen (Application app)   = comment "Application" *> gen app
	gen (Return (Just app)) = comment "Return" *> gen app *> tell ['ABC'.Rtn]
	gen (Return Nothing)    = comment "Return" *> tell ['ABC'.Rtn]

instance gen Application
where
	gen (Name n) = gets addresses >>= \addrs -> case 'M'.get n addrs of
		Just (StackAddr i) -> tell ['ABC'.Push_a i]
		Just (LabelAddr _) -> liftT $ Error VariableLabel
		_                  -> liftT $ Error $ UndefinedName n
	gen (Literal (BLit b)) = tell ['ABC'.Create, 'ABC'.FillB b 0]
	gen (Literal (ILit i)) = tell ['ABC'.Create, 'ABC'.FillI i 0]
	gen (App n args) = gets addresses >>= \addrs -> case 'M'.get n addrs of
		Just (LabelAddr l) ->
			comment "Retrieve arguments" *> mapM gen args *>
			comment "Apply function" *> tell ['ABC'.Jsr l]
		Just (StackAddr _) -> liftT $ Error FunctionOnStack
		_                  -> liftT $ Error $ UndefinedName n

comment :: String -> Gen ()
comment s = tell ['ABC'.Comment s]