1 Some functionality of the original
\gls{mTask
}-
\gls{EDSL
} will not be used in
2 this extension
\gls{EDSL
}. Conversely, some functionality needed was not
3 available in the existing
\gls{EDSL
}. Due to the nature of class based shallow
4 embedding this obstacle is very easy to solve. A type housing the
\gls{EDSL
}
5 does not have to implement all the available classes. Moreover, classes can be
6 added at will without interfering with the existing views.
11 \section{Bytecode compilation
}
12 The
\glspl{mTask
} are sent to the device in bytecode and are saved in the
13 memory of the device. To compile the
\gls{EDSL
} code to bytecode, a view is
14 added to the
\gls{mTask
}-system called
\CI{ByteCode
}. As shown in
15 Listing~
\ref{lst:bcview
}, the
\CI{ByteCode
} view is a boxed
\gls{RWST
} that
16 writes bytecode instructions (
\CI{BC
}) while carrying around a
\CI{BCState
}.
17 The state is kept between devices and contains fresh variable names and a
18 register of shares used.
20 Types implementing the
\gls{mTask
} classes must have two free type variables.
21 Therefore the
\gls{RWST
} is wrapped with a constructor and two phantom type
22 variables are added. This means that the programmer has to unbox the
23 \CI{ByteCode
} object to be able to use return values for the monad. Tailor made
24 access functions are used to achieve this with ease. The fresh variable stream
25 in a compiler using an
\gls{RWST
} is often put in to the
\emph{Reader
} part of
26 the monad. However, not all code is compiled immediately and later on the fresh
27 variable stream can not contain variables that were used before. Therefore this
28 information is put in the state which is kept between compilations.
30 Not all types are suitable for usage in bytecode compiled programs. Every value
31 used in the bytecode view must fit in the
\CI{BCValue
} type which restricts
32 the content. Most notably, the type must be bytecode encodable. A
\CI{BCValue
}
33 must be encodable and decodable without losing type or value information. At
34 the moment a simple encoding scheme is used that uses a single byte prefixes to
35 detect which type the value is. The devices know of these prefixes and act
38 \begin{lstlisting
}[label=
{lst:bcview
},caption=
{Bytecode view
}]
39 :: ByteCode a p = BC (RWS ()
[BC
] BCState ())
40 :: BCValue = E.e: BCValue e & mTaskType, TC e
51 class toByteCode a :: a -> String
52 class fromByteCode a :: String -> a
53 class mTaskType a | toByteCode, fromByteCode, iTask, TC a
55 instance toByteCode Int, ... , UserLED, BCValue
56 instance fromByteCode Int, ... , UserLED, BCValue
58 instance arith ByteCode
60 instance serial ByteCode
63 \section{Implementation
}
64 \subsection{Instruction Set
}
65 The instruction set is given in Listing~
\ref{bc:instr
}. The instruction set is
66 kept large, but under $
255$, to get the highest expressivity for the lowest
70 stack machine. Therefore the it needs instructions like
\emph{Push
} and
71 \emph{Pop
}. The virtual instruction
\CI{BCLab
} is added to allow for an easy
72 implementation. However, this is not a real instruction and the labels are
73 resolved to actual addresses in the final step of compilation to save
76 \begin{lstlisting
}[label=
{bc:instr
},
%
77 caption=
{Bytecode instruction set
}]
79 | BCLab Int | BCPush BCValue | BCPop
81 | BCSdsStore BCShare | BCSdsFetch BCShare | BCSdsPublish BCShare
85 | BCAdd | BCSub | BCMul
90 | BCEq | BCNeq | BCLes | BCGre
92 //Conditionals and jumping
93 | BCJmp Int | BCJmpT Int | BCJmpF Int
97 | BCAnalogRead Pin | BCAnalogWrite Pin | BCDigitalRead Pin | BCDigitalWrite Pin
102 All instructions are can be converted semi-automatically using the generic
103 function
\CI{consIndex\
{*\
}} that gives the index of the constructor. This
104 constructor index is the actual byte value for the instruction. The
105 \CI{BCValue
} type contains existentially quantified types and therefore must
106 have a given implementation for all generic functions.
108 \subsection{Helper functions
}
109 The
\CI{ByteCode
} type is just a boxed
\gls{RWST
} and that gives us access to
110 the whole range of
\gls{RWST
} functions. However, to apply a function the type
111 must be unboxed. After application the type must be boxed again. To achieve
112 this some helper functions have been created. They are listed in
113 Listing~
\ref{lst:helpers
}. The
\CI{op
} and
\CI{op2
} function is crafted to make
114 operators that pop one or two values off the stack respectively. The
\CI{tell`
}
115 is a wrapper around the
\gls{RWST
} function
\CI{tell
} that appends the argument
116 to the
\emph{Writer
} value.
118 \begin{lstlisting
}[label=
{lst:helpers
},caption=
{Some helper functions
}]
119 op2 :: (ByteCode a p1) (ByteCode a p2) BC -> ByteCode b Expr
120 op2 (BC x) (BC y) bc = BC (x >>| y >>| tell
[bc
])
122 op :: (ByteCode a p) BC -> ByteCode a Expr
123 op (BC x) bc = BC (x >>| tell
[bc
])
125 tell` ::
[BC
] -> (ByteCode a p)
126 tell` x = BC (tell x)
128 unBC :: (ByteCode a p) -> RWS ()
[BC
] BCState ()
132 \subsection{Arithmetics \& Peripherals
}
133 Almost all of the code from the simple classes use exclusively helper
134 functions. Listing~
\ref{lst:arithview
} shows some implementations. The classes
135 \CI{boolExpr
} and the classes for the peripherals are implemented in the same
138 \begin{lstlisting
}[label=
{lst:arithview
},caption=
{%
139 Bytecode view implementation for arithmetic and peripheral classes
}]
140 instance arith ByteCode where
141 lit x = tell`
[BCPush (BCValue x)
]
142 (+.) x y = op2 x y BCDiv
145 instance userLed ByteCode where
146 ledOn l = op l BCLedOn
147 ledOff l = op l BCLedOff
150 \subsection{Control Flow
}
151 \begin{lstlisting
}[label=
{lst:controlflow
},
%
152 caption=
{Bytecode view for
\texttt{arith
}}]
153 instance noOp ByteCode where
157 \subsection{Shared Data Sources \& Assignment
}
158 Shared data sources must be acquired from the state and constructing one
159 happens via multiple steps. First a fresh identifier is grabbed from the state.
160 Then a
\CI{BCShare
} record is created with that identifier. A
\CI{BCSdsFetch
}
161 instruction is written and the body is generated to finally add the share to
162 the actual state. The exact implementation is shown in
163 Listing~
\ref{lst:shareview
}.
165 \begin{lstlisting
}[label=
{lst:shareview
},
%
166 caption=
{Bytecode view for
\texttt{arith
}}]
167 instance sds ByteCode where
168 sds f =
{main = BC (freshs
169 >>=
\sdsi->pure
{BCShare | sdsi=sdsi,sdsval=BCValue
0}
170 >>=
\sds->pure (f (tell`
[BCSdsFetch sds
]))
171 >>= \(v In bdy)->modify (addSDS sds v)
172 >>| unBC (unMain bdy))
}
173 pub (BC x) = BC (censor (\
[BCSdsFetch s
]->
[BCSdsPublish s
]) x)
175 addSDS sds v s =
{s & sdss=
[{sds & sdsval=BCValue v
}:s.sdss
]}
178 All assignable types compile to an
\gls{RWST
} that writes one instruction. For
179 example, using an
\gls{SDS
} always results in an expression of the form
180 \CI{sds
\x=
4 In ...
}. The actual
\CI{x
} is the
\gls{RWST
} that always writes
181 one
\CI{BCSdsFetch
} instruction with the correct
\gls{SDS
} embedded. When the
182 call of the
\CI{x
} is not a read but an assignment, the instruction will be
183 rewritten as a
\CI{BCSdsStore
}. The implementation for this is given in
184 Listing~
\ref{lst:assignmentview
}.
186 \begin{lstlisting
}[label=
{lst:assignmentview
},
%
187 caption=
{Bytecode view implementation for assignment.
}]
188 instance assign ByteCode where
189 (=.) (BC v) (BC e) = BC (e >>| censor makeStore v)
191 makeStore
[BCSdsFetch i
] =
[BCSdsStore i
]
192 makeStore
[BCDigitalRead i
] =
[BCDigitalWrite i
]
193 makeStore
[...
] =
[...
]
196 \section{Actual Compilation
}
197 \todo{hulp functies voor compileren
}