I have a procedural EDSL which uses blocks of statements.
These statements are added to the blocks in no particular order although there may be dependencies between statements.
During compilation of the EDSL, however, I need to make sure that the statements are ordered in the order of dependence, e.g.
B := A
C := B
E := D
Since not all statements have dependencies there is no total order (E.g. E := D
above is independent and can be placed anywhere). There are no cyclic dependencies so list ordering should be possible.
I have tried to hack a solution by using Data.List.sortBy
and defining Ordering
which would return EQ
to mean that the statements have no dependencies. This worked for some examples but not in the general case, e.g. ordering the following did nothing:
C := B B := A
D := C = should produce => C := B
B := A D := C
This is because the default sort insertion sort and only makes sure the inserted item is smaller or equal to the next.
I have searched the internets for a Poset implementation but have not found anything applicable:
altfloat:Data.Poset defines Ordering = LT | GT | EQ | NC
(NC
for Non-comparable) which is good but the provided sort
assumes NaN
-like non-comparable items and just throws them away.
logfloat:Data.Number.PartialOrd is similar to the above except uses Maybe Ordering
and I didn't see a sorting function anywhere in the package.
Math.Combinatorics.Poset I haven't figured out how to use it or whether it's applicable.
Below is a minimal example which has both binding and non-binding statements. The order of non-biniding statements matters and they must maintain the original order (i.e. sorting needs to be stable w.r.t. statements that don't have a dependence relation).
I hope there is a simple solution to this without using a full-blown dependence graph...
module Stmts where
import Data.List ( sortBy )
data Var = A | B | C | D | E | F | G | H deriving (Eq, Show)
data Stmt = Var := Var
| Inc Var
deriving (Show)
-- LHS variable
binds :: Stmt -> Maybe Var
binds (v := _) = Just v
binds _ = Nothing
-- RHS variables
references :: Stmt -> [Var]
references (_ := v) = [v]
references (Inc v) = [v]
order :: [Stmt] -> [Stmt]
order = sortBy orderStmts
orderStmts :: Stmt -> Stmt -> Ordering
orderStmts s1 s2 = ord mbv1 mbv2
where
ord Nothing Nothing = EQ -- No dep since they don't bind vars
ord (Just v1) Nothing = LT -- Binding statements have precedence
ord Nothing (Just v2) = GT -- ^^^
ord (Just v1) (Just v2) -- Both statements are binding:
| v1 `elem` refs2 = LT -- * s2 depends on s1
| v2 `elem` refs1 = GT -- * s1 depends on s2
| otherwise = EQ -- * neither
-- *Maybe* they bind variables
mbv1 = binds s1
mbv2 = binds s2
-- Variables they reference
refs1 = references s1
refs2 = references s2
-- The following should return [B := A, C := B, D := C, Inc F, Inc G]
test = order [Inc F, Inc G, C := B, D := C, B := A]