1\documentclass{article}
2\usepackage[english]{babel}
3\usepackage{listings}
4\usepackage{fullpage}
5\usepackage{color}
6
7%include polycode.fmt
8
9%if false
10  Error: DSL for error definition
11   
12  Copyright (c) 2009, 2011 ETH Zurich.
13  All rights reserved.
14  
15  This file is distributed under the terms in the attached LICENSE file.
16  If you do not find this file, copies can be found by writing to:
17  ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
18%endif
19
20\title{Filet-O-Fish Tutorial:\\
21       The Fugu Error Definition Language}
22\author{\Large{Pierre-Evariste \sc{Dagand}}}
23\date{}
24
25\begin{document}
26
27\lstset{basicstyle=\ttfamily,
28        columns=fullflexible,
29        language=C}
30
31\maketitle
32
33
34
35\textcolor{red}{Warning: the comments below are out-of-sync with some
36aspects of the code. Some will claim that this is a clear proof that
37literate programming is useless. Others will note that in non-literate
38code, I would not have written this disclaimer and the code would be a
39complete mess. Anyway, feel free to improve the literate story: we
40have variable-size blocks, error codes are not generated randomly
41anymore, we use a threshold to differentiate success to failure
42cases.}
43
44
45%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
46\section*{Introduction}
47%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
48
49
50
51%% <- Present the goal of the tutorial
52
53In this tutorial, we aim at illustrating the usage of Filet-O-Fish
54(FoF). Therefore, we will implement a small Domain-Specific Language
55(DSL), using FoF in the back-end. However, we will also cover a
56broader topic: how to design a new DSL taking advantage of FoF
57particularities. In particular, this DSL will be an Error Definition
58Language (EDL) called Fugu. Its functionalities are described in
59Section~\ref{sec:edl}.
60
61%%    -> Step-by-step, real life design
62
63Hence, we will adopt a step-by-step approach. While covering the
64various phases of development, we will try to devise a more
65``principled'' approach, which could serve for future development.
66
67%%    -> From C code to high-level language
68
69One of our partis pris is that such work should start from mature C
70code. FoF is meant as a safe meta-language, allowing the DSL designer
71to, first, abstract over C and, then, manipulate these high-level
72constructions in a powerful environment, such as Haskell or a theorem
73prover.
74
75%%    -> With a focus on the back-end 
76
77For the sake of brevity, we will focus our presentation on the DSL
78back-end. Hence, we will not write a parser for Fugu: the syntax of
79this small language will be \emph{embedded} in Haskell, by the means
80of some \emph{combinators}. We will take care of avoiding any
81confusion between all those languages.
82
83%% <- Outline
84%%    -> Follow real development
85%%    <- Intents of the Error Definition language
86%%    <- Presentation of the C sample
87%%    <- Designing the Syntax
88%%    <- Backend
89
90This tutorial is organized as follow. In a first Section, we specify
91our requirements concerning Fugu. In a second Section, we consider
92define a small class of errors and describe how we would like to
93compile them down. From there, we carry a step-by-step implementation
94of Fugu in Section~\ref{sec:backend}.
95
96
97
98%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99\section{An Error Definition System}
100\label{sec:edl}
101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102
103
104%% <- Replacing errno
105%%      <- Overloaded meaning (several causes, one code)
106
107The need for an Error Definition System arises from the deficiencies of
108the traditional scheme, namely the \texttt{errno.h}, or similar,
109file. In this file, developers typically aggregate the error codes of
110all errors that could potentially occur in the system. In order to
111scale, they quickly ``overload'' the meaning of these error codes:
112instead of defining distinct error codes, they use a single code which
113has multiple causes. For example, the {\sc Einval} error of FreeBSD
114signals that ``some invalid argument was supplied: for example,
115specifying an undefined signal to a signal function or a kill system
116call''. In parallel, they also abuse the return values of
117functions. For instance, a function returning a {\sc Null} pointer is
118typically signalling an error. However, this does not provide much
119information to the developer facing such case.
120
121%%      <- Artificially limited namespace
122
123Although this might have been historically relevant, there is no
124incentive, today, to limit the error code space to 255. Using 16 bits
125would allow us to define 65535 error codes. In such a huge space, we
126would be freed from the burden of overloading error codes and
127functions return values: when it is relevant, the developer should be
128able to define new error codes. Hence, he could handle errors more
129precisely, in the code as well as during the debugging process.
130
131%%      <- Flat namespace
132
133However, by imposing a flat name-space, the \texttt{errno.h} approach
134reduces the benefit of defining more precise error codes. When an
135error occurs, the developer is not able to relate it to a subsystem:
136it is simply defined as one error among thousands of others. In a
137sense, the error is more precise but still lacks some context. Being
138able to give a context to an error would be a step forward, notably
139during debugging.
140
141%%      <- Absence of error-tracking
142
143Finally, it is often the case that an error in one function must be
144reported to several functions in the call-stack. Whereas the depth of
145the call-stack is generally low, it is extremely inconvenient to
146define case-specific error codes at each level (the number of cases
147growing exponentially with the depth). Hence, we generally give up
148accuracy and report a global, overloaded error code.
149
150%% <- Design goals
151%%      <- One class of errors per "component"
152
153For these reasons, we provide the developer with the power of
154defining classes of errors per ``component''. The notion of
155\emph{component} is purposely kept fuzzy: it is up to the developer
156to define its components, on a case-by-case basis. For example, it
157could be a single function or a class of related functions.
158
159%%      <- Call-trace
160
161By using a 16 bits space, we are able get rid of the artificial
162limitation of the state-space. But we can also take advantage of this
163sub-word quantity in our 64 bits machines. Indeed, in one machine
164word, we are able to handle 4 error codes. Hence, we can build the
165call-trace by \emph{pushing} an error code in the previous one, and so
166on along the call-path.
167
168%%      <- Automated, human-readable error reporting
169
170By relying on a tool to generate C code for us, we can also get rid of
171a lot of boilerplate code. For example, when defining an error code,
172we label it with a descriptive message as well as a short
173acronym. Then, given an error, we should be able to report it to the
174user, in a meaningful way.
175
176%%      <- Sensibility to wild writes
177
178Finally, in the event of wild write -- such as a buffer overflow -- on
179the error code variable, we would like the error code to become
180meaningless, instead of signaling an unrelated error. Therefore, we
181should use random numbers to identify errors. Hence, the common
182consequences of a wild write, such as overwriting with 0 or with a
183character, could be detected more easily.
184
185
186
187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188\section{C Sample Code}
189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
190
191%% <- Why starting with a C sample
192
193
194%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
195\subsection{Starting from C}
196%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
197
198%%     <- Ability for anyone (non-FoF user) to discuss 
199%%        and modify the model
200%%         <- Importance of user's feed-back
201
202First of all, we would like to stress the importance of starting from
203a mature C code. The first and foremost reason is the ability to
204\emph{discuss} this draft with the final users of the DSL: whereas not
205everyone is familiar with Haskell and FoF, C is the \emph{lingua
206franca} for everyone. Thanks to this support, users are given the
207power to influence the design of the DSL, without having to handle its
208machinery. Moreover, this C file is an inexpensive support for testing
209and debugging: we can quickly modify its behavior without dealing
210with the compilation machinery.
211
212%%     <- FoF should not be meant as a programming language per se
213%%         <- Give a meaning
214
215Although disturbing at first, FoF should \emph{not} be meant as a
216programming language. First and foremost, FoF gives a meaning, the
217semantics, to your DSL language. The fact that it can be compiled to C
218comes as a bonus.
219
220%%     <- C code serves as a weak specification of the intention
221%%         <- Informal alternative semantics
222
223Finally, a ``good'' DSL should be defined by an alternative semantics,
224which would allow the DSL designer to check the correctness of the
225compiler. Having a C sample of a particular instance can play this
226role, for this test-case. Although it does not help in obtaining
227formal guarantees, it is a first step toward a seemingly correct
228compiler.
229
230
231%% <- The Errors
232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
233\subsection{Our Test-Case}
234\label{sec:example}
235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
236
237In the following, we will define the error codes for two components:
238``system'' and ``pci''. The ``\verb!system_err!'' class of errors will
239contain the following cases of success:
240\begin{description}
241        \item[\verb!SYS_OK!] No error
242        \item[\verb!SYS_ILIA!] Mafioso Pasta (private ETH joke, on SOSP lunches)
243\end{description}
244
245And the following cases of error:
246\begin{description}
247        \item[\verb!SYS_FAIL!] Failed
248        \item[\verb!SYS_TEM!] Kernel Hacker
249        \item[\verb!SYS_IPHUS!] Analphabet Greek
250\end{description}
251
252The ``\verb!pci_err!'' class of errors is more serious, with the
253following cases of success:
254\begin{description}
255        \item[\verb!PCI_OK!] No PCI error
256        \item[\verb!PCI_GET_CAP!] That is my cap
257\end{description}
258
259And this case of error:
260\begin{description}
261        \item[\verb!PCI_CAP_NOK!] Lost my cap
262\end{description}
263
264In the next Section, we review the C code \emph{we would like to
265generate} from this or a similar description. Bear in mind that this
266code is the result of a long and contradictory discussion with the
267final users.
268
269%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
270\subsection{The C Sample}
271%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
272
273%% <- Includes
274%%%%%%%%%%%%%%%%%%%%
275\subsubsection{Includes}
276%%%%%%%%%%%%%%%%%%%%
277
278
279First of all, we will need, more or less, the following includes:
280
281\begin{lstlisting}
282 #include <stdlib.h>
283 #include <stdio.h>
284 #include <assert.h>
285 #include <stdbool.h>
286 #include <stdint.h>
287 #include <stdarg.h>
288\end{lstlisting}
289
290
291
292%% <- Typedef
293%%%%%%%%%%%%%%%%%%%%
294\subsubsection{Type Definitions}
295%%%%%%%%%%%%%%%%%%%%
296
297We define an error value as 64 bits machine word. This encompass both
298the current error code and, potentially, 3 others stacked error codes.
299
300\begin{lstlisting}
301typedef uint64_t errval_t;
302\end{lstlisting}
303
304%% <- Enumeration
305%%%%%%%%%%%%%%%%%%%%
306\subsubsection{Error codes}
307%%%%%%%%%%%%%%%%%%%%
308
309We define the error codes in an enumeration. Each code is identified
310by a random number between 0 and 65535. The success codes are
311identified by an odd number whereas failure codes are identified by an
312even number. This will come handy when we define a global
313\verb!err_is_ok! and \verb!err_is_fail! functions.
314
315\begin{lstlisting}
316enum err_code {
317    SYS_OK = 11,
318    SYS_FAIL = 84,
319    SYS_ILIA = 93,
320    SYS_TEM = 2,
321    SYS_IPHUS = 34,
322    PCI_OK = 41,
323    PCI_CAP_NOK = 28,
324    PCI_GET_CAP = 16
325};
326\end{lstlisting}
327
328
329%% <- Labels
330%%%%%%%%%%%%%%%%%%%%
331\subsubsection{Labels}
332%%%%%%%%%%%%%%%%%%%%
333
334Then, we statically define the previous error descriptions, acronyms,
335and domains. This way, we will be able to provide a descriptive
336notification to the user in case of error.
337
338\begin{lstlisting}
339static char *err_domains[] = {
340    "System_err", "Pci_err", "Undefined"
341};
342static char *err_codes[] = {
343    "SYS_OK", "SYS_FAIL", "SYS_ILIA",
344    "SYS_TEM", "SYS_IPHUS", "PCI_OK",
345    "PCI_CAP_NOK", "PCI_GET_CAP",
346    "UNDEFINED"
347};
348static char *err_msgs[] = {
349    "No error", "Failed",
350    "Mafioso Pasta",
351    "Kernel Hacker",
352    "Analphabet Greek",
353    "No PCI error",
354    "Lost my cap",
355    "That is my cap",
356    "Undefined error",
357};
358\end{lstlisting}
359
360
361%% <- Pop and push Error number
362%%%%%%%%%%%%%%%%%%%%
363\subsubsection{Getting and Stacking Errors}
364%%%%%%%%%%%%%%%%%%%%
365
366Given an \verb!errval_t!, we will need to extract the latest error
367code. This is done by accessing the lowest 16 bits.
368
369\begin{lstlisting}
370static inline enum err_code err_no(errval_t err){
371    return (err & ((1 << 16) - 1));
372}
373\end{lstlisting}
374
375Similarly, we can stack up an error code in an existing error value:
376we simply have to shift-left the stack and write the current error
377code in the lowest 16 bits.
378
379\begin{lstlisting}
380static inline errval_t err_push(errval_t err, enum err_code code){
381    return (err << 16) || code;
382}
383\end{lstlisting}
384
385
386
387%% <- Testing for success or failure
388%%%%%%%%%%%%%%%%%%%%
389\subsubsection{Success and Failure}
390%%%%%%%%%%%%%%%%%%%%
391
392We will frequently need to test an error code for failure or
393success. Hence, we provide two function which are both efficient and
394apply to any error code.
395
396\begin{lstlisting}
397static inline bool err_is_ok(errval_t err){
398    return err_no(err) % 2;
399}
400static inline bool err_is_fail(errval_t err){
401    return 1 - (err_no(err) % 2);
402}
403\end{lstlisting}
404
405
406%% <- Error-specific tests
407%%%%%%%%%%%%%%%%%%%%
408\subsubsection{Error-specific Tests}
409%%%%%%%%%%%%%%%%%%%%
410
411Then, we might need to test some error codes against a specific
412error. This is simply achieved by the following functions.
413
414\begin{lstlisting}
415static inline bool err_is_sys_fail(errval_t e){
416    return err_no(e) == SYS_FAIL;
417}
418...
419static inline bool err_is_pci_ok(errval_t e){
420    return err_no(e) == SYS_OK;
421}
422...
423\end{lstlisting}
424
425
426%% <- Asserting success or failure
427%%%%%%%%%%%%%%%%%%%%
428\subsubsection{Asserting Success and Failure}
429%%%%%%%%%%%%%%%%%%%%
430
431Similarly, during the debugging phase, we might need to assert the
432correctness or incorrectness of a result. Hence, the following
433assertions.
434
435\begin{lstlisting}
436static inline void err_assert_ok(errval_t err){
437    assert(err_is_ok(err));
438}
439static inline void err_assert_fail(errval_t err){
440    assert(err_is_fail(err));
441}
442\end{lstlisting}
443
444
445%% <- Printers on errors
446%%%%%%%%%%%%%%%%%%%%
447\subsubsection{Printing Errors}
448%%%%%%%%%%%%%%%%%%%%
449
450
451
452\begin{lstlisting}
453static inline char *err_getstring(errval_t err) {
454
455    switch (err_no(err)) {
456    case SYS_OK: {
457        return err_msgs[0]; 
458        break;
459    }
460    ...
461    case PCI_OK: {
462        return err_msgs[5]; 
463        break;
464    }
465    ...
466    default: {
467        return err_msgs[8];
468        break;
469    }
470    }
471    return NULL;
472}
473
474static inline char *err_getcode(errval_t err ) {
475
476    switch (err_no(err)) {
477    case SYS_OK: {
478        return err_codes[0]; 
479        break;
480    }
481    ...
482    case PCI_OK: {
483        return err_codes[5]; 
484        break;
485    }
486    ...
487    default: {
488        return err_codes[8];
489        break;
490    }
491    }
492    return NULL;
493}
494
495static inline char *err_getdomain(errval_t err ) {
496
497    switch (err_no(err)) {
498    case SYS_OK: {
499        return err_domains[0]; 
500        break;
501    }
502    ...
503    case PCI_OK: {
504        return err_domains[1]; 
505        break;
506    }
507    ...
508    default: {
509        return err_domains[2];
510        break;
511    }
512    }
513    return NULL;
514}
515\end{lstlisting}
516
517
518%% <- Printing the call trace
519%%%%%%%%%%%%%%%%%%%%
520\subsubsection{Printing the Call-trace}
521%%%%%%%%%%%%%%%%%%%%
522
523Finally, mostly during debugging, we would like to inspect the
524call-trace leading to an error. This is achieved by the following
525code. First, it verifies that the stack contains an error. Then, it
526\emph{pop}s error codes from it and prints their domain, description,
527and acronym.
528
529\begin{lstlisting}
530static inline void err_print_calltrace(errval_t err){
531    
532    if (err_is_fail(err)){
533        
534        enum err_code x;
535        while( (x = err_no(err)) != 0 ){
536            printf("Failure: (%12s) %20s [%s]\n",
537                   err_getdomain(x),
538                   err_getstring(x),
539                   err_getcode(x));
540            err = err >> 16;
541        }       
542    }
543}
544\end{lstlisting}
545
546
547This concludes our draft implementation of an error management
548system. Now, we are aware of our needs, what the user needs to specify
549and what constructs should we abstract away. Let us start implementing
550Fugu, now.
551
552
553
554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
555\section{Fugu: the Error Definition Language}
556\label{sec:backend}
557%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
558
559
560%if false
561
562> module FuguBackend where
563
564> import System.IO
565> import System.Random
566> import Data.Char
567> import Data.Maybe
568> import Data.List
569> import Data.Monoid
570> import qualified Data.Bits as B
571
572> import Debug.Trace
573
574> import Semantics
575> import Constructs
576> import PureExpressions
577> import Expressions
578
579> import Constructs.Conditionals
580> import Constructs.References
581> import Constructs.Functions
582> import Constructs.Structures
583> import Constructs.Arrays
584> import Constructs.Enumerations
585> import Constructs.Typedef
586> import Constructs.Strings
587
588> import Libc.Assert
589> import Libc.Printf
590
591> import Compile
592
593%endif
594
595In the following Sections, we describe the implementation of Fugu. As
596usual with me, you are reading a literate code: the Haskell compiler
597processes this very same file. Therefore, you are reading the real
598Fugu's code. In Section \ref{sec:syntax}, we define Fugu's AST and a
599set of combinators, to be able to embed Fugu's language into
600Haskell. In Section \ref{sec:fof_backend}, we implement the back-end
601using Filet-O-Fish.
602
603
604
605%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
606\subsection{Fugu Embedded Syntax}
607\label{sec:syntax}
608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609
610
611
612%%%%%%%%%%%%%%%%%%%%
613\subsubsection{The Abstract-Syntax Tree}
614%%%%%%%%%%%%%%%%%%%%
615
616First, we define the Abstract-Syntax Tree (AST). An error definition
617file is a list of error classes:
618
619> type Errors = [ ErrorClass ]
620
621Where an error class is identified by a string, its name, and a list
622of error definitions:
623
624> data ErrorClass = ErrorClass String [ ErrorField ]
625
626An error definition is composed by a description and a short
627acronym. We also specify an error status, either a failure case or a
628success case.
629
630> data ErrorField = ErrorField ErrorStatus String String
631>
632> data ErrorStatus = Failure
633>                  | Success
634>                  | DefaultSuccess
635>                  deriving Eq
636
637
638
639%%%%%%%%%%%%%%%%%%%%
640\subsubsection{Enbedding Fugu in Haskell}
641%%%%%%%%%%%%%%%%%%%%
642
643Instead of designing a parser, possibly using Parsec, we chose to
644embed the Fugu language inside Haskell. This means that we are going
645to define some Haskell functions, operating on the AST. By combining
646these functions, we will be able to build a complete AST that can be
647processed by Fugu's back-end.
648
649First of all, we can build an |ErrorField| out of the error acronym
650and description, using one of these two combinators:
651
652> success, failure :: String -> String -> ErrorField
653> success acronym description = ErrorField Success acronym description
654> failure acronym description = ErrorField Failure acronym description
655
656Given a list of such fields and a global name for them, we can then
657build an |ErrorClass|:
658
659> errors :: String -> [ ErrorField ] -> ErrorClass
660> errors className errors = ErrorClass className errors
661
662And, finally, given a list of such classes, we get a valid error
663definition ``file'':
664
665> define :: [ErrorClass] -> Errors
666> define errors = errors
667
668
669
670%%%%%%%%%%%%%%%%%%%%
671\subsubsection{Our Example in Fugu}
672%%%%%%%%%%%%%%%%%%%%
673
674
675If we translate our informal description of Section~\ref{sec:example},
676this leads to the following code:
677
678> testE = define [
679>          errors "system_err" [
680>                      success "SYS_ERR_OK" "No error",
681>                      failure "SYS_ERR_FAIL" "Failed",
682>                      success "SYS_ERR_ILIA" "Mafioso Pasta",
683>                      failure "SYS_ERR_TEM" "Kernel Hacker",
684>                      failure "SYS_ERR_IPHUS" "Analphabet Greek"
685>                     ],
686>          errors "pci_err" [
687>                      success "PCI_ERR_OK" "No PCI error",
688>                      failure "PCI_ERR_NOK" "Lost my cap",
689>                      success "PCI_ERR_CAP" "That is my cap"
690>                     ]
691>          ]
692
693
694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
695\subsection{Filet-O-Fish Back-end}
696\label{sec:fof_backend}
697%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
698
699At this point, there is no way we can postpone the implementation of
700the compiler back-end. So, \emph{Ave, Caesar, Morituri Te Salutant}.
701
702
703%% <- Typedefs
704%%%%%%%%%%%%%%%%%%%%
705\subsubsection{Defining Type Aliases}
706%%%%%%%%%%%%%%%%%%%%
707
708
709We start with some type aliasing. In its basic form, FoF only knows
710\emph{primitive} types such as integers, floats, and user-defined
711structures, unions, and arrays. Therefore, if we are to use the
712standard |bool| or |char|, we have to inform FoF of their existence as
713well as to which primitive type they match to.
714
715> boolT :: TypeExpr
716> boolT = typedef uint64T "bool"
717
718Similarly, we can define our own aliases:
719
720> err_codeT :: TypeExpr
721> err_codeT = enumT "err_code" [] -- not right, [] is populated
722> err_code :: Int -> PureExpr
723> err_code = uint16 . toInteger
724>
725> errval_tT :: TypeExpr
726> errval_tT = typedef uintptrT "errval_t"
727
728
729%% <- Defining general functions
730%%%%%%%%%%%%%%%%%%%%
731\subsubsection{Defining General Functions}
732%%%%%%%%%%%%%%%%%%%%
733
734Implementing the |err_no|, |err_push|, |err_is_ok|, and |err_is_fail|
735consists in a straightforward translation from C to FoF. Let us give a
736closer look at |err_no|, for instance. 
737
738The body of |err_no| consists in doing some bit twiddling on the error
739stack and returning this value:
740
741> err_no_int :: Integer -> [PureExpr] -> FoFCode PureExpr
742> err_no_int bitBlock (err : []) =
743>     returnc $ cast err_codeT $ (err .&. ((uint64 1 .<<. uint64 bitBlock) .-. uint64 1))
744
745We build a function out of the function body thanks to the |def|
746operator. Along with the function body, we have to pass the return
747type as well as the parameters types:
748
749> err_noF :: Integer -> FoFCode PureExpr 
750> err_noF bitBlock = def [Static, Inline] "err_no" (err_no_int bitBlock)
751>                                         err_codeT  
752>                                         [(errval_tT, Just "errval")]
753
754And we are done: |err_noF| is corresponds to the C |err_no|
755function. We will see later on how it can be called.
756
757Similarly, we define |err_pushF|. This time, we declare the function
758body in a @where@ clause. Note, however, that we have to provide the
759type of |err_push_int| as it cannot be inferred by the Haskell
760compiler.
761
762
763> err_pushF :: Integer -> FoFCode PureExpr 
764> err_pushF bitBlock = def [Static, Inline] "err_push" err_push_int errval_tT  [(errval_tT, Just "errval"),
765>                                                                               (err_codeT, Just "errcode")]
766>     where err_push_int (err : code : []) =
767>               returnc ((err .<<. uint64 bitBlock) .|. 
768>                        (cast errval_tT $ uint64 (((toInteger 1) `B.shiftL` (fromInteger bitBlock)) - 1) .&. code))
769
770And the same goes for |err_is_okF| and |err_is_failF|:
771
772> err_is_okF :: Integer -> Int -> FoFCode PureExpr
773> err_is_okF bitBlock indexFailures = def [Static, Inline] "err_is_ok" err_is_ok_int boolT [(errval_tT, Just "errval")]
774>     where err_is_ok_int (err : []) =
775>               do
776>               err_no <- err_noF bitBlock
777>               err_no_e <- call err_no [err]
778>               returnc (err_no_e .<. uint64 (toInteger indexFailures))
779>
780> err_is_failF :: Integer -> Int -> FoFCode PureExpr
781> err_is_failF bitBlock indexFailures = def [Static, Inline] "err_is_fail" err_is_fail_int boolT [(errval_tT, Just "errval")]
782>     where err_is_fail_int (err : []) =
783>               do
784>               err_no <- err_noF bitBlock
785>               err_no_e <- call err_no [err]
786>               returnc (err_no_e .>=. uint64 (toInteger indexFailures))
787
788%% <- Defining parameterized functions
789%%%%%%%%%%%%%%%%%%%%
790\subsubsection{Defining Parameterized Functions}
791%%%%%%%%%%%%%%%%%%%%
792
793An example is |err_get_array|, which is used to build the
794|err_getdomain|, |err_getstring|, and |err_getcode| functions. Hence,
795|err_get_array| takes as first argument the postfix of the |err_get*|
796target. It is also provided a reference to the array that contains the
797descriptive strings. And the last argument consists of a list of pairs
798(error code, array index).
799
800The body of the |err_get_array| is a small generalization over our C
801examples: it calls |err_no| on the argument and switch over it. For
802each error code, it reads in the provided array, at the corresponding
803index. The default case simply consists in reading at latest position
804in the array, where ``undefined'' or its variant is written.
805
806> err_get_array :: Integer -> String -> PureExpr -> [(Int, PureExpr)] ->
807>                  PureExpr -> FoFCode PureExpr
808> err_get_array bitBlock name array codes defaultCode = 
809>     def [] ("err_get" ++ name) err_getstring_int (ptrT charT) [(errval_tT, Just "errval")]
810>     where err_getstring_int (err : []) =
811>               do
812>               err_no <- err_noF bitBlock
813>               err_no_e <- call err_no [err]
814>               switch err_no_e
815>                          [ (uint64 $ toInteger code, (return_array array index))
816>                            | (code, index) <- codes ]
817>                          (return_array array defaultCode)
818>               returnc (uint64 0)
819>           return_array array index =
820>               do
821>               x <- readArray array index
822>               returnc $ cast (ptrT charT) x
823
824The last function is |err_print_calltrace|. Although longer, this
825function does not involve technical aspects of FoF. We can observe the
826use of |if| and |while| operators. As well as the definition and usage
827of \emph{references}. We also make use of the |printf| operator.
828
829> err_print_calltraceF :: Integer -> Int -> PureExpr -> PureExpr -> PureExpr -> FoFCode PureExpr
830> err_print_calltraceF bitBlock indexFailures err_getdomain err_getstring err_getcode = 
831>     def [] "err_print_calltrace" err_print_calltrace_int voidT [(errval_tT, Just "errval")]
832>         where err_print_calltrace_int (err : []) =
833>                   do
834>                   err_no <- err_noF bitBlock
835>                   err_is_fail <- err_is_failF bitBlock indexFailures
836>                      
837>                   err_ref <- newRef err
838>
839>                   ifc (do 
840>                        is_fail_e <- call err_is_fail [err]
841>                        return is_fail_e)
842>                       (do 
843>                        x <- newRef $ uint16 0
844>                        iterations <- newRef $ uint16 0
845>
846>                        while (do
847>                               err_ref_val <- readRef err_ref
848>                               err_ref_no <- call err_no [err_ref_val]
849>                               writeRef x err_ref_no
850>                               return $ (err_ref_no .!=. (uint16 0)) .&. (iterations .<. ((sizeof(errval_tT) .*. (uint64 8)) ./. (uint64 bitBlock))))
851>                              (do     
852>                               err_ref_val <- readRef err_ref
853>                               err_ref_dom <- call err_getdomain [err_ref_val]
854>                               err_ref_str <- call err_getstring [err_ref_val]
855>                               err_ref_code <- call err_getcode [err_ref_val]
856>                               printf "Failure: (%15s) %20s [%10s]\\n" 
857>                                      [err_ref_dom, err_ref_str, err_ref_code]
858>                               writeRef iterations (iterations .+. (uint64 1))
859>                               writeRef err_ref (err_ref_val .>>. (uint64 bitBlock))))
860>                       (do return void)
861
862
863%% <- Defining data-structures
864%%%%%%%%%%%%%%%%%%%%
865\subsubsection{Defining Data-Structures}
866%%%%%%%%%%%%%%%%%%%%
867
868The following codes involves more Haskell-specific data-processing of
869the AST, and less FoF-specific manipulations. However, this
870quasi-absence of FoF is also a good sign: FoF succeeds in being
871non-intrusive while being able to provide strong guarantees on the
872compiler's correctness.
873
874%%%%%%%%
875\paragraph{Compiling the Labels}
876%%%%%%%%
877
878The first step here is to extract the labels, ie. the error codes and
879descriptions, from the Error Definition. Once extracted, we turn them
880into FoF strings, thanks to |makeStrings|.
881
882> err_strings :: Errors -> ([String], [String], [String])
883> err_strings errors =
884>     (domains ++ ["Undefined"], 
885>      errcodes ++ ["UNDEFINED"], 
886>      errdescs ++ ["Undefined"])
887>     where domains = [ domain | ErrorClass domain _ <- errors ] 
888>           (errcodes,errdescs) = unzip $ concat [ [ (code, descr)
889>                                                    | ErrorField _ code descr  <- fields ] 
890>                                                  | ErrorClass _ fields <- errors ]
891
892Using |err_strings|, the following function builds the 3 static arrays
893|err_domains|, |err_codes|, and |err_msgs|. Remember that
894|err_strings| returns a triple of lists of strings. We instantiate
895each of these strings as static arrays, hence obtaining a list of
896arrays. Finally, we instantiate these list of arrays as static
897arrays. Hence, we obtain 3 static arrays, each containing some static
898arrays: the various strings. 
899
900> newStringIndex :: String -> (Int, String) -> 
901>                   FoFCode PureExpr
902> newStringIndex name (i, x) = newStringN (name ++ show i) x                      
903
904> err_arrays :: Errors -> FoFCode (PureExpr, PureExpr, PureExpr)
905> err_arrays errors =
906>     do
907>       let (domains, errcodes, errdescs) = err_strings errors
908>       errdomains_str <- sequence $ map (newStringIndex "errdomains_") $ zip [1..] domains
909>       errcodes_str <- sequence $ map (newStringIndex "errcodes_") $ zip [1..] errcodes
910>       errdescs_str <- sequence $ map (newStringIndex "errdescs_") $ zip [1..] errdescs
911>       errdomains_arr <- newStaticArrayN "err_domains" errdomains_str
912>       errcodes_arr <- newStaticArrayN "errcodes" errcodes_str
913>       errdescs_arr <- newStaticArrayN "errdescs" errdescs_str
914>       return (errdomains_arr, errcodes_arr, errdescs_arr)
915
916At this point, we have compiled the labels.
917
918
919%%%%%%%%
920\paragraph{Compiling the Enumeration}
921%%%%%%%%
922
923
924Before compiling the enumeration, we need to compute two lists of
925random, unique, odd and even numbers. This is achieved by the
926following function, which implementation is hidden for the sake of
927brevity. It takes a random number generator, the number $n$ of desired
928integers, and returns a pair composed by $n$ even numbers and $n$
929odd numbers.
930
931> mkRandomUnique :: StdGen -> Int -> ([Int], [Int])
932
933%if false 
934
935> mkRandomUnique gen i = mkRandomUniqueInt gen i i [] []
936>     where mkRandomUniqueInt gen 0 0 accEven accOdd = (accEven, accOdd)
937>           mkRandomUniqueInt gen i j accEven accOdd = 
938>               let (rand, gen') = next gen in
939>               let rand' = rand `mod` 65536 in
940>                   if rand' `mod` 2 == 0 then
941>                      if rand' `elem` accEven then
942>                         mkRandomUniqueInt gen' i j accEven accOdd
943>                      else 
944>                          if i == 0 then
945>                             mkRandomUniqueInt gen' i j accEven accOdd
946>                          else
947>                             mkRandomUniqueInt gen' (i-1) j (rand' : accEven) accOdd
948>                   else
949>                   if rand' `elem` accOdd then
950>                      mkRandomUniqueInt gen' i j accEven accOdd
951>                   else
952>                   if j == 0 then
953>                      mkRandomUniqueInt gen' i j accEven accOdd
954>                   else
955>                   mkRandomUniqueInt gen' i (j-1) accEven (rand' : accOdd)
956
957%endif
958
959Using these lists of even and odd numbers, we assign an identifier to
960each error and return this mapping as a list of triples, containing
961the domain, the acronym, and the unique identifier. Depending whether
962the error indicates a success or a failure, we pick the identifier,
963respectively, among the odd or even numbers.
964
965> mkEnum :: Errors -> (Int, Int, [(String, String, Int)])
966> mkEnum errors = (succCode',
967>                  succCode' + failCode,
968>                  reverse $ codes)
969>     where errorTypes = concat [ [ (typ, name,dom) 
970>                                 | ErrorField typ name _ <- fields ]
971>                               | ErrorClass dom fields <- errors ]
972>           (codes,
973>            succCode', -- note that |succCode'| is used below. Lazyness.
974>            failCode) = foldl filterCode ([], 1, 0) errorTypes
975>           filterCode (codes, succCode, failCode) 
976>                      (typ, name, dom) 
977>                      | typ == DefaultSuccess = 
978>                          let s = (dom, name, 0) in
979>                          (s : codes, succCode, failCode)
980>                      | typ == Success = 
981>                          let s = (dom, name, succCode) in
982>                          (s : codes, succCode + 1, failCode)
983>                      | typ == Failure = 
984>                          let s = (dom, name, succCode' + failCode) in
985>                          (s : codes, succCode, failCode + 1)
986
987
988
989%%%%%%%%%%%%%%%%%%%%
990\subsubsection{Putting Things Together}
991%%%%%%%%%%%%%%%%%%%%
992
993
994The back-end now consists in a straightforward recollection of the
995various, previously developed pieces. Its type is the following:
996
997> backendHeader :: StdGen -> Errors -> FoFCode PureExpr
998
999Meaning that it takes a random number generation, an Error AST and it
1000generates a Semantics. We can notice which components of FoF are used
1001here: Enumerations, Assert, References, Functions, Static arrays,
1002Conditionals, Type definitions, and Printf.
1003
1004The back-end executes the following actions:
1005
1006> backendHeader gen errors =
1007>     do
1008
1009Compile the type-definitions:
1010
1011>     alias errval_tT
1012>
1013>     aliasE "<stdbool.h>" boolT
1014
1015Compile the enumeration
1016
1017>     newEnum "err_code" enumErrCodes ""
1018
1019Compile the functions:
1020
1021>     err_no <- err_noF bitBlock
1022>     err_push <- err_pushF bitBlock
1023>     err_is_ok <- err_is_okF bitBlock numberOfSuccess
1024>     err_is_fail <- err_is_failF bitBlock numberOfSuccess
1025
1026And we are done.
1027
1028>     return void
1029
1030%if false
1031
1032>         where errorCodes = concat [ [ code | ErrorField _ code _ <- fields ]
1033>                                      | ErrorClass _ fields <- errors ] 
1034>               noCodes = length errorCodes 
1035>               (evenNumbers, oddNumbers) = mkRandomUnique gen noCodes
1036>               (numberOfSuccess, numberOfCodes, labeledErrCodes) = mkEnum errors  
1037>               enumErrCodes = map (\(_,x1,x2) -> (x1,x2)) labeledErrCodes
1038>               idErrCodes = map (\(_,_,x) -> x) labeledErrCodes
1039>               mapIdDescription = zip idErrCodes (map uint64 [1..])
1040>               mapIdAcronym = mapIdDescription
1041>               mapIdDomain = zip idErrCodes (map domainIndex labeledErrCodes)
1042>               domains =  [ dom | ErrorClass dom _ <- errors ]
1043>               domainIndex (dom,_,_) = uint64 $ toInteger $ fromJust $ dom `elemIndex` [ dom | ErrorClass dom _ <- errors ]
1044>               noDomains = length domains
1045>               bitBlock = (floor $ logBase 2 $ (fromInteger . toInteger) numberOfCodes) + 1
1046
1047%endif
1048
1049> backendCode :: StdGen -> Errors -> FoFCode PureExpr
1050
1051> backendCode  gen errors =
1052>     do
1053
1054Compute the labels arrays:
1055
1056>     (err_domains, 
1057>      err_codes, 
1058>      err_descs) <- err_arrays errors
1059
1060Compile the array accessors:
1061
1062>     err_getstring <- err_get_array bitBlock "string" err_descs mapIdDescription (err_code noCodes)
1063>     err_getcode <- err_get_array bitBlock "code" err_codes mapIdAcronym (err_code noCodes)
1064>     err_getdomain <- err_get_array bitBlock "domain" err_domains mapIdDomain (err_code noDomains)
1065
1066Finally, compile the call-trace printer:
1067
1068>     err_print_calltrace <- err_print_calltraceF bitBlock numberOfSuccess err_getdomain err_getstring err_getcode
1069
1070And we are done.
1071
1072>     return void
1073
1074%if false
1075
1076>         where errorCodes = concat [ [ code | ErrorField _ code _ <- fields ]
1077>                                      | ErrorClass _ fields <- errors ] 
1078>               noCodes = length errorCodes 
1079>               (evenNumbers, oddNumbers) = mkRandomUnique gen noCodes
1080>               (numberOfSuccess, numberOfCodes, labeledErrCodes) = mkEnum errors  
1081>               enumErrCodes = map (\(_,x1,x2) -> (x1,x2)) labeledErrCodes
1082>               idErrCodes = map (\(_,_,x) -> x) labeledErrCodes
1083>               mapIdDescription = zip idErrCodes (map uint64 [0..])
1084>               mapIdAcronym = mapIdDescription
1085>               mapIdDomain = zip idErrCodes (map domainIndex labeledErrCodes)
1086>               domains =  [ dom | ErrorClass dom _ <- errors ]
1087>               domainIndex (dom,_,_) = uint64 $ toInteger $ fromJust $ dom `elemIndex` [ dom | ErrorClass dom _ <- errors ]
1088>               noDomains = length domains
1089>               bitBlock = (floor $ logBase 2 $ (fromInteger . toInteger) numberOfCodes) + 1
1090
1091%endif
1092
1093%%%%%%%%%%%%%%%%
1094\subsubsection{Compiling the Test-Case}
1095%%%%%%%%%%%%%%%%
1096
1097
1098At this stage, we just have to open a file, call FoF's |compile|
1099function on the back-end applied to the test-case, and we are done!
1100
1101%if false
1102
1103> {-
1104
1105%endif
1106
1107> main :: IO ()
1108> main = do
1109>   let gen = mkStdGen 1
1110>   fileH <- openFile "test_error.h" WriteMode
1111>   hPutStrLn fileH $ show $ fst $ compile (backendHeader gen testE) emptyBinding
1112>   hClose fileH
1113
1114%if false
1115
1116> -}
1117
1118%endif
1119
1120Thanks for your attention!
1121
1122
1123
1124\end{document}
1125