From: Matthijs Kooijman Date: Wed, 20 May 2009 09:48:03 +0000 (+0200) Subject: Add the latest version of the flat functions document. X-Git-Tag: final-thesis~339 X-Git-Url: https://git.stderr.nl/gitweb?a=commitdiff_plain;h=612e72177321ead79248a8dcf49b783f8153d967;p=matthijs%2Fmaster-project%2Freport.git Add the latest version of the flat functions document. This document is unfinished and will probably be deprecated in the future, but is still added to preserve history. --- diff --git a/FlatFunctions.lout b/FlatFunctions.lout new file mode 100644 index 0000000..36eb137 --- /dev/null +++ b/FlatFunctions.lout @@ -0,0 +1,927 @@ +@SysInclude { report } +@SysInclude { tbl } +@SysInclude { haskell } +def @SectionLink right sec {sec @CrossLink {section @NumberOf sec}} + +@Report + @InitialSpace {tex} # Treat multiple spaces as one + @Title {From Haskell to VHDL} + @Author {Matthijs Kooijman} + @Institution {University of Twente} + @DateLine {Yes} + @CoverSheet {No} +# @OptimizePages {Yes} +// +@Section + @Title {Introduction} +@Begin + @LP The aim of this project is to design a structural hardware + description language, embedded in the functional language Haskell. + This means that a program written in (a subset of) Haskell will be + translated into hardware, using a VHDL description. The original + Haskell program is a structural description, meaning that the + programmer explicitly specifies hardware and area vs time trade-offs + are already made explicit in the description and the translator is not + required to do any scheduling. + + @LP The compilation process is divided roughly into four steps: + + @LeftList + @ParagraphItem { Parsing, typechecking and simplifying the Haskell + source(s). By using the API that GHC makes available, this step is + fairly easy. We can feed a Haskell source file into GHC, which then + performs all the neccesary steps to create a @I Core module, which + is a simplified representation of the Haskell source file.} + + @ParagraphItem { Flattening the function that is the top of the design, and + (recursively) any functions it requires. Flattening is the process + of removing nesting from expressions in the functions and making the + routes values take through an expression more explicit. This results + in a flat list of flat functions.} + + @ParagraphItem { Processing the flattened functions. This step does + transformations such as deciding which function arguments will be + used as state values, decide names for signals, etc.} + + @ParagraphItem { Translate the flat functions generated so far into VHDL. + This is a fairly direct translation, all information should be + present by now.} + @EndList + + @LP In this document we will focus on the second step. The first step + is completely handled by GHC and results in a Core module. The + representation GHC uses for the Core language is the subject of + @SectionLink {Core}. The second step translates + a Core module to a set of flat functions. The representation of a flat + function is the subject of @SectionLink {FlatFunctions}. The fourth + step is also partly discussed in this section. Finally, the actual + flattening that happens in the second step is the subject of + @SectionLink {Flattening}. The third step remains undiscussed for now. +@End @Section + +@Section + @Title { The Core language } + @Tag {Core} +@Begin + @LP GHC uses an internal representation for Haskell programs called @I + {Core} . Core is essentially a small programming language, but we + focus on the representation GHC uses. Inside GHC a Core program is + stored using a number of custom data types, which we will describe + here. + + @LP The descriptions given here are therefore very similar to Haskell + source, and can usually be interpreted as such. To simplify this + discussion, some details that are irrelevant from our point of view + are left out, but each of these simplifications is noted in the text. + + @LP There are two main aspects of Core: Its typing system and its + bindings and expression structure. + + @LP So far, I've focussed on the expression signature without looking closely at + the typing structure, except where needed. Thus, I will describe the + expression structure a bit here and provide examples to show the relation with + Haskell source programs. + +@BeginSubSections +@SubSection + @Title { Core bindings } +@Begin +@LP A central role in the core language is played by the @I binding. A binding binds +one or more expressions to one or more identifiers (@I binders). A core program is +essentially a set of bindings, each of which bind to an identifier on global +scope (i.e., you get one binding for each function that is defined). + +The @I CoreBind type is defined as follows: + +@ID @Haskell { + NonRec CoreBndr CoreExpr + | Rec [(CoreBndr, CoreExpr)] +} + +@LP The @I NonRec constructor is the simplest: It binds a single expression to a +single binder. The @I Rec constructors takes a list of binder, expression +pairs, and binds each expression to the corresponding binder. These bindings +happen on the same scope level, meaning that each expression can use any of +the (other) bindings, creating mutually recursive references. + +@LP When a program is initially compiled to Core, it consists of a single @I Rec +binding, containing all the bindings in the original haskell source. These +bindings are allowed to reference each other, possibly recursively, but might +not. One of the Core simplification passes splits out this large @I Rec +constructor into as much small @I Recs and @I NonRecs as possible. + +@LP The @I CoreBnder type is used in @I Let expressions as well, see below. +@End @SubSection + +@SubSection + @Title { Core expressions } +@Begin +@LP The main expression type is @I CoreExpr, @FootNote {In reality, @I CoreExpr +is an alias for @I {Expr CoreBndr}, but different types of binders are only +used by GHC internally.} which has the following +constructors @FootNote{The @I Note constructor was omitted, since it only +serves as annotation and does not influence semantics of the expression.}. + +@DP +@Tbl + aformat { @Cell @I A | @Cell B } +{ + @Rowa + A {Var Id} + B {A variable reference} + @Rowa + A {Lit Literal} + B {A literal value} + @Rowa + A {App CoreExpr CoreExpr} + B {An application. The first expression is the function or data + constructor to apply, the second expression is the argument to apply + the function to. Function application always applies a single + argument, for functions with multiple arguments application is applied + recursively.} + @Rowa + A {Lam CoreBndr CoreExpr} + B {A lambda expression. When this expression is applied to some argument, + the argument is bound to the binder given and the result is the + given expression. Or, slightly more formal, the result is the given + expression with all @I Var expressions corresponding to the binder + replaced with the argument the entire lambda is applied to.} + @Rowa + A {Let CoreBind CoreExpr} + B { + A let expression. The given bind is one or more binder, expression + pairs. Each expression is evaluated and bound to the corresponding + binder. The result is of the entire let expression is then the given + expression. + + @DP Or, slightly more formal, evaluates all expressions inside the given bind and + returns the given expression with all @I Var expressions corresponding to + the binders in the given bind replaced by their respective evaluated + expressions. + } + @Rowa + A { + Case CoreExpr [CoreAlt] + @FootNote {In reality, there are also another Type and CoreBndr + arguments. However, the Type argument only contains the type of the + entire expression, and thus is redundant and not useful for our purpose. + The CoreBndr is a binder to which the scrutinee is bound, but seems + this is only used for forcing evaluation of a term. Therefore, these two + are left out in this discussion.} + } + B { + A case expression, which can be used to do conditional evaluation and + data type ``unpacking''. The given expression (``scrutinee'') is + evaluated, bound to the given binder and depending on its value on of + the CoreAlts is chosen whose expression is returned. + + @DP A @I CoreAlt is a three tuple describing the alternative: + + @IndentedDisplay @I {(AltCon, [CoreBndr], CoreExpr)} + + @DP Here, the @I AltCon is an alternative constructor which can matched + to the scrutinee. An alternative constructor can be one of the default + alternative (which matches any value not matched by other alternative + constructors in the list), a literal (which matches if the scrutinee + evaluates to a literal or a data constructor, which matches if the value + is constructed using that specific data constructor. + + @DP In the last case, when a data constructor is used for the + alternative, the arguments to the data constructor are extracted from + the scrutinee and bound to the corresponding binders (from the second + element of the @I CoreAlt tuple). + + @DP The last element from the tuple is the expression to return, with + the extra bindings in effect. + } + @Rowa + A { + Cast CoreExpr Type + } + B { + Cast the given expression to the given type. This plays an import role + when working with @F newtype declarations and type classes. The exact + workings of this expression are a bit shady, but for now we can get away + with ignore it. + } + @Rowa + A { + Type Type + } + B { + This expression turns a type into an expression, so it can be applied to + functions to implement polymorphic functions and type classes. It cannot + be used anywhere else. + + Again, we can can get away with ignore this expression for now. + } +} +@DP +@BeginSubSubSections + @SubSubSection + @Title {Examples} + @Tag {CoreExamples} + @Begin + + @LP To get a clearer example of how Haskell programs translate to the Core + language, a number of programs are given in both Haskell syntax and the + equivalent core representation. These are all simple examples, focused on + showing a particular aspect of the Core language. + + @LP In these examples, we will use the following data type. Since most of the + primitive datatypes and operators are involved in some kind of + polymorphism (Even the + operator is tricky), those are less useful for + simple examples. Using the following @I Bit datatype will also make these + examples more appropriate for our goals. + + @ID @Haskell { + data Bit = High | Low + } + + @Display @Heading {Fixed value} + @LP This is a trivial example which shows a simple data constructor without + arguments bound to the binders ``high'' and ``low''. Note that in + reality, the binder and @I Id values in @I Var nodes include some extra + info about the module it is defined in and what kind of identifier it + is, but that is left out here for simplicity. + + @CNP @ID @Example + @Title {Haskell code} + { + @LP @Haskell { +high = High +low = Low}} + + @LP This haskell source results in the following list of @I + CoreBndrs. + + @CNP @ID @Example + @Title {Core Structure} + { + @LP @Haskell { +[ NonRec ``high'' (Var High) +, NonRec ``low'; (Var Low)]}} + + @LP In subsequent examples I will leave out the @I NonRec constructor and + binder name and just show the resulting @I CoreExpr. + + @Display @Heading {Function arguments} + This example shows a simple function that simply returns its argument. + + @CNP @ID @Example + @Title {Haskell code} + { + @LP @Haskell { +wire :: Bit -> Bit +wire a = a}} + + @CNP @ID @Example + @Title {Core Structure} + { + @LP @Haskell { +Lam a (Var a)}} + + @Display @Heading { Pattern matching } + This example shows a function with a single argument which uses + pattern matching, which results in a simple case statement. + + @CNP @ID @Example + @Title {Haskell Code} + { + @LP @Haskell { +inv :: Bit -> Bit +inv High = Low +inv Low = High}} + + @ID @Example + @Title {Core Structure} + { + @LP @Haskell { +Lam ds (Case + (Var ds) + [ + (High, [], (Var Low)), + (Low, [], (Var High)) + ] +)}} + + @LP Note that in the tuples describing the alternatives, the first element + is a data constructor directly, while the third argument is an expression + referring to a dataconstructor (henc the Var constructor). + + @Display @Heading {Function application} + This example shows a function that applies other functions (with one and + two arguments) to its own arguments to do its work. It also illustrates + functions accepting multiple arguments. + + @LP For this example we assume there is a function @I nand available to + returns the negated and of its two arguments. + + @CNP @ID @Example + @Title {Haskell code} + { + @LP @Haskell { +or :: Bit -> Bit -> Bit +or a b = inv (nand a b)}} + + @CNP @ID @Example + @Title {Core structure} + { + @LP @Haskell { +Lam a (Lam b (App + (Var inv) + (App + (App + (Var nand) + (Var a)) + (Var b) + ) +))}} + + @Display @Heading {Tuples} + This example shows a function that accepts a tuple of two values and + returns a tuple of two values again. + + @LP This example assumes the availability of two functions @I and and + @I xor, with obvious meaning. + + @CNP @ID @Example + @Title {Haskell code} + { + @LP @Haskell { +-- Accepts two values and returns the sum and carry out respectively. +half_adder :: (Bit, Bit) -> (Bit, Bit) +half_adder (a, b) = + (and a b, xor a b)}} + + @LP Since the core structure corresponding to this (still pretty trivial) + function will be quite complex already, we will first show the desugared and + simplified version of the function, to which the core structure directly + corresponds. + + @CNP @ID @Example + @Title {Simplified haskell code} + { + @LP @Haskell { + half_adder = \ds -> + Case ds of + (a, b) -> + (,) (and a b) (xor a b) +}} + @CNP @ID @Example + @Title {Core structure} + { + @LP @Haskell + { +Lam ds (Case (Var ds) + [( + (,), + [a, b], + (App + (App + (Var (,)) + (App + (App + (Var and) + (Var a) + ) + (Var b) + ) + ) + (App + (App + (Var xor) + (Var a) + ) + (Var b) + ) + ) + )] +)}} + + + @LP Note the (,) in this example, which is the data constructor for a + two-tuple. It can be passed two values and will construct a tuple from + them. @FootNote {In reality, this dataconstructor takes @I four arguments, + namely the types of each of the elements and then the elements themselves. + This is because the two-tuple type really takes type arguments, which are + made explicit on every call to its data constructors.} + + @Display @Heading {Let expressions} + This example shows a function that uses a let expression to use a value + twice. + + @CNP @ID @Example + @Title {Haskell code} + { + @LP @Haskell { +f :: Bit -> (Bit, Bit) +f a = + (x, and a x) + where + x = xor a a}} + + @CNP @ID @Example + @Title {Simplified haskell code} + { + @LP @Haskell { + f = \a -> + let x = xor a a in + (,) x (and a x) +}} + @CNP @ID @Example + @Title {Core structure} + { + @LP @Haskell + { +Lam a (Let + [(NonRec + x + (App + (App + (Var xor) + (Var a) + ) + (Var a) + ) + )] + (App + (App + (Var (,)) + (Var x) + ) + (App + (App + (Var and) + (Var a) + ) + (Var x) + ) + ) +)}} + + @Display @Heading {Pattern matching} + This example shows a multiplexer, i.e., a function that selects one + of two inputs, based on a third input. This illustrates how pattern + matching is translated into core and can be used to create + conditional signal connections. + + @CNP @ID @Example + @Title {Haskell code} + { + @LP @Haskell + { +mux2 :: Bit -> (Bit, Bit) -> Bit +mux2 Low (a, b) = a +mux2 High (a, b) = b}} + + @CNP @ID @Example + @Title {Simplified haskell code} + { + @LP @Haskell { + mux2 = \ds ds1 -> case ds of + Low -> case ds1 of (a, b) -> a + High -> case ds1 of (a, b) -> b}} + + @CNP @ID @Example + @Title {Core structure} + { + @LP @Haskell + { +Lam ds (Lam ds1 (Case (Var ds) + [( + Low, + [], + (Case (Var ds1) + [( + (,) + [a, b], + (Var a) + )] + ) + ),( + High, + [], + (Case (Var ds1) + [( + (,) + [a, b], + (Var b) + )] + ) + )] +))}} + @End @SubSubSection +@EndSubSubSections +@End @SubSection +@EndSubSections +@End @Section + +@Section + @Title {Flat Functions} + @Tag {FlatFunctions} +@Begin + @LP As an intermediate between Core and VHDL, we use "flat" functions (the name + is open for suggestions). A flat function is a simplified version of the + function, which defines a number of signals (corresponding to single haskell + values) which can be used in a number of places and are defined to a single + value. A single value in this case means something that will probably result + in a single VHDL signal or port, so a 2-tuple results in two signals, for + example. + + @LP The function is flat, because there are no directly nested expressions. A + function application can only use signals, not nested applications or other + expressions. These signals can of course be defined as the result of an + expression or function application, so the original expressions can still be + expressed. + + @LP A flat function is defined as a single @I FlatFunction constructor with + the following fields @FootNote {The implementation additionally has a @I + sigs fields listing all the signals, but since that list is directly + deducable from the @I args and @I defs fields, it is left out here}: + + @DP + @Tbl + aformat { @Cell @I A | @Cell B } + { + @Rowa + A {args :: [SignalMap]} + B {A list that maps each argument to this function to a (number of) + signal(s). In other words, this field specifies to which signals the + function arguments should be assigned when this function is called.} + @Rowa + A {res :: SignalMap} + B {A map that maps the return value to a (number of) signal(s). + signal(s). In other words, this field specifies which signals contain + the function result when this function is called.} + @Rowa + A {defs :: [SignalDef]} + B {Definitions for each of the signals in the function. Each @I SignalDef + defines one ore more signals, and each signal should be defined exactly + once. Note that the signals in @I args don't have a @I SignalDef entry.} + } + + @LP The type @I Signal plays a central role in a flat function. It + identifies a signal. It's exact representation is not that important (the + current implementation uses an Int), as long as it can be compared and + unique new @I Signals can be generated. + + @LP The @I SignalMap type has the following two constructors: + + @ID @Haskell { +Single Signal +| Tuple [SignalMap] + } + + @LP This means we can support any type that can be translated to a signal + directly (currently only @I Bit and @I Bool) and tuples containing those + types. A signal map allows us to decompose a Haskell value into individual + pieces that can be used in the resulting VHDL. + + @LP The @I SignalDef type is slightly more complex. For one or more signals, + it specifies how they are defined. In the resulting hardware, these can be + mapped to simple signal assignments or port mappings on component + instantiations. The type has the following constructors: + + @DP @Tbl + aformat { @Cell @I A | @Cell B } + { + @Rowa + A {lines @Break { +FApp + func :: HsFunction @FootNote {In the current implementation, this + value can also be a representation of a higher order function. + However, since this is not functional yet, it is left out of this + discussion.} + args :: [SignalMap] + res :: SignalMap + }} + B {A function application. The function that is called is identified by + the given @I HsFunction, and passed the values in the given + @I SignalMaps as arguments. The result of the function will be + assigned to the @I SignalMap in @I res.} + @Rowa + A {lines @Break { +CondDef + cond :: Signal + true :: Signal + false :: Signal + res :: Signal + }} + B {A conditional signal definition. If the signal pointed to by @I cond + is True, @I true is assigned to @I res, else @I false is assigned to + @I res. The @I cond signal must thus be of type @I Bool. + + This keeps conditional assignment very simple, all complexity in the + conditions will be put into @I SignalExpr in @I UncondDef. } + @Rowa + A {lines @Break { +UncondDef + src :: SignalExpr + dst :: Signal}} + B {Unconditional signal definition. In the most basic form (assigning a + simple signal to another signal) this is not often useful (only in + specific cases where two signals cannot be merged, when assigning an + output port to an input port for example). + + The more interesting use for unconditional definition is when the + @I SignalExpr is more complicated and allows for more complexity. } + } + + @LD @Heading {Identifying functions} + @LP To identify a function to call we use the @I HsFunction type. This + type is used for two purposes. First, it contains the name of the + function to call, so the corresponding @I CoreBndr and @I CoreExpr + can be found in the current module. Secondly, an @I HsFunction + contains information on the use of each argument and the return + value. In particular, it stores for each (part of an) argument or + return value if it should become an input or output Port, or a state + internal to the called function. + + @LP The @I HsFunction type looks as follows: + + @ID @Haskell { +HsFunction { + func :: String, + args :: [UseMap], + res :: UseMap +}} + @LP Here, the UseMap type is defined similar to the @I SignalMap + type @FootNote{In reality, both @I SignalMap and @I UseMap are + generalized into a type @I HsValueMap which takes an element type as + a type parameter}: + + @ID @Haskell { +Single Use +| Tuple [Use]} + + @LP The @I Use type is defined as follows. The @I StateId argument + to the @I State constructor is a unique identifier for the state + variable, which can be used to match input and output state + variables together. This state id should be unique within either + arguments or the return value, but should be matched between them + (@I i.e., there should be exactly one @I State use with id 0 in the + arguments, and exactly once in the return value) @FootNote{In the + current implementation, there is third constructor in the @I Use + type for arguments that get higher order functions passed in. Since + this is not functional yet, this is left out of this discussion}. + + @ID @Haskell { +Port +| State StateId} + + @LP The last type used in this description is @I SignalExpr. It allows for certain + expressions in the resulting VHDL, currently only comparison. The type has + the following constructors: + + @DP @Tbl + aformat { @Cell @I A | @Cell B } + { + @Rowa + A { + Sig Signal + } + B { + A simple signal reference. + } + @Rowa + A { + Literal String + } + B { + A literal value. The given string is the resulting VHDL value. Perhaps + this should better be something less VHDL-specific, but this works for + now. + } + @Rowa + A { + Eq Signal Signal + } + B { + Equality comparison. This is the only comparison supported for now and + results in a @I Bool signal that can be used in a @I CondDef. + } + } + + @BeginSubSections + @SubSection + @Title {Examples} + @Begin + @LP To ease the understanding of this datastructure, we will show + the flattened equivalents of some of the examples given in + @SectionLink {CoreExamples}. When reading these examples, don't + forget that the signal id's themselves are interchangeable, as + long as they remain unique and still link the right expressions + together (@I i.e., the actual numbers are not relevant). + @Display @Heading {Fixed value} + + @CNP @ID @Example + @Title {Haskell code} + { + @LP @Haskell {high = High}} + + @LP This Haskell source results in the following @I FlatFunction. + + @CNP @ID @Example + @Title {Flat function} + { + @LP @Haskell { +FlatFunction { + sigs = [0], + args = [], + res = (Single 0), + defs = [UncondDef (Lit "'1'") 0] +}}} + + @Display @Heading {Function application} + + @CNP @ID @Example + @Title {Haskell code} + { + @LP @Haskell { +or :: Bit -> Bit -> Bit +or a b = inv (nand a b)}} + + @CNP @ID @Example + @Title {Flat function} + { + @LP @Haskell { +FlatFunction { + sigs = [0, 1, 2, 3], + args = [Single 0, Single 1], + res = (Single 3), + defs = [ + (FApp + (HsFunction "nand" [Single Port, Single Port] (Single Port)) + [Single 0, Single 1] + (Single 2) + ), + (FApp + (HsFunction "inv" [Single Port] (Single Port)) + [Single 2] + (Single 3) + ) + ] +}}} + + @Display @Heading {Pattern matching} + @CNP @ID @Example + @Title {Haskell code} + { + @LP @Haskell + { +mux2 :: Bit -> (Bit, Bit) -> Bit +mux2 Low (a, b) = a +mux2 High (a, b) = b}} + + @CNP @ID @Example + @Title {Flat function} + { + @LP @Haskell { +FlatFunction { + sigs = [0, 1, 2, 3, 4, 5], + args = [Single 0, Tuple [Single 3, Single 4]], + res = (Single 5), + defs = [ + (UncondDef (Lit "'1'") 1), + (UncondDef (Eq 0 1) 2), + (CondDef 2 4 3 5) + ] +}}} + @End @SubSection + @EndSubSections + +@End @Section + +@Section + @Title {Flattening of functions} + @Tag {Flattening} +@Begin + @LP The most interesting part of all this is of course the translation of our + Haskell functions (in Core format) to flattened functions. From there, the + translation of VHDL is relatively easy. + + @LP This section details the "flattening" of a function. To this end, we define + a function @I flattenFunction which translates a (nonrecursive) binding to a + flat function. + + @LP The syntax used for these functions is not quite Haskell. It is similar, + but some functions can have side effects or don't return any value. They + are probably best read as if the lines are sequentially executed (though + pattern matching does happen as is normal in Haskell). This notation is + probably closest to the monadic "do" notation in Haskell, but with the + special syntax for that ("do", "<-" and "return") implied. + + @LP @Haskell { +flattenFunction (NonRec b expr) = + (args, res) = flattenExpr expr + defs = getDefs + FlatFunction args res defs + } + + @LP This is a rather simple function, but it does need some explaination. First + of all, the flattenExpr function has some hidden state (In the + implementation, this is state is made explicit using a @I State @I Monad). + To understand the above function, it is sufficient to know that the @I + getDefs function simply returns all the definitions that were added in @I + flattenExpr using the @I addDef function. + + @LP Similary, in the below function definitions, the @I insertBind function + inserts a signal bind (that binds a haskell identifier to a signal in the + flat function) into the current table of binds. This binds remains in the + table for any other functions called by the caller of @I insertBind, but is + removed again when that caller "returns". The @I lookupBind function looks + up such a bind in this table. + + @LP Now, the actual work is done by the @I flattenExpr function, which processes + an arbitrary expressions and returns a tuple with argument @I SignalMaps and + a result @I SignalMap. The argument @I SignalMaps map each argument that + should be passed to the expression (when it has a function type, this is an + empty list for non-function types). The signals these maps map to are the + signals to which the arguments are assumed to have been assigned. In the + final @I FlatFunction, these maps are used to tell to which signals the + input ports should be bound. + + @LP The result @I SignalMap is the inverse of the argument @I SIgnalMaps. It + contains the signals that the results of the expression are bound to. + + @Display @Heading {Lambda expressions} + @LP @Haskell { +flattenExpr (Lam b expr) = + arg = genSignals b + insertBind (b <= arg) + (args, res) = flattenExpr expr + (arg:args, res) + } + + @Display @Heading {Variable references} + @LP @Haskell { +flattenExpr (Var id) = + case id of + ``localid'' -> + ([], lookupBind id) + () -> + ([], Tuple []) + ``datacon'' -> + res = genSignals id + lit = Lit (dataconToLit id) + addDef UncondDef {src = lit, dst = s} + + ([], res) +dataconToLit datacon = + case datacon of + Low -> "'0'" + High -> "'1'" + True -> "true" + False -> "false" + } + + @LP Here, @I genSignals generates a map of new (unique) signals. The structure + on the map is based on the (Haskell) type of whatever is passed to it, such + as the value bound to the identifier in this case. + + @LP The @I ``datacon'' and @I ``localid'' patterns should be read as matching + when the id matching against refers to a local identifier (local to the + function or expression containing it) or a dataconstructor (such as @I True, + @I False, @I High or @I (,)). + + + @Display @Heading {Let expressions} + @LP @Haskell { +flattenExpr (Let (NonRec b bexpr) expr) = + (b_args, b_res) = flattenExpr bexpr + insertBind (b <= b_res) + flattenExpr expr + } + + @Display @Heading {Case expressions} + @LP @Haskell { +flattenExpr (Case scrut alts) = + (args, res) = flattenExpr scrut + flattenAlts res alts + +flattenAlt scrut alt = + TODO + +flattenAlts scrut [alt] = + flattenAlt scrut alt + +flattenAlts scrut (alt:alts) + (true_args, true_res) = flattenAlt scrut alt + (false_args, false_res) = flattenAlts scrut alts + (altcon, bind_vars, expr) = alt + lit = Lit (dataconToLit) + + TODO + } + + @Display @Heading {Function applications} + @LP @Haskell { +flattenExpr (App f arg) = + TODO + } + + +@End @Section + +# vim: set sts=2 sw=2 expandtab: