sifflet-lib 1.2.5.1 → 2.0.0.0
raw patch · 74 files changed
+12474/−10301 lines, 74 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Sifflet.Data.Functoid: FLayoutForest :: [TreeLayout ExprNode] -> BBox -> FunctoidLayout
- Sifflet.Data.Functoid: FLayoutTree :: (TreeLayout ExprNode) -> FunctoidLayout
- Sifflet.Data.Functoid: FunctoidFunc :: Function -> Functoid
- Sifflet.Data.Functoid: FunctoidParts :: String -> [String] -> [Node] -> Functoid
- Sifflet.Data.Functoid: data Functoid
- Sifflet.Data.Functoid: data FunctoidLayout
- Sifflet.Data.Functoid: flayout :: Style -> Functoid -> Env -> Maybe [Value] -> FunctoidLayout
- Sifflet.Data.Functoid: flayoutBBox :: FunctoidLayout -> BBox
- Sifflet.Data.Functoid: flayoutBottom :: FunctoidLayout -> Double
- Sifflet.Data.Functoid: flayoutSize :: FunctoidLayout -> Size
- Sifflet.Data.Functoid: flayoutWiden :: FunctoidLayout -> Double -> FunctoidLayout
- Sifflet.Data.Functoid: flayoutWidth :: FunctoidLayout -> Double
- Sifflet.Data.Functoid: fpArgs :: Functoid -> [String]
- Sifflet.Data.Functoid: fpFunc :: Functoid -> Function
- Sifflet.Data.Functoid: fpName :: Functoid -> String
- Sifflet.Data.Functoid: fpNodes :: Functoid -> [Node]
- Sifflet.Data.Functoid: functoidArgNames :: Functoid -> [String]
- Sifflet.Data.Functoid: functoidHeader :: Functoid -> String
- Sifflet.Data.Functoid: functoidName :: Functoid -> String
- Sifflet.Data.Functoid: instance Show Functoid
- Sifflet.Data.Functoid: instance Translate FunctoidLayout
- Sifflet.Data.Geometry: BBox :: Double -> Double -> Double -> Double -> BBox
- Sifflet.Data.Geometry: Circle :: Position -> Double -> Circle
- Sifflet.Data.Geometry: Position :: Double -> Double -> Position
- Sifflet.Data.Geometry: Size :: Double -> Double -> Size
- Sifflet.Data.Geometry: bbCenter :: BBox -> Position
- Sifflet.Data.Geometry: bbFromRect :: Rectangle -> BBox
- Sifflet.Data.Geometry: bbLeft, bbRight, bbXCenter :: BBox -> Double
- Sifflet.Data.Geometry: bbMerge :: BBox -> BBox -> BBox
- Sifflet.Data.Geometry: bbMergeList :: [BBox] -> BBox
- Sifflet.Data.Geometry: bbPosition :: BBox -> Position
- Sifflet.Data.Geometry: bbSetWidth :: BBox -> Double -> BBox
- Sifflet.Data.Geometry: bbSize :: BBox -> Size
- Sifflet.Data.Geometry: bbToRect :: BBox -> Rectangle
- Sifflet.Data.Geometry: bbTop, bbBottom, bbYCenter :: BBox -> Double
- Sifflet.Data.Geometry: bbX, bbHeight, bbWidth, bbY :: BBox -> Double
- Sifflet.Data.Geometry: circleCenter :: Circle -> Position
- Sifflet.Data.Geometry: circleRadius :: Circle -> Double
- Sifflet.Data.Geometry: class Translate a
- Sifflet.Data.Geometry: class Widen a
- Sifflet.Data.Geometry: data BBox
- Sifflet.Data.Geometry: data Circle
- Sifflet.Data.Geometry: data Position
- Sifflet.Data.Geometry: data Size
- Sifflet.Data.Geometry: instance Eq BBox
- Sifflet.Data.Geometry: instance Eq Circle
- Sifflet.Data.Geometry: instance Eq Position
- Sifflet.Data.Geometry: instance Eq Size
- Sifflet.Data.Geometry: instance Read BBox
- Sifflet.Data.Geometry: instance Read Circle
- Sifflet.Data.Geometry: instance Read Position
- Sifflet.Data.Geometry: instance Read Size
- Sifflet.Data.Geometry: instance Show BBox
- Sifflet.Data.Geometry: instance Show Circle
- Sifflet.Data.Geometry: instance Show Position
- Sifflet.Data.Geometry: instance Show Size
- Sifflet.Data.Geometry: instance Translate BBox
- Sifflet.Data.Geometry: instance Translate Circle
- Sifflet.Data.Geometry: instance Translate Position
- Sifflet.Data.Geometry: instance Translate e => Translate (Tree e)
- Sifflet.Data.Geometry: instance Translate e => Translate [e]
- Sifflet.Data.Geometry: instance Widen BBox
- Sifflet.Data.Geometry: pointInBB :: Position -> BBox -> Bool
- Sifflet.Data.Geometry: pointInCircle :: Position -> Circle -> Bool
- Sifflet.Data.Geometry: posX :: Position -> Double
- Sifflet.Data.Geometry: posY :: Position -> Double
- Sifflet.Data.Geometry: positionCloseEnough :: Position -> Position -> Double -> Bool
- Sifflet.Data.Geometry: positionDelta :: Position -> Position -> (Double, Double)
- Sifflet.Data.Geometry: positionDistance :: Position -> Position -> Double
- Sifflet.Data.Geometry: positionDistanceSquared :: Position -> Position -> Double
- Sifflet.Data.Geometry: sizeH :: Size -> Double
- Sifflet.Data.Geometry: sizeW :: Size -> Double
- Sifflet.Data.Geometry: translate :: Translate a => Double -> Double -> a -> a
- Sifflet.Data.Geometry: widen :: Widen a => a -> Double -> a
- Sifflet.Data.Tree: Name :: String -> Name
- Sifflet.Data.Tree: Node :: a -> Forest a -> Tree a
- Sifflet.Data.Tree: class Repr a where repr = head . reprl reprl x = [repr x] reprs = unwords . reprl reprList pre tween post xs = pre ++ intercalate tween (map repr xs) ++ post
- Sifflet.Data.Tree: data Name
- Sifflet.Data.Tree: data Tree a :: * -> *
- Sifflet.Data.Tree: drawTreeShow :: Show e => Tree e -> String
- Sifflet.Data.Tree: isLeaf :: Tree e -> Bool
- Sifflet.Data.Tree: leaf :: e -> Tree e
- Sifflet.Data.Tree: nameTree :: Tree String -> Tree Name
- Sifflet.Data.Tree: putTree :: Show e => Tree e -> IO ()
- Sifflet.Data.Tree: putTreeR :: Repr e => Tree e -> IO ()
- Sifflet.Data.Tree: putTreeRs :: Repr e => Tree e -> IO ()
- Sifflet.Data.Tree: putTreeS :: Show e => Tree e -> IO ()
- Sifflet.Data.Tree: repr :: Repr a => a -> String
- Sifflet.Data.Tree: reprList :: Repr a => String -> String -> String -> [a] -> String
- Sifflet.Data.Tree: reprl :: Repr a => a -> [String]
- Sifflet.Data.Tree: reprs :: Repr a => a -> String
- Sifflet.Data.Tree: rootLabel :: Tree a -> a
- Sifflet.Data.Tree: subForest :: Tree a -> Forest a
- Sifflet.Data.Tree: tree :: e -> Forest e -> Tree e
- Sifflet.Data.Tree: treeDepth :: Tree e -> Int
- Sifflet.Data.Tree: treeSize :: Tree e -> Int
- Sifflet.Data.TreeGraph: adjCompareEdge :: (Node, WEdge) -> (Node, WEdge) -> Ordering
- Sifflet.Data.TreeGraph: flayoutToGraph :: FunctoidLayout -> WGraph
- Sifflet.Data.TreeGraph: flayoutToGraphRoots :: FunctoidLayout -> [Node]
- Sifflet.Data.TreeGraph: functoidToFunction :: Functoid -> WGraph -> Node -> Env -> SuccFail Function
- Sifflet.Data.TreeGraph: grTranslateGraph :: Double -> Double -> LayoutGraph n e -> LayoutGraph n e
- Sifflet.Data.TreeGraph: grTranslateNode :: Node -> Double -> Double -> LayoutGraph n e -> LayoutGraph n e
- Sifflet.Data.TreeGraph: grTranslateSubtree :: Node -> Double -> Double -> LayoutGraph n e -> LayoutGraph n e
- Sifflet.Data.TreeGraph: graphToExprTree :: WGraph -> Node -> Tree ExprNode
- Sifflet.Data.TreeGraph: graphToOrderedTree :: Gr e WEdge -> Tree e
- Sifflet.Data.TreeGraph: graphToOrderedTreeFrom :: Gr e WEdge -> Node -> Tree e
- Sifflet.Data.TreeGraph: graphToTreeOriginal :: Gr e () -> Node -> Tree e
- Sifflet.Data.TreeGraph: graphToTreeStructure :: Gr n e -> Node -> Tree Node
- Sifflet.Data.TreeGraph: nextNodes :: [e] -> Node -> ([Node], Node)
- Sifflet.Data.TreeGraph: orderedChildren :: Gr e WEdge -> Node -> [Node]
- Sifflet.Data.TreeGraph: orderedTreeToGraph :: Tree e -> Gr e WEdge
- Sifflet.Data.TreeGraph: treeGraphNodesTree :: Tree e -> Tree Node
- Sifflet.Data.TreeGraph: treeLayoutToGraph :: TreeLayout ExprNode -> WGraph
- Sifflet.Data.TreeGraph: type LayoutGraph n e = Gr (LayoutNode n) e
- Sifflet.Data.TreeLayout: ColorRGB :: Double -> Double -> Double -> VColor
- Sifflet.Data.TreeLayout: ColorRGBA :: Double -> Double -> Double -> Double -> VColor
- Sifflet.Data.TreeLayout: FontTextExtents :: Double -> Double -> Double -> Double -> Double -> Double -> Double -> Double -> Double -> Double -> Double -> FontTextExtents
- Sifflet.Data.TreeLayout: GNode :: e -> [TextBox] -> BBox -> [Iolet] -> [Iolet] -> GNode e
- Sifflet.Data.TreeLayout: Iolet :: Circle -> Iolet
- Sifflet.Data.TreeLayout: LayoutNode :: GNode e -> BBox -> LayoutNode e
- Sifflet.Data.TreeLayout: Style :: VFont -> Double -> Double -> Double -> Double -> Double -> (Double, Double) -> Double -> VColor -> VColor -> VColor -> VColor -> VColor -> VColor -> VColor -> VColor -> VColor -> VColor -> Position -> VColor -> VFont -> Double -> Bool -> Bool -> Style
- Sifflet.Data.TreeLayout: TextBox :: String -> BBox -> BBox -> TextBox
- Sifflet.Data.TreeLayout: VFont :: String -> FontSlant -> FontWeight -> Double -> VFont
- Sifflet.Data.TreeLayout: black, lightBlueGreen, lightBlue, cream, white :: VColor
- Sifflet.Data.TreeLayout: data FontTextExtents
- Sifflet.Data.TreeLayout: data GNode e
- Sifflet.Data.TreeLayout: data Iolet
- Sifflet.Data.TreeLayout: data LayoutNode e
- Sifflet.Data.TreeLayout: data Style
- Sifflet.Data.TreeLayout: data TextBox
- Sifflet.Data.TreeLayout: data VColor
- Sifflet.Data.TreeLayout: data VFont
- Sifflet.Data.TreeLayout: exomargin :: Style -> Double
- Sifflet.Data.TreeLayout: extTextHeight :: FontTextExtents -> Double
- Sifflet.Data.TreeLayout: extTextWidth :: FontTextExtents -> Double
- Sifflet.Data.TreeLayout: findRect :: Position -> TreeLayout e -> Maybe Rectangle
- Sifflet.Data.TreeLayout: fontAscent :: FontTextExtents -> Double
- Sifflet.Data.TreeLayout: fontDescent :: FontTextExtents -> Double
- Sifflet.Data.TreeLayout: fontHeight :: FontTextExtents -> Double
- Sifflet.Data.TreeLayout: fontMaxXadvance :: FontTextExtents -> Double
- Sifflet.Data.TreeLayout: fontMaxYadvance :: FontTextExtents -> Double
- Sifflet.Data.TreeLayout: ftExtents :: String -> Render FontTextExtents
- Sifflet.Data.TreeLayout: gnodeInlets :: GNode e -> [Iolet]
- Sifflet.Data.TreeLayout: gnodeNodeBB :: GNode e -> BBox
- Sifflet.Data.TreeLayout: gnodeOutlets :: GNode e -> [Iolet]
- Sifflet.Data.TreeLayout: gnodeText :: GNode e -> String
- Sifflet.Data.TreeLayout: gnodeTextBB :: GNode e -> BBox
- Sifflet.Data.TreeLayout: gnodeTextBoxes :: GNode e -> [TextBox]
- Sifflet.Data.TreeLayout: gnodeValue :: GNode e -> e
- Sifflet.Data.TreeLayout: hpad :: Style -> Double
- Sifflet.Data.TreeLayout: instance Eq FontTextExtents
- Sifflet.Data.TreeLayout: instance Eq Iolet
- Sifflet.Data.TreeLayout: instance Eq TextBox
- Sifflet.Data.TreeLayout: instance Eq e => Eq (GNode e)
- Sifflet.Data.TreeLayout: instance Eq e => Eq (LayoutNode e)
- Sifflet.Data.TreeLayout: instance Read FontTextExtents
- Sifflet.Data.TreeLayout: instance Read Iolet
- Sifflet.Data.TreeLayout: instance Read TextBox
- Sifflet.Data.TreeLayout: instance Show FontTextExtents
- Sifflet.Data.TreeLayout: instance Show Iolet
- Sifflet.Data.TreeLayout: instance Show TextBox
- Sifflet.Data.TreeLayout: instance Show e => Show (GNode e)
- Sifflet.Data.TreeLayout: instance Show e => Show (LayoutNode e)
- Sifflet.Data.TreeLayout: instance Translate (GNode e)
- Sifflet.Data.TreeLayout: instance Translate (LayoutNode e)
- Sifflet.Data.TreeLayout: instance Translate Iolet
- Sifflet.Data.TreeLayout: instance Translate TextBox
- Sifflet.Data.TreeLayout: instance Widen (LayoutNode e)
- Sifflet.Data.TreeLayout: instance Widen TextBox
- Sifflet.Data.TreeLayout: ioletCenter :: Iolet -> Position
- Sifflet.Data.TreeLayout: layoutNodeSource :: LayoutNode e -> e
- Sifflet.Data.TreeLayout: layoutRootBB :: TreeLayout e -> BBox
- Sifflet.Data.TreeLayout: layoutTreeBB :: TreeLayout e -> BBox
- Sifflet.Data.TreeLayout: layoutTreeMoveCenterTo :: Double -> Double -> TreeLayout e -> TreeLayout e
- Sifflet.Data.TreeLayout: lightGray, darkGray, mediumGray :: VColor
- Sifflet.Data.TreeLayout: lineWidth :: Style -> Double
- Sifflet.Data.TreeLayout: makeGNode :: Repr e => Style -> IoletCounter e -> e -> GNode e
- Sifflet.Data.TreeLayout: makeIolets :: Style -> BBox -> (Int, Int) -> ([Iolet], [Iolet])
- Sifflet.Data.TreeLayout: makeIoletsRow :: Style -> Double -> Double -> Int -> [Iolet]
- Sifflet.Data.TreeLayout: makeTextBox :: Style -> String -> TextBox
- Sifflet.Data.TreeLayout: measureText :: Style -> String -> Size
- Sifflet.Data.TreeLayout: nodeGNode :: LayoutNode e -> GNode e
- Sifflet.Data.TreeLayout: nodeTreeBB :: LayoutNode e -> BBox
- Sifflet.Data.TreeLayout: offsetTextBoxCenters :: Position -> TextBox -> TextBox -> TextBox
- Sifflet.Data.TreeLayout: pointInGNode :: Position -> GNode e -> Bool
- Sifflet.Data.TreeLayout: pointInIolet :: Position -> Iolet -> Bool
- Sifflet.Data.TreeLayout: pointInLayoutNode :: Position -> LayoutNode e -> Bool
- Sifflet.Data.TreeLayout: setFont :: VFont -> Render ()
- Sifflet.Data.TreeLayout: style0, defaultStyle, style3, style2, style1 :: Style
- Sifflet.Data.TreeLayout: styleActiveEdgeColor :: Style -> VColor
- Sifflet.Data.TreeLayout: styleActiveFillColor :: Style -> VColor
- Sifflet.Data.TreeLayout: styleActiveTextColor :: Style -> VColor
- Sifflet.Data.TreeLayout: styleAuxColor :: Style -> VColor
- Sifflet.Data.TreeLayout: styleAuxFont :: Style -> VFont
- Sifflet.Data.TreeLayout: styleAuxOffset :: Style -> Position
- Sifflet.Data.TreeLayout: styleFont :: Style -> VFont
- Sifflet.Data.TreeLayout: styleFramePad :: Style -> Double
- Sifflet.Data.TreeLayout: styleIncreasePadding :: Style -> Double -> Style
- Sifflet.Data.TreeLayout: styleIoletRadius :: Style -> Double
- Sifflet.Data.TreeLayout: styleNormalEdgeColor :: Style -> VColor
- Sifflet.Data.TreeLayout: styleNormalFillColor :: Style -> VColor
- Sifflet.Data.TreeLayout: styleNormalTextColor :: Style -> VColor
- Sifflet.Data.TreeLayout: styleSelectedEdgeColor :: Style -> VColor
- Sifflet.Data.TreeLayout: styleSelectedFillColor :: Style -> VColor
- Sifflet.Data.TreeLayout: styleSelectedTextColor :: Style -> VColor
- Sifflet.Data.TreeLayout: styleShowNodeBoxes :: Style -> Bool
- Sifflet.Data.TreeLayout: styleShowNodePorts :: Style -> Bool
- Sifflet.Data.TreeLayout: styleTetherColor :: Style -> VColor
- Sifflet.Data.TreeLayout: styleTextExtents :: Style -> String -> FontTextExtents
- Sifflet.Data.TreeLayout: tbBottom :: TextBox -> Double
- Sifflet.Data.TreeLayout: tbBoxBB :: TextBox -> BBox
- Sifflet.Data.TreeLayout: tbBoxCenter :: TextBox -> Position
- Sifflet.Data.TreeLayout: tbCenter :: TextBox -> Position
- Sifflet.Data.TreeLayout: tbHeight :: TextBox -> Double
- Sifflet.Data.TreeLayout: tbSetWidth :: TextBox -> Double -> TextBox
- Sifflet.Data.TreeLayout: tbText :: TextBox -> String
- Sifflet.Data.TreeLayout: tbTextBB :: TextBox -> BBox
- Sifflet.Data.TreeLayout: tbTextCenter :: TextBox -> Position
- Sifflet.Data.TreeLayout: tbWidth :: TextBox -> Double
- Sifflet.Data.TreeLayout: textMargin :: Style -> Double
- Sifflet.Data.TreeLayout: textXadvance :: FontTextExtents -> Double
- Sifflet.Data.TreeLayout: textXbearing :: FontTextExtents -> Double
- Sifflet.Data.TreeLayout: textYadvance :: FontTextExtents -> Double
- Sifflet.Data.TreeLayout: textYbearing :: FontTextExtents -> Double
- Sifflet.Data.TreeLayout: treeGNodes :: Repr e => Style -> IoletCounter e -> Tree e -> Tree (GNode e)
- Sifflet.Data.TreeLayout: treeLayout :: Repr e => Style -> IoletCounter e -> Tree e -> TreeLayout e
- Sifflet.Data.TreeLayout: treeLayoutPaddedSize :: Style -> TreeLayout e -> Size
- Sifflet.Data.TreeLayout: treeLayoutSize :: TreeLayout e -> Size
- Sifflet.Data.TreeLayout: treeLayoutWiden :: TreeLayout e -> Double -> TreeLayout e
- Sifflet.Data.TreeLayout: treeLayoutWidth :: TreeLayout e -> Double
- Sifflet.Data.TreeLayout: treeSizes :: Style -> Tree (GNode e) -> Tree Size
- Sifflet.Data.TreeLayout: type IoletCounter e = e -> (Int, Int)
- Sifflet.Data.TreeLayout: type TreeLayout e = Tree (LayoutNode e)
- Sifflet.Data.TreeLayout: vfontFace :: VFont -> String
- Sifflet.Data.TreeLayout: vfontSize :: VFont -> Double
- Sifflet.Data.TreeLayout: vfontSlant :: VFont -> FontSlant
- Sifflet.Data.TreeLayout: vfontWeight :: VFont -> FontWeight
- Sifflet.Data.TreeLayout: vpad :: Style -> Double
- Sifflet.Data.TreeLayout: vtinypad :: Style -> (Double, Double)
- Sifflet.Data.TreeLayout: wstyle :: Style
- Sifflet.Data.TreeLayout: yellow, blueGreen, darkBlueGreen :: VColor
- Sifflet.Data.TreeLayout: zeroIoletCounter :: IoletCounter e
- Sifflet.Data.WGraph: WEdge :: Int -> WEdge
- Sifflet.Data.WGraph: WFrame :: Node -> WNode
- Sifflet.Data.WGraph: WSimple :: (LayoutNode ExprNode) -> WNode
- Sifflet.Data.WGraph: connectToFrame :: Node -> Node -> WGraph -> WGraph
- Sifflet.Data.WGraph: data WEdge
- Sifflet.Data.WGraph: data WNode
- Sifflet.Data.WGraph: functionToParts :: Function -> WGraph -> Node -> Functoid
- Sifflet.Data.WGraph: functoidParts :: Functoid -> WGraph -> Node -> Functoid
- Sifflet.Data.WGraph: grAddGraph :: DynGraph g => g n e -> g n e -> g n e
- Sifflet.Data.WGraph: grConnect :: WGraph -> Node -> WEdge -> Node -> WEdge -> WGraph
- Sifflet.Data.WGraph: grDisconnect :: WGraph -> Node -> WEdge -> Node -> WEdge -> Bool -> WGraph
- Sifflet.Data.WGraph: grExtractExprTree :: WGraph -> Node -> Tree ExprNode
- Sifflet.Data.WGraph: grExtractLayoutNode :: WGraph -> Node -> LayoutNode ExprNode
- Sifflet.Data.WGraph: grExtractLayoutTree :: WGraph -> Node -> TreeLayout ExprNode
- Sifflet.Data.WGraph: grInsertNode :: DynGraph g => g n e -> n -> (g n e, Node)
- Sifflet.Data.WGraph: grRelabelNode :: DynGraph g => g a b -> Node -> a -> g a b
- Sifflet.Data.WGraph: grRemoveNode :: DynGraph g => g n e -> Node -> (g n e)
- Sifflet.Data.WGraph: grUpdateFLayout :: WGraph -> [Node] -> FunctoidLayout -> WGraph
- Sifflet.Data.WGraph: grUpdateTreeLayout :: WGraph -> Node -> TreeLayout ExprNode -> WGraph
- Sifflet.Data.WGraph: instance Eq WEdge
- Sifflet.Data.WGraph: instance Eq WNode
- Sifflet.Data.WGraph: instance Ord WEdge
- Sifflet.Data.WGraph: instance Read WEdge
- Sifflet.Data.WGraph: instance Repr WNode
- Sifflet.Data.WGraph: instance Show WEdge
- Sifflet.Data.WGraph: instance Show WNode
- Sifflet.Data.WGraph: llab :: WGraph -> Node -> LayoutNode ExprNode
- Sifflet.Data.WGraph: nodeAllChildren :: WGraph -> Node -> [Node]
- Sifflet.Data.WGraph: nodeAllSimpleDescendants :: WGraph -> Node -> [Node]
- Sifflet.Data.WGraph: nodeBBox :: WGraph -> Node -> BBox
- Sifflet.Data.WGraph: nodeContainerFrameNode :: WGraph -> Node -> Node
- Sifflet.Data.WGraph: nodeExprNode :: WGraph -> Node -> ExprNode
- Sifflet.Data.WGraph: nodeFrameChildren :: WGraph -> Node -> [Node]
- Sifflet.Data.WGraph: nodeInputValues :: WGraph -> Node -> EvalResult
- Sifflet.Data.WGraph: nodeIsOpen :: WGraph -> Node -> Bool
- Sifflet.Data.WGraph: nodeIsSimple :: WGraph -> Node -> Bool
- Sifflet.Data.WGraph: nodeParent :: WGraph -> Node -> Maybe Node
- Sifflet.Data.WGraph: nodePosition :: WGraph -> Node -> Position
- Sifflet.Data.WGraph: nodeProperSimpleDescendants :: WGraph -> Node -> [Node]
- Sifflet.Data.WGraph: nodeSimpleChildren :: WGraph -> Node -> [Node]
- Sifflet.Data.WGraph: nodeText :: WGraph -> Node -> String
- Sifflet.Data.WGraph: nodeValue :: WGraph -> Node -> EvalResult
- Sifflet.Data.WGraph: translateNode :: Double -> Double -> WGraph -> Node -> WGraph
- Sifflet.Data.WGraph: translateNodes :: Double -> Double -> WGraph -> [Node] -> WGraph
- Sifflet.Data.WGraph: translateTree :: Double -> Double -> WGraph -> Node -> WGraph
- Sifflet.Data.WGraph: type WGraph = Gr WNode WEdge
- Sifflet.Data.WGraph: wgraphNew :: WGraph
- Sifflet.Data.WGraph: wlab :: WGraph -> Node -> WNode
- Sifflet.Examples: eFact :: Expr -> Expr
- Sifflet.Examples: eFoo :: Expr -> Expr -> Expr
- Sifflet.Examples: eMax :: Expr -> Expr -> Expr
- Sifflet.Examples: exampleEnv :: Env
- Sifflet.Examples: exampleFunctionNames :: [String]
- Sifflet.Examples: exampleFunctions :: [Function]
- Sifflet.Examples: foo :: Function
- Sifflet.Examples: getExampleFunction :: String -> Function
- Sifflet.Foreign.Exporter: applyFirstMatch :: [Expr -> Expr] -> Expr -> Expr
- Sifflet.Foreign.Exporter: commonRuleAssocRight :: Expr -> Expr
- Sifflet.Foreign.Exporter: commonRuleAtomic :: Expr -> Expr
- Sifflet.Foreign.Exporter: commonRuleFuncOp :: Expr -> Expr
- Sifflet.Foreign.Exporter: commonRuleHigherPrec :: Expr -> Expr
- Sifflet.Foreign.Exporter: commonRuleLeftToRight :: Expr -> Expr
- Sifflet.Foreign.Exporter: commonRulesForSimplifyingExprs :: [Expr -> Expr]
- Sifflet.Foreign.Exporter: findFixed :: Eq a => (a -> a) -> a -> a
- Sifflet.Foreign.Exporter: ruleIfRight :: Expr -> Expr
- Sifflet.Foreign.Exporter: ruleRightToLeft :: Expr -> Expr
- Sifflet.Foreign.Exporter: simplifyExpr :: [Expr -> Expr] -> Expr -> Expr
- Sifflet.Foreign.Exporter: type Exporter = Functions -> FilePath -> IO ()
- Sifflet.Foreign.Haskell: Decl :: String -> Maybe [String] -> [String] -> Expr -> Decl
- Sifflet.Foreign.Haskell: ExportSpec :: [String] -> ExportSpec
- Sifflet.Foreign.Haskell: ImportDecl :: [String] -> ImportDecl
- Sifflet.Foreign.Haskell: Module :: String -> Maybe ExportSpec -> ImportDecl -> [Decl] -> Module
- Sifflet.Foreign.Haskell: class HsPretty a where hsPrettyList pre tween post xs = pre ++ intercalate tween (map hsPretty xs) ++ post
- Sifflet.Foreign.Haskell: data Decl
- Sifflet.Foreign.Haskell: data ExportSpec
- Sifflet.Foreign.Haskell: data ImportDecl
- Sifflet.Foreign.Haskell: data Module
- Sifflet.Foreign.Haskell: declExpr :: Decl -> Expr
- Sifflet.Foreign.Haskell: declIdent :: Decl -> String
- Sifflet.Foreign.Haskell: declParams :: Decl -> [String]
- Sifflet.Foreign.Haskell: declType :: Decl -> Maybe [String]
- Sifflet.Foreign.Haskell: hsPretty :: HsPretty a => a -> String
- Sifflet.Foreign.Haskell: hsPrettyList :: HsPretty a => String -> String -> String -> [a] -> String
- Sifflet.Foreign.Haskell: instance Eq Decl
- Sifflet.Foreign.Haskell: instance Eq ExportSpec
- Sifflet.Foreign.Haskell: instance Eq ImportDecl
- Sifflet.Foreign.Haskell: instance Eq Module
- Sifflet.Foreign.Haskell: instance HsPretty Decl
- Sifflet.Foreign.Haskell: instance HsPretty ExportSpec
- Sifflet.Foreign.Haskell: instance HsPretty Expr
- Sifflet.Foreign.Haskell: instance HsPretty ImportDecl
- Sifflet.Foreign.Haskell: instance HsPretty Module
- Sifflet.Foreign.Haskell: instance HsPretty Operator
- Sifflet.Foreign.Haskell: instance HsPretty Symbol
- Sifflet.Foreign.Haskell: instance Show Decl
- Sifflet.Foreign.Haskell: instance Show ExportSpec
- Sifflet.Foreign.Haskell: instance Show ImportDecl
- Sifflet.Foreign.Haskell: instance Show Module
- Sifflet.Foreign.Haskell: moduleDecls :: Module -> [Decl]
- Sifflet.Foreign.Haskell: moduleExports :: Module -> Maybe ExportSpec
- Sifflet.Foreign.Haskell: moduleImports :: Module -> ImportDecl
- Sifflet.Foreign.Haskell: moduleName :: Module -> String
- Sifflet.Foreign.Haskell: operatorTable :: Map String Operator
- Sifflet.Foreign.Python: PCondS :: Expr -> PStatement -> PStatement -> PStatement
- Sifflet.Foreign.Python: PFun :: Symbol -> [Symbol] -> PStatement -> PStatement
- Sifflet.Foreign.Python: PImport :: String -> PStatement
- Sifflet.Foreign.Python: PModule :: [PStatement] -> PModule
- Sifflet.Foreign.Python: PReturn :: Expr -> PStatement
- Sifflet.Foreign.Python: alterParens :: (Expr -> Expr) -> PStatement -> PStatement
- Sifflet.Foreign.Python: char :: Char -> Expr
- Sifflet.Foreign.Python: class PyPretty a where pyPrettyList pre tween post xs = pre ++ intercalate tween (map pyPretty xs) ++ post
- Sifflet.Foreign.Python: condS :: Expr -> Expr -> Expr -> PStatement
- Sifflet.Foreign.Python: data PModule
- Sifflet.Foreign.Python: data PStatement
- Sifflet.Foreign.Python: fun :: String -> [String] -> Expr -> PStatement
- Sifflet.Foreign.Python: ident :: String -> Symbol
- Sifflet.Foreign.Python: instance Eq PModule
- Sifflet.Foreign.Python: instance Eq PStatement
- Sifflet.Foreign.Python: instance PyPretty Expr
- Sifflet.Foreign.Python: instance PyPretty Operator
- Sifflet.Foreign.Python: instance PyPretty PModule
- Sifflet.Foreign.Python: instance PyPretty PStatement
- Sifflet.Foreign.Python: instance PyPretty Symbol
- Sifflet.Foreign.Python: instance Show PModule
- Sifflet.Foreign.Python: instance Show PStatement
- Sifflet.Foreign.Python: operatorTable :: Map String Operator
- Sifflet.Foreign.Python: pyPretty :: PyPretty a => a -> String
- Sifflet.Foreign.Python: pyPrettyList :: PyPretty a => String -> String -> String -> [a] -> String
- Sifflet.Foreign.Python: ret :: Expr -> PStatement
- Sifflet.Foreign.Python: var :: String -> Expr
- Sifflet.Foreign.ToHaskell: HaskellOptions :: Int -> Int -> HaskellOptions
- Sifflet.Foreign.ToHaskell: data HaskellOptions
- Sifflet.Foreign.ToHaskell: defaultHaskellOptions :: HaskellOptions
- Sifflet.Foreign.ToHaskell: exportHaskell :: HaskellOptions -> Exporter
- Sifflet.Foreign.ToHaskell: exprToHsExpr :: Expr -> Expr
- Sifflet.Foreign.ToHaskell: functionToHsDecl :: Function -> Decl
- Sifflet.Foreign.ToHaskell: functionsToHsModule :: String -> Functions -> Module
- Sifflet.Foreign.ToHaskell: instance Eq HaskellOptions
- Sifflet.Foreign.ToHaskell: instance Show HaskellOptions
- Sifflet.Foreign.ToHaskell: optionsHardMaxLineWidth :: HaskellOptions -> Int
- Sifflet.Foreign.ToHaskell: optionsSoftMaxLineWidth :: HaskellOptions -> Int
- Sifflet.Foreign.ToPython: PythonOptions :: PythonOptions
- Sifflet.Foreign.ToPython: data PythonOptions
- Sifflet.Foreign.ToPython: defToPy :: FunctionDefTuple -> PStatement
- Sifflet.Foreign.ToPython: defaultPythonOptions :: PythonOptions
- Sifflet.Foreign.ToPython: exportPython :: PythonOptions -> Exporter
- Sifflet.Foreign.ToPython: exprToPExpr :: Expr -> Expr
- Sifflet.Foreign.ToPython: fixIdentifierChars :: String -> String
- Sifflet.Foreign.ToPython: functionToPyDef :: Function -> PStatement
- Sifflet.Foreign.ToPython: functionsToPrettyPy :: Functions -> String
- Sifflet.Foreign.ToPython: functionsToPyModule :: Functions -> PModule
- Sifflet.Foreign.ToPython: instance Eq PythonOptions
- Sifflet.Foreign.ToPython: instance Show PythonOptions
- Sifflet.Foreign.ToPython: nameToPython :: String -> Either Operator String
- Sifflet.Foreign.ToScheme: SAtom :: Atom -> SExpr
- Sifflet.Foreign.ToScheme: SBool :: Bool -> Atom
- Sifflet.Foreign.ToScheme: SChar :: Char -> Atom
- Sifflet.Foreign.ToScheme: SFloat :: Double -> Atom
- Sifflet.Foreign.ToScheme: SFunction :: Function -> Atom
- Sifflet.Foreign.ToScheme: SInt :: Integer -> Atom
- Sifflet.Foreign.ToScheme: SList :: [SExpr] -> SExpr
- Sifflet.Foreign.ToScheme: SString :: String -> Atom
- Sifflet.Foreign.ToScheme: SSymbol :: String -> Atom
- Sifflet.Foreign.ToScheme: SchemeOptions :: Bool -> SchemeOptions
- Sifflet.Foreign.ToScheme: data Atom
- Sifflet.Foreign.ToScheme: data SExpr
- Sifflet.Foreign.ToScheme: data SchemeOptions
- Sifflet.Foreign.ToScheme: defToSExpr :: SchemeOptions -> FunctionDefTuple -> SExpr
- Sifflet.Foreign.ToScheme: defaultSchemeOptions :: SchemeOptions
- Sifflet.Foreign.ToScheme: defineWithLambda :: SchemeOptions -> Bool
- Sifflet.Foreign.ToScheme: exportScheme :: SchemeOptions -> Exporter
- Sifflet.Foreign.ToScheme: exprToSExpr :: Expr -> SExpr
- Sifflet.Foreign.ToScheme: exprToScheme :: Expr -> String
- Sifflet.Foreign.ToScheme: exprToSchemePretty :: Expr -> String
- Sifflet.Foreign.ToScheme: exprToSchemeRepr :: Expr -> String
- Sifflet.Foreign.ToScheme: functionNameToSchemeName :: String -> String
- Sifflet.Foreign.ToScheme: functionsToPrettyScheme :: SchemeOptions -> Functions -> String
- Sifflet.Foreign.ToScheme: functionsToSExprs :: SchemeOptions -> Functions -> [SExpr]
- Sifflet.Foreign.ToScheme: inl :: Int -> String
- Sifflet.Foreign.ToScheme: instance Eq Atom
- Sifflet.Foreign.ToScheme: instance Eq SExpr
- Sifflet.Foreign.ToScheme: instance Eq SchemeOptions
- Sifflet.Foreign.ToScheme: instance Pretty SExpr
- Sifflet.Foreign.ToScheme: instance Repr SExpr
- Sifflet.Foreign.ToScheme: instance Show Atom
- Sifflet.Foreign.ToScheme: instance Show SExpr
- Sifflet.Foreign.ToScheme: instance Show SchemeOptions
- Sifflet.Foreign.ToScheme: sepLines2 :: [String] -> String
- Sifflet.Foreign.ToScheme: type Exporter = Functions -> FilePath -> IO ()
- Sifflet.Foreign.ToScheme: type Indent = Int
- Sifflet.Foreign.ToScheme: valueToSExpr :: Value -> SExpr
- Sifflet.Language.Expr: Compound :: [String] -> Expr -> FunctionImpl
- Sifflet.Language.Expr: EBool :: Bool -> Expr
- Sifflet.Language.Expr: ECall :: Symbol -> [Expr] -> Expr
- Sifflet.Language.Expr: EChar :: Char -> Expr
- Sifflet.Language.Expr: EGroup :: Expr -> Expr
- Sifflet.Language.Expr: EIf :: Expr -> Expr -> Expr -> Expr
- Sifflet.Language.Expr: EList :: [Expr] -> Expr
- Sifflet.Language.Expr: ENode :: ExprNodeLabel -> EvalResult -> ExprNode
- Sifflet.Language.Expr: ENumber :: Number -> Expr
- Sifflet.Language.Expr: EOp :: Operator -> Expr -> Expr -> Expr
- Sifflet.Language.Expr: EString :: String -> Expr
- Sifflet.Language.Expr: ESymbol :: Symbol -> Expr
- Sifflet.Language.Expr: EUndefined :: Expr
- Sifflet.Language.Expr: EvalError :: String -> EvalRes e
- Sifflet.Language.Expr: EvalOk :: e -> EvalRes e
- Sifflet.Language.Expr: EvalUntried :: EvalRes e
- Sifflet.Language.Expr: Function :: (Maybe String) -> [VpType] -> VpType -> FunctionImpl -> Function
- Sifflet.Language.Expr: Functions :: [Function] -> Functions
- Sifflet.Language.Expr: GroupLtoR :: OperatorGrouping
- Sifflet.Language.Expr: GroupNone :: OperatorGrouping
- Sifflet.Language.Expr: GroupRtoL :: OperatorGrouping
- Sifflet.Language.Expr: NBool :: Bool -> ExprNodeLabel
- Sifflet.Language.Expr: NChar :: Char -> ExprNodeLabel
- Sifflet.Language.Expr: NList :: [Expr] -> ExprNodeLabel
- Sifflet.Language.Expr: NNumber :: Number -> ExprNodeLabel
- Sifflet.Language.Expr: NString :: String -> ExprNodeLabel
- Sifflet.Language.Expr: NSymbol :: Symbol -> ExprNodeLabel
- Sifflet.Language.Expr: NUndefined :: ExprNodeLabel
- Sifflet.Language.Expr: Operator :: String -> Precedence -> Bool -> OperatorGrouping -> Operator
- Sifflet.Language.Expr: Primitive :: ([Value] -> EvalResult) -> FunctionImpl
- Sifflet.Language.Expr: Symbol :: String -> Symbol
- Sifflet.Language.Expr: VBool :: OBool -> Value
- Sifflet.Language.Expr: VChar :: OChar -> Value
- Sifflet.Language.Expr: VFun :: Function -> Value
- Sifflet.Language.Expr: VList :: [Value] -> Value
- Sifflet.Language.Expr: VNumber :: Number -> Value
- Sifflet.Language.Expr: VString :: OStr -> Value
- Sifflet.Language.Expr: VpTypeBool :: VpType
- Sifflet.Language.Expr: VpTypeChar :: VpType
- Sifflet.Language.Expr: VpTypeFunction :: [VpType] -> VpType -> VpType
- Sifflet.Language.Expr: VpTypeList :: VpType -> VpType
- Sifflet.Language.Expr: VpTypeNum :: VpType
- Sifflet.Language.Expr: VpTypeString :: VpType
- Sifflet.Language.Expr: VpTypeVar :: String -> VpType
- Sifflet.Language.Expr: apply :: Value -> [Value] -> Env -> Int -> EvalResult
- Sifflet.Language.Expr: baseEnv :: Env
- Sifflet.Language.Expr: data EvalRes e
- Sifflet.Language.Expr: data Expr
- Sifflet.Language.Expr: data ExprNode
- Sifflet.Language.Expr: data ExprNodeLabel
- Sifflet.Language.Expr: data Function
- Sifflet.Language.Expr: data FunctionImpl
- Sifflet.Language.Expr: data Functions
- Sifflet.Language.Expr: data Operator
- Sifflet.Language.Expr: data OperatorGrouping
- Sifflet.Language.Expr: data Symbol
- Sifflet.Language.Expr: data Value
- Sifflet.Language.Expr: data VpType
- Sifflet.Language.Expr: decideTypes :: Expr -> [String] -> Env -> Either String ([VpType], VpType)
- Sifflet.Language.Expr: eAdd1, eSub1 :: Expr -> Expr
- Sifflet.Language.Expr: eBool :: Bool -> Expr
- Sifflet.Language.Expr: eCall :: String -> [Expr] -> Expr
- Sifflet.Language.Expr: eChar :: OChar -> Expr
- Sifflet.Language.Expr: eEq, eLe, eLt, eGe, eGt, eNe :: Expr -> Expr -> Expr
- Sifflet.Language.Expr: eFalse, eTrue :: Expr
- Sifflet.Language.Expr: eFloat :: Double -> Expr
- Sifflet.Language.Expr: eIf :: Expr -> Expr -> Expr -> Expr
- Sifflet.Language.Expr: eInt :: Integer -> Expr
- Sifflet.Language.Expr: eList :: [Expr] -> Expr
- Sifflet.Language.Expr: eMinus, eMod, eDiv :: Expr -> Expr -> Expr
- Sifflet.Language.Expr: ePlus :: Expr -> Expr -> Expr
- Sifflet.Language.Expr: eString :: OStr -> Expr
- Sifflet.Language.Expr: eSymbol, eSym :: String -> Expr
- Sifflet.Language.Expr: eTimes :: Expr -> Expr -> Expr
- Sifflet.Language.Expr: eZerop, eNegativep, ePositivep :: Expr -> Expr
- Sifflet.Language.Expr: emptyEnv :: Env
- Sifflet.Language.Expr: emptyTypeEnv :: TypeEnv
- Sifflet.Language.Expr: envFunctionSymbols :: Env -> [String]
- Sifflet.Language.Expr: envFunctions :: Env -> Functions
- Sifflet.Language.Expr: envGet :: Env -> String -> Value
- Sifflet.Language.Expr: envGetFunction :: Env -> String -> Function
- Sifflet.Language.Expr: envIns :: Env -> String -> Value -> Env
- Sifflet.Language.Expr: envInsertL :: Env -> [String] -> [Value] -> Env
- Sifflet.Language.Expr: envLookup :: Env -> String -> Maybe Value
- Sifflet.Language.Expr: envLookupFunction :: Env -> String -> Maybe Function
- Sifflet.Language.Expr: envPop :: Env -> Env
- Sifflet.Language.Expr: envSet :: Env -> String -> Value -> Env
- Sifflet.Language.Expr: envSymbols :: Env -> [String]
- Sifflet.Language.Expr: eval :: Expr -> Env -> EvalResult
- Sifflet.Language.Expr: evalTree :: ExprTree -> Env -> ExprTree
- Sifflet.Language.Expr: exprIsAtomic :: Expr -> Bool
- Sifflet.Language.Expr: exprIsCompound :: Expr -> Bool
- Sifflet.Language.Expr: exprIsLiteral :: Expr -> Bool
- Sifflet.Language.Expr: exprNodeIoletCounter :: Env -> ExprNode -> (Int, Int)
- Sifflet.Language.Expr: exprSymbols :: Expr -> [Symbol]
- Sifflet.Language.Expr: exprToReprTree :: Expr -> Tree String
- Sifflet.Language.Expr: exprToTree :: Expr -> ExprTree
- Sifflet.Language.Expr: exprToValue :: Expr -> SuccFail Value
- Sifflet.Language.Expr: exprVarNames :: Expr -> [String]
- Sifflet.Language.Expr: extendEnv :: [String] -> [Value] -> Env -> Env
- Sifflet.Language.Expr: functionArgNames :: Function -> [String]
- Sifflet.Language.Expr: functionArgTypes :: Function -> [VpType]
- Sifflet.Language.Expr: functionBody :: Function -> Expr
- Sifflet.Language.Expr: functionFromDef :: FunctionDefTuple -> Function
- Sifflet.Language.Expr: functionImplementation :: Function -> FunctionImpl
- Sifflet.Language.Expr: functionNArgs :: Function -> Int
- Sifflet.Language.Expr: functionName :: Function -> String
- Sifflet.Language.Expr: functionResultType :: Function -> VpType
- Sifflet.Language.Expr: functionToDef :: Function -> FunctionDefTuple
- Sifflet.Language.Expr: functionType :: Function -> ([VpType], VpType)
- Sifflet.Language.Expr: instance Eq Expr
- Sifflet.Language.Expr: instance Eq ExprNode
- Sifflet.Language.Expr: instance Eq ExprNodeLabel
- Sifflet.Language.Expr: instance Eq Function
- Sifflet.Language.Expr: instance Eq Functions
- Sifflet.Language.Expr: instance Eq Operator
- Sifflet.Language.Expr: instance Eq OperatorGrouping
- Sifflet.Language.Expr: instance Eq Symbol
- Sifflet.Language.Expr: instance Eq Value
- Sifflet.Language.Expr: instance Eq VpType
- Sifflet.Language.Expr: instance Eq e => Eq (EvalRes e)
- Sifflet.Language.Expr: instance Monad EvalRes
- Sifflet.Language.Expr: instance Pretty Operator
- Sifflet.Language.Expr: instance Pretty Symbol
- Sifflet.Language.Expr: instance Read FunctionImpl
- Sifflet.Language.Expr: instance Read Symbol
- Sifflet.Language.Expr: instance Repr Expr
- Sifflet.Language.Expr: instance Repr ExprNode
- Sifflet.Language.Expr: instance Repr Function
- Sifflet.Language.Expr: instance Repr Symbol
- Sifflet.Language.Expr: instance Repr Value
- Sifflet.Language.Expr: instance Show Expr
- Sifflet.Language.Expr: instance Show ExprNode
- Sifflet.Language.Expr: instance Show ExprNodeLabel
- Sifflet.Language.Expr: instance Show Function
- Sifflet.Language.Expr: instance Show FunctionImpl
- Sifflet.Language.Expr: instance Show Functions
- Sifflet.Language.Expr: instance Show Operator
- Sifflet.Language.Expr: instance Show OperatorGrouping
- Sifflet.Language.Expr: instance Show Symbol
- Sifflet.Language.Expr: instance Show Value
- Sifflet.Language.Expr: instance Show VpType
- Sifflet.Language.Expr: instance Show e => Show (EvalRes e)
- Sifflet.Language.Expr: makeEnv :: [String] -> [Value] -> Env
- Sifflet.Language.Expr: newUndefinedFunction :: String -> [String] -> Function
- Sifflet.Language.Expr: opAssoc :: Operator -> Bool
- Sifflet.Language.Expr: opGrouping :: Operator -> OperatorGrouping
- Sifflet.Language.Expr: opName :: Operator -> String
- Sifflet.Language.Expr: opPrec :: Operator -> Precedence
- Sifflet.Language.Expr: treeToExpr :: ExprTree -> Expr
- Sifflet.Language.Expr: type Env = [EnvFrame]
- Sifflet.Language.Expr: type EvalResult = EvalRes Value
- Sifflet.Language.Expr: type ExprTree = Tree ExprNode
- Sifflet.Language.Expr: type FunctionDefTuple = (String, [String], [VpType], VpType, Expr)
- Sifflet.Language.Expr: type OBool = Bool
- Sifflet.Language.Expr: type OChar = Char
- Sifflet.Language.Expr: type OStr = String
- Sifflet.Language.Expr: type Precedence = Int
- Sifflet.Language.Expr: type TypeEnv = Map String VpType
- Sifflet.Language.Expr: typeCheck :: [String] -> [VpType] -> [Value] -> SuccFail [Value]
- Sifflet.Language.Expr: typeMatch :: VpType -> Value -> TypeEnv -> SuccFail TypeEnv
- Sifflet.Language.Expr: undefinedTypes :: [String] -> ([VpType], VpType)
- Sifflet.Language.Expr: unevalTree :: ExprTree -> ExprTree
- Sifflet.Language.Expr: valueFunction :: Value -> Function
- Sifflet.Language.Expr: valueToLiteral :: Value -> SuccFail Expr
- Sifflet.Language.Expr: valueToLiteral' :: Value -> Expr
- Sifflet.Language.Expr: vpTypeOf :: Value -> SuccFail VpType
- Sifflet.Language.Parser: bool :: Parser Bool
- Sifflet.Language.Parser: double :: Parser Double
- Sifflet.Language.Parser: expr :: Parser Expr
- Sifflet.Language.Parser: integer :: Parser Integer
- Sifflet.Language.Parser: list :: Parser a -> Parser [a]
- Sifflet.Language.Parser: nothingBut :: Parser a -> Parser a
- Sifflet.Language.Parser: number :: Parser Number
- Sifflet.Language.Parser: parseExpr :: String -> SuccFail Expr
- Sifflet.Language.Parser: parseLiteral :: String -> SuccFail Expr
- Sifflet.Language.Parser: parseSuccFail :: Parser a -> String -> SuccFail a
- Sifflet.Language.Parser: parseTest :: (Stream s Identity t, Show a) => Parsec s () a -> s -> IO ()
- Sifflet.Language.Parser: parseTypedInput2 :: (String, VpType) -> SuccFail Value
- Sifflet.Language.Parser: parseTypedInput3 :: (String, String, VpType) -> SuccFail Value
- Sifflet.Language.Parser: parseTypedInputs2 :: [String] -> [VpType] -> SuccFail [Value]
- Sifflet.Language.Parser: parseTypedInputs3 :: [String] -> [String] -> [VpType] -> SuccFail [Value]
- Sifflet.Language.Parser: parseValue :: String -> SuccFail Value
- Sifflet.Language.Parser: qchar :: Parser Char
- Sifflet.Language.Parser: qstring :: Parser String
- Sifflet.Language.Parser: typedValue :: VpType -> Parser Value
- Sifflet.Language.Parser: value :: Parser Value
- Sifflet.Language.SiffML: class ToXml a
- Sifflet.Language.SiffML: consumeSiffMLFile :: XMLConsumer XmlTree a -> FilePath -> IO [a]
- Sifflet.Language.SiffML: instance ToXml Expr
- Sifflet.Language.SiffML: instance ToXml Function
- Sifflet.Language.SiffML: instance ToXml Functions
- Sifflet.Language.SiffML: instance ToXml Symbol
- Sifflet.Language.SiffML: instance ToXml Value
- Sifflet.Language.SiffML: instance ToXml VpType
- Sifflet.Language.SiffML: produceSiffMLFile :: ToXml a => a -> FilePath -> IO ()
- Sifflet.Language.SiffML: testFromXml :: (ToXml a, Show b) => Int -> a -> XMLConsumer XmlTree b -> IO ()
- Sifflet.Language.SiffML: toXml :: ToXml a => a -> XMLProducer
- Sifflet.Language.SiffML: xmlToFunctions :: XMLConsumer XmlTree Functions
- Sifflet.Rendering.Draw: DrawActive :: DrawMode
- Sifflet.Rendering.Draw: DrawNormal :: DrawMode
- Sifflet.Rendering.Draw: DrawSelectedInlet :: Int -> DrawMode
- Sifflet.Rendering.Draw: DrawSelectedNode :: DrawMode
- Sifflet.Rendering.Draw: DrawSelectedOutlet :: Int -> DrawMode
- Sifflet.Rendering.Draw: class Draw a
- Sifflet.Rendering.Draw: data DrawMode
- Sifflet.Rendering.Draw: draw :: Draw a => Style -> DrawMode -> a -> Render ()
- Sifflet.Rendering.Draw: drawBox :: Maybe VColor -> Maybe VColor -> BBox -> Render ()
- Sifflet.Rendering.Draw: drawTextBox :: Maybe VFont -> Maybe VColor -> Maybe VColor -> VColor -> TextBox -> Render ()
- Sifflet.Rendering.Draw: instance Draw (GNode e)
- Sifflet.Rendering.Draw: instance Draw (LayoutNode e)
- Sifflet.Rendering.Draw: instance Draw BBox
- Sifflet.Rendering.Draw: instance Draw Circle
- Sifflet.Rendering.Draw: instance Draw FunctoidLayout
- Sifflet.Rendering.Draw: instance Draw Iolet
- Sifflet.Rendering.Draw: instance Draw Position
- Sifflet.Rendering.Draw: instance Draw TextBox
- Sifflet.Rendering.Draw: instance Draw e => Draw (Tree e)
- Sifflet.Rendering.Draw: instance Draw e => Draw [e]
- Sifflet.Rendering.Draw: instance Eq DrawMode
- Sifflet.Rendering.Draw: modeEdgeCol :: DrawMode -> (Style -> VColor)
- Sifflet.Rendering.Draw: modeFillCol :: DrawMode -> (Style -> VColor)
- Sifflet.Rendering.Draw: modeTextCol :: DrawMode -> (Style -> VColor)
- Sifflet.Rendering.Draw: setColor :: VColor -> Render ()
- Sifflet.Rendering.DrawTreeGraph: graphQuickView :: (Graph g, Show a, Show b) => g a b -> IO ()
- Sifflet.Rendering.DrawTreeGraph: graphRender :: Repr n => Style -> Maybe Node -> Maybe Node -> LayoutGraph n e -> Render ()
- Sifflet.Rendering.DrawTreeGraph: graphWriteImageFile :: Repr n => Style -> Maybe Node -> Maybe Node -> Double -> Double -> LayoutGraph n e -> String -> IO String
- Sifflet.Rendering.DrawTreeGraph: gtkShowTree :: (Repr e, Show e) => Style -> IoletCounter e -> Tree e -> IO ()
- Sifflet.Rendering.DrawTreeGraph: treeRender :: Repr e => Style -> TreeLayout e -> Render ()
- Sifflet.Rendering.DrawTreeGraph: treeWriteImageFile :: Repr e => Style -> IoletCounter e -> Tree e -> String -> IO String
- Sifflet.Text.Pretty: class Pretty a where prettyList pre tween post xs = pre ++ intercalate tween (map pretty xs) ++ post
- Sifflet.Text.Pretty: indentLine :: Int -> String -> String
- Sifflet.Text.Pretty: pretty :: Pretty a => a -> String
- Sifflet.Text.Pretty: prettyList :: Pretty a => String -> String -> String -> [a] -> String
- Sifflet.Text.Pretty: sepComma :: [String] -> String
- Sifflet.Text.Pretty: sepCommaSp :: [String] -> String
- Sifflet.Text.Pretty: sepLines :: [String] -> String
- Sifflet.Text.Pretty: sepLines2 :: [String] -> String
- Sifflet.Text.Pretty: sepSpace :: [String] -> String
- Sifflet.Text.Repr: Name :: String -> Name
- Sifflet.Text.Repr: class Repr a where repr = head . reprl reprl x = [repr x] reprs = unwords . reprl reprList pre tween post xs = pre ++ intercalate tween (map repr xs) ++ post
- Sifflet.Text.Repr: data Name
- Sifflet.Text.Repr: instance Eq Name
- Sifflet.Text.Repr: instance Read Name
- Sifflet.Text.Repr: instance Repr Bool
- Sifflet.Text.Repr: instance Repr Char
- Sifflet.Text.Repr: instance Repr Double
- Sifflet.Text.Repr: instance Repr Float
- Sifflet.Text.Repr: instance Repr Int
- Sifflet.Text.Repr: instance Repr Integer
- Sifflet.Text.Repr: instance Repr Name
- Sifflet.Text.Repr: instance Repr Number
- Sifflet.Text.Repr: instance Show Name
- Sifflet.Text.Repr: repr :: Repr a => a -> String
- Sifflet.Text.Repr: reprList :: Repr a => String -> String -> String -> [a] -> String
- Sifflet.Text.Repr: reprl :: Repr a => a -> [String]
- Sifflet.Text.Repr: reprs :: Repr a => a -> String
- Sifflet.UI.Callback: AfterButtonClicked :: Button -> (IORef VPUI -> IO ()) -> CBMgrCmd
- Sifflet.UI.Callback: MenuItem :: String -> MenuItemAction -> MenuItemSpec
- Sifflet.UI.Callback: MenuSpec :: String -> [MenuItemSpec] -> MenuSpec
- Sifflet.UI.Callback: OnEntryActivate :: Entry -> (IORef VPUI -> IO ()) -> CBMgrCmd
- Sifflet.UI.Callback: OnLayoutButtonPress :: Layout -> (IORef VPUI -> EventM EButton Bool) -> CBMgrCmd
- Sifflet.UI.Callback: OnLayoutButtonRelease :: Layout -> (IORef VPUI -> EventM EButton Bool) -> CBMgrCmd
- Sifflet.UI.Callback: OnLayoutExpose :: Layout -> (IORef VPUI -> EventM EExpose Bool) -> CBMgrCmd
- Sifflet.UI.Callback: OnLayoutMouseMove :: Layout -> (IORef VPUI -> EventM EMotion Bool) -> CBMgrCmd
- Sifflet.UI.Callback: OnMenuItemActivateLeaf :: MenuItem -> (VPUI -> IO VPUI) -> CBMgrCmd
- Sifflet.UI.Callback: OnWindowConfigure :: Window -> (IORef VPUI -> EventM EConfigure Bool) -> CBMgrCmd
- Sifflet.UI.Callback: OnWindowDestroy :: Window -> (IORef VPUI -> IO ()) -> CBMgrCmd
- Sifflet.UI.Callback: OnWindowKeyPress :: Window -> (IORef VPUI -> EventM EKey Bool) -> CBMgrCmd
- Sifflet.UI.Callback: SubMenu :: MenuSpec -> MenuItemSpec
- Sifflet.UI.Callback: UMTest :: CBMgrCmd
- Sifflet.UI.Callback: addMenu :: MenuBar -> CBMgr -> MenuSpec -> IO ()
- Sifflet.UI.Callback: createMenu :: MenuSpec -> CBMgr -> IO Menu
- Sifflet.UI.Callback: createMenuBar :: [MenuSpec] -> CBMgr -> IO MenuBar
- Sifflet.UI.Callback: createMenuItem :: Menu -> CBMgr -> MenuItemSpec -> IO ()
- Sifflet.UI.Callback: data CBMgrCmd
- Sifflet.UI.Callback: data MenuItemSpec
- Sifflet.UI.Callback: data MenuSpec
- Sifflet.UI.Callback: mkCBMgr :: IORef VPUI -> CBMgr
- Sifflet.UI.Callback: modifyIORefIO :: IORef a -> (a -> IO a) -> IO ()
- Sifflet.UI.Callback: type CBMgr = CBMgrCmd -> IO ()
- Sifflet.UI.Callback: type MenuItemAction = VPUI -> IO VPUI
- Sifflet.UI.Canvas: atLeastSize :: Size -> VCanvas -> VCanvas
- Sifflet.UI.Canvas: callFrames :: VCanvas -> String -> [CanvFrame]
- Sifflet.UI.Canvas: cfContext :: CanvFrame -> ToolContext
- Sifflet.UI.Canvas: connect :: VCanvas -> Node -> WEdge -> Node -> WEdge -> IO VCanvas
- Sifflet.UI.Canvas: disconnect :: VCanvas -> Node -> WEdge -> Node -> WEdge -> IO VCanvas
- Sifflet.UI.Canvas: drawCanvas :: VCanvas -> Rectangle -> IO ()
- Sifflet.UI.Canvas: editFunction :: VCanvas -> CanvFrame -> IO VCanvas
- Sifflet.UI.Canvas: frameChanged :: VCanvas -> WGraph -> CanvFrame -> WGraph -> CanvFrame -> IO ()
- Sifflet.UI.Canvas: nodeContainerFrame :: VCanvas -> WGraph -> Node -> CanvFrame
- Sifflet.UI.Canvas: pointSelection :: WGraph -> CanvFrame -> Position -> Maybe Selection
- Sifflet.UI.Canvas: vcAddFrame :: VCanvas -> Functoid -> Maybe [Value] -> FrameType -> Env -> Double -> Double -> Double -> Maybe Node -> IO VCanvas
- Sifflet.UI.Canvas: vcClearFrame :: VCanvas -> CanvFrame -> IO VCanvas
- Sifflet.UI.Canvas: vcClearSelection :: VCanvas -> IO VCanvas
- Sifflet.UI.Canvas: vcCloseFrame :: VCanvas -> CanvFrame -> IO VCanvas
- Sifflet.UI.Canvas: vcEvalDialog :: VCanvas -> CanvFrame -> IO VCanvas
- Sifflet.UI.Canvas: vcFrameAddFunctoidNode :: VCanvas -> CanvFrame -> Functoid -> Double -> Double -> IO VCanvas
- Sifflet.UI.Canvas: vcFrameAddNode :: VCanvas -> CanvFrame -> ExprNode -> [String] -> Double -> Double -> IO VCanvas
- Sifflet.UI.Canvas: vcFrameDeleteNode :: VCanvas -> CanvFrame -> Node -> IO VCanvas
- Sifflet.UI.Canvas: vcFrameDeleteTree :: VCanvas -> CanvFrame -> Node -> IO VCanvas
- Sifflet.UI.Canvas: vcFrameSubframes :: VCanvas -> CanvFrame -> [CanvFrame]
- Sifflet.UI.Canvas: vcGetFrame :: VCanvas -> WGraph -> Node -> CanvFrame
- Sifflet.UI.Canvas: vcInvalidateBox :: VCanvas -> BBox -> IO ()
- Sifflet.UI.Canvas: vcInvalidateFrameWithParent :: VCanvas -> WGraph -> CanvFrame -> IO ()
- Sifflet.UI.Canvas: vcUpdateFrameAndGraph :: VCanvas -> CanvFrame -> WGraph -> VCanvas
- Sifflet.UI.Canvas: vcanvasNew :: Style -> Double -> Double -> IO VCanvas
- Sifflet.UI.Canvas: vcanvasNodeAt :: VCanvas -> Position -> Maybe Node
- Sifflet.UI.Canvas: vcanvasNodeRect :: VCanvas -> Node -> Rectangle
- Sifflet.UI.Canvas: whichFrame :: VCanvas -> Double -> Double -> Maybe CanvFrame
- Sifflet.UI.Frame: CallFrame :: FrameType
- Sifflet.UI.Frame: CanvFrame :: TextBox -> TextBox -> [String] -> Maybe Node -> Node -> Env -> BBox -> Double -> Functoid -> FrameType -> CanvFrame
- Sifflet.UI.Frame: EditFrame :: FrameType
- Sifflet.UI.Frame: argIoletCounter :: [String] -> ExprNode -> (Int, Int)
- Sifflet.UI.Frame: atLeastSizeFrame :: Size -> CanvFrame -> CanvFrame
- Sifflet.UI.Frame: cfBox :: CanvFrame -> BBox
- Sifflet.UI.Frame: cfEnv :: CanvFrame -> Env
- Sifflet.UI.Frame: cfEvalReady :: CanvFrame -> Bool
- Sifflet.UI.Frame: cfFooter :: CanvFrame -> TextBox
- Sifflet.UI.Frame: cfFrameNode :: CanvFrame -> Node
- Sifflet.UI.Frame: cfFunctoid :: CanvFrame -> Functoid
- Sifflet.UI.Frame: cfHeader :: CanvFrame -> TextBox
- Sifflet.UI.Frame: cfLevel :: CanvFrame -> Double
- Sifflet.UI.Frame: cfParent :: CanvFrame -> Maybe Node
- Sifflet.UI.Frame: cfPointInFooter :: CanvFrame -> Double -> Double -> Bool
- Sifflet.UI.Frame: cfPointInHeader :: CanvFrame -> Double -> Double -> Bool
- Sifflet.UI.Frame: cfRoot :: CanvFrame -> Node
- Sifflet.UI.Frame: cfVarNames :: CanvFrame -> [String]
- Sifflet.UI.Frame: data CanvFrame
- Sifflet.UI.Frame: data FrameType
- Sifflet.UI.Frame: frameBodyBox :: CanvFrame -> BBox
- Sifflet.UI.Frame: frameNewWithLayout :: Style -> Position -> Double -> Functoid -> Maybe [Value] -> FrameType -> Node -> Env -> Maybe Node -> (CanvFrame, FunctoidLayout)
- Sifflet.UI.Frame: frameNodeAt :: CanvFrame -> WGraph -> Position -> Maybe Node
- Sifflet.UI.Frame: frameOffset :: Style -> CanvFrame -> Position
- Sifflet.UI.Frame: frameType :: CanvFrame -> FrameType
- Sifflet.UI.Frame: grTranslateFrameNodes :: WGraph -> CanvFrame -> Double -> Double -> WGraph
- Sifflet.UI.Frame: instance Eq CanvFrame
- Sifflet.UI.Frame: instance Eq FrameType
- Sifflet.UI.Frame: instance Read FrameType
- Sifflet.UI.Frame: instance Show CanvFrame
- Sifflet.UI.Frame: instance Show FrameType
- Sifflet.UI.Frame: levelOrder :: CanvFrame -> CanvFrame -> Ordering
- Sifflet.UI.Frame: nodeCompoundFunction :: WGraph -> CanvFrame -> Node -> Maybe Function
- Sifflet.UI.Frame: pointIolet :: Position -> Int -> [Iolet] -> Maybe Int
- Sifflet.UI.Frame: resizeFrame :: CanvFrame -> Double -> Double -> CanvFrame
- Sifflet.UI.Frame: translateFrame :: CanvFrame -> Double -> Double -> CanvFrame
- Sifflet.UI.GtkForeign: setCursor :: Window -> CursorType -> IO ()
- Sifflet.UI.GtkUtil: addEntryWithLabel :: VBox -> (String, Entry, String) -> IO ()
- Sifflet.UI.GtkUtil: createEntryDialog :: String -> [String] -> [String] -> (Reader [String] a) -> Int -> IO (EntryDialog a)
- Sifflet.UI.GtkUtil: data EntryDialog a
- Sifflet.UI.GtkUtil: defaultDialogPosition :: WindowPosition
- Sifflet.UI.GtkUtil: runDialogM :: Dialog -> a -> (a -> IO (Maybe b)) -> IO (Maybe b)
- Sifflet.UI.GtkUtil: runDialogS :: Dialog -> a -> (a -> IO (SuccFail b)) -> IO (Maybe b)
- Sifflet.UI.GtkUtil: runEntryDialog :: Show a => EntryDialog a -> IO (Maybe a)
- Sifflet.UI.GtkUtil: showChoicesDialog :: String -> String -> [String] -> [IO a] -> IO a
- Sifflet.UI.GtkUtil: showErrorMessage :: String -> IO ()
- Sifflet.UI.GtkUtil: showInfoMessage :: String -> String -> IO ()
- Sifflet.UI.GtkUtil: showInputErrorMessage :: String -> IO ()
- Sifflet.UI.GtkUtil: showMessage :: Maybe String -> MessageType -> ButtonsType -> String -> IO ()
- Sifflet.UI.GtkUtil: suppressScimBridge :: IO ()
- Sifflet.UI.GtkUtil: type Reader a b = a -> SuccFail b
- Sifflet.UI.LittleGtk: (::=) :: ReadWriteAttr o a b -> (o -> b) -> AttrOp o
- Sifflet.UI.LittleGtk: (::~) :: ReadWriteAttr o a b -> (o -> a -> b) -> AttrOp o
- Sifflet.UI.LittleGtk: (:=) :: ReadWriteAttr o a b -> b -> AttrOp o
- Sifflet.UI.LittleGtk: (:=>) :: ReadWriteAttr o a b -> IO b -> AttrOp o
- Sifflet.UI.LittleGtk: (:~) :: ReadWriteAttr o a b -> (a -> b) -> AttrOp o
- Sifflet.UI.LittleGtk: (:~>) :: ReadWriteAttr o a b -> (a -> IO b) -> AttrOp o
- Sifflet.UI.LittleGtk: AllEventsMask :: EventMask
- Sifflet.UI.LittleGtk: Arrow :: CursorType
- Sifflet.UI.LittleGtk: BasedArrowDown :: CursorType
- Sifflet.UI.LittleGtk: BasedArrowUp :: CursorType
- Sifflet.UI.LittleGtk: BlankCursor :: CursorType
- Sifflet.UI.LittleGtk: Boat :: CursorType
- Sifflet.UI.LittleGtk: Bogosity :: CursorType
- Sifflet.UI.LittleGtk: BottomLeftCorner :: CursorType
- Sifflet.UI.LittleGtk: BottomRightCorner :: CursorType
- Sifflet.UI.LittleGtk: BottomSide :: CursorType
- Sifflet.UI.LittleGtk: BottomTee :: CursorType
- Sifflet.UI.LittleGtk: BoxSpiral :: CursorType
- Sifflet.UI.LittleGtk: Button1MotionMask :: EventMask
- Sifflet.UI.LittleGtk: Button2MotionMask :: EventMask
- Sifflet.UI.LittleGtk: Button3MotionMask :: EventMask
- Sifflet.UI.LittleGtk: ButtonMotionMask :: EventMask
- Sifflet.UI.LittleGtk: ButtonPressMask :: EventMask
- Sifflet.UI.LittleGtk: ButtonReleaseMask :: EventMask
- Sifflet.UI.LittleGtk: ButtonsCancel :: ButtonsType
- Sifflet.UI.LittleGtk: ButtonsClose :: ButtonsType
- Sifflet.UI.LittleGtk: ButtonsNone :: ButtonsType
- Sifflet.UI.LittleGtk: ButtonsOk :: ButtonsType
- Sifflet.UI.LittleGtk: ButtonsOkCancel :: ButtonsType
- Sifflet.UI.LittleGtk: ButtonsYesNo :: ButtonsType
- Sifflet.UI.LittleGtk: CenterPtr :: CursorType
- Sifflet.UI.LittleGtk: Circle :: CursorType
- Sifflet.UI.LittleGtk: Clock :: CursorType
- Sifflet.UI.LittleGtk: CoffeeMug :: CursorType
- Sifflet.UI.LittleGtk: Cross :: CursorType
- Sifflet.UI.LittleGtk: CrossReverse :: CursorType
- Sifflet.UI.LittleGtk: Crosshair :: CursorType
- Sifflet.UI.LittleGtk: CursorIsPixmap :: CursorType
- Sifflet.UI.LittleGtk: DiamondCross :: CursorType
- Sifflet.UI.LittleGtk: Dot :: CursorType
- Sifflet.UI.LittleGtk: Dotbox :: CursorType
- Sifflet.UI.LittleGtk: DoubleArrow :: CursorType
- Sifflet.UI.LittleGtk: DraftLarge :: CursorType
- Sifflet.UI.LittleGtk: DraftSmall :: CursorType
- Sifflet.UI.LittleGtk: DrapedBox :: CursorType
- Sifflet.UI.LittleGtk: EnterNotifyMask :: EventMask
- Sifflet.UI.LittleGtk: Exchange :: CursorType
- Sifflet.UI.LittleGtk: ExposureMask :: EventMask
- Sifflet.UI.LittleGtk: FileChooserActionCreateFolder :: FileChooserAction
- Sifflet.UI.LittleGtk: FileChooserActionOpen :: FileChooserAction
- Sifflet.UI.LittleGtk: FileChooserActionSave :: FileChooserAction
- Sifflet.UI.LittleGtk: FileChooserActionSelectFolder :: FileChooserAction
- Sifflet.UI.LittleGtk: Fleur :: CursorType
- Sifflet.UI.LittleGtk: FocusChangeMask :: EventMask
- Sifflet.UI.LittleGtk: Gobbler :: CursorType
- Sifflet.UI.LittleGtk: Gumby :: CursorType
- Sifflet.UI.LittleGtk: Hand1 :: CursorType
- Sifflet.UI.LittleGtk: Hand2 :: CursorType
- Sifflet.UI.LittleGtk: Heart :: CursorType
- Sifflet.UI.LittleGtk: Icon :: CursorType
- Sifflet.UI.LittleGtk: IronCross :: CursorType
- Sifflet.UI.LittleGtk: KeyPressMask :: EventMask
- Sifflet.UI.LittleGtk: KeyReleaseMask :: EventMask
- Sifflet.UI.LittleGtk: LastCursor :: CursorType
- Sifflet.UI.LittleGtk: LeaveNotifyMask :: EventMask
- Sifflet.UI.LittleGtk: LeftPtr :: CursorType
- Sifflet.UI.LittleGtk: LeftSide :: CursorType
- Sifflet.UI.LittleGtk: LeftTee :: CursorType
- Sifflet.UI.LittleGtk: Leftbutton :: CursorType
- Sifflet.UI.LittleGtk: LlAngle :: CursorType
- Sifflet.UI.LittleGtk: LrAngle :: CursorType
- Sifflet.UI.LittleGtk: Man :: CursorType
- Sifflet.UI.LittleGtk: MessageError :: MessageType
- Sifflet.UI.LittleGtk: MessageInfo :: MessageType
- Sifflet.UI.LittleGtk: MessageOther :: MessageType
- Sifflet.UI.LittleGtk: MessageQuestion :: MessageType
- Sifflet.UI.LittleGtk: MessageWarning :: MessageType
- Sifflet.UI.LittleGtk: Middlebutton :: CursorType
- Sifflet.UI.LittleGtk: Mouse :: CursorType
- Sifflet.UI.LittleGtk: PackGrow :: Packing
- Sifflet.UI.LittleGtk: PackNatural :: Packing
- Sifflet.UI.LittleGtk: PackRepel :: Packing
- Sifflet.UI.LittleGtk: Pencil :: CursorType
- Sifflet.UI.LittleGtk: Pirate :: CursorType
- Sifflet.UI.LittleGtk: Plus :: CursorType
- Sifflet.UI.LittleGtk: PointerMotionHintMask :: EventMask
- Sifflet.UI.LittleGtk: PointerMotionMask :: EventMask
- Sifflet.UI.LittleGtk: PolicyAutomatic :: PolicyType
- Sifflet.UI.LittleGtk: PropertyChangeMask :: EventMask
- Sifflet.UI.LittleGtk: ProximityInMask :: EventMask
- Sifflet.UI.LittleGtk: ProximityOutMask :: EventMask
- Sifflet.UI.LittleGtk: QuestionArrow :: CursorType
- Sifflet.UI.LittleGtk: Rectangle :: Int -> Int -> Int -> Int -> Rectangle
- Sifflet.UI.LittleGtk: Requisition :: Int -> Int -> Requisition
- Sifflet.UI.LittleGtk: ResponseAccept :: ResponseId
- Sifflet.UI.LittleGtk: ResponseApply :: ResponseId
- Sifflet.UI.LittleGtk: ResponseCancel :: ResponseId
- Sifflet.UI.LittleGtk: ResponseClose :: ResponseId
- Sifflet.UI.LittleGtk: ResponseDeleteEvent :: ResponseId
- Sifflet.UI.LittleGtk: ResponseHelp :: ResponseId
- Sifflet.UI.LittleGtk: ResponseNo :: ResponseId
- Sifflet.UI.LittleGtk: ResponseNone :: ResponseId
- Sifflet.UI.LittleGtk: ResponseOk :: ResponseId
- Sifflet.UI.LittleGtk: ResponseReject :: ResponseId
- Sifflet.UI.LittleGtk: ResponseUser :: Int -> ResponseId
- Sifflet.UI.LittleGtk: ResponseYes :: ResponseId
- Sifflet.UI.LittleGtk: RightPtr :: CursorType
- Sifflet.UI.LittleGtk: RightSide :: CursorType
- Sifflet.UI.LittleGtk: RightTee :: CursorType
- Sifflet.UI.LittleGtk: Rightbutton :: CursorType
- Sifflet.UI.LittleGtk: RtlLogo :: CursorType
- Sifflet.UI.LittleGtk: Sailboat :: CursorType
- Sifflet.UI.LittleGtk: SbDownArrow :: CursorType
- Sifflet.UI.LittleGtk: SbHDoubleArrow :: CursorType
- Sifflet.UI.LittleGtk: SbLeftArrow :: CursorType
- Sifflet.UI.LittleGtk: SbRightArrow :: CursorType
- Sifflet.UI.LittleGtk: SbUpArrow :: CursorType
- Sifflet.UI.LittleGtk: SbVDoubleArrow :: CursorType
- Sifflet.UI.LittleGtk: ScrollMask :: EventMask
- Sifflet.UI.LittleGtk: Shuttle :: CursorType
- Sifflet.UI.LittleGtk: Sizing :: CursorType
- Sifflet.UI.LittleGtk: Spider :: CursorType
- Sifflet.UI.LittleGtk: Spraycan :: CursorType
- Sifflet.UI.LittleGtk: Star :: CursorType
- Sifflet.UI.LittleGtk: StructureMask :: EventMask
- Sifflet.UI.LittleGtk: SubstructureMask :: EventMask
- Sifflet.UI.LittleGtk: Target :: CursorType
- Sifflet.UI.LittleGtk: Tcross :: CursorType
- Sifflet.UI.LittleGtk: TopLeftArrow :: CursorType
- Sifflet.UI.LittleGtk: TopLeftCorner :: CursorType
- Sifflet.UI.LittleGtk: TopRightCorner :: CursorType
- Sifflet.UI.LittleGtk: TopSide :: CursorType
- Sifflet.UI.LittleGtk: TopTee :: CursorType
- Sifflet.UI.LittleGtk: Trek :: CursorType
- Sifflet.UI.LittleGtk: UlAngle :: CursorType
- Sifflet.UI.LittleGtk: Umbrella :: CursorType
- Sifflet.UI.LittleGtk: UrAngle :: CursorType
- Sifflet.UI.LittleGtk: VisibilityNotifyMask :: EventMask
- Sifflet.UI.LittleGtk: Watch :: CursorType
- Sifflet.UI.LittleGtk: WinPosCenter :: WindowPosition
- Sifflet.UI.LittleGtk: WinPosCenterAlways :: WindowPosition
- Sifflet.UI.LittleGtk: WinPosCenterOnParent :: WindowPosition
- Sifflet.UI.LittleGtk: WinPosMouse :: WindowPosition
- Sifflet.UI.LittleGtk: WinPosNone :: WindowPosition
- Sifflet.UI.LittleGtk: XCursor :: CursorType
- Sifflet.UI.LittleGtk: Xterm :: CursorType
- Sifflet.UI.LittleGtk: adjustmentNew :: Double -> Double -> Double -> Double -> Double -> Double -> IO Adjustment
- Sifflet.UI.LittleGtk: boxPackEnd :: (BoxClass self, WidgetClass child) => self -> child -> Packing -> Int -> IO ()
- Sifflet.UI.LittleGtk: boxPackStart :: (BoxClass self, WidgetClass child) => self -> child -> Packing -> Int -> IO ()
- Sifflet.UI.LittleGtk: boxPackStartDefaults :: (BoxClass self, WidgetClass widget) => self -> widget -> IO ()
- Sifflet.UI.LittleGtk: buttonNewWithLabel :: String -> IO Button
- Sifflet.UI.LittleGtk: buttonPressEvent :: WidgetClass self => Signal self (EventM EButton Bool)
- Sifflet.UI.LittleGtk: castToContainer :: GObjectClass obj => obj -> Container
- Sifflet.UI.LittleGtk: class WidgetClass o => ContainerClass o
- Sifflet.UI.LittleGtk: class ObjectClass o => WidgetClass o
- Sifflet.UI.LittleGtk: containerAdd :: (ContainerClass self, WidgetClass widget) => self -> widget -> IO ()
- Sifflet.UI.LittleGtk: containerChild :: (ContainerClass self, WidgetClass widget) => WriteAttr self widget
- Sifflet.UI.LittleGtk: containerForeach :: ContainerClass self => self -> ContainerForeachCB -> IO ()
- Sifflet.UI.LittleGtk: customStoreSetColumn :: TypedTreeModelClass model => model row -> ColumnId row ty -> (row -> ty) -> IO ()
- Sifflet.UI.LittleGtk: data AttrOp o :: * -> *
- Sifflet.UI.LittleGtk: data Button :: *
- Sifflet.UI.LittleGtk: data ButtonsType :: *
- Sifflet.UI.LittleGtk: data CursorType :: *
- Sifflet.UI.LittleGtk: data Dialog :: *
- Sifflet.UI.LittleGtk: data DrawWindow :: *
- Sifflet.UI.LittleGtk: data Entry :: *
- Sifflet.UI.LittleGtk: data EventMask :: *
- Sifflet.UI.LittleGtk: data Expander :: *
- Sifflet.UI.LittleGtk: data FileChooserAction :: *
- Sifflet.UI.LittleGtk: data HBox :: *
- Sifflet.UI.LittleGtk: data Label :: *
- Sifflet.UI.LittleGtk: data Layout :: *
- Sifflet.UI.LittleGtk: data MessageType :: *
- Sifflet.UI.LittleGtk: data Packing :: *
- Sifflet.UI.LittleGtk: data PolicyType :: *
- Sifflet.UI.LittleGtk: data Rectangle :: *
- Sifflet.UI.LittleGtk: data Requisition :: *
- Sifflet.UI.LittleGtk: data ResponseId :: *
- Sifflet.UI.LittleGtk: data ScrolledWindow :: *
- Sifflet.UI.LittleGtk: data Statusbar :: *
- Sifflet.UI.LittleGtk: data VBox :: *
- Sifflet.UI.LittleGtk: data Window :: *
- Sifflet.UI.LittleGtk: data WindowPosition :: *
- Sifflet.UI.LittleGtk: dialogAddButton :: DialogClass self => self -> String -> ResponseId -> IO Button
- Sifflet.UI.LittleGtk: dialogGetActionArea :: DialogClass dc => dc -> IO HBox
- Sifflet.UI.LittleGtk: dialogGetUpper :: DialogClass dc => dc -> IO VBox
- Sifflet.UI.LittleGtk: dialogNew :: IO Dialog
- Sifflet.UI.LittleGtk: dialogRun :: DialogClass self => self -> IO ResponseId
- Sifflet.UI.LittleGtk: dialogSetDefaultResponse :: DialogClass self => self -> ResponseId -> IO ()
- Sifflet.UI.LittleGtk: drawWindowInvalidateRect :: DrawWindowClass self => self -> Rectangle -> Bool -> IO ()
- Sifflet.UI.LittleGtk: entryCompletionInsertPrefix :: EntryCompletion -> IO ()
- Sifflet.UI.LittleGtk: entryCompletionModel :: TreeModelClass model => ReadWriteAttr EntryCompletion (Maybe TreeModel) (Maybe model)
- Sifflet.UI.LittleGtk: entryCompletionNew :: IO EntryCompletion
- Sifflet.UI.LittleGtk: entryCompletionSetTextColumn :: EntryCompletion -> ColumnId row String -> IO ()
- Sifflet.UI.LittleGtk: entryGetCompletion :: EntryClass self => self -> IO EntryCompletion
- Sifflet.UI.LittleGtk: entryGetText :: EntryClass self => self -> IO String
- Sifflet.UI.LittleGtk: entryNew :: IO Entry
- Sifflet.UI.LittleGtk: entrySetCompletion :: EntryClass self => self -> EntryCompletion -> IO ()
- Sifflet.UI.LittleGtk: entrySetText :: EntryClass self => self -> String -> IO ()
- Sifflet.UI.LittleGtk: eventBoxNew :: IO EventBox
- Sifflet.UI.LittleGtk: expanderNew :: String -> IO Expander
- Sifflet.UI.LittleGtk: expanderSetExpanded :: Expander -> Bool -> IO ()
- Sifflet.UI.LittleGtk: exposeEvent :: WidgetClass self => Signal self (EventM EExpose Bool)
- Sifflet.UI.LittleGtk: fileChooserDialogNew :: Maybe String -> Maybe Window -> FileChooserAction -> [(String, ResponseId)] -> IO FileChooserDialog
- Sifflet.UI.LittleGtk: fileChooserGetFilename :: FileChooserClass self => self -> IO (Maybe FilePath)
- Sifflet.UI.LittleGtk: frameNew :: IO Frame
- Sifflet.UI.LittleGtk: get :: o -> ReadWriteAttr o a b -> IO a
- Sifflet.UI.LittleGtk: grabAdd :: WidgetClass wd => wd -> IO ()
- Sifflet.UI.LittleGtk: grabRemove :: WidgetClass w => w -> IO ()
- Sifflet.UI.LittleGtk: hBoxNew :: Bool -> Int -> IO HBox
- Sifflet.UI.LittleGtk: initGUI :: IO [String]
- Sifflet.UI.LittleGtk: keyPressEvent :: WidgetClass self => Signal self (EventM EKey Bool)
- Sifflet.UI.LittleGtk: labelNew :: Maybe String -> IO Label
- Sifflet.UI.LittleGtk: labelSetText :: LabelClass self => self -> String -> IO ()
- Sifflet.UI.LittleGtk: layoutGetDrawWindow :: Layout -> IO DrawWindow
- Sifflet.UI.LittleGtk: layoutNew :: Maybe Adjustment -> Maybe Adjustment -> IO Layout
- Sifflet.UI.LittleGtk: layoutPut :: (LayoutClass self, WidgetClass childWidget) => self -> childWidget -> Int -> Int -> IO ()
- Sifflet.UI.LittleGtk: layoutSetSize :: LayoutClass self => self -> Int -> Int -> IO ()
- Sifflet.UI.LittleGtk: listStoreNew :: [a] -> IO (ListStore a)
- Sifflet.UI.LittleGtk: mainGUI :: IO ()
- Sifflet.UI.LittleGtk: mainQuit :: IO ()
- Sifflet.UI.LittleGtk: makeColumnIdString :: Int -> ColumnId row String
- Sifflet.UI.LittleGtk: menuPopup :: MenuClass self => self -> Maybe (MouseButton, TimeStamp) -> IO ()
- Sifflet.UI.LittleGtk: messageDialogNew :: Maybe Window -> [DialogFlags] -> MessageType -> ButtonsType -> String -> IO MessageDialog
- Sifflet.UI.LittleGtk: on :: object -> Signal object callback -> callback -> IO (ConnectId object)
- Sifflet.UI.LittleGtk: onDestroy :: WidgetClass w => w -> IO () -> IO (ConnectId w)
- Sifflet.UI.LittleGtk: onSizeRequest :: WidgetClass w => w -> IO Requisition -> IO (ConnectId w)
- Sifflet.UI.LittleGtk: renderWithDrawable :: DrawableClass drawable => drawable -> Render a -> IO a
- Sifflet.UI.LittleGtk: scrolledWindowNew :: Maybe Adjustment -> Maybe Adjustment -> IO ScrolledWindow
- Sifflet.UI.LittleGtk: scrolledWindowSetPolicy :: ScrolledWindowClass self => self -> PolicyType -> PolicyType -> IO ()
- Sifflet.UI.LittleGtk: set :: o -> [AttrOp o] -> IO ()
- Sifflet.UI.LittleGtk: statusbarGetContextId :: StatusbarClass self => self -> String -> IO ContextId
- Sifflet.UI.LittleGtk: statusbarNew :: IO Statusbar
- Sifflet.UI.LittleGtk: statusbarPop :: StatusbarClass self => self -> ContextId -> IO ()
- Sifflet.UI.LittleGtk: statusbarPush :: StatusbarClass self => self -> ContextId -> String -> IO MessageId
- Sifflet.UI.LittleGtk: toDialog :: DialogClass o => o -> Dialog
- Sifflet.UI.LittleGtk: type GtkFrame = Frame
- Sifflet.UI.LittleGtk: type GtkLayout = Layout
- Sifflet.UI.LittleGtk: vBoxNew :: Bool -> Int -> IO VBox
- Sifflet.UI.LittleGtk: widgetAddEvents :: WidgetClass self => self -> [EventMask] -> IO ()
- Sifflet.UI.LittleGtk: widgetClassPath :: WidgetClass self => self -> IO (Int, String, String)
- Sifflet.UI.LittleGtk: widgetDestroy :: WidgetClass self => self -> IO ()
- Sifflet.UI.LittleGtk: widgetGrabFocus :: WidgetClass self => self -> IO ()
- Sifflet.UI.LittleGtk: widgetSetCanFocus :: WidgetClass self => self -> Bool -> IO ()
- Sifflet.UI.LittleGtk: widgetSetDoubleBuffered :: WidgetClass self => self -> Bool -> IO ()
- Sifflet.UI.LittleGtk: widgetSetName :: WidgetClass self => self -> String -> IO ()
- Sifflet.UI.LittleGtk: widgetSetSizeRequest :: WidgetClass self => self -> Int -> Int -> IO ()
- Sifflet.UI.LittleGtk: widgetShow :: WidgetClass self => self -> IO ()
- Sifflet.UI.LittleGtk: widgetShowAll :: WidgetClass self => self -> IO ()
- Sifflet.UI.LittleGtk: widgetSizeRequest :: WidgetClass self => self -> IO Requisition
- Sifflet.UI.LittleGtk: widgetVisible :: WidgetClass self => Attr self Bool
- Sifflet.UI.LittleGtk: windowDeletable :: WindowClass self => Attr self Bool
- Sifflet.UI.LittleGtk: windowMove :: WindowClass self => self -> Int -> Int -> IO ()
- Sifflet.UI.LittleGtk: windowNew :: IO Window
- Sifflet.UI.LittleGtk: windowPresent :: WindowClass self => self -> IO ()
- Sifflet.UI.LittleGtk: windowSetDefaultSize :: WindowClass self => self -> Int -> Int -> IO ()
- Sifflet.UI.LittleGtk: windowSetPosition :: WindowClass self => self -> WindowPosition -> IO ()
- Sifflet.UI.LittleGtk: windowSetTitle :: WindowClass self => self -> String -> IO ()
- Sifflet.UI.LittleGtk: windowTitle :: WindowClass self => Attr self String
- Sifflet.UI.RPanel: data RPanel
- Sifflet.UI.RPanel: newRPanel :: String -> Int -> Int -> Int -> IO RPanel
- Sifflet.UI.RPanel: rpanelAddRows :: WidgetClass widget => RPanel -> [[(String, widget)]] -> IO RPanel
- Sifflet.UI.RPanel: rpanelAddWidget :: WidgetClass widget => RPanel -> String -> widget -> IO RPanel
- Sifflet.UI.RPanel: rpanelAddWidgets :: WidgetClass widget => RPanel -> [(String, widget)] -> IO RPanel
- Sifflet.UI.RPanel: rpanelContent :: RPanel -> [[String]]
- Sifflet.UI.RPanel: rpanelId :: RPanel -> String
- Sifflet.UI.RPanel: rpanelNewRow :: RPanel -> IO RPanel
- Sifflet.UI.RPanel: rpanelRoot :: RPanel -> GtkFrame
- Sifflet.UI.Tool: ToolArg :: String -> ToolId
- Sifflet.UI.Tool: ToolConnect :: ToolId
- Sifflet.UI.Tool: ToolDelete :: ToolId
- Sifflet.UI.Tool: ToolDisconnect :: ToolId
- Sifflet.UI.Tool: ToolFunction :: String -> ToolId
- Sifflet.UI.Tool: ToolIf :: ToolId
- Sifflet.UI.Tool: ToolLiteral :: Expr -> ToolId
- Sifflet.UI.Tool: ToolMove :: ToolId
- Sifflet.UI.Tool: checkMods :: [Modifier] -> [Modifier] -> Bool
- Sifflet.UI.Tool: clearFrame :: WinId -> CanvFrame -> VPUI -> IO VPUI
- Sifflet.UI.Tool: closeFrame :: VPUI -> WinId -> CanvFrame -> IO VPUI
- Sifflet.UI.Tool: data ToolId
- Sifflet.UI.Tool: dumpFrame :: VPUI -> WinId -> CanvFrame -> IO ()
- Sifflet.UI.Tool: dumpGraph :: VPUI -> WinId -> IO ()
- Sifflet.UI.Tool: dumpWorkWin :: VPUI -> WinId -> IO ()
- Sifflet.UI.Tool: functionTool :: String -> Tool
- Sifflet.UI.Tool: functionToolsFromLists :: [[String]] -> [[Tool]]
- Sifflet.UI.Tool: instance Eq ToolId
- Sifflet.UI.Tool: instance Show ToolId
- Sifflet.UI.Tool: makeConnectTool :: Tool
- Sifflet.UI.Tool: makeCopyTool :: Tool
- Sifflet.UI.Tool: makeDeleteTool :: Tool
- Sifflet.UI.Tool: makeDisconnectTool :: Tool
- Sifflet.UI.Tool: makeIfTool :: Tool
- Sifflet.UI.Tool: makeMoveTool :: Tool
- Sifflet.UI.Tool: showFunctionEntry :: WinId -> CBMgr -> VPUI -> IO VPUI
- Sifflet.UI.Tool: showLiteralEntry :: WinId -> CBMgr -> VPUI -> IO VPUI
- Sifflet.UI.Tool: vpuiAddFrame :: VPUI -> WinId -> Functoid -> Maybe [Value] -> FrameType -> Env -> Double -> Double -> Double -> Maybe Node -> IO VPUI
- Sifflet.UI.Tool: vpuiSetTool :: ToolId -> WinId -> VPUI -> IO VPUI
- Sifflet.UI.Tool: vpuiWindowSetTool :: Tool -> VPUIWindow -> IO VPUIWindow
- Sifflet.UI.Tool: vwAddFrame :: VPUIWindow -> Functoid -> Maybe [Value] -> FrameType -> Env -> Double -> Double -> Double -> Maybe Node -> IO VPUIWindow
- Sifflet.UI.Tool: wsPopStatusbar :: Workspace -> IO ()
- Sifflet.UI.Tool: wsPushStatusbar :: Workspace -> String -> IO ()
- Sifflet.UI.Types: Dragging :: Node -> Position -> Dragging
- Sifflet.UI.Types: FunctionEditor :: Window -> String -> Function -> VCanvas -> FunctionEditor
- Sifflet.UI.Types: FunctionPadWindow :: Window -> [(String, RPanel)] -> VPUIWindow
- Sifflet.UI.Types: SelectionInlet :: Node -> WEdge -> Selection
- Sifflet.UI.Types: SelectionNode :: Node -> Selection
- Sifflet.UI.Types: SelectionOutlet :: Node -> WEdge -> Selection
- Sifflet.UI.Types: TCCallFrame :: CanvFrame -> ToolContext
- Sifflet.UI.Types: TCEditFrame :: CanvFrame -> ToolContext
- Sifflet.UI.Types: TCExprNode :: ToolContext
- Sifflet.UI.Types: TCWorkspace :: ToolContext
- Sifflet.UI.Types: Tool :: String -> (VCanvas -> IO VCanvas) -> ToolOp -> Tool
- Sifflet.UI.Types: Toolbox :: GtkFrame -> VBox -> Toolbox
- Sifflet.UI.Types: VCanvas :: GtkLayout -> Style -> WGraph -> [CanvFrame] -> Size -> (Double, Double) -> Maybe Tool -> Maybe Node -> Maybe Selection -> Maybe Dragging -> VCanvas
- Sifflet.UI.Types: VPToolkit :: String -> Int -> [[Tool]] -> VPToolkit
- Sifflet.UI.Types: VPUI :: Map WinId VPUIWindow -> [(String, VPToolkit)] -> Maybe FilePath -> Style -> Env -> Env -> Env -> VPUI
- Sifflet.UI.Types: VPUIWorkWin :: Workspace -> Window -> VPUIWindow
- Sifflet.UI.Types: Workspace :: VBox -> VCanvas -> HBox -> Statusbar -> Workspace
- Sifflet.UI.Types: data Dragging
- Sifflet.UI.Types: data FunctionEditor
- Sifflet.UI.Types: data Selection
- Sifflet.UI.Types: data Tool
- Sifflet.UI.Types: data ToolContext
- Sifflet.UI.Types: data Toolbox
- Sifflet.UI.Types: data VCanvas
- Sifflet.UI.Types: data VPToolkit
- Sifflet.UI.Types: data VPUI
- Sifflet.UI.Types: data VPUIWindow
- Sifflet.UI.Types: data Workspace
- Sifflet.UI.Types: draggingNode :: Dragging -> Node
- Sifflet.UI.Types: draggingPosition :: Dragging -> Position
- Sifflet.UI.Types: fedCanvas :: FunctionEditor -> VCanvas
- Sifflet.UI.Types: fedFunction :: FunctionEditor -> Function
- Sifflet.UI.Types: fedFunctionName :: FunctionEditor -> String
- Sifflet.UI.Types: fedWinTitle :: FunctionEditor -> String
- Sifflet.UI.Types: fedWindow :: FunctionEditor -> Window
- Sifflet.UI.Types: instance Eq Dragging
- Sifflet.UI.Types: instance Eq Selection
- Sifflet.UI.Types: instance Read Dragging
- Sifflet.UI.Types: instance Read Selection
- Sifflet.UI.Types: instance Show Dragging
- Sifflet.UI.Types: instance Show Selection
- Sifflet.UI.Types: selInEdge :: Selection -> WEdge
- Sifflet.UI.Types: selNode :: Selection -> Node
- Sifflet.UI.Types: selOutEdge :: Selection -> WEdge
- Sifflet.UI.Types: toToolOpVW :: CanvasToolOp -> ToolOp
- Sifflet.UI.Types: toolActivated :: Tool -> VCanvas -> IO VCanvas
- Sifflet.UI.Types: toolName :: Tool -> String
- Sifflet.UI.Types: toolOp :: Tool -> ToolOp
- Sifflet.UI.Types: toolboxFrame :: Toolbox -> GtkFrame
- Sifflet.UI.Types: toolboxVBox :: Toolbox -> VBox
- Sifflet.UI.Types: toolkitName :: VPToolkit -> String
- Sifflet.UI.Types: toolkitRows :: VPToolkit -> [[Tool]]
- Sifflet.UI.Types: toolkitWidth :: VPToolkit -> Int
- Sifflet.UI.Types: type CanvasToolOp = VCanvas -> ToolContext -> [Modifier] -> Double -> Double -> IO VCanvas
- Sifflet.UI.Types: type ToolOp = VPUI -> WinId -> ToolContext -> [Modifier] -> Double -> Double -> IO VPUI
- Sifflet.UI.Types: type WinId = String
- Sifflet.UI.Types: vcActive :: VCanvas -> Maybe Node
- Sifflet.UI.Types: vcDragging :: VCanvas -> Maybe Dragging
- Sifflet.UI.Types: vcFrames :: VCanvas -> [CanvFrame]
- Sifflet.UI.Types: vcGraph :: VCanvas -> WGraph
- Sifflet.UI.Types: vcLayout :: VCanvas -> GtkLayout
- Sifflet.UI.Types: vcMousePos :: VCanvas -> (Double, Double)
- Sifflet.UI.Types: vcSelected :: VCanvas -> Maybe Selection
- Sifflet.UI.Types: vcSize :: VCanvas -> Size
- Sifflet.UI.Types: vcStyle :: VCanvas -> Style
- Sifflet.UI.Types: vcTool :: VCanvas -> Maybe Tool
- Sifflet.UI.Types: vpuiFileChanged :: VPUI -> Bool
- Sifflet.UI.Types: vpuiFileEnv :: VPUI -> Env
- Sifflet.UI.Types: vpuiFilePath :: VPUI -> Maybe FilePath
- Sifflet.UI.Types: vpuiGetWindow :: VPUI -> WinId -> VPUIWindow
- Sifflet.UI.Types: vpuiGlobalEnv :: VPUI -> Env
- Sifflet.UI.Types: vpuiInitialEnv :: VPUI -> Env
- Sifflet.UI.Types: vpuiInsertWindow :: VPUI -> WinId -> VPUIWindow -> VPUI
- Sifflet.UI.Types: vpuiModCanvas :: VPUI -> WinId -> (VCanvas -> VCanvas) -> VPUI
- Sifflet.UI.Types: vpuiModCanvasIO :: VPUI -> WinId -> (VCanvas -> IO VCanvas) -> IO VPUI
- Sifflet.UI.Types: vpuiRemoveVPUIWindow :: WinId -> VPUI -> VPUI
- Sifflet.UI.Types: vpuiReplaceWindow :: VPUI -> WinId -> VPUIWindow -> VPUI
- Sifflet.UI.Types: vpuiStyle :: VPUI -> Style
- Sifflet.UI.Types: vpuiToolkits :: VPUI -> [(String, VPToolkit)]
- Sifflet.UI.Types: vpuiTryGetWindow :: VPUI -> WinId -> Maybe VPUIWindow
- Sifflet.UI.Types: vpuiUpdateWindow :: VPUI -> WinId -> (VPUIWindow -> VPUIWindow) -> VPUI
- Sifflet.UI.Types: vpuiUpdateWindowIO :: WinId -> (VPUIWindow -> IO VPUIWindow) -> VPUI -> IO VPUI
- Sifflet.UI.Types: vpuiUserEnvAList :: VPUI -> [(String, Value)]
- Sifflet.UI.Types: vpuiWindowGetCanvas :: VPUIWindow -> VCanvas
- Sifflet.UI.Types: vpuiWindowLookupCanvas :: VPUIWindow -> Maybe VCanvas
- Sifflet.UI.Types: vpuiWindowModCanvas :: VPUIWindow -> (VCanvas -> VCanvas) -> VPUIWindow
- Sifflet.UI.Types: vpuiWindowModCanvasIO :: VPUIWindow -> (VCanvas -> IO VCanvas) -> IO VPUIWindow
- Sifflet.UI.Types: vpuiWindowSetCanvas :: VPUIWindow -> VCanvas -> VPUIWindow
- Sifflet.UI.Types: vpuiWindowWindow :: VPUIWindow -> Window
- Sifflet.UI.Types: vpuiWindows :: VPUI -> Map WinId VPUIWindow
- Sifflet.UI.Types: wsBox :: Workspace -> VBox
- Sifflet.UI.Types: wsButtonBar :: Workspace -> HBox
- Sifflet.UI.Types: wsCanvas :: Workspace -> VCanvas
- Sifflet.UI.Types: wsStatusbar :: Workspace -> Statusbar
- Sifflet.UI.Window: fedWindowTitle :: String -> WinId
- Sifflet.UI.Window: keyBindingsHelpText :: String
- Sifflet.UI.Window: newFunctionDialog :: WinId -> CBMgr -> VPUI -> IO VPUI
- Sifflet.UI.Window: newWindowTitled :: String -> IO Window
- Sifflet.UI.Window: openFilePath :: CBMgr -> FilePath -> VPUI -> IO VPUI
- Sifflet.UI.Window: setWSCanvasCallbacks :: VPUI -> WinId -> CBMgr -> IO ()
- Sifflet.UI.Window: showFedWin :: CBMgr -> String -> [String] -> VPUI -> IO VPUI
- Sifflet.UI.Window: showFunctionPadWindow :: CBMgr -> VPUI -> IO VPUI
- Sifflet.UI.Window: showWindow :: WinId -> CBMgr -> (VPUI -> Window -> IO VPUIWindow) -> (VPUI -> WinId -> CBMgr -> IO ()) -> VPUI -> IO (VPUI, VPUIWindow, Bool)
- Sifflet.UI.Window: showWorkWin :: VPUI -> WinId -> CBMgr -> IO VPUI
- Sifflet.UI.Window: showWorkspaceWindow :: WinId -> CBMgr -> Maybe Function -> VPUI -> IO (VPUI, VPUIWindow, Bool)
- Sifflet.UI.Workspace: addApplyCloseButtons :: CBMgr -> WinId -> VPUI -> IO ()
- Sifflet.UI.Workspace: addArgToolButtons :: CBMgr -> WinId -> [String] -> VPUI -> IO ()
- Sifflet.UI.Workspace: baseFunctionsRows :: [[String]]
- Sifflet.UI.Workspace: defineFunction :: WinId -> CanvFrame -> VPUI -> IO VPUI
- Sifflet.UI.Workspace: forallWindowsIO :: (VPUIWindow -> IO VPUIWindow) -> VPUI -> IO VPUI
- Sifflet.UI.Workspace: openNode :: VPUIWindow -> Node -> IO VPUIWindow
- Sifflet.UI.Workspace: removeWindow :: VPUI -> Bool -> WinId -> IO VPUI
- Sifflet.UI.Workspace: vpuiNew :: Style -> Env -> IO VPUI
- Sifflet.UI.Workspace: vpuiQuit :: VPUI -> IO VPUI
- Sifflet.UI.Workspace: workspaceId :: String
- Sifflet.UI.Workspace: workspaceNewDefault :: Style -> (VBox -> IO ()) -> IO Workspace
- Sifflet.UI.Workspace: workspaceNewEditing :: Style -> Env -> Function -> IO Workspace
- Sifflet.Util: Fail :: String -> SuccFail a
- Sifflet.Util: Succ :: a -> SuccFail a
- Sifflet.Util: adjustAList :: Eq k => k -> (v -> v) -> [(k, v)] -> [(k, v)]
- Sifflet.Util: adjustAListM :: (Eq k, Monad m) => k -> (v -> m v) -> [(k, v)] -> m [(k, v)]
- Sifflet.Util: data SuccFail a
- Sifflet.Util: errcat :: [String] -> a
- Sifflet.Util: errcats :: [String] -> a
- Sifflet.Util: fake :: String -> IO ()
- Sifflet.Util: info :: Show t => t -> IO ()
- Sifflet.Util: insertLast :: [a] -> a -> [a]
- Sifflet.Util: insertLastLast :: [[a]] -> a -> [[a]]
- Sifflet.Util: instance Eq a => Eq (SuccFail a)
- Sifflet.Util: instance Monad SuccFail
- Sifflet.Util: instance Read a => Read (SuccFail a)
- Sifflet.Util: instance Show a => Show (SuccFail a)
- Sifflet.Util: map2 :: (a -> b) -> [[a]] -> [[b]]
- Sifflet.Util: mapM2 :: Monad m => (a -> m b) -> [[a]] -> m [[b]]
- Sifflet.Util: par :: String -> [String] -> String
- Sifflet.Util: parseDouble :: String -> String -> SuccFail Double
- Sifflet.Util: parseInt :: String -> String -> SuccFail Int
- Sifflet.Util: parseVerbatim :: String -> String -> SuccFail String
- Sifflet.Util: parsef :: Read a => String -> String -> String -> SuccFail a
- Sifflet.Util: putCatLn :: [String] -> IO ()
- Sifflet.Util: putCatsLn :: [String] -> IO ()
- Sifflet.Util: stub :: String -> IO ()
+ Data.Sifflet.Functoid: FLayoutForest :: [TreeLayout ExprNode] -> BBox -> FunctoidLayout
+ Data.Sifflet.Functoid: FLayoutTree :: (TreeLayout ExprNode) -> FunctoidLayout
+ Data.Sifflet.Functoid: FunctoidFunc :: Function -> Functoid
+ Data.Sifflet.Functoid: FunctoidParts :: String -> [String] -> [Node] -> Functoid
+ Data.Sifflet.Functoid: data Functoid
+ Data.Sifflet.Functoid: data FunctoidLayout
+ Data.Sifflet.Functoid: flayout :: Style -> Functoid -> Env -> Maybe [Value] -> FunctoidLayout
+ Data.Sifflet.Functoid: flayoutBBox :: FunctoidLayout -> BBox
+ Data.Sifflet.Functoid: flayoutBottom :: FunctoidLayout -> Double
+ Data.Sifflet.Functoid: flayoutSize :: FunctoidLayout -> Size
+ Data.Sifflet.Functoid: flayoutWiden :: FunctoidLayout -> Double -> FunctoidLayout
+ Data.Sifflet.Functoid: flayoutWidth :: FunctoidLayout -> Double
+ Data.Sifflet.Functoid: fpArgs :: Functoid -> [String]
+ Data.Sifflet.Functoid: fpFunc :: Functoid -> Function
+ Data.Sifflet.Functoid: fpName :: Functoid -> String
+ Data.Sifflet.Functoid: fpNodes :: Functoid -> [Node]
+ Data.Sifflet.Functoid: functoidArgNames :: Functoid -> [String]
+ Data.Sifflet.Functoid: functoidHeader :: Functoid -> String
+ Data.Sifflet.Functoid: functoidName :: Functoid -> String
+ Data.Sifflet.Functoid: instance Show Functoid
+ Data.Sifflet.Functoid: instance Translate FunctoidLayout
+ Data.Sifflet.Geometry: BBox :: Double -> Double -> Double -> Double -> BBox
+ Data.Sifflet.Geometry: Circle :: Position -> Double -> Circle
+ Data.Sifflet.Geometry: Position :: Double -> Double -> Position
+ Data.Sifflet.Geometry: Size :: Double -> Double -> Size
+ Data.Sifflet.Geometry: bbCenter :: BBox -> Position
+ Data.Sifflet.Geometry: bbFromRect :: Rectangle -> BBox
+ Data.Sifflet.Geometry: bbLeft, bbRight, bbXCenter :: BBox -> Double
+ Data.Sifflet.Geometry: bbMerge :: BBox -> BBox -> BBox
+ Data.Sifflet.Geometry: bbMergeList :: [BBox] -> BBox
+ Data.Sifflet.Geometry: bbPosition :: BBox -> Position
+ Data.Sifflet.Geometry: bbSetWidth :: BBox -> Double -> BBox
+ Data.Sifflet.Geometry: bbSize :: BBox -> Size
+ Data.Sifflet.Geometry: bbToRect :: BBox -> Rectangle
+ Data.Sifflet.Geometry: bbTop, bbBottom, bbYCenter :: BBox -> Double
+ Data.Sifflet.Geometry: bbX, bbHeight, bbWidth, bbY :: BBox -> Double
+ Data.Sifflet.Geometry: circleCenter :: Circle -> Position
+ Data.Sifflet.Geometry: circleRadius :: Circle -> Double
+ Data.Sifflet.Geometry: class Translate a
+ Data.Sifflet.Geometry: class Widen a
+ Data.Sifflet.Geometry: data BBox
+ Data.Sifflet.Geometry: data Circle
+ Data.Sifflet.Geometry: data Position
+ Data.Sifflet.Geometry: data Size
+ Data.Sifflet.Geometry: instance Eq BBox
+ Data.Sifflet.Geometry: instance Eq Circle
+ Data.Sifflet.Geometry: instance Eq Position
+ Data.Sifflet.Geometry: instance Eq Size
+ Data.Sifflet.Geometry: instance Read BBox
+ Data.Sifflet.Geometry: instance Read Circle
+ Data.Sifflet.Geometry: instance Read Position
+ Data.Sifflet.Geometry: instance Read Size
+ Data.Sifflet.Geometry: instance Show BBox
+ Data.Sifflet.Geometry: instance Show Circle
+ Data.Sifflet.Geometry: instance Show Position
+ Data.Sifflet.Geometry: instance Show Size
+ Data.Sifflet.Geometry: instance Translate BBox
+ Data.Sifflet.Geometry: instance Translate Circle
+ Data.Sifflet.Geometry: instance Translate Position
+ Data.Sifflet.Geometry: instance Translate e => Translate (Tree e)
+ Data.Sifflet.Geometry: instance Translate e => Translate [e]
+ Data.Sifflet.Geometry: instance Widen BBox
+ Data.Sifflet.Geometry: pointInBB :: Position -> BBox -> Bool
+ Data.Sifflet.Geometry: pointInCircle :: Position -> Circle -> Bool
+ Data.Sifflet.Geometry: posX :: Position -> Double
+ Data.Sifflet.Geometry: posY :: Position -> Double
+ Data.Sifflet.Geometry: positionCloseEnough :: Position -> Position -> Double -> Bool
+ Data.Sifflet.Geometry: positionDelta :: Position -> Position -> (Double, Double)
+ Data.Sifflet.Geometry: positionDistance :: Position -> Position -> Double
+ Data.Sifflet.Geometry: positionDistanceSquared :: Position -> Position -> Double
+ Data.Sifflet.Geometry: sizeH :: Size -> Double
+ Data.Sifflet.Geometry: sizeW :: Size -> Double
+ Data.Sifflet.Geometry: translate :: Translate a => Double -> Double -> a -> a
+ Data.Sifflet.Geometry: widen :: Widen a => a -> Double -> a
+ Data.Sifflet.Tree: Name :: String -> Name
+ Data.Sifflet.Tree: Node :: a -> Forest a -> Tree a
+ Data.Sifflet.Tree: class Repr a where repr = head . reprl reprl x = [repr x] reprs = unwords . reprl reprList pre tween post xs = pre ++ intercalate tween (map repr xs) ++ post
+ Data.Sifflet.Tree: data Tree a :: * -> *
+ Data.Sifflet.Tree: drawTreeShow :: Show e => Tree e -> String
+ Data.Sifflet.Tree: isLeaf :: Tree e -> Bool
+ Data.Sifflet.Tree: leaf :: e -> Tree e
+ Data.Sifflet.Tree: nameTree :: Tree String -> Tree Name
+ Data.Sifflet.Tree: newtype Name
+ Data.Sifflet.Tree: putTree :: Show e => Tree e -> IO ()
+ Data.Sifflet.Tree: putTreeR :: Repr e => Tree e -> IO ()
+ Data.Sifflet.Tree: putTreeRs :: Repr e => Tree e -> IO ()
+ Data.Sifflet.Tree: putTreeS :: Show e => Tree e -> IO ()
+ Data.Sifflet.Tree: repr :: Repr a => a -> String
+ Data.Sifflet.Tree: reprList :: Repr a => String -> String -> String -> [a] -> String
+ Data.Sifflet.Tree: reprl :: Repr a => a -> [String]
+ Data.Sifflet.Tree: reprs :: Repr a => a -> String
+ Data.Sifflet.Tree: rootLabel :: Tree a -> a
+ Data.Sifflet.Tree: subForest :: Tree a -> Forest a
+ Data.Sifflet.Tree: tree :: e -> Forest e -> Tree e
+ Data.Sifflet.Tree: treeDepth :: Tree e -> Int
+ Data.Sifflet.Tree: treeSize :: Tree e -> Int
+ Data.Sifflet.TreeGraph: adjCompareEdge :: (Node, WEdge) -> (Node, WEdge) -> Ordering
+ Data.Sifflet.TreeGraph: flayoutToGraph :: FunctoidLayout -> WGraph
+ Data.Sifflet.TreeGraph: flayoutToGraphRoots :: FunctoidLayout -> [Node]
+ Data.Sifflet.TreeGraph: functoidToFunction :: Functoid -> WGraph -> Node -> Env -> SuccFail Function
+ Data.Sifflet.TreeGraph: grTranslateGraph :: Double -> Double -> LayoutGraph n e -> LayoutGraph n e
+ Data.Sifflet.TreeGraph: grTranslateNode :: Node -> Double -> Double -> LayoutGraph n e -> LayoutGraph n e
+ Data.Sifflet.TreeGraph: grTranslateSubtree :: Node -> Double -> Double -> LayoutGraph n e -> LayoutGraph n e
+ Data.Sifflet.TreeGraph: graphToExprTree :: WGraph -> Node -> Tree ExprNode
+ Data.Sifflet.TreeGraph: graphToOrderedTree :: Gr e WEdge -> Tree e
+ Data.Sifflet.TreeGraph: graphToOrderedTreeFrom :: Gr e WEdge -> Node -> Tree e
+ Data.Sifflet.TreeGraph: graphToTreeOriginal :: Gr e () -> Node -> Tree e
+ Data.Sifflet.TreeGraph: graphToTreeStructure :: Gr n e -> Node -> Tree Node
+ Data.Sifflet.TreeGraph: nextNodes :: [e] -> Node -> ([Node], Node)
+ Data.Sifflet.TreeGraph: orderedChildren :: Gr e WEdge -> Node -> [Node]
+ Data.Sifflet.TreeGraph: orderedTreeToGraph :: Tree e -> Gr e WEdge
+ Data.Sifflet.TreeGraph: treeGraphNodesTree :: Tree e -> Tree Node
+ Data.Sifflet.TreeGraph: treeLayoutToGraph :: TreeLayout ExprNode -> WGraph
+ Data.Sifflet.TreeGraph: type LayoutGraph n e = Gr (LayoutNode n) e
+ Data.Sifflet.TreeLayout: ColorRGB :: Double -> Double -> Double -> VColor
+ Data.Sifflet.TreeLayout: ColorRGBA :: Double -> Double -> Double -> Double -> VColor
+ Data.Sifflet.TreeLayout: FontTextExtents :: Double -> Double -> Double -> Double -> Double -> Double -> Double -> Double -> Double -> Double -> Double -> FontTextExtents
+ Data.Sifflet.TreeLayout: GNode :: e -> [TextBox] -> BBox -> [Iolet] -> [Iolet] -> GNode e
+ Data.Sifflet.TreeLayout: Iolet :: Circle -> Iolet
+ Data.Sifflet.TreeLayout: LayoutNode :: GNode e -> BBox -> LayoutNode e
+ Data.Sifflet.TreeLayout: Style :: VFont -> Double -> Double -> Double -> Double -> Double -> (Double, Double) -> Double -> VColor -> VColor -> VColor -> VColor -> VColor -> VColor -> VColor -> VColor -> VColor -> VColor -> Position -> VColor -> VFont -> Double -> Bool -> Bool -> Style
+ Data.Sifflet.TreeLayout: TextBox :: String -> BBox -> BBox -> TextBox
+ Data.Sifflet.TreeLayout: VFont :: String -> FontSlant -> FontWeight -> Double -> VFont
+ Data.Sifflet.TreeLayout: black, lightBlueGreen, lightBlue, cream, white :: VColor
+ Data.Sifflet.TreeLayout: data FontTextExtents
+ Data.Sifflet.TreeLayout: data GNode e
+ Data.Sifflet.TreeLayout: data LayoutNode e
+ Data.Sifflet.TreeLayout: data Style
+ Data.Sifflet.TreeLayout: data TextBox
+ Data.Sifflet.TreeLayout: data VColor
+ Data.Sifflet.TreeLayout: data VFont
+ Data.Sifflet.TreeLayout: exomargin :: Style -> Double
+ Data.Sifflet.TreeLayout: extTextHeight :: FontTextExtents -> Double
+ Data.Sifflet.TreeLayout: extTextWidth :: FontTextExtents -> Double
+ Data.Sifflet.TreeLayout: findRect :: Position -> TreeLayout e -> Maybe Rectangle
+ Data.Sifflet.TreeLayout: fontAscent :: FontTextExtents -> Double
+ Data.Sifflet.TreeLayout: fontDescent :: FontTextExtents -> Double
+ Data.Sifflet.TreeLayout: fontHeight :: FontTextExtents -> Double
+ Data.Sifflet.TreeLayout: fontMaxXadvance :: FontTextExtents -> Double
+ Data.Sifflet.TreeLayout: fontMaxYadvance :: FontTextExtents -> Double
+ Data.Sifflet.TreeLayout: ftExtents :: String -> Render FontTextExtents
+ Data.Sifflet.TreeLayout: gnodeInlets :: GNode e -> [Iolet]
+ Data.Sifflet.TreeLayout: gnodeNodeBB :: GNode e -> BBox
+ Data.Sifflet.TreeLayout: gnodeOutlets :: GNode e -> [Iolet]
+ Data.Sifflet.TreeLayout: gnodeText :: GNode e -> String
+ Data.Sifflet.TreeLayout: gnodeTextBB :: GNode e -> BBox
+ Data.Sifflet.TreeLayout: gnodeTextBoxes :: GNode e -> [TextBox]
+ Data.Sifflet.TreeLayout: gnodeValue :: GNode e -> e
+ Data.Sifflet.TreeLayout: hpad :: Style -> Double
+ Data.Sifflet.TreeLayout: instance Eq FontTextExtents
+ Data.Sifflet.TreeLayout: instance Eq Iolet
+ Data.Sifflet.TreeLayout: instance Eq TextBox
+ Data.Sifflet.TreeLayout: instance Eq e => Eq (GNode e)
+ Data.Sifflet.TreeLayout: instance Eq e => Eq (LayoutNode e)
+ Data.Sifflet.TreeLayout: instance Read FontTextExtents
+ Data.Sifflet.TreeLayout: instance Read Iolet
+ Data.Sifflet.TreeLayout: instance Read TextBox
+ Data.Sifflet.TreeLayout: instance Show FontTextExtents
+ Data.Sifflet.TreeLayout: instance Show Iolet
+ Data.Sifflet.TreeLayout: instance Show TextBox
+ Data.Sifflet.TreeLayout: instance Show e => Show (GNode e)
+ Data.Sifflet.TreeLayout: instance Show e => Show (LayoutNode e)
+ Data.Sifflet.TreeLayout: instance Translate (GNode e)
+ Data.Sifflet.TreeLayout: instance Translate (LayoutNode e)
+ Data.Sifflet.TreeLayout: instance Translate Iolet
+ Data.Sifflet.TreeLayout: instance Translate TextBox
+ Data.Sifflet.TreeLayout: instance Widen (LayoutNode e)
+ Data.Sifflet.TreeLayout: instance Widen TextBox
+ Data.Sifflet.TreeLayout: ioletCenter :: Iolet -> Position
+ Data.Sifflet.TreeLayout: layoutNodeSource :: LayoutNode e -> e
+ Data.Sifflet.TreeLayout: layoutRootBB :: TreeLayout e -> BBox
+ Data.Sifflet.TreeLayout: layoutTreeBB :: TreeLayout e -> BBox
+ Data.Sifflet.TreeLayout: layoutTreeMoveCenterTo :: Double -> Double -> TreeLayout e -> TreeLayout e
+ Data.Sifflet.TreeLayout: lightGray, darkGray, mediumGray :: VColor
+ Data.Sifflet.TreeLayout: lineWidth :: Style -> Double
+ Data.Sifflet.TreeLayout: makeGNode :: Repr e => Style -> IoletCounter e -> e -> GNode e
+ Data.Sifflet.TreeLayout: makeIolets :: Style -> BBox -> (Int, Int) -> ([Iolet], [Iolet])
+ Data.Sifflet.TreeLayout: makeIoletsRow :: Style -> Double -> Double -> Int -> [Iolet]
+ Data.Sifflet.TreeLayout: makeTextBox :: Style -> String -> TextBox
+ Data.Sifflet.TreeLayout: measureText :: Style -> String -> Size
+ Data.Sifflet.TreeLayout: newtype Iolet
+ Data.Sifflet.TreeLayout: nodeGNode :: LayoutNode e -> GNode e
+ Data.Sifflet.TreeLayout: nodeTreeBB :: LayoutNode e -> BBox
+ Data.Sifflet.TreeLayout: offsetTextBoxCenters :: Position -> TextBox -> TextBox -> TextBox
+ Data.Sifflet.TreeLayout: pointInGNode :: Position -> GNode e -> Bool
+ Data.Sifflet.TreeLayout: pointInIolet :: Position -> Iolet -> Bool
+ Data.Sifflet.TreeLayout: pointInLayoutNode :: Position -> LayoutNode e -> Bool
+ Data.Sifflet.TreeLayout: setFont :: VFont -> Render ()
+ Data.Sifflet.TreeLayout: style0, defaultStyle, style3, style2, style1 :: Style
+ Data.Sifflet.TreeLayout: styleActiveEdgeColor :: Style -> VColor
+ Data.Sifflet.TreeLayout: styleActiveFillColor :: Style -> VColor
+ Data.Sifflet.TreeLayout: styleActiveTextColor :: Style -> VColor
+ Data.Sifflet.TreeLayout: styleAuxColor :: Style -> VColor
+ Data.Sifflet.TreeLayout: styleAuxFont :: Style -> VFont
+ Data.Sifflet.TreeLayout: styleAuxOffset :: Style -> Position
+ Data.Sifflet.TreeLayout: styleFont :: Style -> VFont
+ Data.Sifflet.TreeLayout: styleFramePad :: Style -> Double
+ Data.Sifflet.TreeLayout: styleIncreasePadding :: Style -> Double -> Style
+ Data.Sifflet.TreeLayout: styleIoletRadius :: Style -> Double
+ Data.Sifflet.TreeLayout: styleNormalEdgeColor :: Style -> VColor
+ Data.Sifflet.TreeLayout: styleNormalFillColor :: Style -> VColor
+ Data.Sifflet.TreeLayout: styleNormalTextColor :: Style -> VColor
+ Data.Sifflet.TreeLayout: styleSelectedEdgeColor :: Style -> VColor
+ Data.Sifflet.TreeLayout: styleSelectedFillColor :: Style -> VColor
+ Data.Sifflet.TreeLayout: styleSelectedTextColor :: Style -> VColor
+ Data.Sifflet.TreeLayout: styleShowNodeBoxes :: Style -> Bool
+ Data.Sifflet.TreeLayout: styleShowNodePorts :: Style -> Bool
+ Data.Sifflet.TreeLayout: styleTetherColor :: Style -> VColor
+ Data.Sifflet.TreeLayout: styleTextExtents :: Style -> String -> FontTextExtents
+ Data.Sifflet.TreeLayout: tbBottom :: TextBox -> Double
+ Data.Sifflet.TreeLayout: tbBoxBB :: TextBox -> BBox
+ Data.Sifflet.TreeLayout: tbBoxCenter :: TextBox -> Position
+ Data.Sifflet.TreeLayout: tbCenter :: TextBox -> Position
+ Data.Sifflet.TreeLayout: tbHeight :: TextBox -> Double
+ Data.Sifflet.TreeLayout: tbSetWidth :: TextBox -> Double -> TextBox
+ Data.Sifflet.TreeLayout: tbText :: TextBox -> String
+ Data.Sifflet.TreeLayout: tbTextBB :: TextBox -> BBox
+ Data.Sifflet.TreeLayout: tbTextCenter :: TextBox -> Position
+ Data.Sifflet.TreeLayout: tbWidth :: TextBox -> Double
+ Data.Sifflet.TreeLayout: textMargin :: Style -> Double
+ Data.Sifflet.TreeLayout: textXadvance :: FontTextExtents -> Double
+ Data.Sifflet.TreeLayout: textXbearing :: FontTextExtents -> Double
+ Data.Sifflet.TreeLayout: textYadvance :: FontTextExtents -> Double
+ Data.Sifflet.TreeLayout: textYbearing :: FontTextExtents -> Double
+ Data.Sifflet.TreeLayout: treeGNodes :: Repr e => Style -> IoletCounter e -> Tree e -> Tree (GNode e)
+ Data.Sifflet.TreeLayout: treeLayout :: Repr e => Style -> IoletCounter e -> Tree e -> TreeLayout e
+ Data.Sifflet.TreeLayout: treeLayoutPaddedSize :: Style -> TreeLayout e -> Size
+ Data.Sifflet.TreeLayout: treeLayoutSize :: TreeLayout e -> Size
+ Data.Sifflet.TreeLayout: treeLayoutWiden :: TreeLayout e -> Double -> TreeLayout e
+ Data.Sifflet.TreeLayout: treeLayoutWidth :: TreeLayout e -> Double
+ Data.Sifflet.TreeLayout: treeSizes :: Style -> Tree (GNode e) -> Tree Size
+ Data.Sifflet.TreeLayout: type IoletCounter e = e -> (Int, Int)
+ Data.Sifflet.TreeLayout: type TreeLayout e = Tree (LayoutNode e)
+ Data.Sifflet.TreeLayout: vfontFace :: VFont -> String
+ Data.Sifflet.TreeLayout: vfontSize :: VFont -> Double
+ Data.Sifflet.TreeLayout: vfontSlant :: VFont -> FontSlant
+ Data.Sifflet.TreeLayout: vfontWeight :: VFont -> FontWeight
+ Data.Sifflet.TreeLayout: vpad :: Style -> Double
+ Data.Sifflet.TreeLayout: vtinypad :: Style -> (Double, Double)
+ Data.Sifflet.TreeLayout: wstyle :: Style
+ Data.Sifflet.TreeLayout: yellow, blueGreen, darkBlueGreen :: VColor
+ Data.Sifflet.TreeLayout: zeroIoletCounter :: IoletCounter e
+ Data.Sifflet.WGraph: WEdge :: Int -> WEdge
+ Data.Sifflet.WGraph: WFrame :: Node -> WNode
+ Data.Sifflet.WGraph: WSimple :: (LayoutNode ExprNode) -> WNode
+ Data.Sifflet.WGraph: adoptChildren :: WGraph -> Node -> [Node] -> WGraph
+ Data.Sifflet.WGraph: allDescendants :: Graph graph => graph a b -> Node -> [Node]
+ Data.Sifflet.WGraph: connectToFrame :: Node -> Node -> WGraph -> WGraph
+ Data.Sifflet.WGraph: data WNode
+ Data.Sifflet.WGraph: functionToParts :: Function -> WGraph -> Node -> Functoid
+ Data.Sifflet.WGraph: functoidParts :: Functoid -> WGraph -> Node -> Functoid
+ Data.Sifflet.WGraph: grAddGraph :: DynGraph g => g n e -> g n e -> g n e
+ Data.Sifflet.WGraph: grConnect :: WGraph -> Node -> WEdge -> Node -> WEdge -> WGraph
+ Data.Sifflet.WGraph: grDisconnect :: WGraph -> Node -> WEdge -> Node -> WEdge -> Bool -> WGraph
+ Data.Sifflet.WGraph: grExtractExprTree :: WGraph -> Node -> Tree ExprNode
+ Data.Sifflet.WGraph: grExtractLayoutNode :: WGraph -> Node -> LayoutNode ExprNode
+ Data.Sifflet.WGraph: grExtractLayoutTree :: WGraph -> Node -> TreeLayout ExprNode
+ Data.Sifflet.WGraph: grInsertNode :: DynGraph g => g n e -> n -> (g n e, Node)
+ Data.Sifflet.WGraph: grRelabelNode :: DynGraph g => g a b -> Node -> a -> g a b
+ Data.Sifflet.WGraph: grRemoveNode :: DynGraph g => g n e -> Node -> (g n e)
+ Data.Sifflet.WGraph: grUpdateFLayout :: WGraph -> [Node] -> FunctoidLayout -> WGraph
+ Data.Sifflet.WGraph: grUpdateTreeLayout :: WGraph -> Node -> TreeLayout ExprNode -> WGraph
+ Data.Sifflet.WGraph: graphOrphans :: Graph graph => graph a b -> [Node]
+ Data.Sifflet.WGraph: instance Eq WEdge
+ Data.Sifflet.WGraph: instance Eq WNode
+ Data.Sifflet.WGraph: instance Ord WEdge
+ Data.Sifflet.WGraph: instance Read WEdge
+ Data.Sifflet.WGraph: instance Repr WNode
+ Data.Sifflet.WGraph: instance Show WEdge
+ Data.Sifflet.WGraph: instance Show WNode
+ Data.Sifflet.WGraph: isWFrame :: WNode -> Bool
+ Data.Sifflet.WGraph: isWSimple :: WNode -> Bool
+ Data.Sifflet.WGraph: llab :: WGraph -> Node -> LayoutNode ExprNode
+ Data.Sifflet.WGraph: newtype WEdge
+ Data.Sifflet.WGraph: nfilter :: (Node -> Bool) -> Gr v e -> Gr v e
+ Data.Sifflet.WGraph: nodeAllChildren :: WGraph -> Node -> [Node]
+ Data.Sifflet.WGraph: nodeAllSimpleDescendants :: WGraph -> Node -> [Node]
+ Data.Sifflet.WGraph: nodeBBox :: WGraph -> Node -> BBox
+ Data.Sifflet.WGraph: nodeContainerFrameNode :: WGraph -> Node -> Node
+ Data.Sifflet.WGraph: nodeExprNode :: WGraph -> Node -> ExprNode
+ Data.Sifflet.WGraph: nodeFrameChildren :: WGraph -> Node -> [Node]
+ Data.Sifflet.WGraph: nodeInputValues :: WGraph -> Node -> EvalResult
+ Data.Sifflet.WGraph: nodeIsOpen :: WGraph -> Node -> Bool
+ Data.Sifflet.WGraph: nodeIsSimple :: WGraph -> Node -> Bool
+ Data.Sifflet.WGraph: nodeParent :: WGraph -> Node -> Maybe Node
+ Data.Sifflet.WGraph: nodePosition :: WGraph -> Node -> Position
+ Data.Sifflet.WGraph: nodeProperSimpleDescendants :: WGraph -> Node -> [Node]
+ Data.Sifflet.WGraph: nodeSimpleChildren :: WGraph -> Node -> [Node]
+ Data.Sifflet.WGraph: nodeText :: WGraph -> Node -> String
+ Data.Sifflet.WGraph: nodeValue :: WGraph -> Node -> EvalResult
+ Data.Sifflet.WGraph: printWGraph :: WGraph -> IO ()
+ Data.Sifflet.WGraph: translateNode :: Double -> Double -> WGraph -> Node -> WGraph
+ Data.Sifflet.WGraph: translateNodes :: Double -> Double -> WGraph -> [Node] -> WGraph
+ Data.Sifflet.WGraph: translateTree :: Double -> Double -> WGraph -> Node -> WGraph
+ Data.Sifflet.WGraph: type WContext = Context WNode WEdge
+ Data.Sifflet.WGraph: type WGraph = Gr WNode WEdge
+ Data.Sifflet.WGraph: wgraphNew :: WGraph
+ Data.Sifflet.WGraph: wlab :: WGraph -> Node -> WNode
+ Graphics.Rendering.Sifflet.Draw: DrawActive :: DrawMode
+ Graphics.Rendering.Sifflet.Draw: DrawNormal :: DrawMode
+ Graphics.Rendering.Sifflet.Draw: DrawSelectedInlet :: Int -> DrawMode
+ Graphics.Rendering.Sifflet.Draw: DrawSelectedNode :: DrawMode
+ Graphics.Rendering.Sifflet.Draw: DrawSelectedOutlet :: Int -> DrawMode
+ Graphics.Rendering.Sifflet.Draw: class Draw a
+ Graphics.Rendering.Sifflet.Draw: data DrawMode
+ Graphics.Rendering.Sifflet.Draw: draw :: Draw a => Style -> DrawMode -> a -> Render ()
+ Graphics.Rendering.Sifflet.Draw: drawBox :: Maybe VColor -> Maybe VColor -> BBox -> Render ()
+ Graphics.Rendering.Sifflet.Draw: drawTextBox :: Maybe VFont -> Maybe VColor -> Maybe VColor -> VColor -> TextBox -> Render ()
+ Graphics.Rendering.Sifflet.Draw: instance Draw (GNode e)
+ Graphics.Rendering.Sifflet.Draw: instance Draw (LayoutNode e)
+ Graphics.Rendering.Sifflet.Draw: instance Draw BBox
+ Graphics.Rendering.Sifflet.Draw: instance Draw Circle
+ Graphics.Rendering.Sifflet.Draw: instance Draw FunctoidLayout
+ Graphics.Rendering.Sifflet.Draw: instance Draw Iolet
+ Graphics.Rendering.Sifflet.Draw: instance Draw Position
+ Graphics.Rendering.Sifflet.Draw: instance Draw TextBox
+ Graphics.Rendering.Sifflet.Draw: instance Draw e => Draw (Tree e)
+ Graphics.Rendering.Sifflet.Draw: instance Draw e => Draw [e]
+ Graphics.Rendering.Sifflet.Draw: instance Eq DrawMode
+ Graphics.Rendering.Sifflet.Draw: modeEdgeCol :: DrawMode -> (Style -> VColor)
+ Graphics.Rendering.Sifflet.Draw: modeFillCol :: DrawMode -> (Style -> VColor)
+ Graphics.Rendering.Sifflet.Draw: modeTextCol :: DrawMode -> (Style -> VColor)
+ Graphics.Rendering.Sifflet.Draw: setColor :: VColor -> Render ()
+ Graphics.Rendering.Sifflet.DrawTreeGraph: graphQuickView :: (Graph g, Show a, Show b) => g a b -> IO ()
+ Graphics.Rendering.Sifflet.DrawTreeGraph: graphRender :: Repr n => Style -> Maybe Node -> Maybe Node -> LayoutGraph n e -> Render ()
+ Graphics.Rendering.Sifflet.DrawTreeGraph: graphWriteImageFile :: Repr n => Style -> Maybe Node -> Maybe Node -> Double -> Double -> LayoutGraph n e -> String -> IO String
+ Graphics.Rendering.Sifflet.DrawTreeGraph: gtkShowTree :: (Repr e, Show e) => Style -> IoletCounter e -> Tree e -> IO ()
+ Graphics.Rendering.Sifflet.DrawTreeGraph: treeRender :: Repr e => Style -> TreeLayout e -> Render ()
+ Graphics.Rendering.Sifflet.DrawTreeGraph: treeWriteImageFile :: Repr e => Style -> IoletCounter e -> Tree e -> String -> IO String
+ Graphics.UI.Sifflet.Callback: AfterButtonClicked :: Button -> CBMgrAction -> CBMgrCmd
+ Graphics.UI.Sifflet.Callback: AfterWindowKeyPress :: Window -> (IORef VPUI -> EventM EKey Bool) -> CBMgrCmd
+ Graphics.UI.Sifflet.Callback: MenuItem :: String -> MenuItemAction -> MenuItemSpec
+ Graphics.UI.Sifflet.Callback: MenuSpec :: String -> [MenuItemSpec] -> MenuSpec
+ Graphics.UI.Sifflet.Callback: OnEntryActivate :: Entry -> CBMgrAction -> CBMgrCmd
+ Graphics.UI.Sifflet.Callback: OnLayoutButtonPress :: Layout -> (IORef VPUI -> EventM EButton Bool) -> CBMgrCmd
+ Graphics.UI.Sifflet.Callback: OnLayoutButtonRelease :: Layout -> (IORef VPUI -> EventM EButton Bool) -> CBMgrCmd
+ Graphics.UI.Sifflet.Callback: OnLayoutExpose :: Layout -> (IORef VPUI -> EventM EExpose Bool) -> CBMgrCmd
+ Graphics.UI.Sifflet.Callback: OnLayoutMouseMove :: Layout -> (IORef VPUI -> EventM EMotion Bool) -> CBMgrCmd
+ Graphics.UI.Sifflet.Callback: OnMenuItemActivateLeaf :: MenuItem -> (VPUI -> IO VPUI) -> CBMgrCmd
+ Graphics.UI.Sifflet.Callback: OnWindowConfigure :: Window -> (IORef VPUI -> EventM EConfigure Bool) -> CBMgrCmd
+ Graphics.UI.Sifflet.Callback: OnWindowDestroy :: Window -> CBMgrAction -> CBMgrCmd
+ Graphics.UI.Sifflet.Callback: SubMenu :: MenuSpec -> MenuItemSpec
+ Graphics.UI.Sifflet.Callback: UMTest :: CBMgrCmd
+ Graphics.UI.Sifflet.Callback: WithUIRef :: CBMgrAction -> CBMgrCmd
+ Graphics.UI.Sifflet.Callback: addMenu :: MenuBar -> CBMgr -> MenuSpec -> IO ()
+ Graphics.UI.Sifflet.Callback: createMenu :: MenuSpec -> CBMgr -> IO Menu
+ Graphics.UI.Sifflet.Callback: createMenuBar :: [MenuSpec] -> CBMgr -> IO MenuBar
+ Graphics.UI.Sifflet.Callback: createMenuItem :: Menu -> CBMgr -> MenuItemSpec -> IO ()
+ Graphics.UI.Sifflet.Callback: data CBMgrCmd
+ Graphics.UI.Sifflet.Callback: data MenuItemSpec
+ Graphics.UI.Sifflet.Callback: data MenuSpec
+ Graphics.UI.Sifflet.Callback: mkCBMgr :: IORef VPUI -> CBMgr
+ Graphics.UI.Sifflet.Callback: modifyIORefIO :: (a -> IO a) -> IORef a -> IO ()
+ Graphics.UI.Sifflet.Callback: type CBMgr = CBMgrCmd -> IO ()
+ Graphics.UI.Sifflet.Callback: type CBMgrAction = IORef VPUI -> IO ()
+ Graphics.UI.Sifflet.Callback: type MenuItemAction = VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Canvas: atLeastSize :: Size -> VCanvas -> VCanvas
+ Graphics.UI.Sifflet.Canvas: callFrames :: VCanvas -> String -> [CanvFrame]
+ Graphics.UI.Sifflet.Canvas: cfContext :: CanvFrame -> ToolContext
+ Graphics.UI.Sifflet.Canvas: connect :: VCanvas -> Node -> WEdge -> Node -> WEdge -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: defaultFileSaveClipBox :: VCanvas -> BBox
+ Graphics.UI.Sifflet.Canvas: disconnect :: VCanvas -> Node -> WEdge -> Node -> WEdge -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: drawCanvas :: VCanvas -> Rectangle -> IO ()
+ Graphics.UI.Sifflet.Canvas: editFunction :: VCanvas -> CanvFrame -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: frameChanged :: VCanvas -> WGraph -> CanvFrame -> WGraph -> CanvFrame -> IO ()
+ Graphics.UI.Sifflet.Canvas: nodeContainerFrame :: VCanvas -> WGraph -> Node -> CanvFrame
+ Graphics.UI.Sifflet.Canvas: pointSelection :: WGraph -> CanvFrame -> Position -> Maybe Selection
+ Graphics.UI.Sifflet.Canvas: renderCanvas :: VCanvas -> BBox -> Bool -> Render ()
+ Graphics.UI.Sifflet.Canvas: vcAddFrame :: VCanvas -> Functoid -> Maybe [Value] -> FrameType -> Env -> Double -> Double -> Double -> Maybe Node -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: vcClearFrame :: VCanvas -> CanvFrame -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: vcClearSelection :: VCanvas -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: vcCloseFrame :: VCanvas -> CanvFrame -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: vcEvalDialog :: VCanvas -> CanvFrame -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: vcFrameAddFunctoidNode :: VCanvas -> CanvFrame -> Functoid -> Double -> Double -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: vcFrameAddNode :: VCanvas -> CanvFrame -> ExprNode -> [String] -> Double -> Double -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: vcFrameDeleteNode :: VCanvas -> CanvFrame -> Node -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: vcFrameDeleteTree :: VCanvas -> CanvFrame -> Node -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: vcFrameSubframes :: VCanvas -> CanvFrame -> [CanvFrame]
+ Graphics.UI.Sifflet.Canvas: vcGetFrame :: VCanvas -> WGraph -> Node -> CanvFrame
+ Graphics.UI.Sifflet.Canvas: vcInvalidateBox :: VCanvas -> BBox -> IO ()
+ Graphics.UI.Sifflet.Canvas: vcInvalidateFrameWithParent :: VCanvas -> WGraph -> CanvFrame -> IO ()
+ Graphics.UI.Sifflet.Canvas: vcUpdateFrameAndGraph :: VCanvas -> CanvFrame -> WGraph -> VCanvas
+ Graphics.UI.Sifflet.Canvas: vcanvasNew :: Style -> Double -> Double -> IO VCanvas
+ Graphics.UI.Sifflet.Canvas: vcanvasNodeAt :: VCanvas -> Position -> Maybe Node
+ Graphics.UI.Sifflet.Canvas: vcanvasNodeRect :: VCanvas -> Node -> Rectangle
+ Graphics.UI.Sifflet.Canvas: whichFrame :: VCanvas -> Double -> Double -> Maybe CanvFrame
+ Graphics.UI.Sifflet.EditArgsPanel: data EditArgsPanel
+ Graphics.UI.Sifflet.EditArgsPanel: editArgsPanelRoot :: EditArgsPanel -> PanelRoot
+ Graphics.UI.Sifflet.EditArgsPanel: expandToFit :: (WidgetClass v, WidgetClass w) => v -> w -> IO ()
+ Graphics.UI.Sifflet.EditArgsPanel: instance Show Model
+ Graphics.UI.Sifflet.EditArgsPanel: makeEditArgsPanel :: CBMgr -> [ArgSpec] -> ArgSpecAction -> IO EditArgsPanel
+ Graphics.UI.Sifflet.EditArgsPanel: type ArgSpecAction = [ArgSpec] -> IO ()
+ Graphics.UI.Sifflet.Frame: CallFrame :: FrameType
+ Graphics.UI.Sifflet.Frame: CanvFrame :: TextBox -> TextBox -> [String] -> Maybe Node -> Node -> Env -> BBox -> Double -> Functoid -> FrameType -> CanvFrame
+ Graphics.UI.Sifflet.Frame: EditFrame :: FrameType
+ Graphics.UI.Sifflet.Frame: argIoletCounter :: [String] -> ExprNode -> (Int, Int)
+ Graphics.UI.Sifflet.Frame: atLeastSizeFrame :: Size -> CanvFrame -> CanvFrame
+ Graphics.UI.Sifflet.Frame: cfBox :: CanvFrame -> BBox
+ Graphics.UI.Sifflet.Frame: cfEnv :: CanvFrame -> Env
+ Graphics.UI.Sifflet.Frame: cfEvalReady :: CanvFrame -> Bool
+ Graphics.UI.Sifflet.Frame: cfFooter :: CanvFrame -> TextBox
+ Graphics.UI.Sifflet.Frame: cfFrameNode :: CanvFrame -> Node
+ Graphics.UI.Sifflet.Frame: cfFunctoid :: CanvFrame -> Functoid
+ Graphics.UI.Sifflet.Frame: cfHeader :: CanvFrame -> TextBox
+ Graphics.UI.Sifflet.Frame: cfLevel :: CanvFrame -> Double
+ Graphics.UI.Sifflet.Frame: cfParent :: CanvFrame -> Maybe Node
+ Graphics.UI.Sifflet.Frame: cfPointInFooter :: CanvFrame -> Double -> Double -> Bool
+ Graphics.UI.Sifflet.Frame: cfPointInHeader :: CanvFrame -> Double -> Double -> Bool
+ Graphics.UI.Sifflet.Frame: cfRoot :: CanvFrame -> Node
+ Graphics.UI.Sifflet.Frame: cfVarNames :: CanvFrame -> [String]
+ Graphics.UI.Sifflet.Frame: data CanvFrame
+ Graphics.UI.Sifflet.Frame: data FrameType
+ Graphics.UI.Sifflet.Frame: frameBodyBox :: CanvFrame -> BBox
+ Graphics.UI.Sifflet.Frame: frameNewWithLayout :: Style -> Position -> Double -> Functoid -> Maybe [Value] -> FrameType -> Node -> Env -> Maybe Node -> (CanvFrame, FunctoidLayout)
+ Graphics.UI.Sifflet.Frame: frameNodeAt :: CanvFrame -> WGraph -> Position -> Maybe Node
+ Graphics.UI.Sifflet.Frame: frameOffset :: Style -> CanvFrame -> Position
+ Graphics.UI.Sifflet.Frame: frameType :: CanvFrame -> FrameType
+ Graphics.UI.Sifflet.Frame: grTranslateFrameNodes :: WGraph -> CanvFrame -> Double -> Double -> WGraph
+ Graphics.UI.Sifflet.Frame: instance Eq CanvFrame
+ Graphics.UI.Sifflet.Frame: instance Eq FrameType
+ Graphics.UI.Sifflet.Frame: instance Read FrameType
+ Graphics.UI.Sifflet.Frame: instance Show CanvFrame
+ Graphics.UI.Sifflet.Frame: instance Show FrameType
+ Graphics.UI.Sifflet.Frame: levelOrder :: CanvFrame -> CanvFrame -> Ordering
+ Graphics.UI.Sifflet.Frame: nodeCompoundFunction :: WGraph -> CanvFrame -> Node -> Maybe Function
+ Graphics.UI.Sifflet.Frame: pointIolet :: Position -> Int -> [Iolet] -> Maybe Int
+ Graphics.UI.Sifflet.Frame: resizeFrame :: CanvFrame -> Double -> Double -> CanvFrame
+ Graphics.UI.Sifflet.Frame: translateFrame :: CanvFrame -> Double -> Double -> CanvFrame
+ Graphics.UI.Sifflet.GtkForeign: setCursor :: Window -> CursorType -> IO ()
+ Graphics.UI.Sifflet.GtkUtil: addEntryWithLabel :: VBox -> (String, Entry, String) -> IO ()
+ Graphics.UI.Sifflet.GtkUtil: createEntryDialog :: String -> [String] -> [String] -> (Reader [String] a) -> Int -> IO (EntryDialog a)
+ Graphics.UI.Sifflet.GtkUtil: data EntryDialog a
+ Graphics.UI.Sifflet.GtkUtil: defaultDialogPosition :: WindowPosition
+ Graphics.UI.Sifflet.GtkUtil: runDialogM :: Dialog -> a -> (a -> IO (Maybe b)) -> IO (Maybe b)
+ Graphics.UI.Sifflet.GtkUtil: runDialogS :: Dialog -> a -> (a -> IO (SuccFail b)) -> IO (Maybe b)
+ Graphics.UI.Sifflet.GtkUtil: runEntryDialog :: Show a => EntryDialog a -> IO (Maybe a)
+ Graphics.UI.Sifflet.GtkUtil: showChoicesDialog :: String -> String -> [String] -> [IO a] -> IO a
+ Graphics.UI.Sifflet.GtkUtil: showErrorMessage :: String -> IO ()
+ Graphics.UI.Sifflet.GtkUtil: showInfoMessage :: String -> String -> IO ()
+ Graphics.UI.Sifflet.GtkUtil: showInputErrorMessage :: String -> IO ()
+ Graphics.UI.Sifflet.GtkUtil: showMessage :: Maybe String -> MessageType -> ButtonsType -> String -> IO ()
+ Graphics.UI.Sifflet.GtkUtil: suppressScimBridge :: IO ()
+ Graphics.UI.Sifflet.GtkUtil: type Reader a b = a -> SuccFail b
+ Graphics.UI.Sifflet.LittleGtk: (::=) :: ReadWriteAttr o a b -> (o -> b) -> AttrOp o
+ Graphics.UI.Sifflet.LittleGtk: (::~) :: ReadWriteAttr o a b -> (o -> a -> b) -> AttrOp o
+ Graphics.UI.Sifflet.LittleGtk: (:=) :: ReadWriteAttr o a b -> b -> AttrOp o
+ Graphics.UI.Sifflet.LittleGtk: (:=>) :: ReadWriteAttr o a b -> IO b -> AttrOp o
+ Graphics.UI.Sifflet.LittleGtk: (:~) :: ReadWriteAttr o a b -> (a -> b) -> AttrOp o
+ Graphics.UI.Sifflet.LittleGtk: (:~>) :: ReadWriteAttr o a b -> (a -> IO b) -> AttrOp o
+ Graphics.UI.Sifflet.LittleGtk: AllEventsMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: Arrow :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: BasedArrowDown :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: BasedArrowUp :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: BlankCursor :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Boat :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Bogosity :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: BottomLeftCorner :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: BottomRightCorner :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: BottomSide :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: BottomTee :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: BoxSpiral :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Button1MotionMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: Button2MotionMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: Button3MotionMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: ButtonMotionMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: ButtonPressMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: ButtonReleaseMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: ButtonsCancel :: ButtonsType
+ Graphics.UI.Sifflet.LittleGtk: ButtonsClose :: ButtonsType
+ Graphics.UI.Sifflet.LittleGtk: ButtonsNone :: ButtonsType
+ Graphics.UI.Sifflet.LittleGtk: ButtonsOk :: ButtonsType
+ Graphics.UI.Sifflet.LittleGtk: ButtonsOkCancel :: ButtonsType
+ Graphics.UI.Sifflet.LittleGtk: ButtonsYesNo :: ButtonsType
+ Graphics.UI.Sifflet.LittleGtk: CenterPtr :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Circle :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Clock :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: CoffeeMug :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Cross :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: CrossReverse :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Crosshair :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: CursorIsPixmap :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: DiamondCross :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Dot :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Dotbox :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: DoubleArrow :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: DraftLarge :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: DraftSmall :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: DrapedBox :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: EnterNotifyMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: Exchange :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: ExposureMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: FileChooserActionCreateFolder :: FileChooserAction
+ Graphics.UI.Sifflet.LittleGtk: FileChooserActionOpen :: FileChooserAction
+ Graphics.UI.Sifflet.LittleGtk: FileChooserActionSave :: FileChooserAction
+ Graphics.UI.Sifflet.LittleGtk: FileChooserActionSelectFolder :: FileChooserAction
+ Graphics.UI.Sifflet.LittleGtk: Fleur :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: FocusChangeMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: Gobbler :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Gumby :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Hand1 :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Hand2 :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Heart :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Icon :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: IronCross :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: KeyPressMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: KeyReleaseMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: LastCursor :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: LeaveNotifyMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: LeftPtr :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: LeftSide :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: LeftTee :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Leftbutton :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: LlAngle :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: LrAngle :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Man :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: MessageError :: MessageType
+ Graphics.UI.Sifflet.LittleGtk: MessageInfo :: MessageType
+ Graphics.UI.Sifflet.LittleGtk: MessageOther :: MessageType
+ Graphics.UI.Sifflet.LittleGtk: MessageQuestion :: MessageType
+ Graphics.UI.Sifflet.LittleGtk: MessageWarning :: MessageType
+ Graphics.UI.Sifflet.LittleGtk: Middlebutton :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Mouse :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: PackGrow :: Packing
+ Graphics.UI.Sifflet.LittleGtk: PackNatural :: Packing
+ Graphics.UI.Sifflet.LittleGtk: PackRepel :: Packing
+ Graphics.UI.Sifflet.LittleGtk: Pencil :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Pirate :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Plus :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: PointerMotionHintMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: PointerMotionMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: PolicyAutomatic :: PolicyType
+ Graphics.UI.Sifflet.LittleGtk: PropertyChangeMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: ProximityInMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: ProximityOutMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: QuestionArrow :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Rectangle :: Int -> Int -> Int -> Int -> Rectangle
+ Graphics.UI.Sifflet.LittleGtk: Requisition :: Int -> Int -> Requisition
+ Graphics.UI.Sifflet.LittleGtk: ResponseAccept :: ResponseId
+ Graphics.UI.Sifflet.LittleGtk: ResponseApply :: ResponseId
+ Graphics.UI.Sifflet.LittleGtk: ResponseCancel :: ResponseId
+ Graphics.UI.Sifflet.LittleGtk: ResponseClose :: ResponseId
+ Graphics.UI.Sifflet.LittleGtk: ResponseDeleteEvent :: ResponseId
+ Graphics.UI.Sifflet.LittleGtk: ResponseHelp :: ResponseId
+ Graphics.UI.Sifflet.LittleGtk: ResponseNo :: ResponseId
+ Graphics.UI.Sifflet.LittleGtk: ResponseNone :: ResponseId
+ Graphics.UI.Sifflet.LittleGtk: ResponseOk :: ResponseId
+ Graphics.UI.Sifflet.LittleGtk: ResponseReject :: ResponseId
+ Graphics.UI.Sifflet.LittleGtk: ResponseUser :: Int -> ResponseId
+ Graphics.UI.Sifflet.LittleGtk: ResponseYes :: ResponseId
+ Graphics.UI.Sifflet.LittleGtk: RightPtr :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: RightSide :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: RightTee :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Rightbutton :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: RtlLogo :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Sailboat :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: SbDownArrow :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: SbHDoubleArrow :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: SbLeftArrow :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: SbRightArrow :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: SbUpArrow :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: SbVDoubleArrow :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: ScrollMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: Shuttle :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Sizing :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Spider :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Spraycan :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Star :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: StructureMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: SubstructureMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: Target :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Tcross :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: TopLeftArrow :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: TopLeftCorner :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: TopRightCorner :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: TopSide :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: TopTee :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Trek :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: UlAngle :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Umbrella :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: UrAngle :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: VisibilityNotifyMask :: EventMask
+ Graphics.UI.Sifflet.LittleGtk: Watch :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: WinPosCenter :: WindowPosition
+ Graphics.UI.Sifflet.LittleGtk: WinPosCenterAlways :: WindowPosition
+ Graphics.UI.Sifflet.LittleGtk: WinPosCenterOnParent :: WindowPosition
+ Graphics.UI.Sifflet.LittleGtk: WinPosMouse :: WindowPosition
+ Graphics.UI.Sifflet.LittleGtk: WinPosNone :: WindowPosition
+ Graphics.UI.Sifflet.LittleGtk: XCursor :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: Xterm :: CursorType
+ Graphics.UI.Sifflet.LittleGtk: adjustmentNew :: Double -> Double -> Double -> Double -> Double -> Double -> IO Adjustment
+ Graphics.UI.Sifflet.LittleGtk: boxPackEnd :: (BoxClass self, WidgetClass child) => self -> child -> Packing -> Int -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: boxPackStart :: (BoxClass self, WidgetClass child) => self -> child -> Packing -> Int -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: boxPackStartDefaults :: (BoxClass self, WidgetClass widget) => self -> widget -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: buttonNewWithLabel :: String -> IO Button
+ Graphics.UI.Sifflet.LittleGtk: buttonPressEvent :: WidgetClass self => Signal self (EventM EButton Bool)
+ Graphics.UI.Sifflet.LittleGtk: castToContainer :: GObjectClass obj => obj -> Container
+ Graphics.UI.Sifflet.LittleGtk: class WidgetClass o => ContainerClass o
+ Graphics.UI.Sifflet.LittleGtk: class ObjectClass o => WidgetClass o
+ Graphics.UI.Sifflet.LittleGtk: containerAdd :: (ContainerClass self, WidgetClass widget) => self -> widget -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: containerChild :: (ContainerClass self, WidgetClass widget) => WriteAttr self widget
+ Graphics.UI.Sifflet.LittleGtk: containerForeach :: ContainerClass self => self -> ContainerForeachCB -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: containerGetChildren :: ContainerClass self => self -> IO [Widget]
+ Graphics.UI.Sifflet.LittleGtk: customStoreSetColumn :: TypedTreeModelClass model => model row -> ColumnId row ty -> (row -> ty) -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: data AttrOp o :: * -> *
+ Graphics.UI.Sifflet.LittleGtk: data Button :: *
+ Graphics.UI.Sifflet.LittleGtk: data ButtonsType :: *
+ Graphics.UI.Sifflet.LittleGtk: data CursorType :: *
+ Graphics.UI.Sifflet.LittleGtk: data Dialog :: *
+ Graphics.UI.Sifflet.LittleGtk: data DrawWindow :: *
+ Graphics.UI.Sifflet.LittleGtk: data Entry :: *
+ Graphics.UI.Sifflet.LittleGtk: data EventMask :: *
+ Graphics.UI.Sifflet.LittleGtk: data Expander :: *
+ Graphics.UI.Sifflet.LittleGtk: data FileChooserAction :: *
+ Graphics.UI.Sifflet.LittleGtk: data HBox :: *
+ Graphics.UI.Sifflet.LittleGtk: data Label :: *
+ Graphics.UI.Sifflet.LittleGtk: data Layout :: *
+ Graphics.UI.Sifflet.LittleGtk: data MessageType :: *
+ Graphics.UI.Sifflet.LittleGtk: data Packing :: *
+ Graphics.UI.Sifflet.LittleGtk: data PolicyType :: *
+ Graphics.UI.Sifflet.LittleGtk: data Rectangle :: *
+ Graphics.UI.Sifflet.LittleGtk: data Requisition :: *
+ Graphics.UI.Sifflet.LittleGtk: data ResponseId :: *
+ Graphics.UI.Sifflet.LittleGtk: data ScrolledWindow :: *
+ Graphics.UI.Sifflet.LittleGtk: data Statusbar :: *
+ Graphics.UI.Sifflet.LittleGtk: data Table :: *
+ Graphics.UI.Sifflet.LittleGtk: data VBox :: *
+ Graphics.UI.Sifflet.LittleGtk: data Window :: *
+ Graphics.UI.Sifflet.LittleGtk: data WindowPosition :: *
+ Graphics.UI.Sifflet.LittleGtk: dialogAddButton :: DialogClass self => self -> String -> ResponseId -> IO Button
+ Graphics.UI.Sifflet.LittleGtk: dialogGetActionArea :: DialogClass dc => dc -> IO HBox
+ Graphics.UI.Sifflet.LittleGtk: dialogGetUpper :: DialogClass dc => dc -> IO VBox
+ Graphics.UI.Sifflet.LittleGtk: dialogNew :: IO Dialog
+ Graphics.UI.Sifflet.LittleGtk: dialogRun :: DialogClass self => self -> IO ResponseId
+ Graphics.UI.Sifflet.LittleGtk: dialogSetDefaultResponse :: DialogClass self => self -> ResponseId -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: drawWindowInvalidateRect :: DrawWindowClass self => self -> Rectangle -> Bool -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: entryCompletionInsertPrefix :: EntryCompletion -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: entryCompletionModel :: TreeModelClass model => ReadWriteAttr EntryCompletion (Maybe TreeModel) (Maybe model)
+ Graphics.UI.Sifflet.LittleGtk: entryCompletionNew :: IO EntryCompletion
+ Graphics.UI.Sifflet.LittleGtk: entryCompletionSetTextColumn :: EntryCompletion -> ColumnId row String -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: entryGetCompletion :: EntryClass self => self -> IO EntryCompletion
+ Graphics.UI.Sifflet.LittleGtk: entryGetText :: EntryClass self => self -> IO String
+ Graphics.UI.Sifflet.LittleGtk: entryNew :: IO Entry
+ Graphics.UI.Sifflet.LittleGtk: entrySetCompletion :: EntryClass self => self -> EntryCompletion -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: entrySetText :: EntryClass self => self -> String -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: eventBoxNew :: IO EventBox
+ Graphics.UI.Sifflet.LittleGtk: expanderNew :: String -> IO Expander
+ Graphics.UI.Sifflet.LittleGtk: expanderSetExpanded :: Expander -> Bool -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: exposeEvent :: WidgetClass self => Signal self (EventM EExpose Bool)
+ Graphics.UI.Sifflet.LittleGtk: fileChooserDialogNew :: Maybe String -> Maybe Window -> FileChooserAction -> [(String, ResponseId)] -> IO FileChooserDialog
+ Graphics.UI.Sifflet.LittleGtk: fileChooserGetFilename :: FileChooserClass self => self -> IO (Maybe FilePath)
+ Graphics.UI.Sifflet.LittleGtk: frameNew :: IO Frame
+ Graphics.UI.Sifflet.LittleGtk: frameSetLabel :: FrameClass self => self -> String -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: get :: o -> ReadWriteAttr o a b -> IO a
+ Graphics.UI.Sifflet.LittleGtk: grabAdd :: WidgetClass wd => wd -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: grabRemove :: WidgetClass w => w -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: hBoxNew :: Bool -> Int -> IO HBox
+ Graphics.UI.Sifflet.LittleGtk: initGUI :: IO [String]
+ Graphics.UI.Sifflet.LittleGtk: keyPressEvent :: WidgetClass self => Signal self (EventM EKey Bool)
+ Graphics.UI.Sifflet.LittleGtk: labelNew :: Maybe String -> IO Label
+ Graphics.UI.Sifflet.LittleGtk: labelSetText :: LabelClass self => self -> String -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: layoutGetDrawWindow :: Layout -> IO DrawWindow
+ Graphics.UI.Sifflet.LittleGtk: layoutNew :: Maybe Adjustment -> Maybe Adjustment -> IO Layout
+ Graphics.UI.Sifflet.LittleGtk: layoutPut :: (LayoutClass self, WidgetClass childWidget) => self -> childWidget -> Int -> Int -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: layoutSetSize :: LayoutClass self => self -> Int -> Int -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: listStoreNew :: [a] -> IO (ListStore a)
+ Graphics.UI.Sifflet.LittleGtk: mainGUI :: IO ()
+ Graphics.UI.Sifflet.LittleGtk: mainQuit :: IO ()
+ Graphics.UI.Sifflet.LittleGtk: makeColumnIdString :: Int -> ColumnId row String
+ Graphics.UI.Sifflet.LittleGtk: menuPopup :: MenuClass self => self -> Maybe (MouseButton, TimeStamp) -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: messageDialogNew :: Maybe Window -> [DialogFlags] -> MessageType -> ButtonsType -> String -> IO MessageDialog
+ Graphics.UI.Sifflet.LittleGtk: on :: object -> Signal object callback -> callback -> IO (ConnectId object)
+ Graphics.UI.Sifflet.LittleGtk: onDestroy :: WidgetClass w => w -> IO () -> IO (ConnectId w)
+ Graphics.UI.Sifflet.LittleGtk: onSizeRequest :: WidgetClass w => w -> IO Requisition -> IO (ConnectId w)
+ Graphics.UI.Sifflet.LittleGtk: renderWithDrawable :: DrawableClass drawable => drawable -> Render a -> IO a
+ Graphics.UI.Sifflet.LittleGtk: scrolledWindowNew :: Maybe Adjustment -> Maybe Adjustment -> IO ScrolledWindow
+ Graphics.UI.Sifflet.LittleGtk: scrolledWindowSetPolicy :: ScrolledWindowClass self => self -> PolicyType -> PolicyType -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: set :: o -> [AttrOp o] -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: statusbarGetContextId :: StatusbarClass self => self -> String -> IO ContextId
+ Graphics.UI.Sifflet.LittleGtk: statusbarNew :: IO Statusbar
+ Graphics.UI.Sifflet.LittleGtk: statusbarPop :: StatusbarClass self => self -> ContextId -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: statusbarPush :: StatusbarClass self => self -> ContextId -> String -> IO MessageId
+ Graphics.UI.Sifflet.LittleGtk: tableAttachDefaults :: (TableClass self, WidgetClass widget) => self -> widget -> Int -> Int -> Int -> Int -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: tableNew :: Int -> Int -> Bool -> IO Table
+ Graphics.UI.Sifflet.LittleGtk: toDialog :: DialogClass o => o -> Dialog
+ Graphics.UI.Sifflet.LittleGtk: type GtkFrame = Frame
+ Graphics.UI.Sifflet.LittleGtk: type GtkLayout = Layout
+ Graphics.UI.Sifflet.LittleGtk: vBoxNew :: Bool -> Int -> IO VBox
+ Graphics.UI.Sifflet.LittleGtk: widgetAddEvents :: WidgetClass self => self -> [EventMask] -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: widgetClassPath :: WidgetClass self => self -> IO (Int, String, String)
+ Graphics.UI.Sifflet.LittleGtk: widgetDestroy :: WidgetClass self => self -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: widgetGrabFocus :: WidgetClass self => self -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: widgetSetCanFocus :: WidgetClass self => self -> Bool -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: widgetSetDoubleBuffered :: WidgetClass self => self -> Bool -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: widgetSetName :: WidgetClass self => self -> String -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: widgetSetSizeRequest :: WidgetClass self => self -> Int -> Int -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: widgetShow :: WidgetClass self => self -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: widgetShowAll :: WidgetClass self => self -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: widgetSizeRequest :: WidgetClass self => self -> IO Requisition
+ Graphics.UI.Sifflet.LittleGtk: widgetVisible :: WidgetClass self => Attr self Bool
+ Graphics.UI.Sifflet.LittleGtk: windowDeletable :: WindowClass self => Attr self Bool
+ Graphics.UI.Sifflet.LittleGtk: windowMove :: WindowClass self => self -> Int -> Int -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: windowNew :: IO Window
+ Graphics.UI.Sifflet.LittleGtk: windowPresent :: WindowClass self => self -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: windowSetDefaultSize :: WindowClass self => self -> Int -> Int -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: windowSetPosition :: WindowClass self => self -> WindowPosition -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: windowSetTitle :: WindowClass self => self -> String -> IO ()
+ Graphics.UI.Sifflet.LittleGtk: windowTitle :: WindowClass self => Attr self String
+ Graphics.UI.Sifflet.RPanel: data RPanel
+ Graphics.UI.Sifflet.RPanel: newRPanel :: String -> Int -> Int -> Int -> IO RPanel
+ Graphics.UI.Sifflet.RPanel: rpanelAddRows :: WidgetClass widget => RPanel -> [[(String, widget)]] -> IO RPanel
+ Graphics.UI.Sifflet.RPanel: rpanelAddWidget :: WidgetClass widget => RPanel -> String -> widget -> IO RPanel
+ Graphics.UI.Sifflet.RPanel: rpanelAddWidgets :: WidgetClass widget => RPanel -> [(String, widget)] -> IO RPanel
+ Graphics.UI.Sifflet.RPanel: rpanelContent :: RPanel -> [[String]]
+ Graphics.UI.Sifflet.RPanel: rpanelId :: RPanel -> String
+ Graphics.UI.Sifflet.RPanel: rpanelNewRow :: RPanel -> IO RPanel
+ Graphics.UI.Sifflet.RPanel: rpanelRoot :: RPanel -> GtkFrame
+ Graphics.UI.Sifflet.Tool: ToolArg :: String -> Int -> ToolId
+ Graphics.UI.Sifflet.Tool: ToolConnect :: ToolId
+ Graphics.UI.Sifflet.Tool: ToolDelete :: ToolId
+ Graphics.UI.Sifflet.Tool: ToolDisconnect :: ToolId
+ Graphics.UI.Sifflet.Tool: ToolFunction :: String -> ToolId
+ Graphics.UI.Sifflet.Tool: ToolIf :: ToolId
+ Graphics.UI.Sifflet.Tool: ToolLiteral :: Expr -> ToolId
+ Graphics.UI.Sifflet.Tool: ToolMove :: ToolId
+ Graphics.UI.Sifflet.Tool: checkMods :: [Modifier] -> [Modifier] -> Bool
+ Graphics.UI.Sifflet.Tool: clearFrame :: WinId -> CanvFrame -> VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Tool: closeFrame :: VPUI -> WinId -> CanvFrame -> IO VPUI
+ Graphics.UI.Sifflet.Tool: data ToolId
+ Graphics.UI.Sifflet.Tool: dumpFrame :: VPUI -> WinId -> CanvFrame -> IO ()
+ Graphics.UI.Sifflet.Tool: dumpGraph :: VPUI -> WinId -> IO ()
+ Graphics.UI.Sifflet.Tool: dumpWorkWin :: VPUI -> WinId -> IO ()
+ Graphics.UI.Sifflet.Tool: functionArgToolSpecs :: Function -> [ArgSpec]
+ Graphics.UI.Sifflet.Tool: functionTool :: String -> Tool
+ Graphics.UI.Sifflet.Tool: functionToolsFromLists :: [[String]] -> [[Tool]]
+ Graphics.UI.Sifflet.Tool: instance Eq ToolId
+ Graphics.UI.Sifflet.Tool: instance Show ToolId
+ Graphics.UI.Sifflet.Tool: makeConnectTool :: Tool
+ Graphics.UI.Sifflet.Tool: makeCopyTool :: Tool
+ Graphics.UI.Sifflet.Tool: makeDeleteTool :: Tool
+ Graphics.UI.Sifflet.Tool: makeDisconnectTool :: Tool
+ Graphics.UI.Sifflet.Tool: makeIfTool :: Tool
+ Graphics.UI.Sifflet.Tool: makeMoveTool :: Tool
+ Graphics.UI.Sifflet.Tool: showFunctionEntry :: WinId -> CBMgr -> VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Tool: showLiteralEntry :: WinId -> CBMgr -> VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Tool: vpuiAddFrame :: VPUI -> WinId -> Functoid -> Maybe [Value] -> FrameType -> Env -> Double -> Double -> Double -> Maybe Node -> IO VPUI
+ Graphics.UI.Sifflet.Tool: vpuiSetTool :: ToolId -> WinId -> VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Tool: vpuiWindowSetTool :: Tool -> VPUIWindow -> IO VPUIWindow
+ Graphics.UI.Sifflet.Tool: vwAddFrame :: VPUIWindow -> Functoid -> Maybe [Value] -> FrameType -> Env -> Double -> Double -> Double -> Maybe Node -> IO VPUIWindow
+ Graphics.UI.Sifflet.Tool: wsPopStatusbar :: Workspace -> IO ()
+ Graphics.UI.Sifflet.Tool: wsPushStatusbar :: Workspace -> String -> IO ()
+ Graphics.UI.Sifflet.Types: Dragging :: Node -> Position -> Dragging
+ Graphics.UI.Sifflet.Types: FunctionPadWindow :: Window -> [(String, RPanel)] -> VPUIWindow
+ Graphics.UI.Sifflet.Types: SelectionInlet :: Node -> WEdge -> Selection
+ Graphics.UI.Sifflet.Types: SelectionNode :: Node -> Selection
+ Graphics.UI.Sifflet.Types: SelectionOutlet :: Node -> WEdge -> Selection
+ Graphics.UI.Sifflet.Types: TCCallFrame :: CanvFrame -> ToolContext
+ Graphics.UI.Sifflet.Types: TCEditFrame :: CanvFrame -> ToolContext
+ Graphics.UI.Sifflet.Types: TCExprNode :: ToolContext
+ Graphics.UI.Sifflet.Types: TCWorkspace :: ToolContext
+ Graphics.UI.Sifflet.Types: Tool :: String -> (VCanvas -> IO VCanvas) -> ToolOp -> Tool
+ Graphics.UI.Sifflet.Types: Toolbox :: GtkFrame -> VBox -> Toolbox
+ Graphics.UI.Sifflet.Types: VCanvas :: GtkLayout -> Style -> WGraph -> [CanvFrame] -> Size -> (Double, Double) -> Maybe Tool -> Maybe Node -> Maybe Selection -> Maybe Dragging -> VCanvas
+ Graphics.UI.Sifflet.Types: VPToolkit :: String -> Int -> [[Tool]] -> VPToolkit
+ Graphics.UI.Sifflet.Types: VPUI :: Map WinId VPUIWindow -> [(String, VPToolkit)] -> Maybe FilePath -> Style -> Env -> Env -> Env -> Bool -> VPUI
+ Graphics.UI.Sifflet.Types: VPUIWorkWin :: Workspace -> Window -> VPUIWindow
+ Graphics.UI.Sifflet.Types: Workspace :: VBox -> VCanvas -> HBox -> Statusbar -> [ArgSpec] -> Workspace
+ Graphics.UI.Sifflet.Types: data Dragging
+ Graphics.UI.Sifflet.Types: data Selection
+ Graphics.UI.Sifflet.Types: data Tool
+ Graphics.UI.Sifflet.Types: data ToolContext
+ Graphics.UI.Sifflet.Types: data Toolbox
+ Graphics.UI.Sifflet.Types: data VCanvas
+ Graphics.UI.Sifflet.Types: data VPToolkit
+ Graphics.UI.Sifflet.Types: data VPUI
+ Graphics.UI.Sifflet.Types: data VPUIWindow
+ Graphics.UI.Sifflet.Types: data Workspace
+ Graphics.UI.Sifflet.Types: draggingNode :: Dragging -> Node
+ Graphics.UI.Sifflet.Types: draggingPosition :: Dragging -> Position
+ Graphics.UI.Sifflet.Types: instance Eq Dragging
+ Graphics.UI.Sifflet.Types: instance Eq Selection
+ Graphics.UI.Sifflet.Types: instance Read Dragging
+ Graphics.UI.Sifflet.Types: instance Read Selection
+ Graphics.UI.Sifflet.Types: instance Show Dragging
+ Graphics.UI.Sifflet.Types: instance Show Selection
+ Graphics.UI.Sifflet.Types: selInEdge :: Selection -> WEdge
+ Graphics.UI.Sifflet.Types: selNode :: Selection -> Node
+ Graphics.UI.Sifflet.Types: selOutEdge :: Selection -> WEdge
+ Graphics.UI.Sifflet.Types: toToolOpVW :: CanvasToolOp -> ToolOp
+ Graphics.UI.Sifflet.Types: toolActivated :: Tool -> VCanvas -> IO VCanvas
+ Graphics.UI.Sifflet.Types: toolName :: Tool -> String
+ Graphics.UI.Sifflet.Types: toolOp :: Tool -> ToolOp
+ Graphics.UI.Sifflet.Types: toolboxFrame :: Toolbox -> GtkFrame
+ Graphics.UI.Sifflet.Types: toolboxVBox :: Toolbox -> VBox
+ Graphics.UI.Sifflet.Types: toolkitName :: VPToolkit -> String
+ Graphics.UI.Sifflet.Types: toolkitRows :: VPToolkit -> [[Tool]]
+ Graphics.UI.Sifflet.Types: toolkitWidth :: VPToolkit -> Int
+ Graphics.UI.Sifflet.Types: type CanvasToolOp = VCanvas -> ToolContext -> [Modifier] -> Double -> Double -> IO VCanvas
+ Graphics.UI.Sifflet.Types: type ToolOp = VPUI -> WinId -> ToolContext -> [Modifier] -> Double -> Double -> IO VPUI
+ Graphics.UI.Sifflet.Types: type WinId = String
+ Graphics.UI.Sifflet.Types: vcActive :: VCanvas -> Maybe Node
+ Graphics.UI.Sifflet.Types: vcDragging :: VCanvas -> Maybe Dragging
+ Graphics.UI.Sifflet.Types: vcFrames :: VCanvas -> [CanvFrame]
+ Graphics.UI.Sifflet.Types: vcGraph :: VCanvas -> WGraph
+ Graphics.UI.Sifflet.Types: vcLayout :: VCanvas -> GtkLayout
+ Graphics.UI.Sifflet.Types: vcMousePos :: VCanvas -> (Double, Double)
+ Graphics.UI.Sifflet.Types: vcSelected :: VCanvas -> Maybe Selection
+ Graphics.UI.Sifflet.Types: vcSize :: VCanvas -> Size
+ Graphics.UI.Sifflet.Types: vcStyle :: VCanvas -> Style
+ Graphics.UI.Sifflet.Types: vcTool :: VCanvas -> Maybe Tool
+ Graphics.UI.Sifflet.Types: vpuiDebugging :: VPUI -> Bool
+ Graphics.UI.Sifflet.Types: vpuiFileChanged :: VPUI -> Bool
+ Graphics.UI.Sifflet.Types: vpuiFileEnv :: VPUI -> Env
+ Graphics.UI.Sifflet.Types: vpuiFilePath :: VPUI -> Maybe FilePath
+ Graphics.UI.Sifflet.Types: vpuiGetWindow :: VPUI -> WinId -> VPUIWindow
+ Graphics.UI.Sifflet.Types: vpuiGlobalEnv :: VPUI -> Env
+ Graphics.UI.Sifflet.Types: vpuiInitialEnv :: VPUI -> Env
+ Graphics.UI.Sifflet.Types: vpuiInsertWindow :: VPUI -> WinId -> VPUIWindow -> VPUI
+ Graphics.UI.Sifflet.Types: vpuiModCanvas :: VPUI -> WinId -> (VCanvas -> VCanvas) -> VPUI
+ Graphics.UI.Sifflet.Types: vpuiModCanvasIO :: VPUI -> WinId -> (VCanvas -> IO VCanvas) -> IO VPUI
+ Graphics.UI.Sifflet.Types: vpuiRemoveVPUIWindow :: WinId -> VPUI -> VPUI
+ Graphics.UI.Sifflet.Types: vpuiReplaceWindow :: VPUI -> WinId -> VPUIWindow -> VPUI
+ Graphics.UI.Sifflet.Types: vpuiStyle :: VPUI -> Style
+ Graphics.UI.Sifflet.Types: vpuiToolkits :: VPUI -> [(String, VPToolkit)]
+ Graphics.UI.Sifflet.Types: vpuiTryGetWindow :: VPUI -> WinId -> Maybe VPUIWindow
+ Graphics.UI.Sifflet.Types: vpuiUpdateWindow :: VPUI -> WinId -> (VPUIWindow -> VPUIWindow) -> VPUI
+ Graphics.UI.Sifflet.Types: vpuiUpdateWindowIO :: WinId -> (VPUIWindow -> IO VPUIWindow) -> VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Types: vpuiUserEnvAList :: VPUI -> [(String, Value)]
+ Graphics.UI.Sifflet.Types: vpuiWindowGetCanvas :: VPUIWindow -> VCanvas
+ Graphics.UI.Sifflet.Types: vpuiWindowLookupCanvas :: VPUIWindow -> Maybe VCanvas
+ Graphics.UI.Sifflet.Types: vpuiWindowModCanvas :: VPUIWindow -> (VCanvas -> VCanvas) -> VPUIWindow
+ Graphics.UI.Sifflet.Types: vpuiWindowModCanvasIO :: VPUIWindow -> (VCanvas -> IO VCanvas) -> IO VPUIWindow
+ Graphics.UI.Sifflet.Types: vpuiWindowSetCanvas :: VPUIWindow -> VCanvas -> VPUIWindow
+ Graphics.UI.Sifflet.Types: vpuiWindowWindow :: VPUIWindow -> Window
+ Graphics.UI.Sifflet.Types: vpuiWindows :: VPUI -> Map WinId VPUIWindow
+ Graphics.UI.Sifflet.Types: wsArgToolSpecs :: Workspace -> [ArgSpec]
+ Graphics.UI.Sifflet.Types: wsButtonBar :: Workspace -> HBox
+ Graphics.UI.Sifflet.Types: wsCanvas :: Workspace -> VCanvas
+ Graphics.UI.Sifflet.Types: wsRootWidget :: Workspace -> VBox
+ Graphics.UI.Sifflet.Types: wsStatusbar :: Workspace -> Statusbar
+ Graphics.UI.Sifflet.Window: fedWindowTitle :: String -> WinId
+ Graphics.UI.Sifflet.Window: getOrCreateFunctionPadWindow :: CBMgr -> Bool -> VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Window: getOrCreateWindow :: WinId -> CBMgr -> (VPUI -> Window -> IO VPUIWindow) -> (VPUI -> WinId -> CBMgr -> IO ()) -> Bool -> VPUI -> IO (VPUI, VPUIWindow, Bool)
+ Graphics.UI.Sifflet.Window: keyBindingsHelpText :: String
+ Graphics.UI.Sifflet.Window: newFunctionDialog :: WinId -> CBMgr -> VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Window: newWindowTitled :: String -> IO Window
+ Graphics.UI.Sifflet.Window: openFilePath :: CBMgr -> FilePath -> VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Window: setWSCanvasCallbacks :: VPUI -> WinId -> CBMgr -> IO ()
+ Graphics.UI.Sifflet.Window: showFedWin :: CBMgr -> String -> [String] -> VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Window: showFunctionPadWindow :: CBMgr -> VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Window: showWindow :: WinId -> CBMgr -> (VPUI -> Window -> IO VPUIWindow) -> (VPUI -> WinId -> CBMgr -> IO ()) -> VPUI -> IO (VPUI, VPUIWindow, Bool)
+ Graphics.UI.Sifflet.Window: showWorkWin :: VPUI -> WinId -> CBMgr -> IO VPUI
+ Graphics.UI.Sifflet.Window: showWorkspaceWindow :: WinId -> CBMgr -> Maybe Function -> VPUI -> IO (VPUI, VPUIWindow, Bool)
+ Graphics.UI.Sifflet.Workspace: addFedWinButtons :: CBMgr -> WinId -> VPUI -> IO ()
+ Graphics.UI.Sifflet.Workspace: baseFunctionsRows :: [[String]]
+ Graphics.UI.Sifflet.Workspace: defineFunction :: WinId -> CanvFrame -> VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Workspace: forallWindowsIO :: (VPUIWindow -> IO VPUIWindow) -> VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Workspace: openNode :: VPUIWindow -> Node -> IO VPUIWindow
+ Graphics.UI.Sifflet.Workspace: removeWindow :: VPUI -> Bool -> WinId -> IO VPUI
+ Graphics.UI.Sifflet.Workspace: vpuiNew :: Style -> Env -> Bool -> IO VPUI
+ Graphics.UI.Sifflet.Workspace: vpuiQuit :: VPUI -> IO VPUI
+ Graphics.UI.Sifflet.Workspace: workspaceId :: String
+ Graphics.UI.Sifflet.Workspace: workspaceNewDefault :: Style -> (VBox -> IO ()) -> IO Workspace
+ Graphics.UI.Sifflet.Workspace: workspaceNewEditing :: Style -> Env -> Function -> IO Workspace
+ Language.Sifflet.Examples: eFact :: Expr -> Expr
+ Language.Sifflet.Examples: eFoo :: Expr -> Expr -> Expr
+ Language.Sifflet.Examples: eMax :: Expr -> Expr -> Expr
+ Language.Sifflet.Examples: exampleEnv :: Env
+ Language.Sifflet.Examples: exampleFunctionNames :: [String]
+ Language.Sifflet.Examples: exampleFunctions :: [Function]
+ Language.Sifflet.Examples: foo :: Function
+ Language.Sifflet.Examples: getExampleFunction :: String -> Function
+ Language.Sifflet.Export.Exporter: applyFirstMatch :: [Expr -> Expr] -> Expr -> Expr
+ Language.Sifflet.Export.Exporter: commonRuleAssocRight :: Expr -> Expr
+ Language.Sifflet.Export.Exporter: commonRuleAtomic :: Expr -> Expr
+ Language.Sifflet.Export.Exporter: commonRuleFuncOp :: Expr -> Expr
+ Language.Sifflet.Export.Exporter: commonRuleHigherPrec :: Expr -> Expr
+ Language.Sifflet.Export.Exporter: commonRuleLeftToRight :: Expr -> Expr
+ Language.Sifflet.Export.Exporter: commonRulesForSimplifyingExprs :: [Expr -> Expr]
+ Language.Sifflet.Export.Exporter: findFixed :: Eq a => (a -> a) -> a -> a
+ Language.Sifflet.Export.Exporter: ruleIfRight :: Expr -> Expr
+ Language.Sifflet.Export.Exporter: ruleRightToLeft :: Expr -> Expr
+ Language.Sifflet.Export.Exporter: simplifyExpr :: [Expr -> Expr] -> Expr -> Expr
+ Language.Sifflet.Export.Exporter: type Exporter = Functions -> FilePath -> IO ()
+ Language.Sifflet.Export.Haskell: Decl :: String -> Maybe [String] -> [String] -> Expr -> Decl
+ Language.Sifflet.Export.Haskell: ExportSpec :: [String] -> ExportSpec
+ Language.Sifflet.Export.Haskell: ImportDecl :: [String] -> ImportDecl
+ Language.Sifflet.Export.Haskell: Module :: String -> Maybe ExportSpec -> ImportDecl -> [Decl] -> Module
+ Language.Sifflet.Export.Haskell: class HsPretty a where hsPrettyList pre tween post xs = pre ++ intercalate tween (map hsPretty xs) ++ post
+ Language.Sifflet.Export.Haskell: data Decl
+ Language.Sifflet.Export.Haskell: data Module
+ Language.Sifflet.Export.Haskell: declExpr :: Decl -> Expr
+ Language.Sifflet.Export.Haskell: declIdent :: Decl -> String
+ Language.Sifflet.Export.Haskell: declParams :: Decl -> [String]
+ Language.Sifflet.Export.Haskell: declType :: Decl -> Maybe [String]
+ Language.Sifflet.Export.Haskell: hsPretty :: HsPretty a => a -> String
+ Language.Sifflet.Export.Haskell: hsPrettyList :: HsPretty a => String -> String -> String -> [a] -> String
+ Language.Sifflet.Export.Haskell: instance Eq Decl
+ Language.Sifflet.Export.Haskell: instance Eq ExportSpec
+ Language.Sifflet.Export.Haskell: instance Eq ImportDecl
+ Language.Sifflet.Export.Haskell: instance Eq Module
+ Language.Sifflet.Export.Haskell: instance HsPretty Decl
+ Language.Sifflet.Export.Haskell: instance HsPretty ExportSpec
+ Language.Sifflet.Export.Haskell: instance HsPretty Expr
+ Language.Sifflet.Export.Haskell: instance HsPretty ImportDecl
+ Language.Sifflet.Export.Haskell: instance HsPretty Module
+ Language.Sifflet.Export.Haskell: instance HsPretty Operator
+ Language.Sifflet.Export.Haskell: instance HsPretty Symbol
+ Language.Sifflet.Export.Haskell: instance Show Decl
+ Language.Sifflet.Export.Haskell: instance Show ExportSpec
+ Language.Sifflet.Export.Haskell: instance Show ImportDecl
+ Language.Sifflet.Export.Haskell: instance Show Module
+ Language.Sifflet.Export.Haskell: moduleDecls :: Module -> [Decl]
+ Language.Sifflet.Export.Haskell: moduleExports :: Module -> Maybe ExportSpec
+ Language.Sifflet.Export.Haskell: moduleImports :: Module -> ImportDecl
+ Language.Sifflet.Export.Haskell: moduleName :: Module -> String
+ Language.Sifflet.Export.Haskell: newtype ExportSpec
+ Language.Sifflet.Export.Haskell: newtype ImportDecl
+ Language.Sifflet.Export.Haskell: operatorTable :: Map String Operator
+ Language.Sifflet.Export.Python: PCondS :: Expr -> PStatement -> PStatement -> PStatement
+ Language.Sifflet.Export.Python: PFun :: Symbol -> [Symbol] -> PStatement -> PStatement
+ Language.Sifflet.Export.Python: PImport :: String -> PStatement
+ Language.Sifflet.Export.Python: PModule :: [PStatement] -> PModule
+ Language.Sifflet.Export.Python: PReturn :: Expr -> PStatement
+ Language.Sifflet.Export.Python: alterParens :: (Expr -> Expr) -> PStatement -> PStatement
+ Language.Sifflet.Export.Python: char :: Char -> Expr
+ Language.Sifflet.Export.Python: class PyPretty a where pyPrettyList pre tween post xs = pre ++ intercalate tween (map pyPretty xs) ++ post
+ Language.Sifflet.Export.Python: condS :: Expr -> Expr -> Expr -> PStatement
+ Language.Sifflet.Export.Python: data PStatement
+ Language.Sifflet.Export.Python: fun :: String -> [String] -> Expr -> PStatement
+ Language.Sifflet.Export.Python: ident :: String -> Symbol
+ Language.Sifflet.Export.Python: instance Eq PModule
+ Language.Sifflet.Export.Python: instance Eq PStatement
+ Language.Sifflet.Export.Python: instance PyPretty Expr
+ Language.Sifflet.Export.Python: instance PyPretty Operator
+ Language.Sifflet.Export.Python: instance PyPretty PModule
+ Language.Sifflet.Export.Python: instance PyPretty PStatement
+ Language.Sifflet.Export.Python: instance PyPretty Symbol
+ Language.Sifflet.Export.Python: instance Show PModule
+ Language.Sifflet.Export.Python: instance Show PStatement
+ Language.Sifflet.Export.Python: newtype PModule
+ Language.Sifflet.Export.Python: operatorTable :: Map String Operator
+ Language.Sifflet.Export.Python: pyPretty :: PyPretty a => a -> String
+ Language.Sifflet.Export.Python: pyPrettyList :: PyPretty a => String -> String -> String -> [a] -> String
+ Language.Sifflet.Export.Python: ret :: Expr -> PStatement
+ Language.Sifflet.Export.Python: var :: String -> Expr
+ Language.Sifflet.Export.ToHaskell: HaskellOptions :: Int -> Int -> HaskellOptions
+ Language.Sifflet.Export.ToHaskell: data HaskellOptions
+ Language.Sifflet.Export.ToHaskell: defaultHaskellOptions :: HaskellOptions
+ Language.Sifflet.Export.ToHaskell: exportHaskell :: HaskellOptions -> Exporter
+ Language.Sifflet.Export.ToHaskell: exprToHsExpr :: Expr -> Expr
+ Language.Sifflet.Export.ToHaskell: functionToHsDecl :: Function -> Decl
+ Language.Sifflet.Export.ToHaskell: functionsToHsModule :: String -> Functions -> Module
+ Language.Sifflet.Export.ToHaskell: instance Eq HaskellOptions
+ Language.Sifflet.Export.ToHaskell: instance Show HaskellOptions
+ Language.Sifflet.Export.ToHaskell: optionsHardMaxLineWidth :: HaskellOptions -> Int
+ Language.Sifflet.Export.ToHaskell: optionsSoftMaxLineWidth :: HaskellOptions -> Int
+ Language.Sifflet.Export.ToPython: PythonOptions :: PythonOptions
+ Language.Sifflet.Export.ToPython: data PythonOptions
+ Language.Sifflet.Export.ToPython: defToPy :: FunctionDefTuple -> PStatement
+ Language.Sifflet.Export.ToPython: defaultPythonOptions :: PythonOptions
+ Language.Sifflet.Export.ToPython: exportPython :: PythonOptions -> Exporter
+ Language.Sifflet.Export.ToPython: exprToPExpr :: Expr -> Expr
+ Language.Sifflet.Export.ToPython: fixIdentifierChars :: String -> String
+ Language.Sifflet.Export.ToPython: functionToPyDef :: Function -> PStatement
+ Language.Sifflet.Export.ToPython: functionsToPrettyPy :: Functions -> String
+ Language.Sifflet.Export.ToPython: functionsToPyModule :: Functions -> PModule
+ Language.Sifflet.Export.ToPython: instance Eq PythonOptions
+ Language.Sifflet.Export.ToPython: instance Show PythonOptions
+ Language.Sifflet.Export.ToPython: nameToPython :: String -> Either Operator String
+ Language.Sifflet.Export.ToScheme: SAtom :: Atom -> SExpr
+ Language.Sifflet.Export.ToScheme: SBool :: Bool -> Atom
+ Language.Sifflet.Export.ToScheme: SChar :: Char -> Atom
+ Language.Sifflet.Export.ToScheme: SFloat :: Double -> Atom
+ Language.Sifflet.Export.ToScheme: SFunction :: Function -> Atom
+ Language.Sifflet.Export.ToScheme: SInt :: Integer -> Atom
+ Language.Sifflet.Export.ToScheme: SList :: [SExpr] -> SExpr
+ Language.Sifflet.Export.ToScheme: SString :: String -> Atom
+ Language.Sifflet.Export.ToScheme: SSymbol :: String -> Atom
+ Language.Sifflet.Export.ToScheme: SchemeOptions :: Bool -> SchemeOptions
+ Language.Sifflet.Export.ToScheme: data Atom
+ Language.Sifflet.Export.ToScheme: data SExpr
+ Language.Sifflet.Export.ToScheme: data SchemeOptions
+ Language.Sifflet.Export.ToScheme: defToSExpr :: SchemeOptions -> FunctionDefTuple -> SExpr
+ Language.Sifflet.Export.ToScheme: defaultSchemeOptions :: SchemeOptions
+ Language.Sifflet.Export.ToScheme: defineWithLambda :: SchemeOptions -> Bool
+ Language.Sifflet.Export.ToScheme: exportScheme :: SchemeOptions -> Exporter
+ Language.Sifflet.Export.ToScheme: exprToSExpr :: Expr -> SExpr
+ Language.Sifflet.Export.ToScheme: exprToScheme :: Expr -> String
+ Language.Sifflet.Export.ToScheme: exprToSchemePretty :: Expr -> String
+ Language.Sifflet.Export.ToScheme: exprToSchemeRepr :: Expr -> String
+ Language.Sifflet.Export.ToScheme: functionNameToSchemeName :: String -> String
+ Language.Sifflet.Export.ToScheme: functionsToPrettyScheme :: SchemeOptions -> Functions -> String
+ Language.Sifflet.Export.ToScheme: functionsToSExprs :: SchemeOptions -> Functions -> [SExpr]
+ Language.Sifflet.Export.ToScheme: inl :: Int -> String
+ Language.Sifflet.Export.ToScheme: instance Eq Atom
+ Language.Sifflet.Export.ToScheme: instance Eq SExpr
+ Language.Sifflet.Export.ToScheme: instance Eq SchemeOptions
+ Language.Sifflet.Export.ToScheme: instance Pretty SExpr
+ Language.Sifflet.Export.ToScheme: instance Repr SExpr
+ Language.Sifflet.Export.ToScheme: instance Show Atom
+ Language.Sifflet.Export.ToScheme: instance Show SExpr
+ Language.Sifflet.Export.ToScheme: instance Show SchemeOptions
+ Language.Sifflet.Export.ToScheme: sepLines2 :: [String] -> String
+ Language.Sifflet.Export.ToScheme: type Exporter = Functions -> FilePath -> IO ()
+ Language.Sifflet.Export.ToScheme: type Indent = Int
+ Language.Sifflet.Export.ToScheme: valueToSExpr :: Value -> SExpr
+ Language.Sifflet.Expr: ArgSpec :: String -> Int -> ArgSpec
+ Language.Sifflet.Expr: Compound :: [String] -> Expr -> FunctionImpl
+ Language.Sifflet.Expr: EApp :: Expr -> Expr -> Expr
+ Language.Sifflet.Expr: EBool :: Bool -> Expr
+ Language.Sifflet.Expr: ECall :: Symbol -> [Expr] -> Expr
+ Language.Sifflet.Expr: EChar :: Char -> Expr
+ Language.Sifflet.Expr: EGroup :: Expr -> Expr
+ Language.Sifflet.Expr: EIf :: Expr -> Expr -> Expr -> Expr
+ Language.Sifflet.Expr: ELambda :: Symbol -> Expr -> Expr
+ Language.Sifflet.Expr: EList :: [Expr] -> Expr
+ Language.Sifflet.Expr: ENumber :: Number -> Expr
+ Language.Sifflet.Expr: EOp :: Operator -> Expr -> Expr -> Expr
+ Language.Sifflet.Expr: EString :: String -> Expr
+ Language.Sifflet.Expr: ESymbol :: Symbol -> Expr
+ Language.Sifflet.Expr: EUndefined :: Expr
+ Language.Sifflet.Expr: EvalError :: String -> EvalRes e
+ Language.Sifflet.Expr: EvalOk :: e -> EvalRes e
+ Language.Sifflet.Expr: EvalUntried :: EvalRes e
+ Language.Sifflet.Expr: Function :: (Maybe String) -> [Type] -> Type -> FunctionImpl -> Function
+ Language.Sifflet.Expr: Functions :: [Function] -> Functions
+ Language.Sifflet.Expr: GroupLtoR :: OperatorGrouping
+ Language.Sifflet.Expr: GroupNone :: OperatorGrouping
+ Language.Sifflet.Expr: GroupRtoL :: OperatorGrouping
+ Language.Sifflet.Expr: Operator :: String -> Precedence -> Bool -> OperatorGrouping -> Operator
+ Language.Sifflet.Expr: Primitive :: ([Value] -> EvalResult) -> FunctionImpl
+ Language.Sifflet.Expr: Symbol :: String -> Symbol
+ Language.Sifflet.Expr: TypeCons :: TypeConsName -> [Type] -> Type
+ Language.Sifflet.Expr: TypeVar :: TypeVarName -> Type
+ Language.Sifflet.Expr: VBool :: OBool -> Value
+ Language.Sifflet.Expr: VChar :: OChar -> Value
+ Language.Sifflet.Expr: VFun :: Function -> Value
+ Language.Sifflet.Expr: VList :: [Value] -> Value
+ Language.Sifflet.Expr: VNumber :: Number -> Value
+ Language.Sifflet.Expr: VString :: OStr -> Value
+ Language.Sifflet.Expr: appToCall :: Expr -> Expr
+ Language.Sifflet.Expr: apply :: Value -> [Value] -> Env -> Int -> EvalResult
+ Language.Sifflet.Expr: argInlets :: ArgSpec -> Int
+ Language.Sifflet.Expr: argName :: ArgSpec -> String
+ Language.Sifflet.Expr: aspecsLookup :: String -> [ArgSpec] -> Maybe Int
+ Language.Sifflet.Expr: baseEnv :: Env
+ Language.Sifflet.Expr: callToApp :: Expr -> Expr
+ Language.Sifflet.Expr: data ArgSpec
+ Language.Sifflet.Expr: data EvalRes e
+ Language.Sifflet.Expr: data Expr
+ Language.Sifflet.Expr: data Function
+ Language.Sifflet.Expr: data FunctionImpl
+ Language.Sifflet.Expr: data Operator
+ Language.Sifflet.Expr: data OperatorGrouping
+ Language.Sifflet.Expr: data Type
+ Language.Sifflet.Expr: data Value
+ Language.Sifflet.Expr: eAdd1, eSub1 :: Expr -> Expr
+ Language.Sifflet.Expr: eBool :: Bool -> Expr
+ Language.Sifflet.Expr: eCall :: String -> [Expr] -> Expr
+ Language.Sifflet.Expr: eChar :: OChar -> Expr
+ Language.Sifflet.Expr: eEq, eLe, eLt, eGe, eGt, eNe :: Expr -> Expr -> Expr
+ Language.Sifflet.Expr: eFalse, eTrue :: Expr
+ Language.Sifflet.Expr: eFloat :: Double -> Expr
+ Language.Sifflet.Expr: eIf :: Expr -> Expr -> Expr -> Expr
+ Language.Sifflet.Expr: eInt :: Integer -> Expr
+ Language.Sifflet.Expr: eList :: [Expr] -> Expr
+ Language.Sifflet.Expr: eMinus, eMod, eDiv :: Expr -> Expr -> Expr
+ Language.Sifflet.Expr: ePlus :: Expr -> Expr -> Expr
+ Language.Sifflet.Expr: eString :: OStr -> Expr
+ Language.Sifflet.Expr: eSymbol, eSym :: String -> Expr
+ Language.Sifflet.Expr: eTimes :: Expr -> Expr -> Expr
+ Language.Sifflet.Expr: eZerop, eNegativep, ePositivep :: Expr -> Expr
+ Language.Sifflet.Expr: emptyEnv :: Env
+ Language.Sifflet.Expr: envFunctionSymbols :: Env -> [String]
+ Language.Sifflet.Expr: envFunctions :: Env -> Functions
+ Language.Sifflet.Expr: envGet :: Env -> String -> Value
+ Language.Sifflet.Expr: envGetFunction :: Env -> String -> Function
+ Language.Sifflet.Expr: envIns :: Env -> String -> Value -> Env
+ Language.Sifflet.Expr: envInsertL :: Env -> [String] -> [Value] -> Env
+ Language.Sifflet.Expr: envLookup :: Env -> String -> Maybe Value
+ Language.Sifflet.Expr: envLookupFunction :: Env -> String -> Maybe Function
+ Language.Sifflet.Expr: envPop :: Env -> Env
+ Language.Sifflet.Expr: envSet :: Env -> String -> Value -> Env
+ Language.Sifflet.Expr: envSymbols :: Env -> [String]
+ Language.Sifflet.Expr: eval :: Expr -> Env -> EvalResult
+ Language.Sifflet.Expr: evalWithLimit :: Expr -> Env -> Int -> EvalResult
+ Language.Sifflet.Expr: exprIsAtomic :: Expr -> Bool
+ Language.Sifflet.Expr: exprIsCompound :: Expr -> Bool
+ Language.Sifflet.Expr: exprIsLiteral :: Expr -> Bool
+ Language.Sifflet.Expr: exprSymbols :: Expr -> [Symbol]
+ Language.Sifflet.Expr: exprToValue :: Expr -> SuccFail Value
+ Language.Sifflet.Expr: exprVarNames :: Expr -> [String]
+ Language.Sifflet.Expr: extendEnv :: [String] -> [Value] -> Env -> Env
+ Language.Sifflet.Expr: functionArgNames :: Function -> [String]
+ Language.Sifflet.Expr: functionArgResultTypes :: Function -> ([Type], Type)
+ Language.Sifflet.Expr: functionArgSpecs :: Function -> [ArgSpec]
+ Language.Sifflet.Expr: functionArgTypes :: Function -> [Type]
+ Language.Sifflet.Expr: functionBody :: Function -> Expr
+ Language.Sifflet.Expr: functionFromDef :: FunctionDefTuple -> Function
+ Language.Sifflet.Expr: functionImplementation :: Function -> FunctionImpl
+ Language.Sifflet.Expr: functionNArgs :: Function -> Int
+ Language.Sifflet.Expr: functionName :: Function -> String
+ Language.Sifflet.Expr: functionResultType :: Function -> Type
+ Language.Sifflet.Expr: functionToDef :: Function -> FunctionDefTuple
+ Language.Sifflet.Expr: functionType :: Function -> Type
+ Language.Sifflet.Expr: instance Eq ArgSpec
+ Language.Sifflet.Expr: instance Eq Expr
+ Language.Sifflet.Expr: instance Eq Function
+ Language.Sifflet.Expr: instance Eq Functions
+ Language.Sifflet.Expr: instance Eq Operator
+ Language.Sifflet.Expr: instance Eq OperatorGrouping
+ Language.Sifflet.Expr: instance Eq Symbol
+ Language.Sifflet.Expr: instance Eq Type
+ Language.Sifflet.Expr: instance Eq Value
+ Language.Sifflet.Expr: instance Eq e => Eq (EvalRes e)
+ Language.Sifflet.Expr: instance Monad EvalRes
+ Language.Sifflet.Expr: instance Pretty Operator
+ Language.Sifflet.Expr: instance Pretty Symbol
+ Language.Sifflet.Expr: instance Read FunctionImpl
+ Language.Sifflet.Expr: instance Read Symbol
+ Language.Sifflet.Expr: instance Repr Expr
+ Language.Sifflet.Expr: instance Repr Function
+ Language.Sifflet.Expr: instance Repr Symbol
+ Language.Sifflet.Expr: instance Repr Value
+ Language.Sifflet.Expr: instance Show ArgSpec
+ Language.Sifflet.Expr: instance Show Expr
+ Language.Sifflet.Expr: instance Show Function
+ Language.Sifflet.Expr: instance Show FunctionImpl
+ Language.Sifflet.Expr: instance Show Functions
+ Language.Sifflet.Expr: instance Show Operator
+ Language.Sifflet.Expr: instance Show OperatorGrouping
+ Language.Sifflet.Expr: instance Show Symbol
+ Language.Sifflet.Expr: instance Show Type
+ Language.Sifflet.Expr: instance Show Value
+ Language.Sifflet.Expr: instance Show e => Show (EvalRes e)
+ Language.Sifflet.Expr: makeEnv :: [String] -> [Value] -> Env
+ Language.Sifflet.Expr: mapply :: Expr -> [Expr] -> Expr
+ Language.Sifflet.Expr: mcall :: Expr -> [Expr] -> Expr
+ Language.Sifflet.Expr: newUndefinedFunction :: String -> [String] -> Function
+ Language.Sifflet.Expr: newtype Functions
+ Language.Sifflet.Expr: newtype Symbol
+ Language.Sifflet.Expr: opAssoc :: Operator -> Bool
+ Language.Sifflet.Expr: opGrouping :: Operator -> OperatorGrouping
+ Language.Sifflet.Expr: opName :: Operator -> String
+ Language.Sifflet.Expr: opPrec :: Operator -> Precedence
+ Language.Sifflet.Expr: stackSize :: Int
+ Language.Sifflet.Expr: toLambdaExpr :: [String] -> Expr -> SuccFail Expr
+ Language.Sifflet.Expr: type Env = [EnvFrame]
+ Language.Sifflet.Expr: type EvalResult = EvalRes Value
+ Language.Sifflet.Expr: type FunctionDefTuple = (String, [String], [Type], Type, Expr)
+ Language.Sifflet.Expr: type OBool = Bool
+ Language.Sifflet.Expr: type OChar = Char
+ Language.Sifflet.Expr: type OStr = String
+ Language.Sifflet.Expr: type Precedence = Int
+ Language.Sifflet.Expr: type TypeConsName = String
+ Language.Sifflet.Expr: type TypeVarName = String
+ Language.Sifflet.Expr: typeBool, typeString, typeNum, typeChar :: Type
+ Language.Sifflet.Expr: typeFunction :: [Type] -> Type -> Type
+ Language.Sifflet.Expr: typeList :: Type -> Type
+ Language.Sifflet.Expr: valueFunction :: Value -> Function
+ Language.Sifflet.Expr: valueToLiteral :: Value -> SuccFail Expr
+ Language.Sifflet.Expr: valueToLiteral' :: Value -> Expr
+ Language.Sifflet.ExprTree: ENode :: ExprNodeLabel -> EvalResult -> ExprNode
+ Language.Sifflet.ExprTree: NBool :: Bool -> ExprNodeLabel
+ Language.Sifflet.ExprTree: NChar :: Char -> ExprNodeLabel
+ Language.Sifflet.ExprTree: NList :: [Expr] -> ExprNodeLabel
+ Language.Sifflet.ExprTree: NNumber :: Number -> ExprNodeLabel
+ Language.Sifflet.ExprTree: NString :: String -> ExprNodeLabel
+ Language.Sifflet.ExprTree: NSymbol :: Symbol -> ExprNodeLabel
+ Language.Sifflet.ExprTree: NUndefined :: ExprNodeLabel
+ Language.Sifflet.ExprTree: data ExprNode
+ Language.Sifflet.ExprTree: data ExprNodeLabel
+ Language.Sifflet.ExprTree: evalTree :: ExprTree -> Env -> ExprTree
+ Language.Sifflet.ExprTree: exprNodeIoletCounter :: Env -> [ArgSpec] -> IoletCounter ExprNode
+ Language.Sifflet.ExprTree: exprToReprTree :: Expr -> Tree String
+ Language.Sifflet.ExprTree: exprToTree :: Expr -> ExprTree
+ Language.Sifflet.ExprTree: instance Eq ExprNode
+ Language.Sifflet.ExprTree: instance Eq ExprNodeLabel
+ Language.Sifflet.ExprTree: instance Repr ExprNode
+ Language.Sifflet.ExprTree: instance Show ExprNode
+ Language.Sifflet.ExprTree: instance Show ExprNodeLabel
+ Language.Sifflet.ExprTree: treeToExpr :: ExprTree -> SuccFail Expr
+ Language.Sifflet.ExprTree: type ExprTree = Tree ExprNode
+ Language.Sifflet.ExprTree: unevalTree :: ExprTree -> ExprTree
+ Language.Sifflet.Parser: bool :: Parser Bool
+ Language.Sifflet.Parser: double :: Parser Double
+ Language.Sifflet.Parser: expr :: Parser Expr
+ Language.Sifflet.Parser: integer :: Parser Integer
+ Language.Sifflet.Parser: list :: Parser a -> Parser [a]
+ Language.Sifflet.Parser: nothingBut :: Parser a -> Parser a
+ Language.Sifflet.Parser: number :: Parser Number
+ Language.Sifflet.Parser: parseExpr :: String -> SuccFail Expr
+ Language.Sifflet.Parser: parseLiteral :: String -> SuccFail Expr
+ Language.Sifflet.Parser: parseSuccFail :: Parser a -> String -> SuccFail a
+ Language.Sifflet.Parser: parseTest :: (Stream s Identity t, Show a) => Parsec s () a -> s -> IO ()
+ Language.Sifflet.Parser: parseTypedInput2 :: (String, Type) -> SuccFail Value
+ Language.Sifflet.Parser: parseTypedInput3 :: (String, String, Type) -> SuccFail Value
+ Language.Sifflet.Parser: parseTypedInputs2 :: [String] -> [Type] -> SuccFail [Value]
+ Language.Sifflet.Parser: parseTypedInputs3 :: [String] -> [String] -> [Type] -> SuccFail [Value]
+ Language.Sifflet.Parser: parseValue :: String -> SuccFail Value
+ Language.Sifflet.Parser: qchar :: Parser Char
+ Language.Sifflet.Parser: qstring :: Parser String
+ Language.Sifflet.Parser: typedValue :: Type -> Parser Value
+ Language.Sifflet.Parser: value :: Parser Value
+ Language.Sifflet.SiffML: class ToXml a
+ Language.Sifflet.SiffML: consumeSiffMLFile :: XMLConsumer XmlTree a -> FilePath -> IO [a]
+ Language.Sifflet.SiffML: instance ToXml Expr
+ Language.Sifflet.SiffML: instance ToXml Function
+ Language.Sifflet.SiffML: instance ToXml Functions
+ Language.Sifflet.SiffML: instance ToXml Symbol
+ Language.Sifflet.SiffML: instance ToXml Type
+ Language.Sifflet.SiffML: instance ToXml Value
+ Language.Sifflet.SiffML: produceSiffMLFile :: ToXml a => a -> FilePath -> IO ()
+ Language.Sifflet.SiffML: testFromXml :: (ToXml a, Show b) => Int -> a -> XMLConsumer XmlTree b -> IO ()
+ Language.Sifflet.SiffML: toXml :: ToXml a => a -> XMLProducer
+ Language.Sifflet.SiffML: xmlToFunctions :: XMLConsumer XmlTree Functions
+ Language.Sifflet.TypeCheck: NameSupply :: TypeVarName -> Int -> NameSupply
+ Language.Sifflet.TypeCheck: TypeScheme :: [TypeVarName] -> Type -> TypeScheme
+ Language.Sifflet.TypeCheck: arrow :: Type -> Type -> Type
+ Language.Sifflet.TypeCheck: baseTypeEnv :: TypeEnv
+ Language.Sifflet.TypeCheck: data NameSupply
+ Language.Sifflet.TypeCheck: data TypeScheme
+ Language.Sifflet.TypeCheck: decideTypes :: String -> Expr -> [String] -> Env -> SuccFail ([Type], Type)
+ Language.Sifflet.TypeCheck: deltaSubst :: TypeVarName -> Type -> Subst
+ Language.Sifflet.TypeCheck: emptyTypeEnv :: TypeEnv
+ Language.Sifflet.TypeCheck: envToTypeEnv :: Env -> TypeEnv
+ Language.Sifflet.TypeCheck: extend :: Subst -> TypeVarName -> Type -> SuccFail Subst
+ Language.Sifflet.TypeCheck: fromLambdaType :: Type -> SuccFail ([Type], Type)
+ Language.Sifflet.TypeCheck: idSubst :: Subst
+ Language.Sifflet.TypeCheck: install :: TypeEnv -> TypeVarName -> TypeScheme -> TypeEnv
+ Language.Sifflet.TypeCheck: instance Eq NameSupply
+ Language.Sifflet.TypeCheck: instance Eq TypeScheme
+ Language.Sifflet.TypeCheck: instance Show NameSupply
+ Language.Sifflet.TypeCheck: instance Show TypeScheme
+ Language.Sifflet.TypeCheck: nameSupplyNext :: NameSupply -> (TypeVarName, NameSupply)
+ Language.Sifflet.TypeCheck: nameSupplyTake :: NameSupply -> Int -> ([TypeVarName], NameSupply)
+ Language.Sifflet.TypeCheck: newInstance :: NameSupply -> TypeScheme -> (Type, NameSupply)
+ Language.Sifflet.TypeCheck: newNameSupply :: TypeVarName -> NameSupply
+ Language.Sifflet.TypeCheck: schematicsTS :: TypeScheme -> [TypeVarName]
+ Language.Sifflet.TypeCheck: subst :: Subst -> Type -> Type
+ Language.Sifflet.TypeCheck: substComp :: Subst -> Subst -> Subst
+ Language.Sifflet.TypeCheck: substTE :: Subst -> TypeEnv -> TypeEnv
+ Language.Sifflet.TypeCheck: substTS :: Subst -> TypeScheme -> TypeScheme
+ Language.Sifflet.TypeCheck: tcExpr :: TypeEnv -> NameSupply -> Expr -> SuccFail (Subst, Type, NameSupply)
+ Language.Sifflet.TypeCheck: tcExprs :: TypeEnv -> NameSupply -> [Expr] -> SuccFail (Subst, [Type], NameSupply)
+ Language.Sifflet.TypeCheck: tcVar :: TypeEnv -> NameSupply -> String -> SuccFail (Subst, Type, NameSupply)
+ Language.Sifflet.TypeCheck: type Subst = TypeVarName -> Type
+ Language.Sifflet.TypeCheck: type TypeEnv = Map TypeVarName TypeScheme
+ Language.Sifflet.TypeCheck: typeCheckValues :: [String] -> [Type] -> [Value] -> SuccFail [Value]
+ Language.Sifflet.TypeCheck: typeVarsIn :: Type -> [TypeVarName]
+ Language.Sifflet.TypeCheck: unify :: Subst -> (Type, Type) -> SuccFail Subst
+ Language.Sifflet.TypeCheck: unknownsTE :: TypeEnv -> [TypeVarName]
+ Language.Sifflet.TypeCheck: unknownsTS :: TypeScheme -> [TypeVarName]
+ Language.Sifflet.TypeCheck: valueType :: Value -> SuccFail Type
+ Language.Sifflet.Util: Fail :: String -> SuccFail a
+ Language.Sifflet.Util: Succ :: a -> SuccFail a
+ Language.Sifflet.Util: adjustAList :: Eq k => k -> (v -> v) -> [(k, v)] -> [(k, v)]
+ Language.Sifflet.Util: adjustAListM :: (Eq k, Monad m) => k -> (v -> m v) -> [(k, v)] -> m [(k, v)]
+ Language.Sifflet.Util: data SuccFail a
+ Language.Sifflet.Util: errcat :: [String] -> a
+ Language.Sifflet.Util: errcats :: [String] -> a
+ Language.Sifflet.Util: fake :: String -> IO ()
+ Language.Sifflet.Util: info :: Show t => t -> IO ()
+ Language.Sifflet.Util: insertLast :: [a] -> a -> [a]
+ Language.Sifflet.Util: insertLastLast :: [[a]] -> a -> [[a]]
+ Language.Sifflet.Util: instance Eq a => Eq (SuccFail a)
+ Language.Sifflet.Util: instance Monad SuccFail
+ Language.Sifflet.Util: instance Read a => Read (SuccFail a)
+ Language.Sifflet.Util: instance Show a => Show (SuccFail a)
+ Language.Sifflet.Util: map2 :: (a -> b) -> [[a]] -> [[b]]
+ Language.Sifflet.Util: mapM2 :: Monad m => (a -> m b) -> [[a]] -> m [[b]]
+ Language.Sifflet.Util: par :: String -> [String] -> String
+ Language.Sifflet.Util: parseDouble :: String -> String -> SuccFail Double
+ Language.Sifflet.Util: parseInt :: String -> String -> SuccFail Int
+ Language.Sifflet.Util: parseVerbatim :: String -> String -> SuccFail String
+ Language.Sifflet.Util: parsef :: Read a => String -> String -> String -> SuccFail a
+ Language.Sifflet.Util: putCatLn :: [String] -> IO ()
+ Language.Sifflet.Util: putCatsLn :: [String] -> IO ()
+ Language.Sifflet.Util: stub :: String -> IO ()
+ Text.Sifflet.Pretty: class Pretty a where prettyList pre tween post xs = pre ++ intercalate tween (map pretty xs) ++ post
+ Text.Sifflet.Pretty: indentLine :: Int -> String -> String
+ Text.Sifflet.Pretty: pretty :: Pretty a => a -> String
+ Text.Sifflet.Pretty: prettyList :: Pretty a => String -> String -> String -> [a] -> String
+ Text.Sifflet.Pretty: sepComma :: [String] -> String
+ Text.Sifflet.Pretty: sepCommaSp :: [String] -> String
+ Text.Sifflet.Pretty: sepLines :: [String] -> String
+ Text.Sifflet.Pretty: sepLines2 :: [String] -> String
+ Text.Sifflet.Pretty: sepSpace :: [String] -> String
+ Text.Sifflet.Repr: Name :: String -> Name
+ Text.Sifflet.Repr: class Repr a where repr = head . reprl reprl x = [repr x] reprs = unwords . reprl reprList pre tween post xs = pre ++ intercalate tween (map repr xs) ++ post
+ Text.Sifflet.Repr: instance Eq Name
+ Text.Sifflet.Repr: instance Read Name
+ Text.Sifflet.Repr: instance Repr Bool
+ Text.Sifflet.Repr: instance Repr Char
+ Text.Sifflet.Repr: instance Repr Double
+ Text.Sifflet.Repr: instance Repr Float
+ Text.Sifflet.Repr: instance Repr Int
+ Text.Sifflet.Repr: instance Repr Integer
+ Text.Sifflet.Repr: instance Repr Name
+ Text.Sifflet.Repr: instance Repr Number
+ Text.Sifflet.Repr: instance Show Name
+ Text.Sifflet.Repr: newtype Name
+ Text.Sifflet.Repr: repr :: Repr a => a -> String
+ Text.Sifflet.Repr: reprList :: Repr a => String -> String -> String -> [a] -> String
+ Text.Sifflet.Repr: reprl :: Repr a => a -> [String]
+ Text.Sifflet.Repr: reprs :: Repr a => a -> String
Files
- Data/Sifflet/Functoid.hs +104/−0
- Data/Sifflet/Geometry.hs +162/−0
- Data/Sifflet/Tree.hs +67/−0
- Data/Sifflet/TreeGraph.hs +252/−0
- Data/Sifflet/TreeLayout.hs +660/−0
- Data/Sifflet/WGraph.hs +478/−0
- Graphics/Rendering/Sifflet/Draw.hs +223/−0
- Graphics/Rendering/Sifflet/DrawTreeGraph.hs +182/−0
- Graphics/UI/Sifflet.hs +30/−0
- Graphics/UI/Sifflet/Callback.hs +152/−0
- Graphics/UI/Sifflet/Canvas.hs +1011/−0
- Graphics/UI/Sifflet/EditArgsPanel.hs +332/−0
- Graphics/UI/Sifflet/Frame.hs +307/−0
- Graphics/UI/Sifflet/GtkForeign.hs +123/−0
- Graphics/UI/Sifflet/GtkUtil.hs +224/−0
- Graphics/UI/Sifflet/LittleGtk.hs +185/−0
- Graphics/UI/Sifflet/RPanel.hs +154/−0
- Graphics/UI/Sifflet/Tool.hs +585/−0
- Graphics/UI/Sifflet/Types.hs +301/−0
- Graphics/UI/Sifflet/Window.hs +1259/−0
- Graphics/UI/Sifflet/Workspace.hs +597/−0
- ISSUES +403/−0
- Language/Sifflet/Examples.hs +277/−0
- Language/Sifflet/Export/Exporter.hs +192/−0
- Language/Sifflet/Export/Haskell.hs +159/−0
- Language/Sifflet/Export/Python.hs +198/−0
- Language/Sifflet/Export/ToHaskell.hs +136/−0
- Language/Sifflet/Export/ToPython.hs +171/−0
- Language/Sifflet/Export/ToScheme.hs +321/−0
- Language/Sifflet/Expr.hs +1021/−0
- Language/Sifflet/ExprTree.hs +227/−0
- Language/Sifflet/Parser.hs +319/−0
- Language/Sifflet/SiffML.hs +474/−0
- Language/Sifflet/TypeCheck.hs +590/−0
- Language/Sifflet/Util.hs +135/−0
- RELEASE-NOTES +159/−0
- Sifflet/Data/Functoid.hs +0/−98
- Sifflet/Data/Geometry.hs +0/−162
- Sifflet/Data/Tree.hs +0/−67
- Sifflet/Data/TreeGraph.hs +0/−250
- Sifflet/Data/TreeLayout.hs +0/−660
- Sifflet/Data/WGraph.hs +0/−410
- Sifflet/Examples.hs +0/−277
- Sifflet/Foreign/Exporter.hs +0/−191
- Sifflet/Foreign/Haskell.hs +0/−155
- Sifflet/Foreign/Python.hs +0/−194
- Sifflet/Foreign/ToHaskell.hs +0/−135
- Sifflet/Foreign/ToPython.hs +0/−170
- Sifflet/Foreign/ToScheme.hs +0/−318
- Sifflet/Language/Expr.hs +0/−1148
- Sifflet/Language/Parser.hs +0/−309
- Sifflet/Language/SiffML.hs +0/−468
- Sifflet/Rendering/Draw.hs +0/−223
- Sifflet/Rendering/DrawTreeGraph.hs +0/−182
- Sifflet/Text/Pretty.hs +0/−56
- Sifflet/Text/Repr.hs +0/−65
- Sifflet/UI.hs +0/−30
- Sifflet/UI/Callback.hs +0/−140
- Sifflet/UI/Canvas.hs +0/−982
- Sifflet/UI/Frame.hs +0/−306
- Sifflet/UI/GtkForeign.hs +0/−123
- Sifflet/UI/GtkUtil.hs +0/−224
- Sifflet/UI/LittleGtk.hs +0/−179
- Sifflet/UI/RPanel.hs +0/−154
- Sifflet/UI/Tool.hs +0/−561
- Sifflet/UI/Types.hs +0/−308
- Sifflet/UI/Window.hs +0/−1172
- Sifflet/UI/Workspace.hs +0/−405
- Sifflet/Util.hs +0/−135
- Text/Sifflet/Pretty.hs +56/−0
- Text/Sifflet/Repr.hs +65/−0
- datafiles/siffml-1.0.rnc +60/−0
- datafiles/siffml-2.0.rnc +71/−0
- sifflet-lib.cabal +52/−44
+ Data/Sifflet/Functoid.hs view
@@ -0,0 +1,104 @@+module Data.Sifflet.Functoid+ (Functoid(..)+ , functoidName, functoidArgNames, functoidHeader+ , FunctoidLayout(..), flayout+ , flayoutBBox, flayoutSize, flayoutWidth, flayoutBottom+ , flayoutWiden)++where++import Data.Graph.Inductive as G++import Data.Sifflet.Geometry+import Data.Sifflet.TreeLayout++import Language.Sifflet.Expr+import Language.Sifflet.ExprTree+++-- | A Functoid is either a FunctoidParts, representing a partially+-- defined function, or a (completely defined) Function.+-- A FunctoidParts represents an incompletely defined function,+-- which has a function name, argument names, and a list of graph nodes +-- representing the function body which might not yet form+-- a complete expression tree, e.g., nodes might+-- be unconnected.++data Functoid = FunctoidParts {fpName :: String,+ fpArgs :: [String],+ fpNodes :: [G.Node]}+ | FunctoidFunc {fpFunc :: Function}+ deriving (Show)++functoidName :: Functoid -> String+functoidName (FunctoidParts {fpName = name}) = name+functoidName (FunctoidFunc function) = functionName function++functoidArgNames :: Functoid -> [String]+functoidArgNames (FunctoidParts {fpArgs = args}) = args+functoidArgNames (FunctoidFunc function) = functionArgNames function++functoidHeader :: Functoid -> String+functoidHeader f = unwords (functoidName f : functoidArgNames f)++-- | What is a FunctoidLayout? You can think of it as a +-- tree layout for a Frame,+-- or for a Functoid, or as a Forest of Tree Layouts!+-- For an Edit frame, it must be a forest, since the nodes are not+-- yet necessarily connected into a tree.+-- For a call frame, it will be a single TreeLayout (singleton list).++data FunctoidLayout = FLayoutTree (TreeLayout ExprNode)+ | FLayoutForest [TreeLayout ExprNode] BBox++-- | Graphically lay out the Functoid.+-- mvalues: Nothing implies an uncalled frame,+-- Just values implies a called frame, even with 0 arguments.++flayout :: Style -> Functoid -> Env -> Maybe [Value] -> FunctoidLayout+flayout style functoid env mvalues =+ case functoid of+ FunctoidParts {} ->+ -- STUB: [] and bbox ***+ -- WELL: is this supposed to CREATE the tlo or RETRIEVE the tlo?+ -- Either way, it won't work without access to the WGraph!+ FLayoutForest [] (BBox (hpad style) (vpad style) 300 300)+ FunctoidFunc function ->+ let expr = functionBody function+ aspecs = functionArgSpecs function+ exprTree = case mvalues of+ Nothing -> exprToTree expr+ Just _values -> evalTree (exprToTree expr) env+ tlo = treeLayout style + (exprNodeIoletCounter env aspecs)+ exprTree+ in FLayoutTree tlo++flayoutBBox :: FunctoidLayout -> BBox+flayoutBBox aflayout =+ case aflayout of+ FLayoutTree t -> layoutTreeBB t+ FLayoutForest _ bbox -> bbox++flayoutSize :: FunctoidLayout -> Size+flayoutSize = bbSize . flayoutBBox++flayoutWidth :: FunctoidLayout -> Double+flayoutWidth = bbWidth . flayoutBBox++flayoutBottom :: FunctoidLayout -> Double+flayoutBottom = bbBottom . flayoutBBox++flayoutWiden :: FunctoidLayout -> Double -> FunctoidLayout +flayoutWiden aflayout minWidth =+ case aflayout of+ FLayoutTree t -> FLayoutTree (treeLayoutWiden t minWidth)+ FLayoutForest f bbox -> FLayoutForest f bbox -- STUB ***++instance Translate FunctoidLayout where+ translate dx dy fl =+ case fl of+ FLayoutTree t -> + FLayoutTree (translate dx dy t)+ FLayoutForest f b ->+ FLayoutForest (translate dx dy f) (translate dx dy b)
+ Data/Sifflet/Geometry.hs view
@@ -0,0 +1,162 @@+module Data.Sifflet.Geometry+ (Position(..), + positionDelta, positionDistance,+ positionDistanceSquared, positionCloseEnough,+ Circle(..), pointInCircle,+ Size(..),+ BBox(..), + bbX, bbY, bbWidth, bbSetWidth, bbHeight, bbPosition, bbSize,+ bbToRect, bbFromRect, bbCenter, bbLeft, bbXCenter, bbRight,+ bbTop, bbYCenter, bbBottom,+ bbMerge, bbMergeList, pointInBB,+ Widen(widen),+ Translate(..)+ )++where++import Data.Tree as T+import Graphics.UI.Gtk(Rectangle(Rectangle))++-- A Position may be interpreted either absolutely, as a point (x, y);+-- or relatively, as an offset (dx, dy)++data Position = Position {posX :: Double, posY :: Double} -- x, y+ deriving (Eq, Read, Show)++positionDelta :: Position -> Position -> (Double, Double)+positionDelta (Position x1 y1) (Position x2 y2) = (x2 - x1, y2 - y1)++positionDistance :: Position -> Position -> Double+positionDistance p1 p2 = sqrt (positionDistanceSquared p1 p2)++positionDistanceSquared :: Position -> Position -> Double+positionDistanceSquared (Position x1 y1) (Position x2 y2) =+ (x1 - x2) ** 2 + (y1 - y2) ** 2++positionCloseEnough :: Position -> Position -> Double -> Bool+positionCloseEnough p1 p2 radius =+ -- Essentially asks if p1 and p2 are nearly intersecting,+ -- i.e., if p1 is within a circle with center p2 and the given radius+ positionDistanceSquared p1 p2 <= radius ** 2++++data Circle = Circle {circleCenter :: Position,+ circleRadius :: Double}+ deriving (Eq, Read, Show)++pointInCircle :: Position -> Circle -> Bool+pointInCircle point (Circle center radius) =+ positionCloseEnough point center radius++data Size = Size {sizeW :: Double, sizeH :: Double} -- width, height+ deriving (Eq, Read, Show)++-- | BBox x y width height; (x, y) is the top left corner++data BBox = BBox Double Double Double Double+ deriving (Eq, Read, Show)++-- | BBox accessors and utilities++bbX, bbY, bbWidth, bbHeight :: BBox -> Double+bbX (BBox x _y _w _h) = x++bbY (BBox _x y _w _h) = y+bbWidth (BBox _x _y w _h) = w+bbHeight (BBox _x _y _w h) = h++bbPosition :: BBox -> Position+bbPosition (BBox x y _w _h) = Position x y++bbSize :: BBox -> Size+bbSize (BBox _x _y w h) = Size w h++bbCenter :: BBox -> Position+bbCenter (BBox x y w h) = Position (x + w / 2) (y + h / 2)++bbSetWidth :: BBox -> Double -> BBox+bbSetWidth (BBox x y _w h) nwidth = BBox x y nwidth h++bbLeft, bbXCenter, bbRight :: BBox -> Double+bbLeft = bbX+bbXCenter (BBox x _y w _h) = x + w / 2+bbRight (BBox x _y w _h) = x + w++bbTop, bbYCenter, bbBottom :: BBox -> Double+bbTop = bbY+bbYCenter (BBox _x y _w h) = y + h / 2+bbBottom (BBox _x y _w h) = y + h++bbToRect :: BBox -> Rectangle+bbToRect (BBox x y w h) = + Rectangle (round x) (round y) (round w) (round h)++bbFromRect :: Rectangle -> BBox+bbFromRect (Rectangle x y w h) =+ BBox (fromIntegral x) (fromIntegral y)+ (fromIntegral w) (fromIntegral h)++-- | Form a new BBox which encloses two bboxes+bbMerge :: BBox -> BBox -> BBox+bbMerge bb1 bb2 =+ let f1 ! f2 = f1 (f2 bb1) (f2 bb2)+ bottom = max ! bbBottom -- i.e., max (bbBottom bb1) (bbBottom bb2)+ top = min ! bbTop+ left = min ! bbLeft+ right = max ! bbRight+ in BBox left top (right - left) (bottom - top)++bbMergeList :: [BBox] -> BBox+bbMergeList [] = error "bbMergeList: empty list"+bbMergeList (b:bs) = foldl bbMerge b bs+ +-- Test whether a point (e.g., from mouse click) is within a+-- bounding box+pointInBB :: Position -> BBox -> Bool+pointInBB (Position x y) (BBox x1 y1 w h) =+ x >= x1 &&+ x <= x1 + w &&+ y >= y1 &&+ y <= y1 + h++class Widen a where+ -- | Make an object have at least a specified minimum width;+ -- does nothing if it's already at least that wide+ widen :: a -> Double -> a++instance Widen BBox where+ widen bb@(BBox x y w h) minWidth =+ if w >= minWidth+ then bb+ else BBox x y minWidth h++-- | A Translate is a thing that can be repositioned by+-- delta x and delta y++class Translate a where+ translate :: Double -- ^ delta X+ -> Double -- ^ delta Y+ -> a -- ^ thing in old position+ -> a -- ^ thing in new position++instance (Translate e) => Translate [e] where+ translate dx dy = map (translate dx dy)++instance (Translate e) => Translate (Tree e) where+ translate dx dy t = + T.Node (translate dx dy (rootLabel t))+ (translate dx dy (subForest t))+++instance Translate BBox where+ translate dx dy (BBox x y w h) = BBox (x + dx) (y + dy) w h++instance Translate Position where+ translate dx dy (Position x y) = Position (x + dx) (y + dy)++instance Translate Circle where+ translate dx dy (Circle center radius) =+ Circle (translate dx dy center) radius+
+ Data/Sifflet/Tree.hs view
@@ -0,0 +1,67 @@+-- Tree.hs+-- General (not binary) trees++module Data.Sifflet.Tree + (T.Tree(..), tree, leaf, isLeaf, treeSize, treeDepth+ , Repr(..)+ -- Since String is not an instance of Repr,+ -- we need to convert (Tree String) to (Tree Name)+ , Name(..), nameTree+ , putTree, putTreeR, putTreeRs, putTreeS+ , drawTreeShow+ )++where++import Data.Tree as T+import Text.Sifflet.Repr++-- Makers++tree :: e -> Forest e -> T.Tree e+tree root subtrees = T.Node {rootLabel = root, subForest = subtrees}++leaf :: e -> T.Tree e+leaf x = tree x []++isLeaf :: T.Tree e -> Bool+isLeaf (T.Node _root []) = True+isLeaf _ = False++treeSize :: T.Tree e -> Int+treeSize (T.Node _root subtrees) = 1 + sum (map treeSize subtrees)++treeDepth :: T.Tree e -> Int+treeDepth (T.Node _root []) = 1+treeDepth (T.Node _root subtrees) = 1 + maximum (map treeDepth subtrees)++{- tree_map or treeMap:+ removed; use (Functor) fmap instead+-}++{- tree_mapM:+ removed, use Data.Traversable.mapM instead.+-}++nameTree :: T.Tree String -> T.Tree Name+nameTree = fmap Name++putTree :: (Show e) => T.Tree e -> IO ()+putTree = putTreeS++-- putTreeR -- using repr (first part)+putTreeR :: (Repr e) => T.Tree e -> IO()+putTreeR = putStrLn . drawTree . fmap repr++-- putTreeRs -- using reprs (all parts)+putTreeRs :: (Repr e) => T.Tree e -> IO()+putTreeRs = putStrLn . drawTree . fmap reprs+++-- putTreeS -- using show+putTreeS :: (Show e) => T.Tree e -> IO ()+putTreeS = putStrLn . drawTreeShow++drawTreeShow :: (Show e) => T.Tree e -> String+drawTreeShow = drawTree . fmap show+
+ Data/Sifflet/TreeGraph.hs view
@@ -0,0 +1,252 @@+module Data.Sifflet.TreeGraph + (LayoutGraph, + flayoutToGraph, treeLayoutToGraph,+ -- treeToGraph, + orderedTreeToGraph, + treeGraphNodesTree, graphToTreeOriginal,+ graphToTreeStructure, + flayoutToGraphRoots,+ graphToOrderedTree, graphToOrderedTreeFrom,+ orderedChildren, adjCompareEdge,+ nextNodes, -- exported for testing; any other reason?+ grTranslateNode, grTranslateSubtree, grTranslateGraph,++ -- moved from Workspace.hs:+ functoidToFunction, graphToExprTree,++ )++where++import Data.List (sort, sortBy)++import Data.Graph.Inductive as G++import Data.Sifflet.Functoid+import Data.Sifflet.Geometry+import Data.Sifflet.Tree as T+import Data.Sifflet.TreeLayout+import Data.Sifflet.WGraph++import Language.Sifflet.Expr+import Language.Sifflet.ExprTree+import Language.Sifflet.TypeCheck++import Language.Sifflet.Util++type LayoutGraph n e = Gr (LayoutNode n) e++flayoutToGraph :: FunctoidLayout -> WGraph+flayoutToGraph tlo = + case tlo of + FLayoutTree t -> treeLayoutToGraph t+ FLayoutForest ts _bbox -> + foldl grAddGraph wgraphNew (map treeLayoutToGraph ts)++treeLayoutToGraph :: TreeLayout ExprNode -> WGraph+treeLayoutToGraph = orderedTreeToGraph . fmap WSimple+++-- flayoutToGraphRoots returns a list of graph nodes (Ints)+-- corresponding to the root of the tree, or the roots of+-- the trees in the forest.+-- The list is ordered in the same sense as the graph nodes.++flayoutToGraphRoots :: FunctoidLayout -> [G.Node]+flayoutToGraphRoots (FLayoutTree _t) = [1]+flayoutToGraphRoots (FLayoutForest trees _bbox) =+ let loop _ [] res = reverse res+ loop next (t:ts) res =+ loop (next + treeSize t) ts (next:res)+ in loop 1 trees []+++-- {-# DEPRECATED treeToGraph "use ??? instead" #-}++-- treeToGraph :: Tree e -> Gr e ()+-- treeToGraph (T.Node root subtrees) = +-- -- Convert an ordered tree to a graph.+-- -- The graph edge labels from parent to child +-- -- are integers corresponding to the order of the children.+-- -- So, to recover the ordered list of children, +-- -- sort by the edge label.+-- let g0 = empty :: Gr e ()+-- g1 = insNode (1, root) g0++-- grow :: Gr e () -> [(Tree e, G.Node)] -> G.Node -> Gr e ()+-- -- grow graph [(tree, parent)] node+-- grow g [] _ = g+-- grow g ((t, p):tps) n =+-- -- insert t forming g', insert ts into g'+-- let edge = ((), p) -- predecessors, or both?+-- g' = ([edge], n, (rootLabel t), []) & g -- assume pred only+-- in grow g' (tps ++ [(s, n) | s <- subForest t])+-- (succ n)++-- in grow g1 [(s, 1) | s <- subtrees] 2++sprout :: G.Node -> Tree e -> [(G.Node, WEdge, Tree e)]+sprout parent (T.Node _ subtrees) =+ -- create triples (parent graph node, edge, subtree)+ let m = length subtrees - 1+ in [(parent, WEdge e, s) | (e, s) <- zip [0..m] subtrees]+++orderedTreeToGraph :: Tree e -> Gr e WEdge+orderedTreeToGraph otree = + -- Convert an ordered tree to a graph.+ -- The graph edge labels from parent to child + -- are integers corresponding to the order of the children.+ -- So, to recover the ordered list of children, + -- sort by the edge label.+ let g0 = empty :: Gr e WEdge+ g1 = insNode (1, rootLabel otree) g0++ grow :: Gr e WEdge -> [(G.Node, WEdge, Tree e)] -> G.Node -> Gr e WEdge+ -- grow graph [(parent, edgeLabel, subtree), ...] node+ grow g [] _ = g+ grow g ((p, e, t):pets) n =+ -- insert pet forming g', insert pets into g';+ -- n is the node id for the root of this subtree+ let adj = (e, p) -- to parent (priority, node)+ g' = ([adj], n, (rootLabel t), []) & g + n' = succ n+ in grow g' (pets ++ + sprout n t+ -- [(s, n) | s <- subForest t])+ )+ n'++ in grow g1 (sprout 1 otree) 2++-- And what about this (e, Node) type? Is it some sort of monad?++treeGraphNodesTree :: Tree e -> Tree Node+treeGraphNodesTree atree =+ -- returns a tree of Nodes of the Graph (treeToGraph atree)+ let gnTree :: Tree e -> Node -> Node -> (Tree Node, Node)+ gnTree (T.Node _root subtrees) rootNode next =+ -- rootNode = Node (number) for the root,+ -- next = next unused Node+ let (nNodes, next') = nextNodes subtrees next+ (subtrees', next'') = gnSubtrees subtrees nNodes next'+ in (T.Node rootNode subtrees', next'')++ gnSubtrees :: [Tree e] -> [Node] -> Node -> ([Tree Node], Node)+ gnSubtrees [] [] next = ([], next)+ gnSubtrees (t:ts) (n:ns) next = + let (t', next') = gnTree t n next+ (ts', next'') = gnSubtrees ts ns next'+ in ((t' : ts'), next'')+ gnSubtrees _ _ _ = error "gnSubtrees: list lengths do not match"++ in fst (gnTree atree 1 2)++nextNodes :: [e] -> Node -> ([Node], Node)+nextNodes items next = + -- next is the next unused Node (number).+ -- Returns list of new nodes, and a new "next" node+ -- E.g., nextNodes [a, b] 3 = ([3, 4], 5)+ -- nextNodes [c, d, e] 5 = ([5, 6, 7], 8)+ -- nextNodes [] 8 = ([], 8)+ let n = length items+ next' = next + n+ in ([next .. (next' - 1)], next')++-- When a tree is converted to a graph,+-- each tree node's ordered children get graph node numbers+-- in ascending order. Therefore, when reconstructing the tree,+-- sorting the graph nodes restores the order of the children+-- as in the tree.++graphToOrderedTree :: Gr e WEdge -> Tree e+-- inverse of orderedTreeToGraph+graphToOrderedTree g = graphToOrderedTreeFrom g 1++graphToOrderedTreeFrom :: Gr e WEdge -> G.Node -> Tree e+graphToOrderedTreeFrom g n =+ case lab g n of+ Just label -> + T.Node label (map (graphToOrderedTreeFrom g) (orderedChildren g n))+ Nothing -> + errcats ["missing label for node", show n]++-- | List of the nodes children, ordered by edge number+orderedChildren :: Gr e WEdge -> G.Node -> [G.Node]+orderedChildren g = map fst . sortBy adjCompareEdge . lsuc g++adjCompareEdge :: (Node, WEdge) -> (Node, WEdge) -> Ordering+adjCompareEdge (_n1, e1) (_n2, e2) = compare e1 e2++{-# DEPRECATED graphToTreeOriginal "use ??? instead" #-}++graphToTreeOriginal :: Gr e () -> G.Node -> Tree e+-- (\g -> graphToTreeOriginal g 1) is the inverse of treeToGraph+graphToTreeOriginal g n =+ case lab g n of+ Just label -> T.Node label (map (graphToTreeOriginal g) + (sort (suc g n)))+ _ -> errcats ["missing label for node", show n]++{-# DEPRECATED graphToTreeStructure "use ??? instead" #-}++graphToTreeStructure :: Gr n e -> G.Node -> Tree G.Node+-- This is *not* an inverse of treeToGraph.+-- Rather, graphToTreeStructure (treeToGraph t 1) is a tree t' of Nodes+-- (i.e., integer identifiers of nodes in the graph)+-- which parallels the structure of t.++graphToTreeStructure g n = T.Node n (map (graphToTreeStructure g)+ (sort (suc g n)))++grTranslateNode :: + Node -> Double -> Double -> LayoutGraph n e -> LayoutGraph n e+grTranslateNode node dx dy graph = + grUpdateNodeLabel graph node (translate dx dy)++grTranslateSubtree :: + Node -> Double -> Double -> LayoutGraph n e -> LayoutGraph n e+grTranslateSubtree root dx dy graph = + let trSubtrees :: [Node] -> LayoutGraph n e -> LayoutGraph n e+ trSubtrees [] g = g+ trSubtrees (r:rs) g = trSubtrees (rs ++ suc g r)+ (grTranslateNode r dx dy g)+ in trSubtrees [root] graph++grTranslateGraph :: Double -> Double -> LayoutGraph n e -> LayoutGraph n e+grTranslateGraph dx dy graph = nmap (translate dx dy) graph++grUpdateNodeLabel :: (DynGraph g) => g a b -> Node -> (a -> a) -> g a b+grUpdateNodeLabel graph node updater =+ case match node graph of+ (Nothing, _) -> error "no such node"+ (Just (preds, jnode, label, succs), graph') ->+ -- jnode == node+ (preds, jnode, updater label, succs) & graph'++functoidToFunction :: + Functoid -> WGraph -> G.Node -> Env -> SuccFail Function+functoidToFunction functoid graph frameNode env =+ case functoid of+ FunctoidFunc f -> Succ f+ FunctoidParts {fpName = name, fpArgs = args} ->+ case suc graph frameNode of+ [root] ->+ do+ {+ expr <- treeToExpr $ graphToExprTree graph root+ ; let impl = Compound args expr+ ; (atypes, rtype) <- decideTypes name expr args env+ ; Succ (Function (Just name) atypes rtype impl)+ }+ _ -> Fail "The graph structure is not a tree!"++graphToExprTree :: WGraph -> G.Node -> Tree ExprNode+graphToExprTree g root =+ let extractExprNode wnode =+ case wnode of+ WSimple layoutNode -> gnodeValue (nodeGNode layoutNode)+ WFrame _ -> error "graphToExprTreeFrom: unexpected WFrame node"+ in fmap extractExprNode (graphToOrderedTreeFrom g root)++
+ Data/Sifflet/TreeLayout.hs view
@@ -0,0 +1,660 @@+module Data.Sifflet.TreeLayout + (VColor(..), white, cream, black, lightBlue, lightBlueGreen+ , lightGray, mediumGray, darkGray+ , yellow, darkBlueGreen, blueGreen+ , Style(..), VFont(..), defaultStyle, style0, style1, style2, style3+ , wstyle+ , styleIncreasePadding+ , FontTextExtents(..), setFont, measureText, styleTextExtents, ftExtents+ , TextBox(..), makeTextBox, tbWidth, tbHeight, tbBottom+ , tbCenter, tbTextCenter, tbBoxCenter, offsetTextBoxCenters+ , tbSetWidth+ , treeSizes+ , GNode(..), treeGNodes, gnodeText, gnodeTextBB+ , Iolet(..), ioletCenter, makeIolets, makeIoletsRow+ , pointInIolet+ , IoletCounter, zeroIoletCounter+ , makeGNode+ , TreeLayout, LayoutNode(..)+ , layoutNodeSource, layoutRootBB, layoutTreeBB+ , treeLayout+ , treeLayoutPaddedSize, treeLayoutSize, treeLayoutWidth+ , layoutTreeMoveCenterTo+ , pointInLayoutNode, pointInGNode, findRect+ , treeLayoutWiden+ )++where++import System.IO.Unsafe++-- All this VVV is needed just to measure a piece of text;+-- maybe font and text should be split out to a new module?++import Graphics.Rendering.Cairo (FontExtents(..),+ FontSlant(..), FontWeight(..),+ Format(..), Render,+ TextExtents(..),+ fontExtents,+ renderWith,+ textExtents,+ selectFontFace, setFontSize,+ withImageSurface)++import Graphics.UI.Gtk (Rectangle)+import Data.Traversable () -- imports only instance declarations++import Data.Sifflet.Geometry+import Data.Sifflet.Tree as T hiding (tree)+import Text.Sifflet.Repr ()+import Language.Sifflet.Util++-- Do colors really belong here???++data VColor = ColorRGB Double Double Double |+ ColorRGBA Double Double Double Double++black, white, cream, lightBlue, lightBlueGreen :: VColor++black = ColorRGB 0 0 0+white = ColorRGB 1 1 1+cream = ColorRGB 0.94902 0.94902 0.82745+lightBlue = ColorRGB 0.43529 0.43922 0.95686+lightBlueGreen = ColorRGB 0 1 1++lightGray, mediumGray, darkGray :: VColor+lightGray = ColorRGBA 0.75 0.75 0.75 0.5+mediumGray = ColorRGB 0.5 0.5 0.5+darkGray = ColorRGB 0.25 0.25 0.25++yellow, darkBlueGreen, blueGreen :: VColor++yellow = ColorRGB 0.9 0.9 0+darkBlueGreen = ColorRGB 0 0.3 0.3+blueGreen = ColorRGB 0 0.4 0.4++-- darkRed = ColorRGB 0.5 0 0, -- dark red++data VFont = VFont {vfontFace :: String, vfontSlant :: FontSlant,+ vfontWeight :: FontWeight,+ vfontSize :: Double}++data Style = + Style {styleFont :: VFont,+ lineWidth :: Double,+ textMargin :: Double, -- in box on all four sides+ hpad, vpad :: Double, -- internode (interbox) spacing+ exomargin :: Double, -- margin of the tree within the window+ vtinypad :: (Double, Double), -- node-edge spacing (above, below)+ styleFramePad :: Double, -- pad between frames+ styleNormalTextColor, styleNormalFillColor, + styleNormalEdgeColor,+ styleActiveTextColor, styleActiveFillColor,+ styleActiveEdgeColor,+ styleSelectedTextColor, styleSelectedFillColor, + styleSelectedEdgeColor,+ styleTetherColor :: VColor,+ -- auxiliary text style, e.g., the value of an ExprNode+ styleAuxOffset :: Position, -- center-to-center offset dx dy+ styleAuxColor :: VColor,+ styleAuxFont :: VFont,+ styleIoletRadius :: Double, -- could extend to other shapes ***+ styleShowNodeBoxes :: Bool, -- draw a box around expr nodes+ styleShowNodePorts :: Bool -- draw the I/O ports of expr nodes+ }++styleIncreasePadding :: Style -> Double -> Style+-- increase hpad, vpad of style by multiplication+styleIncreasePadding style factor =+ style {hpad = factor * hpad style,+ vpad = factor * vpad style}++style0, style1, style2, style3, defaultStyle :: Style++style0 = + let green = ColorRGB 0.1 0.9 0.1+ veryDarkGray = ColorRGB 0.1 0.1 0.1+ brighterGreen = ColorRGB 0.5 0.95 0.5+ in Style {styleFont = VFont "serif" FontSlantNormal FontWeightNormal 18, + lineWidth = 2, + textMargin = 18.0, -- text within its box+ hpad = 27, vpad = 36, -- inter-node separation+ exomargin = 0,+ vtinypad = (4.5, 4.5), -- node-edge separation+ styleFramePad = 35, -- padding between frames+ -- Foreground and background colors+ styleNormalTextColor = green,+ styleNormalEdgeColor = green,+ styleNormalFillColor = veryDarkGray,+ styleActiveTextColor = brighterGreen,+ styleActiveEdgeColor = brighterGreen,+ styleActiveFillColor = mediumGray,+ styleSelectedTextColor = blueGreen,+ styleSelectedEdgeColor = blueGreen,+ styleSelectedFillColor = darkGray,+ styleTetherColor = white,+ styleAuxOffset = Position 0.0 (-20.0),+ styleAuxColor = lightGray,+ styleAuxFont = + VFont "serif" FontSlantItalic FontWeightNormal 12,+ styleIoletRadius = 5,+ styleShowNodeBoxes = True, -- draw a box around expr nodes+ styleShowNodePorts = True -- draw the I/O ports of expr nodes+ }++style1 = + let veryDarkBlue = ColorRGB 0 0 0.1+ pinkIHope = ColorRGB 1 0.9 0.9+ in style0 {styleNormalTextColor = veryDarkBlue,+ styleNormalEdgeColor = veryDarkBlue,+ styleNormalFillColor = lightGray,+ styleActiveTextColor = pinkIHope,+ styleActiveEdgeColor = pinkIHope+ }++style2 = + let darkPink = ColorRGB 1 0.5 0.5+ in style1 {styleNormalFillColor = white,+ styleActiveTextColor = darkPink,+ styleActiveEdgeColor = darkPink}++style3 = + let semiTranspBlue = ColorRGBA 0 0.2 0.9 0.5+ in style0 {exomargin = 10,+ styleNormalTextColor = yellow,+ styleNormalEdgeColor = yellow,+ styleNormalFillColor = darkBlueGreen,+ styleActiveTextColor = yellow,+ styleActiveEdgeColor = yellow,+ styleActiveFillColor = blueGreen,+ styleAuxColor = semiTranspBlue+ } ++defaultStyle = style3++wstyle :: Style+wstyle = + style3 { + -- smaller to allow multiple frames on one canvas+ styleFont = VFont "serif" FontSlantNormal FontWeightNormal 14,+ textMargin = 8.0,+ hpad = 10.0, vpad = 24.0, + vtinypad = (0.0, 0.0),+ exomargin = 10.0, -- between tree and window edges+ styleFramePad = 15,++ -- colors+ styleNormalTextColor = yellow,+ styleNormalEdgeColor = mediumGray,+ styleNormalFillColor = darkBlueGreen,++ styleActiveTextColor = yellow,+ styleActiveEdgeColor = mediumGray,+ styleActiveFillColor = blueGreen,++ styleSelectedTextColor = black,+ styleSelectedEdgeColor = lightBlueGreen,+ styleSelectedFillColor = lightBlueGreen,++ styleTetherColor =+ ColorRGBA 0.7 0.7 0.7 0.3, -- light gray+ + -- node values+ styleAuxOffset = Position 0 (-16),+ styleAuxFont = VFont "serif" FontSlantItalic FontWeightNormal 12,+ styleAuxColor = ColorRGBA 0.9 0.5 0.5 1.0,+ -- ColorRGBA 1 0.9 0.9 0.75 + styleIoletRadius = 5+ }+ ++pointInLayoutNode :: Position -> LayoutNode e -> Bool+pointInLayoutNode point = pointInGNode point . nodeGNode++pointInGNode :: Position -> GNode e -> Bool+pointInGNode point = pointInBB point . gnodeNodeBB++-- UNUSED: findNode (except in findRect)+-- find the node, if any, containing a point +-- might need to adjust this to take iolets into account ******+findNode :: Position -> TreeLayout e -> Maybe (LayoutNode e)+findNode point (T.Node node@(LayoutNode _rootGNode treeBB) sublayouts) =+ if pointInBB point treeBB+ then if pointInLayoutNode point node+ then Just node+ else findInSubs point sublayouts+ else Nothing++ where findInSubs :: Position -> [TreeLayout e] -> Maybe (LayoutNode e)+ findInSubs _p [] = Nothing+ findInSubs p (l:ls) = + let found = findNode p l in+ case found of+ (Just _) -> found+ Nothing -> findInSubs p ls++-- UNUSED: findRect+findRect :: Position -> TreeLayout e -> Maybe Rectangle+findRect point tlo = + case findNode point tlo of+ Nothing -> Nothing+ Just node -> Just (bbToRect (gnodeNodeBB (nodeGNode node)))++setFont :: VFont -> Render ()+setFont (VFont face slant weight size) = do+ selectFontFace face slant weight+ setFontSize size++data FontTextExtents = FontTextExtents {+ fontAscent::Double, fontDescent::Double, fontHeight::Double,+ fontMaxXadvance::Double, fontMaxYadvance::Double,+ textXbearing::Double, textYbearing::Double,+ extTextWidth::Double, extTextHeight::Double,+ textXadvance::Double, textYadvance::Double}+ deriving (Eq, Read, Show)++measureText :: Style -> String -> Size+measureText style str =+ let extents = styleTextExtents style str+ in Size (textXadvance extents)+ (fontHeight extents + fontDescent extents)++-- Rationale for unsafePerformIO:+-- font extents of a string will not change unless the font itself+-- changes, which is extremely unlikely++styleTextExtents :: Style -> String -> FontTextExtents+styleTextExtents style str = + unsafePerformIO $ + withImageSurface FormatARGB32 0 0 $ \ surface ->+ renderWith surface $ + setFont (styleFont style) >>+ ftExtents str++-- | ftExtents: used for what?+ftExtents :: String -> Render FontTextExtents+ftExtents text = do+ FontExtents asc desc fheight maxxadv maxyadv <- fontExtents+ TextExtents xbear ybear twidth theight xadv yadv <- textExtents text+ return (FontTextExtents asc desc fheight maxxadv maxyadv+ xbear ybear twidth theight xadv yadv)++data TextBox = TextBox {tbText :: String,+ tbTextBB :: BBox, -- BBox of text without margins+ tbBoxBB :: BBox -- of text with margins+ }+ deriving (Eq, Read, Show)++makeTextBox :: Style -> String -> TextBox+makeTextBox style text =+ let extents = styleTextExtents style text+ Size textW textH = measureText style text+ -- textW = textXadvance extents+ -- textH = fontHeight extents + fontDescent extents+ margin = textMargin style++ boxW = textW + 2.0 * margin -- max (textW + 2.0 * margin) minWidth+ boxH = textH + 2.0 * margin++ boxX = 0+ boxY = 0++ textX = (boxW - textW) / 2.0+ -- raise text slightly, by a fraction of its font descent+ -- (why? because it just looks better!)+ -- Maybe this should be made an aspect of style?+ raise = 0.5 * fontDescent extents+ textY = fontHeight extents + (boxH - textH) / 2.0 - raise++ textBB = BBox textX textY textW textH+ boxBB = BBox boxX boxY boxW boxH++ in TextBox text textBB boxBB++instance Widen TextBox where+ widen tb@(TextBox _text textBB boxBB) minWidth =+ let w = bbWidth boxBB+ in if w >= minWidth+ then tb+ else let dw = minWidth - w + in tb {tbTextBB = translate (dw / 2) 0 textBB,+ tbBoxBB = widen boxBB minWidth}++instance Translate TextBox where+ translate dx dy (TextBox text textBB boxBB) = + TextBox text (translate dx dy textBB) (translate dx dy boxBB)+++tbWidth :: TextBox -> Double+tbWidth = bbWidth .tbBoxBB++tbSetWidth :: TextBox -> Double -> TextBox+tbSetWidth tbox w = + let TextBox text textBB boxBB = tbox+ boxBB' = bbSetWidth boxBB w+ (dx, dy) = positionDelta (bbCenter boxBB) (bbCenter boxBB')+ in TextBox text (translate dx dy textBB) boxBB'++tbHeight :: TextBox -> Double+tbHeight = bbHeight . tbBoxBB++tbBottom :: TextBox -> Double+tbBottom = bbBottom . tbBoxBB++-- The geometric "center" of a TextBox is the center of its "BoxBB"+tbCenter :: TextBox -> Position+tbCenter = tbBoxCenter+ +tbBoxCenter :: TextBox -> Position+tbBoxCenter = bbCenter . tbBoxBB++-- and the center of its text should have the same x coordinate,+-- but due to the way text is positioned, maybe not the same y.+-- But do we ever need this?+tbTextCenter :: TextBox -> Position+tbTextCenter = bbCenter . tbTextBB++offsetTextBoxCenters :: Position -> TextBox -> TextBox -> TextBox+offsetTextBoxCenters offset anchor floater =+ -- Position the floater text box so that its center differs+ -- from the anchor text box by the given offset.+ -- (The offset argument comes first, like dx dy in translate.)+ let Position ax ay = tbBoxCenter anchor+ Position fx fy = tbBoxCenter floater+ Position ox oy = offset+ -- translate floater by (dx, dy), where fx + dx = ax + ox, + -- so dx = ax + ox - fx; and dy similarly+ dx = ax + ox - fx+ dy = ay + oy - fy+ in translate dx dy floater+++-- GNode = graphical node, suitable for drawing+data GNode e = GNode {gnodeValue :: e, + gnodeTextBoxes :: [TextBox], -- one or two only+ gnodeNodeBB :: BBox, -- encloses all textboxes+ gnodeInlets :: [Iolet], -- input connectors+ gnodeOutlets :: [Iolet] -- output connectors+ }+ deriving (Eq)++gnodeText :: GNode e -> String+gnodeText = tbText . head . gnodeTextBoxes++gnodeTextBB :: GNode e -> BBox+gnodeTextBB = tbTextBB . head . gnodeTextBoxes++instance (Show e) => Show (GNode e) where+ show (GNode v tbs nodeBB inlets outlets) = + par "GNode" [show v, show tbs, show nodeBB, show inlets, show outlets]++instance Translate (GNode e) where+ translate dx dy (GNode value textboxes nodeBB inlets outlets) = + GNode value + (map (translate dx dy) textboxes)+ (translate dx dy nodeBB)+ (translate dx dy inlets)+ (translate dx dy outlets)+++-- An IoletCounter returns (no. of inlets, no. of outlets)+type IoletCounter e = e -> (Int, Int)++-- A sort of "default" IoletCounter returning zeroes+zeroIoletCounter :: IoletCounter e+zeroIoletCounter _node = (0, 0)++-- An IoletsMaker ???+-- type IoletsMaker e = Style -> e -> ([Position], [Position])++makeGNode :: (Repr e) => Style -> IoletCounter e -> e -> GNode e+makeGNode style countIolets value =+ -- The function countIolets creates a tuple (# of inlets, # of outlets)+ -- for the node, of course being (0, 0) if we don't want any.+ let textboxes1 = map (makeTextBox style) (reprl value)+ textboxes2 = case textboxes1 of+ [_tb] -> textboxes1+ [tb1, tb2] -> + [tb1, + offsetTextBoxCenters (styleAuxOffset style) tb1 tb2]+ _ -> wrong textboxes1+ nodeBB = case textboxes2 of+ [tb] -> tbBoxBB tb+ [tb1, tb2] -> bbMerge (tbBoxBB tb1) (tbBoxBB tb2)+ _ -> wrong textboxes2+ wrong tbs = + errcats ["makeGNode: wrong no. of text boxes;",+ "expected 1 or 2, but got", show (length tbs)]+ (inlets, outlets) = makeIolets style nodeBB (countIolets value)+ in GNode value textboxes2 nodeBB inlets outlets++makeIolets :: Style -> BBox -> (Int, Int) -> ([Iolet], [Iolet])+makeIolets style bbox (nin, nout) =+ -- make the (inlets, outlets)+ (makeIoletsRow style (bbXCenter bbox) (bbBottom bbox) nin,+ makeIoletsRow style (bbXCenter bbox) (bbTop bbox) nout)++makeIoletsRow :: Style -> Double -> Double -> Int -> [Iolet]+makeIoletsRow style cx cy n =+ -- make a row of n Iolets, centered at (cx, cy)+ let radius = styleIoletRadius style+ diam = 2 * radius -- diameter+ w = fromIntegral n * diam+ x1 = cx - w / 2 + radius -- the center of the first iolet+ x i = x1 + fromIntegral (i - 1) * diam+ make i = Iolet (Circle (Position (x i) cy) radius)+ in map make [1..n]++-- | An Iolet is a circular port.+-- Other shapes could be added.+newtype Iolet = Iolet Circle+ deriving (Eq, Read, Show)++instance Translate Iolet where+ translate dx dy (Iolet circle) = Iolet (translate dx dy circle)++ioletCenter :: Iolet -> Position+ioletCenter (Iolet circle) = circleCenter circle++pointInIolet :: Position -> Iolet -> Bool+pointInIolet point (Iolet circle) = pointInCircle point circle++-- Build a Tree of node Sizes parallel to the Repr tree,+-- each node in the Size tree is the size of +-- the corresponding node in the repr tree.++-- treeSizes: given a tree of *node* sizes, for the root nodes of the tree+-- and its subtrees, returns a tree of *tree* sizes++treeSizes :: Style -> Tree (GNode e) -> Tree Size+treeSizes style gTree = + let subtreeSizes = map (treeSizes style) (subForest gTree)+ (BBox _ _ rootWidth rootHeight) = gnodeNodeBB (rootLabel gTree)+ treeWidth = max rootWidth (paddedWidth style subtreeSizes)+ treeHeight = rootHeight + paddedHeight style subtreeSizes+ in T.Node (Size treeWidth treeHeight) subtreeSizes++-- (paddedWidth style subtrees) and (paddedHeight style subtrees)+-- compute the total size of all subtrees+-- of of a Size tree, including padding.+paddedWidth :: Style -> [Tree Size] -> Double+paddedWidth _style [] = 0+paddedWidth style subtrees =+ sum [w | (T.Node (Size w _h) _) <- subtrees] ++ hpad style * fromIntegral (length subtrees - 1)++-- UNUSED:+-- -- | pairSum is unused; should it be?+-- pairSum :: (Double, Double) -> Double+-- pairSum (a, b) = a + b++paddedHeight :: Style -> [Tree Size] -> Double+paddedHeight _style [] = 0+paddedHeight style subtrees = + vpad style + maximum [h | (T.Node (Size _w h) _) <- subtrees]++-- A TreeLayout is a Tree of LayoutNodes.+-- The root node in a TreeLayout identifies the root of the source Tree +-- (nodeSource) and the node's bounding box (nodeNodeBB).+-- It also tells the bounding box of the whole tree (nodeTreeBB).+-- The subtrees of the TreeLayout are the TreeLayouts +-- of the subtrees of the source Tree.++type TreeLayout e = Tree (LayoutNode e)++data LayoutNode e = LayoutNode {nodeGNode :: GNode e,+ nodeTreeBB :: BBox}+ deriving (Eq)++layoutNodeSource :: LayoutNode e -> e+layoutNodeSource = gnodeValue . nodeGNode++instance (Show e) => Show (LayoutNode e) where+ show (LayoutNode gnode treebb) = par "LayoutNode" [show gnode, show treebb]++instance Translate (LayoutNode e) where+ translate dx dy (LayoutNode gnode treeBB) = + LayoutNode (translate dx dy gnode)+ (translate dx dy treeBB)++instance Widen (LayoutNode e) where+ widen node@(LayoutNode gNode treeBB) minWidth =+ let dw = bbWidth treeBB - minWidth+ in if dw <= 0+ then node+ else LayoutNode (translate (dw / 2) 0 gNode)+ (widen treeBB minWidth)++-- Try this, it's tough!+-- instance (Draw n, Widen n) = Widen (Tree n) where ...+-- But why not just+-- instance (Widen n) = Widen (Tree n) where ...+++-- UNUSED+-- -- | layoutRootSource: unused+-- layoutRootSource :: TreeLayout e -> e+-- layoutRootSource tree = layoutNodeSource (rootLabel tree)++layoutRootBB :: TreeLayout e -> BBox+layoutRootBB = gnodeNodeBB . nodeGNode . rootLabel++layoutTreeBB :: TreeLayout e -> BBox+layoutTreeBB = nodeTreeBB . rootLabel++treeLayout :: (Repr e) => Style -> IoletCounter e -> Tree e -> TreeLayout e+treeLayout style counter tree = + let t1 = treeGNodes style counter tree+ t2 = treeSizes style t1+ start = Position (hpad style) (vpad style)+ t3 = treeLayout2 style start tree t1 t2+ in treeLayoutAddMargin t3 (exomargin style)+++-- treeGNodes produces a tree of (GNode e),+-- based on a top left corner at (0, 0) which, of course, will need+-- to be translated in the final tlo.++treeGNodes :: Repr e => + Style -> IoletCounter e -> Tree e -> Tree (GNode e)+treeGNodes style counter tree = fmap (makeGNode style counter) tree++treeLayout2 :: (Repr e) =>+ Style -- ^ tree style+ -> Position -- ^ top left after padding+ -> Tree e -- ^ the tree being laid out + -- (actually we don't need the tree as an argument;+ -- it's implied by the GNode tree)+ -> Tree (GNode e) -- ^ "node sizes"+ -> Tree Size -- ^ tree Sizes+ -> TreeLayout e++treeLayout2 style+ (Position startX startY)+ (T.Node _root subtrees)+ (T.Node gnode subGNodes) + (T.Node (Size treeWidth treeHeight) subTreeSizes) + = + let + nodeHeight = bbHeight (gnodeNodeBB gnode)+ -- center the root in its level+ -- UNUSED:+ -- nodeCenterX = startX + treeWidth / 2+ -- nodeCenterY = startY + nodeHeight++ -- center the subtrees (may be wider or narrower than the root)+ subtreesTotalWidth = paddedWidth style subTreeSizes+ subX = startX + (treeWidth - subtreesTotalWidth) / 2 -- first subtree+ subY = startY + nodeHeight + vpad style++ sublayouts :: (Repr e) =>+ Double -> [Tree e] -> [Tree (GNode e)] -> [Tree Size] -> + [TreeLayout e]+ sublayouts _ [] [] [] = []+ sublayouts x (t:ts) (g:gs) (s:ss) = + treeLayout2 style (Position x subY) t g s :+ sublayouts (x + sizeW (rootLabel s) + hpad style) ts gs ss+ sublayouts _ _ _ _ = error "treeLayout2: mismatched list lengths"++ in T.Node (LayoutNode -- root node+ (centerGNode gnode startX startY treeWidth nodeHeight) -- node+ (BBox startX startY treeWidth treeHeight)) -- whole tree+ (sublayouts subX subtrees subGNodes subTreeSizes)++-- | Center a GNode in a rectangular area+centerGNode :: GNode e -> Double -> Double -> Double -> Double -> GNode e+centerGNode gnode startx starty awidth aheight =+ let cx = startx + awidth / 2 -- desired center+ cy = starty + aheight / 2+ Position cgx cgy = bbCenter (gnodeNodeBB gnode)+ in translate (cx - cgx) (cy - cgy) gnode+ +treeLayoutPaddedSize :: Style -> TreeLayout e -> Size+treeLayoutPaddedSize style tlo =+ let Size w h = treeLayoutSize tlo+ in Size (w + 2.0 * hpad style) (h + 2.0 * vpad style)++treeLayoutSize :: TreeLayout e -> Size+treeLayoutSize tlo = + let BBox _x _y w h = layoutTreeBB tlo in Size w h++layoutTreeMoveCenterTo :: + Double -> Double -> TreeLayout e -> TreeLayout e+layoutTreeMoveCenterTo newX newY layoutTree =+ let Position oldX oldY = bbCenter (nodeTreeBB (rootLabel layoutTree))+ in translate (newX - oldX) (newY - oldY) layoutTree++treeLayoutAddMargin :: TreeLayout e -> Double -> TreeLayout e+treeLayoutAddMargin tree margin =+ let LayoutNode {nodeGNode = rootGNode, nodeTreeBB = treeBB} =+ rootLabel tree+ subtrees = subForest tree+ BBox x y w h = treeBB+ treeBB' = BBox (x - margin) (y - margin) + (w + 2 * margin) (h + 2 * margin)+ root' = translate margin margin (LayoutNode rootGNode treeBB')+ subtrees' = translate margin margin subtrees+ in T.Node root' subtrees'++ ++treeLayoutWidth :: TreeLayout e -> Double+treeLayoutWidth = bbWidth . layoutTreeBB++treeLayoutWiden :: TreeLayout e -> Double -> TreeLayout e+treeLayoutWiden tlo minWidth =+ let w = treeLayoutWidth tlo+ in+ if w >= minWidth+ then tlo+ else let dw = minWidth - w+ T.Node root subs = translate (dw / 2) 0 tlo+ LayoutNode rootGNode treeBB = root+ root' = LayoutNode rootGNode + (translate (-dw / 2) + 0 + (widen treeBB minWidth))+ in T.Node root' subs+++
+ Data/Sifflet/WGraph.hs view
@@ -0,0 +1,478 @@+module Data.Sifflet.WGraph + (WNode(..), WEdge(..), WGraph, WContext+ , wgraphNew, isWSimple, isWFrame, grInsertNode, grRemoveNode+ , connectToFrame+ , grConnect, grDisconnect+ , grAddGraph+ , grExtractExprTree, grExtractLayoutNode, grExtractLayoutTree+ + , wlab, llab, nodeExprNode, nodeText, nodeValue+ , nodeBBox, nodePosition, nodeInputValues+ , graphOrphans, adoptChildren+ , nodeAllChildren, nodeSimpleChildren, allDescendants, nodeFrameChildren+ , nodeAllSimpleDescendants, nodeProperSimpleDescendants+ , nodeIsSimple, nodeIsOpen, nodeContainerFrameNode+ , nodeParent++ , grUpdateFLayout, grUpdateTreeLayout+ , printWGraph+ , translateNodes+ , translateNode, grRelabelNode+ , translateTree+ , functoidParts, functionToParts+ -- Additional operations on graphs+ , nfilter+ )++where ++import Data.List (sort)+import Data.Maybe (fromJust)++import Data.Graph.Inductive as G++import Data.Sifflet.Functoid+import Data.Sifflet.Geometry+import Data.Sifflet.Tree as T -- exports Data.Tree.Tree(Node), etc.+import Data.Sifflet.TreeLayout++import Language.Sifflet.Expr+import Language.Sifflet.ExprTree++import Text.Sifflet.Repr ()++import Language.Sifflet.Util++-- | A WGraph consists of WNodes with (sort of) Int-labled edges;+-- the edge labels serve to order the children of a node.++type WGraph = Gr WNode WEdge++type WContext = Context WNode WEdge++newtype WEdge = WEdge Int+ deriving (Eq, Read, Show)++instance Ord WEdge where+ compare (WEdge i) (WEdge j) = compare i j++-- | Two kinds of WNodes:+-- A WSimple node represents a node in an expression tree, e.g., "if", "+"+-- A WFrame node represents a panel or frame that displays an expression tree,+-- function call, or something similar.++data WNode = WSimple (LayoutNode ExprNode) + | WFrame G.Node+ deriving (Eq, Show)++instance Repr WNode where+ repr (WSimple lnode) = repr (gnodeValue (nodeGNode lnode))+ repr (WFrame _) = "<frame>"++wgraphNew :: WGraph+wgraphNew = empty++isWSimple :: WNode -> Bool+isWSimple (WSimple _) = True+isWSimple _ = False++isWFrame :: WNode -> Bool+isWFrame (WFrame _) = True+isWFrame _ = False++-- | Print a description of the WGraph+printWGraph :: WGraph -> IO ()+printWGraph g = + let vs = nodes g+ wnodeLabel v =+ case lab g v of + Just (WFrame v') ->+ "(WFrame with reference to vertex " ++ show v' ++ ")"+ Just (WSimple (LayoutNode {nodeGNode = gnode})) ->+ repr (gnodeValue gnode)+ Nothing ->+ "(unlabeled!)"+ printVertex v = do+ putStrLn $ "Vertex " ++ show v ++ + " with label " ++ wnodeLabel v+ putStrLn $ " " ++ show (indeg g v) ++ " predecessors: " +++ show [(v', wnodeLabel v') | v' <- pre g v]+ putStrLn $ " " ++ show (outdeg g v) ++ " successors: " +++ show [(v', wnodeLabel v') | v' <- suc g v]+ in do+ putStrLn $ "A WGraph with " ++ show (length vs) ++ " vertices"+ mapM_ printVertex vs++-- | Insert new node with given label into graph,+-- without any new edges;+-- return the new graph and the new node (number)+grInsertNode :: (DynGraph g) => g n e -> n -> (g n e, G.Node)+grInsertNode graph label =+ let (_, hi) = nodeRange graph+ newNode = succ hi+ nodeContext = ([], newNode, label, [])+ graph' = nodeContext & graph+ in (graph', newNode)++-- | Remove a node from the graph; return the updated graph.+grRemoveNode :: (DynGraph g) => g n e -> G.Node -> (g n e)+grRemoveNode graph node = + let (mctx, g') = match node graph+ in case mctx of+ Nothing -> errcats ["grRemoveNode: node not found:", show node]+ Just _ -> g'++-- | Connect parent to child, using inlet as the order of the child+-- (0, 1, ...). outlet is ignored, since there is only outlet 0.+-- As rendered, the parent's inlet-th inlet will have a line+-- to the child's outlet-th outlet.+-- This is achieved by inserting a labeled edge (parent, child, inlet)+-- and clearing any incompatible edge. The incompatibles are:+-- a. from same parent on same inlet to a different child.+-- b. from the same parent on a different inlet to the same child.+-- c. from same child (on same outlet) to a different parent.+--+-- NOTE: This is confusing, because, from the data flow perspective,+-- data flows OUT of the child INTO the parent, but from the+-- "tree in graph" perspective, links are directed OUT of the parent+-- INTO the child. So beware!++grConnect :: WGraph -> G.Node -> WEdge -> G.Node -> WEdge -> WGraph+grConnect g parent inlet child _outlet =+ let (mPcontext, g') = match parent g+ in case mPcontext of+ Nothing -> error "grConnect: parent not found"+ -- should parent below be same as parent above?+ Just (pins, jparent, plabel, pouts) ->+ -- jparent == parent+ let pouts' = filter (edgeNotTo child)+ (filter (edgeNotEqual inlet) pouts)+ (mCcontext, g'') = match child g'+ in case mCcontext of+ Nothing -> error "grConnect: child not found"+ Just (_cins, jchild, clabel, couts) ->+ -- jchild == child+ -- remove ANY links into the jchild (c)+ let cins' = []+ in + (pins, jparent, plabel, (inlet, jchild) : pouts') & + ((cins', jchild, clabel, couts) & g'')++edgeNotEqual :: WEdge -> (WEdge, G.Node) -> Bool+edgeNotEqual edge pair = edge /= fst pair++edgeNotTo :: G.Node -> (WEdge, G.Node) -> Bool+edgeNotTo node pair = node /= snd pair++-- | Removes a link between parent and child+-- where the edge was labeled inlet (order of child).+-- Ignores outlet, which should always be 0.+-- If child is not the inlet-th child of parent,+-- well, this is an error, but grDisconnect ignores it.+-- If toFrameP is true, the child node is+-- reconnected as a child to its frame+grDisconnect :: WGraph -> G.Node -> WEdge -> G.Node -> WEdge -> Bool -> WGraph+grDisconnect g parent inlet child _outlet toFrameP =+ let (mcontext, g') = match parent g+ g'' = case mcontext of+ Nothing -> error "grDisconnect: parent not found"+ Just (ins, jparent, label, outs) ->+ -- jparent == parent+ let outs' = filter (/= (inlet, child)) outs+ in (ins, jparent, label, outs') & g'+ in if toFrameP+ then connectToFrame child (nodeContainerFrameNode g parent) g''+ else g''++connectToFrame :: G.Node -> G.Node -> WGraph -> WGraph+connectToFrame child frameNode g =+ insEdge (frameNode, child, WEdge (outdeg g frameNode)) g++grAddGraph :: (DynGraph g) => g n e -> g n e -> g n e+grAddGraph g1 g2 =+ let (_, hi1) = nodeRange g1+ (lo2, _) = nodeRange g2+ diff = hi1 - lo2 + 1++ adjIncr :: Adj e -> Adj e+ adjIncr adj = [(x, node + diff) | (x, node) <- adj]++ ctxIncr :: Context n e -> Context n e+ ctxIncr (adjFrom, node, label, adjTo) = + (adjIncr adjFrom, node + diff, label, adjIncr adjTo)++ loop :: (DynGraph g) => g n e -> g n e -> g n e+ loop ga gb =+ if isEmpty gb+ then ga+ else let (acontext, gb') = matchAny gb in+ ctxIncr acontext & loop ga gb'+ in loop g1 g2++-- | Extract from a graph the expression with root node n, +-- returning a Tree of ExprNode.+-- Use only the WSimple nodes of the graph (and n had better be one).+grExtractExprTree :: WGraph -> G.Node -> Tree ExprNode+grExtractExprTree g = fmap layoutNodeSource . grExtractLayoutTree g++-- ------------------------------------------------------------+-- | Finding characteristics of the WNodes in a graph+-- It is an implicit error if there is no label for the node++-- | wlab is like lab with no Maybe: the node *must* have a label+wlab :: WGraph -> Node -> WNode+wlab g n = fromJust (lab g n)++-- | llab is the tree layout node of a WSimple node+llab :: WGraph -> Node -> LayoutNode ExprNode+llab g n = + case wlab g n of+ WSimple lnode -> lnode+ WFrame _fnode ->+ errcat ["llab: node is not simple",+ "\nnode ", (show n),+ "\n in graph\n",+ (show g)]++-- | The ExprNode represented by the graph node+nodeExprNode :: WGraph -> Node -> ExprNode+nodeExprNode g n = gnodeValue (nodeGNode (llab g n))++-- | The repr of the node's value+nodeText :: WGraph -> Node -> String+nodeText g n = tbText (head (gnodeTextBoxes (nodeGNode (llab g n))))++-- | The result of an evaluated node in an expression tree+nodeValue :: WGraph -> Node -> EvalResult+nodeValue g n = mvalue+ where ENode _ mvalue = nodeExprNode g n++-- | The node's BBox+nodeBBox :: WGraph -> Node -> BBox+nodeBBox g n = gnodeNodeBB (nodeGNode (llab g n))++nodePosition :: WGraph -> Node -> Position+nodePosition g = bbPosition . nodeBBox g++nodeInputValues :: WGraph -> Node -> EvalResult+nodeInputValues graph node = + mapM (nodeValue graph) (nodeSimpleChildren graph node) >>= + EvalOk . VList++-- | Find all parentless nodes in a graph+graphOrphans :: (Graph graph) => graph a b -> [Node]+graphOrphans g = filter (\ v -> pre g v == []) (nodes g)+ +-- | Connect the given children to a new parent+adoptChildren :: WGraph -> G.Node -> [G.Node] -> WGraph+adoptChildren g0 parent children =+ let adopt g child = insEdge (parent, child, WEdge (outdeg g parent)) g+ in foldl adopt g0 children++-- | Finding the children (nodes, numbers) of a node in a graph :+-- all children, only WSimple-labeled children, only WFrame-labeled children+-- When constructing the graph, ordered children of a tree node +-- get graph node numbers in ascending order; therefore,+-- sorting the graph nodes gives back the original order of+-- children in the tree (plus WFrames that are added later,+-- and those should always be after the simple children)++nodeAllChildren :: WGraph -> Node -> [Node]+nodeAllChildren g n = sort (suc g n) ++nodeSimpleChildren :: WGraph -> Node -> [Node]+nodeSimpleChildren g n = filter (nodeIsSimple g) (nodeAllChildren g n)++-- | All (proper and improper) descendants of a node in a graph+allDescendants :: (Graph graph) => graph a b -> Node -> [Node]+allDescendants g n = reachable n g++nodeAllSimpleDescendants :: WGraph -> Node -> [Node]+nodeAllSimpleDescendants g n = + n : concatMap (nodeAllSimpleDescendants g) (nodeSimpleChildren g n)++nodeProperSimpleDescendants :: WGraph -> Node -> [Node]+nodeProperSimpleDescendants g n = tail (nodeAllSimpleDescendants g n)++nodeFrameChildren :: WGraph -> Node -> [Node]+nodeFrameChildren g n = filter (not . nodeIsSimple g) (nodeAllChildren g n)+ +nodeIsSimple :: WGraph -> Node -> Bool+nodeIsSimple g n = + -- is node n of graph g a WSimple node?+ case lab g n of+ Just (WSimple _) -> True+ _ -> False++-- | An open node has a WFrame-labeled child+nodeIsOpen :: WGraph -> Node -> Bool+nodeIsOpen graph node = nodeFrameChildren graph node /= []++-- The root node of the tree in which node occurs.+-- This is a WSimple node, it may not back up to a WFrame node.+--+-- THIS CODE IS UNTESTED, AND CURRENTLY UNUSED (2009/2/4)++-- -- nodeTreeRoot: unused+-- nodeTreeRoot :: WGraph -> Node -> Node+-- nodeTreeRoot g n = findRoot n+-- where findRoot node = +-- case pre g node of+-- [] -> (error "nodeTreeRoot: no simple root found")+-- [parent] -> +-- if not (nodeIsSimple g parent)+-- then node+-- else findRoot parent+-- _ -> error "nodeTreeRoot: node has multiple parents"++-- -- The root (a WSimple node) of the tree in which a node occurs+-- -- Huh, don't need this!++-- -- nodeSimpleRoot: unused+-- nodeSimpleRoot :: WGraph -> Node -> Node+-- nodeSimpleRoot = undefined++-- | The graph node of the frame that contains the given node+nodeContainerFrameNode :: WGraph -> Node -> Node+nodeContainerFrameNode g n = + let findFrame node =+ if not (nodeIsSimple g node)+ then node+ else case pre g node of+ [parent] -> findFrame parent+ [] -> err "has no parent but is not a frame node" node+ _:_ -> err "has multiple parents" node+ err msg node =+ errcat["nodeContainerFrameNode: node ",+ show node, " ", msg, "\n",+ "in graph\n", show g]+ + in findFrame n++-- | The parent (if any) of a node+nodeParent :: WGraph -> Node -> Maybe Node+nodeParent g n = + case pre g n of+ [] -> Nothing+ [parent] -> Just parent+ _ -> error "nodeParent: multiple parents"+++grUpdateFLayout :: WGraph -> [G.Node] -> FunctoidLayout -> WGraph+grUpdateFLayout gr ns tlo =+ case tlo of+ FLayoutTree t -> + case ns of+ [rootNode] -> grUpdateTreeLayout gr rootNode t+ _ -> error "grUpdateFLayout: tree tlo, but not a single root"+ FLayoutForest f _b ->+ let accum g (n, t) = grUpdateTreeLayout g n t+ in foldl accum gr (zip ns f)++-- | Replace the tree embedded in graph g with root n, with a new tree.+grUpdateTreeLayout :: WGraph -> G.Node -> TreeLayout ExprNode -> WGraph+grUpdateTreeLayout gr n0 t0 = updateTree gr n0 t0+ where updateTree g n (T.Node root subtrees) =+ case match n g of+ (Just (adjFrom, jn, WSimple _, adjTo), g1) ->+ -- jn == n+ let g2 = (adjFrom, jn, WSimple root, adjTo) & g1+ in updateForest g2 (nodeSimpleChildren g jn) subtrees+ (Just (_adjFrom, _n, _, _adjTo), _) ->+ error "grUpdateTreeLayout: root node is not a WSimple"+ (Nothing, _) -> + errcats ["grUpdateTreeLayout: no such node:", show n]++ updateForest g [] [] = g+ updateForest g (n:ns) (t:ts) = + case lab g n of+ Just (WSimple _) -> updateForest (updateTree g n t) ns ts+ Just (WFrame _) -> updateForest g ns (t:ts) -- shouldn't happen!+ Nothing -> error "grUpdateTreeLayout: no label for node"++ updateForest _g _ [] = error "too many ns"+ updateForest _g [] _ = error "too many ts"++-- | Extract just the single tree layout node of the given graph node+grExtractLayoutNode :: WGraph -> G.Node -> LayoutNode ExprNode+grExtractLayoutNode g n =+ case lab g n of+ Just (WSimple lnode) -> lnode+ _ -> errcats ["grExtractLayoutNode:",+ "no label for node, or node is not WSimple:",+ "node", show n]++-- | Extract the tree layout (tree) descended from the given root node+grExtractLayoutTree :: WGraph -> G.Node -> TreeLayout ExprNode+grExtractLayoutTree g n =+ T.Node (grExtractLayoutNode g n)+ (map (grExtractLayoutTree g) (nodeSimpleChildren g n))+++-- | Translate the nodes forming a tree with the given root+translateTree :: Double -> Double -> WGraph -> G.Node -> WGraph+translateTree dx dy wgraph root = + grUpdateTreeLayout wgraph root + (translate dx dy (grExtractLayoutTree wgraph root))+++translateNodes :: Double -> Double -> WGraph -> [G.Node] -> WGraph+translateNodes dx dy = foldl (translateNode dx dy)++translateNode :: Double -> Double -> WGraph -> G.Node -> WGraph+translateNode dx dy wg node =+ case match node wg of+ (Nothing, _) ->+ errcats ["translateNode: no such node:", show node]+ (Just (adjFrom, jnode, WSimple layoutNode, adjTo), g') ->+ -- jnode == node+ (adjFrom, jnode, WSimple (translate dx dy layoutNode), adjTo) & g'+ (Just _, _) ->+ errcats ["translateNode: node is not a WSimple:", show node]+++-- | Replace the label of a node in a graph+grRelabelNode :: (DynGraph g) => g a b -> G.Node -> a -> g a b+grRelabelNode g n newLabel =+ case match n g of+ (Just (adjFrom, jn, _oldLabel, adjTo), g') ->+ -- jn == n+ (adjFrom, jn, newLabel, adjTo) & g'+ (Nothing, _) -> errcats ["grRelabelNode: no such node:", show n]++-- | Get the parts of a Functoid.+-- See note on functionToParts (just below).+-- Seems to be unused ***++functoidParts :: Functoid -> WGraph -> G.Node -> Functoid+functoidParts functoid graph frameNode =+ case functoid of+ fp@FunctoidParts {} -> fp+ FunctoidFunc function -> functionToParts function graph frameNode++-- | Convert a function to its parts.+-- COULDN'T THIS BE DONE USING the function's implementation,+-- and not need to use the graph? Then this could go in Functoid.hs+-- without circular import between it and WGraph+functionToParts :: Function -> WGraph -> G.Node -> Functoid+functionToParts (Function mname _atypes _rtype impl) graph frameNode =+ case impl of + Primitive _ -> error "functionToParts: function is primitive"+ Compound argnames _body ->+ FunctoidParts {fpName = case mname of+ Nothing -> "unnamed function"+ Just jname -> jname,+ fpArgs = argnames, + fpNodes = nodeProperSimpleDescendants graph frameNode}++++-- | Filter the nodes of a graph++nfilter :: (Node -> Bool) -> Gr v e -> Gr v e+nfilter f g =+ nfilter' f g (nodes g)++nfilter' :: (Node -> Bool) -> Gr v e -> [Node] -> Gr v e+nfilter' _f g [] = g+nfilter' f g (n:ns) = nfilter' f (if f n then g else delNode n g) ns
+ Graphics/Rendering/Sifflet/Draw.hs view
@@ -0,0 +1,223 @@+module Graphics.Rendering.Sifflet.Draw+ ( + Draw(..), DrawMode(..)+ , drawBox+ , drawTextBox+ , modeTextCol, modeEdgeCol, modeFillCol+ , setColor++ )++where++import Control.Monad+import Graphics.Rendering.Cairo hiding (translate)++import Data.Sifflet.Functoid+import Data.Sifflet.Geometry+import Data.Sifflet.Tree+import Data.Sifflet.TreeLayout++setColor :: VColor -> Render ()++setColor (ColorRGB red green blue) =+ setSourceRGB red green blue ++setColor (ColorRGBA red green blue alpha) =+ setSourceRGBA red green blue alpha++-- Drawing things+-- THINK: is this class well conceived?+-- A lot of things could be done with draw and translate+-- if it were not tied to Style and DrawMode++class Draw a where+ draw :: Style -> DrawMode -> a -> Render ()++-- VV DrawMode is now very awkward, since +-- a node needs to know not only if it is selected,+-- but which port(s) are selected++data DrawMode = DrawNormal | DrawActive + | DrawSelectedNode + | DrawSelectedInlet Int + | DrawSelectedOutlet Int+ deriving (Eq)++instance Draw FunctoidLayout where+ draw style mode (FLayoutTree t) = draw style mode t+ draw style mode (FLayoutForest f _) = draw style mode f++instance (Draw e) => Draw [e] where+ draw style mode = mapM_ (draw style mode)++instance (Draw e) => Draw (Tree e) where+ draw style mode t = do+ draw style mode (rootLabel t)+ draw style mode (subForest t)++instance Draw (LayoutNode e) where+ draw style mode (LayoutNode gnode _treeBB) = draw style mode gnode++instance Draw (GNode e) where++ draw style mode (GNode _value textboxes nodeBB inlets outlets) = + do+ -- draw node box+ let (nodeTextCol, nodeEdgeCol, nodeFillCol) = + case mode of+ DrawActive -> + (styleActiveTextColor, + styleActiveEdgeColor,+ styleActiveFillColor)+ DrawSelectedNode -> + (styleSelectedTextColor, + styleSelectedEdgeColor,+ styleSelectedFillColor)+ _ -> (styleNormalTextColor, + styleNormalEdgeColor, + styleNormalFillColor)+ + -- (overall box for the node)+ when (styleShowNodeBoxes style) $+ drawBox (Just (nodeFillCol style))+ (Just (nodeEdgeCol style)) nodeBB+ ++ -- assert textboxes has one or two elements+ -- draw the first text box+ drawTextBox (Just (styleFont style))+ (Just (nodeFillCol style)) -- background+ Nothing -- frame color+ (nodeTextCol style) -- text color+ (head textboxes)++ -- draw the second textbox, if any, using "aux" style+ case (tail textboxes) of+ [tbAux] -> + drawTextBox (Just (styleAuxFont style))+ Nothing+ Nothing+ (styleAuxColor style)+ tbAux+ _ -> return ()+++ -- Draw the iolets+ when (styleShowNodePorts style) $ do+ drawInlets style mode inlets+ drawOutlets style mode outlets+ ++instance Draw TextBox where+ draw style mode =+ drawTextBox (Just (styleFont style))+ (Just (modeFillCol mode style)) + Nothing+ (modeTextCol mode style) ++drawTextBox :: Maybe VFont -> Maybe VColor -> Maybe VColor -> VColor ->+ TextBox -> Render ()+drawTextBox mfont mbgcolor mframecolor textcolor + (TextBox text textBB boxBB) = do+ let BBox textX textY _textW _textH = textBB+ drawBox mbgcolor mframecolor boxBB+ setColor textcolor+ case mfont of + (Just font) -> setFont font+ _ -> return ()+ moveTo textX textY+ showText text++instance Draw BBox where+ draw style mode = + drawBox (Just (modeFillCol mode style)) + (Just (modeEdgeCol mode style)) ++drawBox :: Maybe VColor -> Maybe VColor -> BBox -> Render ()+drawBox mBgColor mFgColor (BBox x y w h) = + -- draw the BBox, in the specified colors, irrespective of style+ let setup color = + do+ rectangle x y w h+ setColor color+ in case (mBgColor, mFgColor) of+ (Just bgColor, Just fgColor) ->+ do+ setup bgColor+ fillPreserve+ setColor fgColor+ stroke+ (Just bgColor, Nothing) -> + do+ setup bgColor+ fill+ (Nothing, Just fgColor) -> + do+ setup fgColor+ stroke+ _ -> return ()++instance Draw Position where+ draw _style _mode _pos = return () -- bare points are invisible ??? ******++instance Draw Iolet where+ draw style mode (Iolet circle) = draw style mode circle++drawIolet :: Iolet -> VColor -> VColor -> Render ()+drawIolet (Iolet circle) = drawCircle circle++drawInlets :: Style -> DrawMode -> [Iolet] -> Render ()+drawInlets style mode inlets =+ let selected i = mode == DrawSelectedInlet i+ in drawIolets selected style inlets++drawOutlets :: Style -> DrawMode -> [Iolet] -> Render ()+drawOutlets style mode outlets =+ let selected o = mode == DrawSelectedOutlet o+ in drawIolets selected style outlets++drawIolets :: (Int -> Bool) -> Style -> [Iolet] -> Render ()+drawIolets selected style iolets =+ -- (selected n) should be true iff n is the selected iolet+ let loop _ [] = return ()+ loop n (p:ps) = + uncurry (drawIolet p)+ (if selected n + then (styleSelectedFillColor style, + styleSelectedEdgeColor style)+ else (styleNormalFillColor style, + styleNormalEdgeColor style)) >>+ loop (n + 1) ps+ in loop 0 iolets+ +instance Draw Circle where+ draw style mode circle = + drawCircle circle (modeFillCol mode style) (modeEdgeCol mode style) ++drawCircle :: Circle -> VColor -> VColor -> Render ()+drawCircle (Circle (Position x y) r) bgColor fgColor = do+ newPath -- otherwise we get a line to the arc+ arc x y r 0 (2 * pi)+ setColor bgColor+ fillPreserve+ setColor fgColor+ stroke++-- Helper functions to find the background and foreground colors for a mode++modeFillCol :: DrawMode -> (Style -> VColor)+modeFillCol DrawNormal = styleNormalFillColor+modeFillCol DrawActive = styleActiveFillColor+modeFillCol _ = styleSelectedFillColor++modeTextCol :: DrawMode -> (Style -> VColor)+modeTextCol DrawNormal = styleNormalTextColor+modeTextCol DrawActive = styleActiveTextColor+modeTextCol _ = styleSelectedTextColor++modeEdgeCol :: DrawMode -> (Style -> VColor)+modeEdgeCol DrawNormal = styleNormalEdgeColor+modeEdgeCol DrawActive = styleActiveEdgeColor+modeEdgeCol _ = styleSelectedEdgeColor+
+ Graphics/Rendering/Sifflet/DrawTreeGraph.hs view
@@ -0,0 +1,182 @@+-- | Tree graph rendering+module Graphics.Rendering.Sifflet.DrawTreeGraph+ (+ graphQuickView+ , graphWriteImageFile, graphRender, treeRender+ , treeWriteImageFile, gtkShowTree+ )++where++import System.IO+import Data.IORef+import System.Cmd+import Graphics.UI.Gtk.Gdk.EventM++import Graphics.Rendering.Cairo hiding (translate)+import Data.Graph.Inductive as G++import Graphics.UI.Sifflet.LittleGtk++import Data.Sifflet.Geometry+import Data.Sifflet.Tree+import Data.Sifflet.TreeGraph+import Data.Sifflet.TreeLayout+import Graphics.Rendering.Sifflet.Draw+import Text.Sifflet.Repr ()++-- ============================================================+-- GRAPH VIEWING AND RENDERING+-- ============================================================++-- Quick view of a graph using GraphViz++graphQuickView :: (Graph g, Show a, Show b) => g a b -> IO ()+graphQuickView g = + let dot_src = graphviz g "graphQuickView" (6, 4) (1, 1) Portrait+ dot_file = "tmp.dot"+ png_file = "tmp.png"+ in do+ h <- openFile dot_file WriteMode+ hPutStr h dot_src+ hClose h+ _ <- system ("dot -Tpng -o" ++ png_file ++ " " ++ dot_file)+ _ <- system ("feh " ++ png_file)+ return ()+++graphWriteImageFile :: (Repr n) => + Style -> Maybe Node -> Maybe Node ->+ Double -> Double -> + LayoutGraph n e -> String -> + IO String +graphWriteImageFile style mactive mselected dwidth dheight graph file = do+ withImageSurface FormatARGB32 (round dwidth) (round dheight) $ \surf -> do+ renderWith surf $ + graphRender style mactive mselected graph+ surfaceWriteToPNG surf file+ return file++graphRender :: (Repr n) => + Style -> Maybe Node -> Maybe Node -> LayoutGraph n e -> + Render ()+graphRender style mactive mselected graph = do+ let renderNode :: Node -> Render ()+ renderNode node = do+ let Just layoutNode = lab graph node + -- ^^ is this safe? it can't be Nothing?+ nodeBB = gnodeNodeBB (nodeGNode layoutNode)+ active = (mactive == Just node)+ selected = case mselected of+ Nothing -> False+ Just sel -> sel == node+ mode = if active then DrawActive+ else if selected then DrawSelectedNode -- !!!+ else DrawNormal+ xcenter = bbXCenter nodeBB+ draw style mode layoutNode+ connectParent node xcenter (bbTop nodeBB)+ return ()++ connectParent node x y = + let parents = pre graph node in+ case parents of+ [] -> return ()+ [parent] -> do+ let Just playoutNode = lab graph parent+ parentBB = gnodeNodeBB (nodeGNode playoutNode)+ px = bbXCenter parentBB+ py = bbBottom parentBB+ setColor (styleNormalEdgeColor style)+ moveTo px (py + snd (vtinypad style)) -- bottom of parent+ lineTo x (y - fst (vtinypad style)) -- top of node+ stroke++ _ -> error "Too many parents"+ + setAntialias AntialiasDefault++ -- canvas background+ setColor (styleNormalFillColor style)+ let Just layoutNode = lab graph 1 -- root node, represents whole tree+ BBox x y bwidth bheight = nodeTreeBB layoutNode+ rectangle x y bwidth bheight+ fill++ -- draw the graph/tree+ setLineWidth (lineWidth style)+ mapM_ renderNode (nodes graph)++treeRender :: (Repr e) => Style -> TreeLayout e -> Render ()+treeRender style = graphRender style Nothing Nothing . orderedTreeToGraph++treeWriteImageFile :: (Repr e) => + Style -> IoletCounter e -> Tree e -> String -> IO String++treeWriteImageFile style counter atree filename = do+ let tlo = treeLayout style counter atree+ Size surfWidth surfHeight = treeLayoutPaddedSize style tlo+ withImageSurface FormatARGB32 (round surfWidth) (round surfHeight) $ + \ surf -> do+ renderWith surf $ treeRender style tlo+ surfaceWriteToPNG surf filename+ return filename++-- ============================================================+-- Simple tree viewing.+-- gtkShowTree displays a single tree very simply+-- Works for any kind of (Repr e, Show e) => Tree e.++gtkShowTree :: (Repr e, Show e) => + Style -> IoletCounter e -> Tree e -> IO ()+gtkShowTree style counter atree = do+ let tlo = treeLayout style counter atree+ Size dwidth dheight = treeLayoutPaddedSize style tlo++ tloRef <- newIORef tlo++ _ <- initGUI++ -- init window+ window <- windowNew+ set window [windowTitle := "Test Cairo Tree"]+ _ <- onDestroy window mainQuit++ -- init vbox+ vbox <- vBoxNew False 5 -- width not homogeneous; spacing+ set window [containerChild := vbox]++ -- init canvas+ canvas <- layoutNew Nothing Nothing+ _ <- onSizeRequest canvas + (return (Requisition (round dwidth) (round dheight)))+ widgetSetCanFocus canvas True -- to receive key events++ -- event handlers+ _ <- on canvas exposeEvent (updateCanvas style canvas tloRef)+ _ <- on canvas keyPressEvent (keyPress window)++ -- pack, show, and run+ boxPackStartDefaults vbox canvas+ widgetShowAll window+ mainGUI++updateCanvas :: (Repr e) => Style -> Layout -> IORef (TreeLayout e) + -> EventM EExpose Bool+updateCanvas style canvas tloRef = + tryEvent $ liftIO $ do+ { + tlo <- readIORef tloRef+ ; win <- layoutGetDrawWindow canvas+ ; renderWithDrawable win (treeRender style tlo)+ }++keyPress :: Window -> EventM EKey Bool+keyPress window =+ tryEvent $ do+ {+ kname <- eventKeyName+ ; case kname of+ "q" -> liftIO $ widgetDestroy window -- implies mainQuit+ _ -> stopEvent+ }
+ Graphics/UI/Sifflet.hs view
@@ -0,0 +1,30 @@+{- Workspace.hs implements the graphical workspace of Sifflet -}++module Graphics.UI.Sifflet+ (+ module Graphics.UI.Sifflet.Frame+ , module Graphics.UI.Sifflet.LittleGtk+ , module Graphics.UI.Sifflet.RPanel+ , module Graphics.UI.Sifflet.Types+ , module Graphics.UI.Sifflet.Callback+ , module Graphics.UI.Sifflet.Canvas+ , module Graphics.UI.Sifflet.Tool+ , module Graphics.UI.Sifflet.Workspace+ , module Graphics.UI.Sifflet.GtkForeign+ , module Graphics.UI.Sifflet.GtkUtil+ , module Graphics.UI.Sifflet.Window+ )++where++import Graphics.UI.Sifflet.Callback+import Graphics.UI.Sifflet.Canvas+import Graphics.UI.Sifflet.Frame+import Graphics.UI.Sifflet.Tool+import Graphics.UI.Sifflet.Workspace+import Graphics.UI.Sifflet.GtkForeign+import Graphics.UI.Sifflet.GtkUtil+import Graphics.UI.Sifflet.LittleGtk+import Graphics.UI.Sifflet.RPanel+import Graphics.UI.Sifflet.Types+import Graphics.UI.Sifflet.Window
+ Graphics/UI/Sifflet/Callback.hs view
@@ -0,0 +1,152 @@+module Graphics.UI.Sifflet.Callback+ (+ CBMgr, CBMgrAction, CBMgrCmd(..), mkCBMgr+ , MenuSpec(..), MenuItemSpec(..), MenuItemAction+ , createMenuBar, addMenu, createMenu, createMenuItem+ , modifyIORefIO+ ) ++where++import Data.IORef++import Graphics.UI.Gtk++import Graphics.UI.Sifflet.Types++-- | The CBMgr (Callback manager) encapsulates (in an enclosure, no less!)+-- an IORef VPUI. It is used *solely* to set up callbacks+-- and similar stuff in Gtk, where the callback needs access+-- to the IORef. By passing a CBMgr to a function, we can+-- avoid passing the IORef directly, and all the harm and+-- confusion that could result.+--+-- We only need *one* CBMgr for the application;+-- however, two CBMgrs with the same IORef are logically equivalent,+-- so there would be no harm in having two as long as they share one IORef.+type CBMgr = CBMgrCmd -> IO ()++type CBMgrAction = IORef VPUI -> IO ()++-- | Commands for the CBMgr+data CBMgrCmd+ = -- window events+ OnWindowConfigure Window (IORef VPUI -> EventM EConfigure Bool)+ | OnWindowDestroy Window CBMgrAction+ | AfterWindowKeyPress Window (IORef VPUI -> EventM EKey Bool)+ -- layout events+ | OnLayoutExpose Layout (IORef VPUI -> EventM EExpose Bool)+ | OnLayoutMouseMove Layout (IORef VPUI -> EventM EMotion Bool)+ | OnLayoutButtonPress Layout (IORef VPUI -> EventM EButton Bool)+ | OnLayoutButtonRelease Layout (IORef VPUI -> EventM EButton Bool)+ -- other events+ | OnMenuItemActivateLeaf MenuItem (VPUI -> IO VPUI)+ | OnEntryActivate Entry CBMgrAction+ | AfterButtonClicked Button CBMgrAction++ -- Surrender the UIRef to an arbitrary action+ | WithUIRef CBMgrAction ++ | UMTest++-- | Create the CBMgr+mkCBMgr :: IORef VPUI -> CBMgr+mkCBMgr uiref cmd = + case cmd of+ -- window events+ OnWindowConfigure window action ->+ on window configureEvent (action uiref) >> return ()+ OnWindowDestroy window action ->+ onDestroy window (action uiref) >> return ()+ AfterWindowKeyPress window action ->+ after window keyPressEvent (action uiref) >> return ()+ -- layout events+ OnLayoutExpose layout action ->+ on layout exposeEvent (action uiref) >> return ()+ OnLayoutMouseMove layout action ->+ on layout motionNotifyEvent (action uiref) >> return ()+ OnLayoutButtonPress layout action ->+ on layout buttonPressEvent (action uiref) >> return ()+ OnLayoutButtonRelease layout action ->+ on layout buttonReleaseEvent (action uiref) >> return ()+ -- other events+ OnMenuItemActivateLeaf menuItem action ->+ onActivateLeaf menuItem (modifyIORefIO action uiref) >> return ()+ OnEntryActivate entry action ->+ onEntryActivate entry (action uiref) >> return ()+ AfterButtonClicked button action ->+ afterClicked button (action uiref) >> return ()++ -- Not an event at all; the fact that I need this means the+ -- CBMgr concept was misguided!+ WithUIRef action -> action uiref++ UMTest -> + putStrLn "UMTest"+++-- ============================================================+-- MENUS++-- Easy creation of menus from lists.+-- Originally from ~/src/haskell-etudes/gtk2hs/gMenu.hs++data MenuSpec = MenuSpec String [MenuItemSpec]+data MenuItemSpec = MenuItem String MenuItemAction+ | SubMenu MenuSpec++type MenuItemAction = VPUI -> IO VPUI -- was just IO ()++createMenuBar :: [MenuSpec] -> CBMgr -> IO MenuBar+createMenuBar menuSpecs cbmgr = do+ bar <- menuBarNew+ mapM_ (addMenu bar cbmgr) menuSpecs+ return bar++addMenu :: MenuBar -> CBMgr -> MenuSpec -> IO ()+addMenu mbar cbmgr mspec@(MenuSpec name _itemSpecs) = do+ menuHead <- menuItemNewWithLabel name -- visible "item" at top of the menu+ menuShellAppend mbar menuHead+ -- Right-justify help menu.+ -- Deprecated (bad for right-to-left languages),+ -- but retained for compatibility with menus_hard.py.+ menuItemSetRightJustified menuHead (name == "Help") -- ??????++ -- menu = the container for menu items+ menu <- createMenu mspec cbmgr+ menuItemSetSubmenu menuHead menu++createMenu :: MenuSpec -> CBMgr -> IO Menu+createMenu (MenuSpec _name itemSpecs) cbmgr = do+ menu <- menuNew+ mapM_ (createMenuItem menu cbmgr) itemSpecs+ return menu++createMenuItem :: Menu -> CBMgr -> MenuItemSpec -> IO ()+createMenuItem menu cbmgr mispec = + case mispec of+ MenuItem label action ->+ do+ {+ item <- menuItemNewWithLabel label+ ; cbmgr (OnMenuItemActivateLeaf item action)+ -- may need to read/write IORef here ***+ ; menuShellAppend menu item+ }+ SubMenu subspec@(MenuSpec label _itemSpecs) ->+ do+ {+ item <- menuItemNewWithLabel label+ ; submenu <- createMenu subspec cbmgr + ; menuItemSetSubmenu item submenu+ ; menuShellAppend menu item+ }++-- | Read an IORef, update with IO, and write the updated value.+-- This is like (flip modifyIORef), but the type of the +-- first argument is (a -> IO a) instead of (a -> a).+-- Note that if a = VPUI, then updateIO :: VPUI -> IO VPUI+-- and consequently modifyIORefIO updateIO :: CBMgrAction.++modifyIORefIO :: (a -> IO a) -> IORef a -> IO ()+modifyIORefIO updateIO ref = readIORef ref >>= updateIO >>= writeIORef ref
+ Graphics/UI/Sifflet/Canvas.hs view
@@ -0,0 +1,1011 @@+-- File: Canvas.hs+-- Canvas and CanvFrame data and operations++module Graphics.UI.Sifflet.Canvas+ (+ atLeastSize+ , cfContext+ , connect+ , defaultFileSaveClipBox+ , disconnect+ , drawCanvas+ , editFunction+ , frameChanged+ , nodeContainerFrame+ , pointSelection+ , renderCanvas+ , vcAddFrame+ , vcClearSelection+ , vcClearFrame+ , vcCloseFrame+ , vcEvalDialog+ , vcFrameAddFunctoidNode+ , vcFrameAddNode+ , vcFrameDeleteNode+ , vcFrameDeleteTree+ , vcFrameSubframes+ , vcGetFrame+ , vcInvalidateFrameWithParent+ , vcInvalidateBox+ , vcUpdateFrameAndGraph+ , vcanvasNew+ , vcanvasNodeAt+ , vcanvasNodeRect+ , whichFrame + , callFrames+ )++where++-- debug imports+-- import System.IO.Unsafe++-- standard imports++import Control.Monad+import Data.List as List++import Data.Graph.Inductive as G++import Graphics.Rendering.Cairo hiding (translate)+import qualified Graphics.Rendering.Cairo as Cairo++-- Sifflet imports++import Data.Sifflet.Functoid+import Data.Sifflet.Geometry as Geometry+import Data.Sifflet.Tree as T+import Data.Sifflet.TreeGraph+import Data.Sifflet.TreeLayout+import Data.Sifflet.WGraph++import Language.Sifflet.Expr+import Language.Sifflet.ExprTree+import Language.Sifflet.Parser++import Graphics.Rendering.Sifflet.Draw++import Graphics.UI.Sifflet.Frame+import Graphics.UI.Sifflet.GtkUtil+import Graphics.UI.Sifflet.LittleGtk+import Graphics.UI.Sifflet.Types+import Language.Sifflet.Util++-- Experimental:+enableDoubleBuffering :: Bool+enableDoubleBuffering = True++vcanvasNew :: Style -> Double -> Double -> IO VCanvas+vcanvasNew style width height = do+ -- create gtkLayout (the "drawing canvas")+ gtkLayout <- layoutNew Nothing Nothing+ -- Turn double buffering on or off. + -- Normally, double buffering eliminates flicker,+ -- but if the rendering is through the network,+ -- it might be better to disable it.+ -- See docs for Graphics.UI.Gtk.Gdk.DrawWindow+ -- (drawWindow{Begin,End}PaintRegion), and+ -- Graphics.UI.Gtk.Abstract.Widget+ -- (widgetSetDoubleBuffered).+ widgetSetDoubleBuffered gtkLayout enableDoubleBuffering++ -- create the VCanvas+ let vCanvas = VCanvas {vcLayout = gtkLayout, vcStyle = style, + vcGraph = wgraphNew,+ vcFrames = [],+ -- vcSize is the requested size of the+ -- canvas (Gtk.Layout)+ vcSize = Size width height,+ -- but this is the requested size;+ -- how can I get the actual, current size?+ -- Answer: + -- layoutGetSize :: Layout -> IO (Int, Int)+ -- removed: vcLocalEnv = env,+ vcMousePos = (0, 0), -- ???+ vcTool = Nothing,+ vcDragging = Nothing,+ vcActive = Nothing,+ vcSelected = Nothing++ }+ -- Most essential event handlers+ _ <- onSizeRequest gtkLayout+ (return (Requisition (round width) (round height)))+ + return vCanvas++nodeContainerFrame :: VCanvas -> WGraph -> G.Node -> CanvFrame+nodeContainerFrame vcanvas g = vcGetFrame vcanvas g . nodeContainerFrameNode g++-- Ask the VCanvas to find the frame whose frame node element is the+-- given Node; it is an error if not found or if there is more than one.+vcGetFrame :: VCanvas -> WGraph -> Node -> CanvFrame+vcGetFrame vcanvas graph frameNode = + let frames = [f | f <- vcFrames vcanvas, cfFrameNode f == frameNode]+ err phrase = errcats ["vcGetFrame", phrase,+ "frameNode:", show frameNode, + "\nframes:", show frames,+ "\ngraph:\n", show graph]+ in case frames of+ [frame] -> frame+ [] -> err "no frame found"+ (_:_:_) -> err "multiple frames found"++-- | Ask the vcanvas to update the frame and install a new graph.+-- Frames are identified by their frame nodes, so the new frame +-- must have the same frame node as the old.+-- It is an unreported error if there is not exactly one match.+vcUpdateFrameAndGraph :: VCanvas -> CanvFrame -> WGraph -> VCanvas+vcUpdateFrameAndGraph vcanvas newFrame newGraph =+ let frames = vcFrames vcanvas+ frameNode = cfFrameNode newFrame -- should match frameNode of old frame+ frames' = + [if cfFrameNode f == frameNode then newFrame else f | f <- frames]+ in vcanvas {vcFrames = frames', vcGraph = newGraph}++-- | Like vcUpdateFrameAndGraph, but keep the canvas's old graph.+vcUpdateFrame :: VCanvas -> CanvFrame -> VCanvas+vcUpdateFrame vcanvas newFrame =+ vcUpdateFrameAndGraph vcanvas newFrame (vcGraph vcanvas)++-- Delete a frame from the vcanvas's frames ref+-- This does not update the graph -- see vcCloseFrame for that.+vcDeleteFrame :: VCanvas -> CanvFrame -> VCanvas+vcDeleteFrame vcanvas frame =+ let frames = vcFrames vcanvas+ node = cfFrameNode frame+ frames' = [f | f <- frames, cfFrameNode f /= node]+ in vcanvas {vcFrames = frames'}++-- RENDERING+-- perhaps ought to be its own module++-- Perhaps this ought to be called graphRenderFunctoid! ***++graphRenderFunctoidParts :: + Style -> Maybe Node -> Maybe Selection -> WGraph -> CanvFrame -> Render ()+graphRenderFunctoidParts style mact msel graph frame = + case cfFunctoid frame of+ FunctoidFunc _ -> error "graphRenderFunctoidParts: not an edit frame"+ FunctoidParts {} -> + graphRenderForest style mact msel graph + (nodeProperSimpleDescendants graph + (cfFrameNode frame))++graphRenderForest :: + Style -> Maybe Node -> Maybe Selection -> WGraph -> [G.Node] -> Render ()+graphRenderForest style mact msel graph roots = + let renderNode node = graphRenderTree style mact msel graph node False+ in mapM_ renderNode roots++graphRenderTree :: Style -> Maybe Node -> Maybe Selection -> WGraph ->+ G.Node -> Bool -> Render ()+graphRenderTree style mact msel graph rootNode fillBackground =+ let loop :: Maybe Iolet -> G.Node -> Render ()+ loop mInlet currentNode = do+ -- Render the root+ (inlets, outs) <-+ graphRenderNode style mact msel graph currentNode mInlet+ -- Render the subtrees+ loopWithInlets 0 inlets (sortBy adjCompareEdge outs)+ + -- loopWithInlets n inletPositions outs:+ -- n = the current inlet number, starting from 0+ -- inletPositions = the points to connect to on the parent+ -- outs = a list of (child, edge) pairs (adjs)+ -- going to *simple* children (i.e., not frame nodes)+ -- There must be at least as many inlets as there are outs.+ -- If the edge of the first outs equals n, we use the+ -- first inletPosition. Otherwise we skip the inlet+ -- but not out.+ loopWithInlets :: Int -> [Iolet] -> [(G.Node, WEdge)] -> Render ()+ loopWithInlets _n _is [] = return ()+ loopWithInlets n (i:is) (a:as) =+ -- n: number of child, i: inlet, a: adjacency (node, edge)+ let (node, edge) = a in+ if edge == WEdge n+ then do+ loop (Just i) node -- draw node with current inlet+ loopWithInlets (n + 1) is as -- and draw the rest+ else -- skip current inlet+ loopWithInlets (n + 1) is (a:as) + + loopWithInlets n [] (a:as) = + -- This should not happen, and is deliberately rendered+ -- with a strange look!+ -- This repeats some of graphRenderNode, slightly modified+ let ctx = context graph rootNode+ lnode :: LayoutNode ExprNode+ WSimple lnode = lab' ctx+ bb = gnodeNodeBB (nodeGNode lnode)+ defaultInlet = + Iolet (Geometry.Circle+ (Position (bbXCenter bb) (bbYCenter bb)) 0)+ + in loopWithInlets n [defaultInlet] (a:as)+ in do+ graphStartRender style graph rootNode fillBackground+ loop Nothing rootNode++graphStartRender :: Style -> WGraph -> G.Node -> Bool -> Render ()+graphStartRender style graph rootNode fillBackground = do+ -- global actions: can be done once for the whole drawing+ -- instead of once per subtree+ -- Choose: Antialias{Default,None,Gray,Subpixel}+ setAntialias AntialiasDefault++ -- draw the canvas background+ setColor (styleNormalFillColor style)+ let rootCtx = context graph rootNode+ WSimple lroot = lab' rootCtx+ BBox x y w' h' = nodeTreeBB lroot+ when fillBackground $ do { rectangle x y w' h'; fill}++ -- now set up for the rest+ -- setColor (styleNormalTextColor style)+ setLineWidth (lineWidth style)+++-- | Render a node.+-- Returns a list of inlets and a list of "outs":+-- a list of (child, edge) pairs (adjs) +-- going to *simple* children only (not to frames formed+-- by expanding a node).+-- This can then be used if we wish to render the children of+-- the node, as when rendering a tree.++graphRenderNode :: + Style -> Maybe Node -> Maybe Selection -> WGraph ->+ G.Node -> Maybe Iolet -> Render ([Iolet], [(G.Node, WEdge)])+graphRenderNode style mact msel graph node mInlet = + -- status of this node+ let nodeActive = mact == Just node+ mode = if nodeActive+ then DrawActive+ else case msel of+ Nothing -> DrawNormal+ Just sel ->+ if selNode sel /= node then DrawNormal+ else case sel of+ SelectionNode _ -> DrawSelectedNode+ SelectionInlet {selInEdge = WEdge i} ->+ DrawSelectedInlet i+ SelectionOutlet {selOutEdge = WEdge o} ->+ DrawSelectedOutlet o+ + connectInlet :: Iolet -> Double -> Double -> Render ()+ connectInlet inlet tx ty = do+ -- draw the line from the parent inlet to this node's outlet+ let Position px py = ioletCenter inlet+ setColor (styleNormalEdgeColor style)+ moveTo px (py + snd (vtinypad style)) -- bottom of parent+ lineTo tx (ty - fst (vtinypad style)) -- top of this node+ stroke ++ ctx = context graph node+ lnode :: LayoutNode ExprNode+ WSimple lnode = lab' ctx++ -- where to connect to this node+ nodeBB = gnodeNodeBB (nodeGNode lnode)+ xcenter = bbXCenter nodeBB+ inlets = gnodeInlets (nodeGNode lnode)+ -- children (nodes and edges)+ outs = lsuc' ctx :: [(G.Node, WEdge)]+ -- omit links to frames opened from a node+ outs' = [(child, edge) |+ (child, edge) <- outs, nodeIsSimple graph child]++ -- Do we need more inlets to match the outs' ?+ deficit = length outs' - length inlets+ -- The default inlet is lifted to the center+ -- to show something is not right!+ defaultInlet = Iolet (Geometry.Circle + (Position xcenter (bbYCenter nodeBB)) 0)+ inlets' = if deficit > 0+ then inlets ++ replicate deficit defaultInlet+ else inlets++ in do+ + -- Render the node+ draw style mode lnode++ -- Connect to its parent (if any)+ case mInlet of+ Nothing -> return ()+ Just inlet -> connectInlet inlet xcenter (bbTop nodeBB) ++ return (inlets', outs')+++-- END OF RENDERING+-- ---------------------------------------------------------------------++-- | Make nothing be selected++vcClearSelection :: VCanvas -> IO VCanvas+vcClearSelection canvas =+ case vcSelected canvas of+ Nothing -> return canvas+ Just sel ->+ let node = selectionNode sel+ in do+ vcInvalidateSimpleNode canvas node+ return (canvas {vcSelected = Nothing})++-- | The Graph Node of a Selection+selectionNode :: Selection -> G.Node+selectionNode sel =+ case sel of+ SelectionNode n -> n+ SelectionInlet n _ -> n+ SelectionOutlet n _ -> n++-- | What is selected (if anything) at a point++pointSelection :: WGraph -> CanvFrame -> Position -> Maybe Selection+pointSelection graph frame point =+ -- Try to find something to select at the point,+ -- i.e., a node or an iolet on the node+ case cfFunctoid frame of+ FunctoidFunc _ -> error "graphFindFunctionPart: not an edit frame"+ FunctoidParts {fpNodes = grNodes} ->+ let layoutNodes = map (grExtractLayoutNode graph) grNodes+ tuples = zip grNodes layoutNodes++ loop :: [(G.Node, LayoutNode ExprNode)] -> Maybe Selection+ loop [] = Nothing+ loop (t:ts) = -- (n:ns) =+ let (gn, ln) = t -- graph node, tlo node+ gnode = nodeGNode ln+ inlets = gnodeInlets gnode+ outlets = gnodeOutlets gnode+ in+ -- look at the ports first,+ case pointIolet point 0 inlets of+ Just i ->+ Just (SelectionInlet gn (WEdge i))+ Nothing ->+ case pointIolet point 0 outlets of+ Just o ->+ Just (SelectionOutlet gn (WEdge o))+ Nothing ->+ -- try in the node proper+ if pointInGNode point gnode+ then+ Just (SelectionNode gn)+ else+ -- try the remaining tuples+ loop ts ++ in loop tuples++-- | Connect nodes++connect :: VCanvas -> G.Node -> WEdge -> G.Node -> WEdge -> IO VCanvas+connect canvas parent inlet child outlet = do+ -- if parent and child are different,+ -- connect the ith inlet of node parent+ -- to the oth outlet of node child+ -- provided that doing so would not create a cycle+ -- parent -> child -> ... -> parent++ let graph = vcGraph canvas+ if elem parent (reachable child graph)+ then + do+ showErrorMessage "Sorry, this connection would create a cycle."+ return canvas+ else + -- now we need to store a labeled edges (inlet -> outlet)+ -- and to clear any previous connections of the two.+ let graph' = grConnect graph parent inlet child outlet+ in return $ canvas {vcGraph = graph'}++-- | Disconnect nodes++-- disconnect wouldn't need to be in the IO monad,+-- except that it needs the same type signature as connect+disconnect :: VCanvas -> G.Node -> WEdge -> G.Node -> WEdge + -> IO VCanvas+disconnect canvas parent inlet child outlet = do+ -- Opposite of connect, except we don't have to check for cycles+ -- of any kind. We also reconnect the child to the frame node+ -- as its "parent."+ let graph = vcGraph canvas+ graph' = grDisconnect graph parent inlet child outlet True+ return $ canvas {vcGraph = graph'}++vcFrameAddFunctoidNode :: + VCanvas -> CanvFrame -> Functoid -> Double -> Double -> IO VCanvas+vcFrameAddFunctoidNode canvas frame nodeFunc x y = + let exprNode = ENode (NSymbol (Symbol (functoidName nodeFunc))) EvalUntried+ args = functoidArgNames nodeFunc+ in vcFrameAddNode canvas frame exprNode args x y++vcFrameAddNode :: VCanvas -> CanvFrame -> ExprNode -> [String] + -> Double -> Double -> IO VCanvas+vcFrameAddNode canvas frame exprNode inletLabels x y =+ case cfFunctoid frame of+ FunctoidFunc _function -> + error "vcFrameAddNode: frame is not an edit frame"+ fp@FunctoidParts {fpNodes = ns} -> + do+ let -- Converting to a tree to lay it out seems overkill--+ exprTree = T.Node exprNode []+ style = styleIncreasePadding (vcStyle canvas) 10+ counter = argIoletCounter inletLabels+ layoutTree = treeLayout style counter exprTree++ let graph = vcGraph canvas+ layoutTree' = layoutTreeMoveCenterTo x y layoutTree+ layoutRoot = rootLabel layoutTree'+ newNode = WSimple layoutRoot+ -- insert into graph+ (graph', gNodeId) = grInsertNode graph newNode+ frameNode = cfFrameNode frame+ edge = (frameNode, gNodeId, WEdge (outdeg graph frameNode + 1))+ graph'' = insEdge edge graph'+ -- insert into the fpNodes+ ns' = (gNodeId:ns)+ fp' = fp {fpNodes = ns'}++ {-+ -- DON'T!+ -- Adjust header and footer for new body size++ -- BUT: IS THERE ANYTHING HERE THAT I SHOULD KEEP+ -- WHEN I MAKE FRAMES KEEP A MINIMUM SIZE TO FIT+ -- THE LAYOUT OF THEIR BODIES?++ layoutNodes = map (grExtractLayoutNode graph') ns'+ bodyBB = -- layoutTreeBB layoutTree'+ bbMergeList (map nodeTreeBB layoutNodes)+ header' = alignHeader (cfHeader frame) bodyBB+ footer' = alignFooter (cfFooter frame) bodyBB+ -- grow box to fit new node+ box' = bbMergeList [tbBoxBB header', tbBoxBB footer', bodyBB]+ -- insert into frame+ frame' = frame {cfHeader = header',+ cfFooter = footer',+ cfBox = box', + cfFunctoid = fp'}+ -}+ frame' = frame {cfFunctoid = fp'}++ -- store new frame and graph into canvas+ canvas' = vcUpdateFrameAndGraph canvas frame' graph''++ -- Ready to redraw+ frameChanged canvas graph frame graph'' frame' ++ return canvas'++vcFrameDeleteNode :: VCanvas -> CanvFrame -> G.Node -> IO VCanvas+vcFrameDeleteNode canvas frame node = + let -- Remove the graph node from the frame and canvas,+ -- giving its orphaned children the frame as their new parent+ graph = vcGraph canvas+ frameNode = cfFrameNode frame+ children = nodeAllChildren graph node+ -- Remove node from graph+ graph' = grRemoveNode graph node+ -- Frame adopts orphans+ graph'' = foldl (\ g child -> connectToFrame child frameNode g) + graph' + children+ -- Remove node from funcparts+ fp@FunctoidParts {fpNodes = ns} = cfFunctoid frame+ fp' = fp {fpNodes = List.delete node ns}+ frame' = frame {cfFunctoid = fp'}+ + -- Update references+ canvas' = vcUpdateFrameAndGraph canvas frame' graph''+ in do+ -- Ask to be redrawn+ frameChanged canvas graph frame graph'' frame'+ return canvas'++-- | Remove the (sub)tree rooted at the given node.+-- Removes it from the graph of the canvas+-- and from the FunctoidParts of the frame.+vcFrameDeleteTree :: VCanvas -> CanvFrame -> G.Node -> IO VCanvas+vcFrameDeleteTree canvas frame rootNode = ++ let removeTree :: (WGraph, [G.Node]) -> G.Node -> (WGraph, [G.Node])+ removeTree (g, ns) root =+ let g' = grRemoveNode g root+ ns' = List.delete root ns+ in foldl removeTree (g', ns') (nodeAllChildren g root)+ + graph = vcGraph canvas+ fp@FunctoidParts {fpNodes = fnodes} = cfFunctoid frame+ (graph', fnodes') = removeTree (graph, fnodes) rootNode+ frame' = frame {cfFunctoid = fp {fpNodes = fnodes'}}+ + -- Update references+ canvas' = vcUpdateFrameAndGraph canvas frame' graph'+ in do+ -- Ask to be redrawn+ frameChanged canvas graph frame graph' frame'+ return canvas'+++-- | Add a frame representing a functoid to the canvas.+--+-- Use mvalues = Nothing if you do not want the frame to be evaluated+-- as a function call, otherwise mvalues = Just values.+-- +-- prevEnv is *supposed* to be the previous environment, +-- i.e., that of the+-- "parent" frame or the canvas, not of the new frame,+-- because vcAddFrame itself will extend the environment+-- with the new (vars, values).+-- But this is odd, because openNode calls vcAddFrame +-- apparently with the *new* environment as prevEnv,+-- and yet it works correctly.+--+-- Caution: I think it is necessary for the canvas to have been realized+-- before calling this function!++vcAddFrame :: VCanvas -> Functoid -> Maybe [Value] -> FrameType+ -> Env -> Double -> Double -> Double -> Maybe G.Node + -> IO VCanvas+vcAddFrame canvas functoid mvalues mode prevEnv x y z mparent = do+ let graph = vcGraph canvas+ (_, hi) = nodeRange graph+ frameNode = hi + 1 -- implicit: root = hi + 2+ style = vcStyle canvas+ (newFrame, tlo) = frameNewWithLayout style (Position x y) z + functoid mvalues + CallFrame -- mode may change below+ frameNode prevEnv mparent+ inAdj = case mparent of+ Nothing -> []+ Just parent -> + -- Adjacency (priority, parent)+ [(WEdge (outdeg graph parent), parent)]+ -- add the new frame node, possibly linked to its parent,+ -- and the tree of the new frame+ graph' = grAddGraph + ((inAdj, frameNode, WFrame frameNode, []) & graph)+ (flayoutToGraph tlo)+ -- connect the frame to the tree-layout roots+ layoutRoots = map (+ frameNode) (flayoutToGraphRoots tlo)+ outEdges = [(frameNode, root, WEdge priority) | + (priority, root) <- zip [0..] layoutRoots]+ graph'' = insEdges outEdges graph'+ -- update the frames and the graph in the canvas+ frames = vcFrames canvas++ canvas' = canvas {vcFrames = (newFrame:frames)+ , vcGraph = graph''}++ -- Make sure canvas is big enough to contain newFrame+ -- This is also done when the window is resized+ -- (../Callbacks.hs: configuredCallback).++ frameBB = cfBox newFrame+ canvas'' = + atLeastSize (Size (bbRight frameBB) (bbBottom frameBB)) canvas'++ -- Request redraw of the region, including tether to parent, if any+ -- error occurs here if the widget is not yet realized, I think+ vcInvalidateFrameWithParent canvas graph'' newFrame+ case mode of+ CallFrame -> return canvas''+ EditFrame -> editFunction canvas'' newFrame ++-- | Return a canvas of at least the specified size+-- and otherwise like the given canvas.+atLeastSize :: Size -> VCanvas -> VCanvas+atLeastSize minSize@(Size minW minH) canvas =+ let Size w h = vcSize canvas+ frames = vcFrames canvas+ frames' = if canvasEditing canvas+ then -- only one frame, expand it to fill desired size+ [atLeastSizeFrame minSize (head frames)]+ else frames+ in canvas {vcSize = Size (max w minW) (max h minH), vcFrames = frames'}++vcInvalidateFrameWithParent :: VCanvas -> WGraph -> CanvFrame -> IO ()+vcInvalidateFrameWithParent vcanvas graph frame =+ -- "Invalidate" the frame itself, and if it has a parent,+ -- the region between it and its parent, so that the frame+ -- and its tether will be redrawn+ let box1 = cfBox frame+ box2 = + case cfParent frame of+ Just parent -> bbMerge (nodeBBox graph parent) box1+ Nothing -> box1+ in vcInvalidateBox vcanvas box2++vcInvalidateSimpleNode :: VCanvas -> G.Node -> IO ()+vcInvalidateSimpleNode vcanvas node = + -- For WSimple nodes --+ -- Similarly, invalidate the node itself + -- (but not yet its parent (if any))+ case wlab (vcGraph vcanvas) node of+ WFrame _ -> error "vcInvalidateSimpleNodeWithParent: node is not simple"+ WSimple layoutNode ->+ vcInvalidateBox vcanvas (gnodeNodeBB (nodeGNode layoutNode))++vcInvalidateBox :: VCanvas -> BBox -> IO ()+vcInvalidateBox vcanvas (BBox x y width height) = + -- take into account the line width of the box as drawn+ -- and the radius of Iolets. We'll do this even+ -- though frames do not have Iolets on their edges!+ -- This is also a little broader than necessary even for+ -- WSimple nodes, since the Iolets are on top and bottom,+ -- not on the side.+ let style = vcStyle vcanvas+ t = lineWidth style + styleIoletRadius style+ rect = bbToRect (BBox (x - t) (y - t) + (width + 2 * t) (height + 2 * t)) + in do+ win <- layoutGetDrawWindow (vcLayout vcanvas)+ drawWindowInvalidateRect win rect False++frameChanged :: + VCanvas -> WGraph -> CanvFrame -> WGraph -> CanvFrame -> IO ()+frameChanged vcanvas g f g' f' = + -- (graph g, frame f) has been replaced by (g', f')+ do+ vcInvalidateFrameWithParent vcanvas g f -- old+ vcInvalidateFrameWithParent vcanvas g' f' -- new++-- MORE RENDERING+-- ---------------------------------------------------------------------++-- | Draw the canvas in its window, on screen+drawCanvas :: VCanvas -> Rectangle -> IO ()+drawCanvas canvas cliprect = do+ drawWin <- layoutGetDrawWindow (vcLayout canvas)+ renderWithDrawable drawWin $ renderCanvas canvas (bbFromRect cliprect) False++-- | Render the canvas in Cairo +-- (use with renderWith to provide an alternate surface, such as+-- an SVG file).++renderCanvas :: VCanvas -> BBox -> Bool -> Render ()+renderCanvas canvas clipbox translateClip =+ -- alternatively match {eventRegion = region}, from which you can get a+ -- list of rectangles++ let graph = vcGraph canvas+ mactive = vcActive canvas+ mselected = vcSelected canvas+ frames = vcFrames canvas+ Size w h = vcSize canvas+ style = vcStyle canvas++ setClip (BBox x y width height) = do+ rectangle x y width height+ clip++ drawBackground = do+ setColor (ColorRGB 0.4 0.4 0.4)+ rectangle 0 0 w h+ fill++ renderFrame frame = + case frameType frame of+ EditFrame -> renderEditFrame frame+ CallFrame -> renderCallFrame frame++ renderEditFrame frame = do+ renderFrameHeader frame+ -- renderFrameFooter frame+ -- the body:+ setAntialias AntialiasDefault+ setColor (styleNormalFillColor style)+ drawBox (Just (styleNormalFillColor style)) Nothing + (frameBodyBox frame)+ -- now draw the nodes, if any:+ graphRenderFunctoidParts style mactive mselected graph frame+ renderFrameBorder frame+ -- end renderEditFrame++ renderCallFrame frame = do+ let frameRoot = cfRoot frame+ -- Just (WSimple _) = lab graph frameRoot -- can't be WFrame!+ -- BBox x y width height = nodeTreeBB layoutNode++ -- draw tether from parent (if any)+ -- drawTether (nodeParent graph (cfFrameNode frame)) frame+ fancyTether (nodeParent graph (cfFrameNode frame)) frame++ -- render the graph -- requires the graph, which is *not*+ -- in the frame! That's why this is not a "frame method".+ graphRenderTree style mactive mselected graph frameRoot True++ -- render the header and footer+ -- MAYBE do this before the tether?+ -- Footer needs to be drawn after body+ -- in case the frame is resized too small+ renderFrameHeader frame+ renderFrameFooter frame + renderFrameBorder frame+ -- end renderCallFrame++ renderFrameHeader frame = drawtb cream black black (cfHeader frame)++ renderFrameFooter frame =+ drawtb cream black + (if cfEvalReady frame + then lightBlue + else styleAuxColor style)+ (cfFooter frame)++ renderFrameBorder frame = drawBox Nothing (Just black) (cfBox frame)+ drawtb bgcolor framecolor textcolor tbox = + drawTextBox (Just (styleFont style)) + (Just bgcolor) + (Just framecolor) + textcolor tbox+ +-- plainTether :: Maybe G.Node -> CanvFrame -> Render ()+-- plainTether Nothing _ = return ()+-- plainTether (Just parent) frame = +-- let pb = nodeBBox graph parent+-- fb = cfBox frame+-- line f1 f2 = do+-- moveTo (f1 pb) (f2 pb) +-- lineTo (f1 fb) (f2 fb)+-- in do+-- -- outline the frame's parent+-- drawBox Nothing (Just (styleTetherColor style)) pb+-- -- draw tether lines+-- setColor (styleTetherColor style)+-- line bbLeft bbTop+-- line bbLeft bbBottom+-- line bbRight bbTop+-- line bbRight bbBottom+-- stroke++ fancyTether :: Maybe G.Node -> CanvFrame -> Render ()+ fancyTether Nothing _ = return ()+ fancyTether (Just parent) frame = + let pb = nodeBBox graph parent+ fb = cfBox frame+ side f1 f2 f3 f4 =+ do+ newPath+ moveTo (f1 pb) (f2 pb)+ lineTo (f1 fb) (f2 fb)+ lineTo (f3 fb) (f4 fb)+ lineTo (f3 pb) (f4 pb)+ closePath+ fill+ in do+ -- outline the frame's parent+ drawBox Nothing (Just (styleTetherColor style)) pb+ -- draw tether lines+ setColor (styleTetherColor style)+ side bbLeft bbTop bbRight bbTop+ side bbRight bbTop bbRight bbBottom+ side bbRight bbBottom bbLeft bbBottom+ side bbLeft bbBottom bbLeft bbTop+ -- end fancyTether++ -- end of let decls in renderCanvas+ + -- main body of renderCanvas+ in do+ when translateClip+ (Cairo.translate (- (bbX clipbox)) (- (bbY clipbox)))+ setClip clipbox+ drawBackground+ -- sorting is a possible bottleneck? + mapM_ renderFrame (sortBy levelOrder frames)++defaultFileSaveClipBox :: VCanvas -> BBox+defaultFileSaveClipBox canvas =+ let bboxes = map cfBox (vcFrames canvas)+ BBox x1 y1 w1 h1 = bbMergeList bboxes -- contains all frames+ pad = exomargin (vcStyle canvas)+ in BBox (x1 - pad) (y1 - pad) (w1 + 2 * pad) (h1 + 2 * pad)++-- | Find the node, if any, at a given position on the canvas.+vcanvasNodeAt :: VCanvas -> Position -> Maybe G.Node+vcanvasNodeAt vcanvas point =+ + -- searchFrames could be done with the Maybe monad, I guess+ let searchFrames :: [CanvFrame] -> Maybe G.Node+ searchFrames [] = Nothing+ searchFrames (f:fs) =+ case frameNodeAt f (vcGraph vcanvas) point of+ Nothing -> searchFrames fs+ Just node -> Just node+ + in searchFrames (vcFrames vcanvas)+ ++vcanvasNodeRect :: VCanvas -> G.Node -> Rectangle+vcanvasNodeRect vcanvas node =+ let Just (WSimple layoutNode) = lab (vcGraph vcanvas) node+ in bbToRect (gnodeNodeBB (nodeGNode layoutNode))++whichFrame :: VCanvas -> Double -> Double -> Maybe CanvFrame+whichFrame vcanvas x y = + -- Find the frame, if any, in which (x, y) occurs.+ -- If there's more than one match, we should return the+ -- one "on top" or at the highest level -- this is not yet implemented.+ let frames = vcFrames vcanvas+ inFrame position = pointInBB position . cfBox+ matches = filter (inFrame (Position x y)) frames+ in case matches of+ [] -> Nothing+ [m1] -> Just m1+ (m1:_:_) -> + -- multiple frames match, so here needs some additional work+ Just m1++-- | editFunction: reverse of defineFunction: replace the call frame by+-- an edit frame; does not change the VPUI (global env.), just the canvas..+editFunction :: VCanvas -> CanvFrame -> IO VCanvas+editFunction canvas frame = + case frameType frame of+ EditFrame -> + return canvas+ + CallFrame ->+ let FunctoidFunc function = cfFunctoid frame+ parts = functionToParts function (vcGraph canvas) + (cfFrameNode frame)+ frame' = frame {cfFunctoid = parts, frameType = EditFrame}+ -- Make the frame fill the canvas.+ frame'' = atLeastSizeFrame (vcSize canvas) frame'+ in return $ vcUpdateFrame canvas frame''++-- | Find a frame's subframes, i.e., those that were expanded+-- to trace the execution of a function call.+-- Cannot be in an edit frame.+vcFrameSubframes :: VCanvas -> CanvFrame -> [CanvFrame]+vcFrameSubframes canvas frame =+ let graph = vcGraph canvas+ subframeNodes = + case frameType frame of+ EditFrame -> []+ CallFrame -> grTreeSubframeNodes graph (cfRoot frame)+ in map (vcGetFrame canvas graph) subframeNodes+++-- | Given a graph with a rooted tree, collect list of "subframes,"+-- i.e., frames that are children of nodes in the tree+grTreeSubframeNodes :: WGraph -> G.Node -> [G.Node]+grTreeSubframeNodes g root =+ nodeFrameChildren g root ++ + concatMap (grTreeSubframeNodes g) (nodeSimpleChildren g root)++vcEvalDialog :: VCanvas -> CanvFrame -> IO VCanvas+vcEvalDialog canvas frame =+ let FunctoidFunc function = cfFunctoid frame -- FunctoidParts shouldn't happen+ varnames = cfVarNames frame+ in if null varnames+ then evalFrame canvas frame [] -- skip dialog, no inputs+ else let argDefault env arg = + case envLookup env arg of+ Nothing -> ""+ Just v -> repr v+ defaults = map (argDefault (cfEnv frame)) varnames++ reader :: Reader [String] [Value]+ reader inputs = parseTypedInputs3 inputs varnames + (functionArgTypes function)+ in do+ dialog <- + createEntryDialog "Input Values" varnames defaults reader (-1)+ result <- runEntryDialog dialog+ case result of+ Nothing -> return canvas+ Just values -> evalFrame canvas frame values++-- | Evaluate the frame, having gotten a list of values from the dialog++evalFrame :: VCanvas -> CanvFrame -> [Value] -> IO VCanvas+evalFrame canvas frame values = do+ -- Close subframes (those that were made by+ -- expanding a node of this frame)+ canvas' <- vcCloseSubframes canvas frame++ -- Re-evaluate expression tree and update display+ let graph = vcGraph canvas'++ -- Pop the current frame's values, if any, before+ -- extending with the new values.+ -- A frame with no values has a dummy extension,+ -- so this is still okay.+ frameNode = cfFrameNode frame+ -- It's a call frame, so it has a root+ root = cfRoot frame+ style = vcStyle canvas'+ headerTB = cfHeader frame++ -- The tlo may *change*, since showing values may require+ -- extra space.++ (frame', tlo') = frameNewWithLayout style + (bbPosition (tbBoxBB headerTB))+ (cfLevel frame)+ (cfFunctoid frame) (Just values) CallFrame+ frameNode + (envPop (cfEnv frame))+ Nothing++ -- Since the frame is a call frame, we should have a tree tlo.++ case tlo' of+ FLayoutTree _t ->+ do+ -- update the tree in the graph+ let graph' = grUpdateFLayout graph [root] tlo'+ canvas'' = vcUpdateFrameAndGraph canvas' frame' graph'+ + -- request redrawing of old and new areas+ frameChanged canvas' graph frame graph' frame'+ + return canvas''++ FLayoutForest _f _b ->+ error "vcEvalDialog: finishDialog: tlo is not a tree"++-- WORK HERE ***+-- This will be a lot like vcEvalDialog, except we are *un*-evaluating.+-- :-(++-- | vcClearFrame - clear a frame in a canvas; not yet implemented+-- What does this mean?++vcClearFrame :: VCanvas -> CanvFrame -> IO VCanvas+vcClearFrame canvas _frame = + showInfoMessage "Sorry" "Stub: vcClear is not yet implemented" >>+ return canvas++-- | Close a frame and any subframes of it++vcCloseFrame :: VCanvas -> CanvFrame -> IO VCanvas+vcCloseFrame canvas frame = do+ -- close any subframes of this frame+ canvas' <- vcCloseSubframes canvas frame++ -- remove it from the frames list+ let canvas'' = vcDeleteFrame canvas' frame++ -- remove it and its edges from the graph+ graph = vcGraph canvas''+ graph' = delNodes (allDescendants graph (cfFrameNode frame)) graph+ canvas''' = canvas'' {vcGraph = graph'}++ -- graphically invalidate the region of the frame+ -- and its tether (i.e., to its parent)+ -- (yes, using the *old* vcanvas, graph, and frame)++ vcInvalidateFrameWithParent canvas (vcGraph canvas) frame+ return canvas'''++-- | Close any subframes of the frame, but not the frame itself+vcCloseSubframes :: VCanvas -> CanvFrame -> IO VCanvas+vcCloseSubframes canvas frame =+ foldM vcCloseFrame canvas (vcFrameSubframes canvas frame)+++cfContext :: CanvFrame -> ToolContext+cfContext frame =+ case frameType frame of+ EditFrame -> TCEditFrame frame+ CallFrame -> TCCallFrame frame++-- | Is our canvas editing a function?+canvasEditing :: VCanvas -> Bool+canvasEditing canvas =+ case vcFrames canvas of+ [oneFrame] -> frameType oneFrame == EditFrame+ _ -> False++-- | Find the frames that are calling the named function+callFrames :: VCanvas -> String -> [CanvFrame]+callFrames canvas funcName =+ let isCaller frame = functoidName (cfFunctoid frame) == funcName+ in filter isCaller (vcFrames canvas)
+ Graphics/UI/Sifflet/EditArgsPanel.hs view
@@ -0,0 +1,332 @@+-- | A "panel" to be shown on the Function Editor, for adjusting the+-- arguments of the function being edited -- adding and deleting arguments,+-- or modifying their "kind" (number of inlets, needed for higher order+-- functions)++module Graphics.UI.Sifflet.EditArgsPanel+ (+ ArgSpecAction+ , EditArgsPanel+ , makeEditArgsPanel+ , editArgsPanelRoot+ , expandToFit+ )++where++import Data.IORef++import Graphics.UI.Gtk (EventBox, hButtonBoxNew, + containerRemove,+ ButtonBoxClass, buttonActivated,+ widgetGetParent,+ widgetQueueResize)++import Language.Sifflet.Expr+import Graphics.UI.Sifflet.Callback+import Graphics.UI.Sifflet.LittleGtk+import Language.Sifflet.Util (SuccFail(..), parseInt)++type ArgSpecAction = [ArgSpec] -> IO ()++type PanelRoot = EventBox++data EditArgsPanel = + EditArgsPanel {editArgsPanelRoot :: PanelRoot,+ editArgsPanelAction :: ArgSpecAction}++type StateRef = IORef State++data State = State Model UI++newtype Model = Model [ArgSpec]+ deriving (Show)++data UI = UI PanelRoot Table Label [ArgRow]++data ArgRow = ArgRow Entry Entry Button+++-- | Create an EditArgsPanel+-- It currently returns a EditArgsPanel object, +-- and you can put the root of it into a container, e.g.,+-- set window [containerChild := editArgsPanelRoot panel]++-- I think it needs to return some more information than this, though,+-- and maybe know about its parent or larger context, so that when+-- applied, it can have a "global" effect.+-- Maybe just in the form of IO () actions, though.+-- +-- It has this structure (for n existing arguments):++-- EventBox ( = editArgsPanelRoot panel)+-- +----------------------------------++-- | Frame |+-- | +------------------------------+ |+-- | | VBox | |+-- | | +--------------------------+ | |+-- | | | Table (n+2 rows)x(3 cols)| | |+-- | | | +----------------------+ | | |+-- | | | | | | | |+-- | | | | (arguments) | | | |+-- | | | +----------------------+ | | |+-- | | | | | |+-- | | | HButtonBox | | |+-- | | | +----------------------+ | | |+-- | | | | (buttons) | | | |+-- | | | +----------------------+ | | |+-- | | | | | |+-- | | | Label | | |+-- | | | +----------------------+ | | |+-- | | | | (status messages) | | | |+-- | | | +----------------------+ | | |+-- | | +--------------------------+ | |+-- | +------------------------------+ |+-- +----------------------------------+++makeEditArgsPanel :: CBMgr -> [ArgSpec] -> ArgSpecAction+ -> IO EditArgsPanel+makeEditArgsPanel cbMgr argSpecs okayAction = do+ root <- eventBoxNew -- needed for everything to be visible!+ frame <- frameNew+ frameSetLabel frame "Edit Arguments"++ let panel = EditArgsPanel {editArgsPanelRoot = root,+ editArgsPanelAction = okayAction}++ vbox <- vBoxNew False 5 -- non-homogeneous sizes, 5 pixels separation++ -- Create a table of n + 2 rows x 3 cols, non-homogeneous sizes+ table <- tableNew (length argSpecs + 2) 3 False + status <- labelNew Nothing++ stateRef <- newIORef (State (Model argSpecs) (UI root table status []))+ dressTable stateRef++ btnBox <- hButtonBoxNew+ fillButtonBox cbMgr btnBox stateRef panel++ containerAdd root frame+ containerAdd frame vbox+ boxPackStartDefaults vbox table+ boxPackStartDefaults vbox btnBox+ boxPackStartDefaults vbox status++ widgetShowAll root+ return panel++-- | Fill the table with rows for each argument in the model and more++dressTable :: StateRef -> IO ()+dressTable sref = do+ -- read state+ State (Model args) ui@(UI root table status _rows) <- readIORef sref++ -- add "headings"+ argLabel <- labelNew (Just "Name")+ inputsLabel <- labelNew (Just "Inlets")+ tableAttachCell table argLabel 0 0+ tableAttachCell table inputsLabel 0 1++-- add rows for existing and new arguments+ let n = length args+ argRows <- mapM (uncurry (argRowNew sref)) + (zip (args ++ [ArgSpec "" 0]) [0 .. n])+ mapM_ (uncurry (attachRow table)) + (zip argRows [1 .. n + 1])+ setStatusOK ui+ widgetShowAll table++ -- write state+ writeIORef sref (State (Model args) (UI root table status argRows))+++-- | Resize a widget to be at least as big as a(nother) widget.+-- Normally the widget being resized is one that contains the other.++expandToFit :: (WidgetClass v, WidgetClass w) => v -> w -> IO ()+expandToFit container widget = do+ Requisition w1 h1 <- widgetSizeRequest container+ Requisition w2 h2 <- widgetSizeRequest widget+ let (w, h) = (max w1 w2, max h1 h2)+ widgetSetSizeRequest container w h+ widgetQueueResize container++-- | Remove from table and destroy all arg rows+-- (does not alter model)++stripTable :: StateRef -> IO ()+stripTable sref = do+ State model ui@(UI root table status _rows) <- readIORef sref+ let stripWidget widget = + do+ containerRemove table widget+ widgetDestroy widget++ widgets <- containerGetChildren table+ mapM_ stripWidget widgets+ -- Why is this /not/ equivalent to above two statements?+ -- liftM (mapM_ stripWidget) (containerGetChildren table)+ setStatusOK ui++ writeIORef sref (State model (UI root table status []))++attachRow :: Table -> ArgRow -> Int -> IO ()+attachRow table (ArgRow nameEntry nEntry btn) nrow = do+ tableAttachCell table nameEntry nrow 0+ tableAttachCell table nEntry nrow 1+ tableAttachCell table btn nrow 2++-- | Attach a widget to a table in a single cell, specified by its+-- row column (top lef = 0 0) coordinates++tableAttachCell :: (WidgetClass w) => Table -> w -> Int -> Int -> IO ()+tableAttachCell t w top left = + tableAttachDefaults t w left (left + 1) top (top + 1)+++fillButtonBox :: (ButtonBoxClass b) => + CBMgr -> b -> StateRef -> EditArgsPanel -> IO ()+fillButtonBox cbMgr btnBox sref panel = + let addButton (label, action) = do+ b <- buttonNewWithLabel label+ containerAdd btnBox b+ -- on b buttonActivated action+ cbMgr (AfterButtonClicked b action) + addButton' label action = do+ b <- buttonNewWithLabel label+ containerAdd btnBox b+ _ <- on b buttonActivated action+ return ()++ applyAction :: IO ()+ applyAction = applyArgRows sref (editArgsPanelAction panel)++ okayAction = + applyAction >> closePanel panel++ in do+ -- addButton' "OK" (cbMgr (WithUIRef okayAction))+ addButton' "OK" okayAction+ mapM_ addButton [+ -- ("Strip", \ _ -> stripTable sref),+ -- ("Dress", \ _ -> dressTable sref),+ ("Cancel", \ _ -> closePanel panel)]++-- | Close (destroy) the panel.+-- I hope this also removes it from its parent.++closePanel :: EditArgsPanel -> IO ()+closePanel panel = widgetDestroy (editArgsPanelRoot panel)++-- | Create a new ArgRow+argRowNew :: StateRef -> ArgSpec -> Int -> IO ArgRow+argRowNew sref (ArgSpec name inlets) n = do+ e1 <- makeEntry name+ e2 <- makeEntry (show inlets)+ -- Create and set up button+ let (label, action) = + case name of+ "" -> ("Add", addArg sref n)+ _ -> ("Remove", removeArg sref n)+ b <- buttonNewWithLabel label+ _ <- on b buttonActivated action+ return (ArgRow e1 e2 b)++-- | Add a new argument, nth in the model, from the (n+1)th row in the table++-- Still to do: make sure the name is non-blank, and doesn't duplicate+-- an existing argument (or global variable??)+-- and is a valid symbol name (not a number, etc.)?++addArg :: StateRef -> Int -> IO ()+addArg sref n = do+ State (Model argSpecs) ui@(UI root _table _status argRows) <- readIORef sref+ readResult <- readArgRow (argRows !! n)++ case readResult of++ Succ argTool -> + do+ writeIORef sref (State (Model (argSpecs ++ [argTool])) ui)+ stripTable sref+ dressTable sref+ -- Make sure the container (layout) is big enough + -- to show the enlarged panel+ mparent <- widgetGetParent root+ case mparent of+ Nothing -> return () -- shouldn't happen+ Just parent -> expandToFit parent root+ setStatusOK ui++ Fail msg -> setStatus ui $ "Add: " ++ msg++readArgRow :: ArgRow -> IO (SuccFail ArgSpec)+readArgRow (ArgRow e1 e2 _) = do+ name <- entryGetText e1+ case name of+ "" -> return $ Fail "blank name is not allowed"+ _ -> do+ inletsStr <- entryGetText e2+ return $ case parseInt ("Inlets for " ++ name) inletsStr of+ Succ inlets -> Succ (ArgSpec name inlets)+ Fail msg -> Fail msg++-- | Remove an argument, nth in the model, (n+1)th row in the table+removeArg :: StateRef -> Int -> IO ()+removeArg sref n = do+ State (Model argSpecs) ui <- readIORef sref+ writeIORef sref (State (Model (listRemove argSpecs n)) ui)+ stripTable sref+ dressTable sref+ setStatusOK ui++-- | Remove the nth element of a list (0 is the head)+listRemove :: [a] -> Int -> [a]+listRemove [] _ = error "listRemove: empty"+listRemove (_:xs) 0 = xs+listRemove (x:xs) n = x : (listRemove xs (n - 1))++makeEntry :: String -> IO Entry+makeEntry text = do+ entry <- entryNew+ entrySetText entry text+ return entry++applyArgRows :: StateRef -> ArgSpecAction -> IO ()+applyArgRows sref action = do+ State _model ui@(UI _root _table _status argRows) <- readIORef sref+ readResult <- readArgRows argRows+ case readResult of+ Succ newSpecs -> + do+ setStatusOK ui+ writeIORef sref (State (Model newSpecs) ui)+ action newSpecs+ Fail msg ->+ setStatus ui ("Apply: " ++ msg) >> + return ()+++-- Use a monad transformer or "lift" here???++readArgRows :: [ArgRow] -> IO (SuccFail [ArgSpec])+readArgRows [] = return (Succ []) -- shouldn't happen!+readArgRows (_:[]) = return (Succ []) -- last row is for the "add" button+readArgRows (row:rows) = do+ results <- readArgRows rows+ case results of+ Fail msg -> return $ Fail msg+ Succ specs -> + do+ result <- readArgRow row+ case result of+ Fail msg -> return $ Fail msg+ Succ spec -> return $ Succ (spec:specs)+ +setStatusOK :: UI -> IO ()+setStatusOK ui = setStatus ui ""+ +setStatus :: UI -> String -> IO ()+setStatus (UI _ _ status _) msg = + labelSetText status msg
+ Graphics/UI/Sifflet/Frame.hs view
@@ -0,0 +1,307 @@+module Graphics.UI.Sifflet.Frame+ (+ CanvFrame(..), FrameType(..)+ , argIoletCounter+ , atLeastSizeFrame+ , cfEvalReady+ , cfPointInHeader+ , cfPointInFooter+ , cfRoot+ , frameNewWithLayout+ , frameBodyBox+ , frameNodeAt+ , frameOffset+ , levelOrder+ , nodeCompoundFunction+ , pointIolet+ , resizeFrame+ , translateFrame+ , grTranslateFrameNodes+ )++where++import Data.Function+import Data.List++import Data.Graph.Inductive as G++import Data.Sifflet.Functoid+import Data.Sifflet.Geometry+import Data.Sifflet.Tree+import Data.Sifflet.TreeLayout+import Data.Sifflet.WGraph+import Language.Sifflet.Expr+import Language.Sifflet.ExprTree+import Language.Sifflet.Util++-- ---------------------------------------------------------------------+-- CanvFrame operations+-- ---------------------------------------------------------------------+++-- | A CanvFrame represents (indirectly, through cfRoot, and we+-- access to the graph which is provided by the VCanvas)+-- a "subgraph" such as the expression tree+-- of a function which is being edited or called.++data CanvFrame = CanvFrame {+ cfHeader :: TextBox -- ^ top area of the frame+ , cfFooter :: TextBox -- ^ bottom area+ , cfVarNames :: [String] -- ^ variable (parameter) names+ , cfParent :: Maybe G.Node -- ^ the node opened to make this frame+ , cfFrameNode :: G.Node -- ^ this frame as a node in the graph;+ -- also serves as the ID of the frame.+ , cfEnv :: Env -- ^ environment for evaluation+ , cfBox :: BBox -- ^ box of the whole frame (header, tree, and footer)+ , cfLevel :: Double -- ^ 0 = bottom level, 1 = next higher, etc.+ , cfFunctoid :: Functoid -- ^ includes tlo for an edit frame+ , frameType :: FrameType -- ^ edit or call frame+ }+ deriving (Show)+ +++-- | CanvFrame needs to be Eq in order to be Ord,+-- but maybe the Eq and Ord definitions should be more+-- in the same spirit?+instance Eq CanvFrame where (==) = (==) `on` cfFrameNode++data FrameType = EditFrame | CallFrame deriving (Eq, Read, Show)++-- | Use levelOrder for sorting frames before drawing them+levelOrder :: CanvFrame -> CanvFrame -> Ordering+levelOrder f1 f2 = compare (cfLevel f1) (cfLevel f2) ++-- | The root of the tree displayed in the frame+cfRoot :: CanvFrame -> G.Node+cfRoot frame = + case frameType frame of+ EditFrame -> error "cfRoot: an edit frame has no root"+ CallFrame -> succ (cfFrameNode frame)++-- | A frame is "eval ready" -- that is, okay to run the Eval Frame dialog --+-- if it is a call frame with no parent+cfEvalReady :: CanvFrame -> Bool+cfEvalReady frame = + (frameType frame == CallFrame) && (cfParent frame == Nothing)++cfPointInHeader :: CanvFrame -> Double -> Double -> Bool+cfPointInHeader frame x y = + pointInBB (Position x y) (tbBoxBB (cfHeader frame))++cfPointInFooter :: CanvFrame -> Double -> Double -> Bool+cfPointInFooter frame x y = + pointInBB (Position x y) (tbBoxBB (cfFooter frame))++frameBodyBox :: CanvFrame -> BBox+frameBodyBox frame =+ -- the space between the header and footer+ let hbb = tbBoxBB (cfHeader frame)+ fbb = tbBoxBB (cfFooter frame)+ top = bbBottom hbb+ bottom = bbTop fbb+ -- assuming both header and footer are left aligned and same width+ in BBox (bbLeft hbb) top (bbWidth hbb) (bottom - top)++editFrameNodes :: CanvFrame -> [G.Node]+editFrameNodes frame =+ case frameType frame of+ EditFrame -> fpNodes (cfFunctoid frame)+ CallFrame -> error "editFrameNodes: not an EditFrame"++frameNodeAt :: CanvFrame -> WGraph -> Position -> Maybe G.Node+frameNodeAt frame graph point = + case frameType frame of+ CallFrame -> callFrameNodeAt frame graph point+ EditFrame -> editFrameNodeAt frame graph point++editFrameNodeAt :: CanvFrame -> WGraph -> Position -> Maybe G.Node+editFrameNodeAt _frame _graph _point = Nothing -- STUB ***+ -- this could probably make use of the function that finds ***+ -- a selection from a point ***++callFrameNodeAt :: CanvFrame -> WGraph -> Position -> Maybe G.Node+callFrameNodeAt frame graph point = + let search :: [G.Node] -> Maybe G.Node+ search [] = Nothing+ search (r:rs) =+ case lab graph r of+ Just (WFrame _) -> search rs+ Just (WSimple layoutNode) ->+ let LayoutNode rootGNode treeBB = layoutNode+ in+ if pointInBB point treeBB + -- it's in the tree rooted at r+ then if pointInBB point (gnodeNodeBB rootGNode) + -- it's in the root node+ then Just r+ else search (suc graph r) -- maybe in the subtrees+ else search rs -- maybe in the siblings+ Nothing -> + errcats ["editFrameNodeAt: search: no label for node",+ show r]+ -- since this is a call frame, it had better have a root+ in search [cfRoot frame]++atLeastSizeFrame :: Size -> CanvFrame -> CanvFrame+atLeastSizeFrame (Size minW minH) frame =+ let BBox _ _ width height = cfBox frame+ dwidth = if minW > width then minW - width else 0+ dheight = if minH > height then minH - height else 0+ in resizeFrame frame dwidth dheight++resizeFrame :: CanvFrame -> Double -> Double -> CanvFrame+resizeFrame frame dw dh = + let BBox x y bwidth height = frameBodyBox frame+ -- do not shrink body to negative size+ bwidth' = max 0 (bwidth + dw)+ height' = max 0 (height + dh)+ bodyBB' = BBox x y bwidth' height'+ header' = alignHeader (cfHeader frame) bodyBB'+ footer' = alignFooter (cfFooter frame) bodyBB'+ frameBox' = bbMergeList [tbBoxBB header', tbBoxBB footer', bodyBB']+ in frame {cfHeader = header', cfFooter = footer', cfBox = frameBox'}+++translateFrame :: CanvFrame -> Double -> Double -> CanvFrame+translateFrame frame dx dy =+ frame {cfHeader = translate dx dy (cfHeader frame),+ cfFooter = translate dx dy (cfFooter frame),+ cfBox = translate dx dy (cfBox frame)}+++-- | Where to position a new frame that is grown out of an old frame?+-- This is a very rough draft of frameOffset+frameOffset :: Style -> CanvFrame -> Position+frameOffset style oldFrame = + let bb = cfBox oldFrame+ in Position (bbRight bb + styleFramePad style) (bbTop bb - 40)++++nodeCompoundFunction :: WGraph -> CanvFrame -> Node -> Maybe Function+nodeCompoundFunction graph frame node = + case lab graph node of+ Nothing -> error "nodeCompoundFunction: no label for node"+ Just (WFrame _) -> error "nodeCompoundFunction: node has a WFrame label"+ Just (WSimple layoutNode) ->+ case gnodeValue (nodeGNode layoutNode) of+ ENode (NSymbol (Symbol "if")) _mvalue -> Nothing -- not a function+ ENode (NSymbol (Symbol symbolName)) _mvalue ->+ case envLookup (cfEnv frame) symbolName of+ Nothing -> Nothing -- unbound symbol okay, at least sometimes+ Just (VFun func@(Function _ _ _ (Compound _ _))) -> Just func+ Just _ -> Nothing -- not a compound function+ _ -> Nothing -- not a symbol++grTranslateFrameNodes :: WGraph -> CanvFrame -> Double -> Double -> WGraph+grTranslateFrameNodes wgraph frame dx dy =+ case frameType frame of+ CallFrame -> translateTree dx dy wgraph (cfRoot frame)+ EditFrame -> translateNodes dx dy wgraph (editFrameNodes frame)+ +pointIolet :: Position -> Int -> [Iolet] -> Maybe Int+pointIolet point n iolets =+ -- find the number of the iolet, if any, containing point+ case iolets of + [] -> Nothing+ (p:ps) ->+ if pointInIolet point p + then Just n+ else pointIolet point (n + 1) ps+++-- | argIoletCounter returns (no. of inlets, no. of outlets)+-- derived from the argument list of a function still being defined+argIoletCounter :: [String] -> ExprNode -> (Int, Int)+argIoletCounter labels _exprNode = (length labels, 1)++-- | Aligning a CanvFrame's header and footer with the body of the frame.+-- Aligns the header above, and the footer below, the body of the frame,+-- also matching the width if the body widened++alignHeader :: TextBox -> BBox -> TextBox+alignHeader header bodybox =+ let headerBB = tbBoxBB header+ y0 = bbBottom headerBB+ y1 = bbTop bodybox+ x0 = bbLeft headerBB+ x1 = bbLeft bodybox+ in translate (x1 - x0) (y1 - y0) (tbSetWidth header (bbWidth bodybox))++alignFooter :: TextBox -> BBox -> TextBox+alignFooter footer bodybox =+ let footerBB = tbBoxBB footer+ y0 = bbTop footerBB+ y1 = bbBottom bodybox+ x0 = bbLeft footerBB+ x1 = bbLeft bodybox+ in translate (x1 - x0) (y1 - y0) (tbSetWidth footer (bbWidth bodybox))+++-- | Figure out the frame layout for a function. Returns the layout and frame.+-- Currently, the frame is marked as a "call frame"; if you want to edit it,+-- call (editFrame? editFunction?)++frameNewWithLayout :: Style -> Position -> Double+ -> Functoid -> Maybe [Value] -> FrameType -- added arg+ -> Node -> Env -> Maybe G.Node + -> (CanvFrame, FunctoidLayout) -- reversed tuple+frameNewWithLayout style (Position x y) z + functoid mvalues mode frameNode prevEnv mparent = + -- Figure out the positions for a function call with the+ -- given function and (possibly) values as arguments+ let headerText = functoidHeader functoid+ vars = functoidArgNames functoid+ footerText = buildFooterText vars mvalues+ env = case mvalues of+ Nothing ->+ -- dummy extension to be popped off+ extendEnv [] [] prevEnv + Just values ->+ extendEnv vars values prevEnv+ -- body tlo+ layout0 = flayout style functoid env mvalues+ -- header and footer layouts+ headerTB0 = makeTextBox style headerText -- at 0 0+ footerTB0 = makeTextBox style footerText -- at 0 0++ -- make all three the same width+ Size lw lh = flayoutSize layout0+ fwidth = maximum [tbWidth headerTB0, lw, tbWidth footerTB0]+ -- The __widen functions ensure that each part + -- (header, footer, tree tlo) have the desired width.+ headerTB1 = translate x y (widen headerTB0 fwidth)+ (dx, dy) = (x - hpad style, tbBottom headerTB1 - vpad style)+ layout1 = translate dx dy (flayoutWiden layout0 fwidth)+ footerTB1 = translate x (flayoutBottom layout1) + (widen footerTB0 fwidth)+ frameBox = BBox x y fwidth + (tbHeight headerTB0 + lh + tbHeight footerTB0)+ frame = CanvFrame {cfHeader = headerTB1,+ cfFooter = footerTB1,+ cfVarNames = vars,+ cfParent = mparent,+ cfFrameNode = frameNode,+ cfEnv = env, + cfBox = frameBox,+ cfLevel = z,+ cfFunctoid = functoid,+ frameType = mode}+ in (frame, layout1)++++buildFooterText :: [String] -> Maybe [Value] -> String+buildFooterText vars mvalues = + let items =+ case mvalues of+ Nothing -> vars+ Just [] -> vars+ Just values ->+ if length vars /= length values+ then error "buildFooterText: mismatched lists"+ else [var ++ " = " ++ repr value |+ (var, value) <- zip vars values]+ in concat (intersperse ", " items)
+ Graphics/UI/Sifflet/GtkForeign.hs view
@@ -0,0 +1,123 @@+{-# LANGUAGE ForeignFunctionInterface, CPP #-}++-- | Provides a function to set the cursor to a specified shape.+-- Generalized from an example in email+-- Re: [Gtk2hs-users] Changing the mouse cursor shape+-- From: Chris Mears <chris@cm...> - 2007-05-22 07:48+-- found on+-- https://sourceforge.net/mailarchive/forum.php?thread_name=87ps50ie9o.fsf%40loki.cmears.id.au&forum_name=gtk2hs-users+-- and modified by removing the definition of data Cursor+-- since that is now part of gtk2hs/gtk+-- although the enum values are not :-(++-- The CursorType type itself is now exported from+-- Graphics.UI.Gtk.Gdk.Cursor, as also cursorNew+-- and cursorNewFromPixmap.++module Graphics.UI.Sifflet.GtkForeign (setCursor)+where++import System.Glib+import System.Glib.FFI+import Foreign.ForeignPtr ()++import Graphics.UI.Gtk++foreign import ccall "gdk_window_set_cursor" gdkWindowSetCursor :: + Ptr DrawWindow -> Ptr Cursor -> IO ()++foreign import ccall "gdk_cursor_new" gdkCursorNew :: Int -> IO (Ptr Cursor)++-- | Set the X Window cursor for a window to a specified type+setCursor :: Window -> CursorType -> IO ()+setCursor window cursorType = do+ c <- gdkCursorNew (cursorTypeNumber cursorType)+ d <- widgetGetDrawWindow window+ let GObject fp = toGObject d+ withForeignPtr (castForeignPtr fp) $ \ptr -> gdkWindowSetCursor ptr c++-- These numbers are from /usr/include/gtk-2.0/gdk/gdkcursor.h++cursorTypeNumber :: CursorType -> Int+cursorTypeNumber cursorType =+ case cursorType of+ XCursor -> 0+ Arrow -> 2+ BasedArrowDown -> 4+ BasedArrowUp -> 6+ Boat -> 8+ Bogosity -> 10+ BottomLeftCorner -> 12+ BottomRightCorner -> 14+ BottomSide -> 16+ BottomTee -> 18+ BoxSpiral -> 20+ CenterPtr -> 22+ Circle -> 24+ Clock -> 26+ CoffeeMug -> 28+ Cross -> 30+ CrossReverse -> 32+ Crosshair -> 34+ DiamondCross -> 36+ Dot -> 38+ Dotbox -> 40+ DoubleArrow -> 42+ DraftLarge -> 44+ DraftSmall -> 46+ DrapedBox -> 48+ Exchange -> 50+ Fleur -> 52+ Gobbler -> 54+ Gumby -> 56+ Hand1 -> 58+ Hand2 -> 60+ Heart -> 62+ Icon -> 64+ IronCross -> 66+ LeftPtr -> 68+ LeftSide -> 70+ LeftTee -> 72+ Leftbutton -> 74+ LlAngle -> 76+ LrAngle -> 78+ Man -> 80+ Middlebutton -> 82+ Mouse -> 84+ Pencil -> 86+ Pirate -> 88+ Plus -> 90+ QuestionArrow -> 92+ RightPtr -> 94+ RightSide -> 96+ RightTee -> 98+ Rightbutton -> 100+ RtlLogo -> 102+ Sailboat -> 104+ SbDownArrow -> 106+ SbHDoubleArrow -> 108+ SbLeftArrow -> 110+ SbRightArrow -> 112+ SbUpArrow -> 114+ SbVDoubleArrow -> 116+ Shuttle -> 118+ Sizing -> 120+ Spider -> 122+ Spraycan -> 124+ Star -> 126+ Target -> 128+ Tcross -> 130+ TopLeftArrow -> 132+ TopLeftCorner -> 134+ TopRightCorner -> 136+ TopSide -> 138+ TopTee -> 140+ Trek -> 142+ UlAngle -> 144+ Umbrella -> 146+ UrAngle -> 148+ Watch -> 150+ Xterm -> 152+ LastCursor -> 153+ BlankCursor -> (-2)+ CursorIsPixmap -> (-1)
+ Graphics/UI/Sifflet/GtkUtil.hs view
@@ -0,0 +1,224 @@+{-# LANGUAGE CPP #-}++module Graphics.UI.Sifflet.GtkUtil + (suppressScimBridge+ , showChoicesDialog, defaultDialogPosition+ , runDialogM, runDialogS+ -- , runDialogHelper+ , showInputErrorMessage, showErrorMessage+ , showInfoMessage+ , showMessage+ , EntryDialog, Reader+ , createEntryDialog, runEntryDialog+ , addEntryWithLabel+ )++where++import Control.Monad++#if !defined(mingw32_HOST_OS) && !defined(__MINGW32__) +import System.Posix.Env+#endif++import Graphics.UI.Sifflet.LittleGtk+import Language.Sifflet.Util++-- SCIM Bridge causes problems, so shush it++suppressScimBridge :: IO ()+suppressScimBridge = +#if defined(mingw32_HOST_OS) || defined(__MINGW32__) + return ()+#else+ putEnv "GTK_IM_MODULE=gtk-im-context-simple"+#endif++-- ============================================================+-- CUSTOMIZABLE DIALOGS++-- | Show a message and a set of choices;+-- run the action corresponding to the selected choice;+-- you might want to include a "Cancel" option with the action return ().+-- There *should* be as many actions as there are options.+showChoicesDialog :: String -> String -> [String] -> [IO a] -> IO a+showChoicesDialog title message options actions = do+ {+ -- Create basic dialog+ dialog <- dialogNew+ ; windowSetTitle dialog title+ ; widgetSetName dialog ("Sifflet-" ++ title)++ -- Add message+ ; vbox <- dialogGetUpper dialog+ ; label <- labelNew (Just message)+ ; boxPackStartDefaults vbox label+ ; widgetShowAll vbox++ -- Add buttons+ -- Work around bug in gtk2hs v. 0.10.0 and 0.10.1,+ -- which is fixed in darcs gtk2hs.+ -- fromResponse (ResponseUser i) requires i > 0.+ -- so zip [1..] instead of zip [0..]+ ; let indexOptions = zip [1..] options+ addButton (i, option) = + dialogAddButton dialog option (ResponseUser i)+ ; forM_ indexOptions addButton+ ; dialogGetActionArea dialog >>= widgetShowAll++ -- Run dialog+ ; response <- dialogRun dialog+ ; widgetDestroy dialog -- here? or after handling response?+ ; case response of+ ResponseUser i -> + let j = i - 1 -- work around bug described above+ in if j >= 0 && j < length actions+ then actions !! j+ else errcats ["showChoicesDialog: response index",+ show j, "is out of range for actions"]+ _ -> errcats ["showChoicesDialog: expected a ResponseUser _ but got",+ show response]+ }+ +createDialog :: String -> (VBox -> IO a) -> IO (Dialog, a)+createDialog title addContent = do+ -- Create basic dialog+ dialog <- dialogNew+ windowSetTitle dialog title+ widgetSetName dialog ("Sifflet-" ++ title)++ -- Add custom content+ vbox <- dialogGetUpper dialog+ content <- addContent vbox++ -- Add standard buttons+ _ <- dialogAddButton dialog "OK" ResponseOk+ _ <- dialogAddButton dialog "Cancel" ResponseCancel+ dialogSetDefaultResponse dialog ResponseOk -- has no effect?++ return (dialog, content)++-- | Where to put a dialog window.+-- Possible values are+-- WinPosNone WinPosCenter WinPosMouse WinPosCenterAlways +-- WinPosCenterOnParent++defaultDialogPosition :: WindowPosition+defaultDialogPosition = WinPosMouse++-- | Customizable framework for running a dialog++runDialogS :: Dialog -> a -> (a -> IO (SuccFail b)) -> IO (Maybe b)+runDialogS dialog inputs processInputs = + runDialogHelper dialog inputs processInputs True++runDialogM :: Dialog -> a -> (a -> IO (Maybe b)) -> IO (Maybe b)+runDialogM dialog inputs processInputs =+ let process' inputs' = do+ result <- processInputs inputs'+ case result of+ Nothing -> return $ Fail "_Nothing_"+ Just value -> return $ Succ value+ in runDialogHelper dialog inputs process' False++runDialogHelper :: Dialog -> a -> (a -> IO (SuccFail b)) -> Bool -> IO (Maybe b)+runDialogHelper dialog inputs processInputs retryOnError = do+ -- Position and show the dialog+ windowSetPosition dialog defaultDialogPosition+ widgetShowAll dialog+ windowPresent dialog++ let run = do+ respId <- dialogRun dialog+ case respId of+ ResponseOk ->+ do+ result <- processInputs inputs+ case result of+ Fail msg ->+ if retryOnError+ then do+ showErrorMessage msg+ run -- try again+ else finish Nothing+ Succ value -> finish (Just value)+ _ -> finish Nothing++ finish result = do+ widgetDestroy dialog+ return result++ run++showInputErrorMessage :: String -> IO ()+showInputErrorMessage message =+ showErrorMessage ("Input Error:\n" ++ message)++showErrorMessage :: String -> IO ()+showErrorMessage = showMessage (Just "Error") MessageError ButtonsClose++showInfoMessage :: String -> String -> IO ()+showInfoMessage title = showMessage (Just title) MessageInfo ButtonsClose++showMessage :: Maybe String -> MessageType -> ButtonsType -> String -> IO ()+showMessage mtitle messagetype buttonstype message = do+ {+ msgDialog <- + messageDialogNew+ Nothing -- ? or (Just somewindow) -- what does this do?+ [] -- flags+ messagetype+ buttonstype+ message+ ; case mtitle of+ Nothing -> widgetSetName msgDialog "Sifflet-dialog"+ Just title -> windowSetTitle msgDialog title >>+ widgetSetName msgDialog ("Sifflet-" ++ title)+ ; windowSetPosition msgDialog defaultDialogPosition+ ; windowPresent msgDialog+ ; _ <- dialogRun msgDialog+ ; widgetDestroy msgDialog+ }++-- ============================================================+-- INPUT DIALOGS++type Reader a b = (a -> SuccFail b)++data EntryDialog a = EntryDialog Dialog [Entry] (Reader [String] a)++createEntryDialog :: + String -> [String] -> [String] -> (Reader [String] a) -> Int -> + IO (EntryDialog a)+createEntryDialog title labels defaults reader width = do+ -- Interpret width = -1 as don't care++ (dialog, entries) <- + createDialog title (addEntries labels defaults)+ windowSetDefaultSize dialog width (-1)+ return $ EntryDialog dialog entries reader++runEntryDialog :: (Show a) => EntryDialog a -> IO (Maybe a)+runEntryDialog (EntryDialog dialog entries reader) = + let -- processInputs :: [Entry] -> IO (SuccFail a)+ -- weird error like in runComboBoxDialog ^+ processInputs entries' = do+ inputs <- mapM entryGetText entries'+ return (reader inputs)++ in runDialogS dialog entries processInputs++addEntries :: [String] -> [String] -> VBox -> IO [Entry]+addEntries labels defaults vbox = do+ entries <- mapM (const entryNew) labels+ mapM_ (addEntryWithLabel vbox) (zip3 labels entries defaults)+ return entries++-- | Add a labeled text entry to the vbox.++addEntryWithLabel :: VBox -> (String, Entry, String) -> IO ()+addEntryWithLabel vbox (name, entry, defaultValue) = do+ label <- labelNew (Just name)+ entrySetText entry defaultValue+ boxPackStartDefaults vbox label+ boxPackStartDefaults vbox entry
+ Graphics/UI/Sifflet/LittleGtk.hs view
@@ -0,0 +1,185 @@+-- | The purpose of this module is simply to re-export the+-- Gtk functions and types that I use, and avoid those+-- that I don't, particularly those that conflict with+-- names in Sifflet, like Layout.+-- Just hiding these is not enough, because Graphics.UI.Gtk+-- keeps changing what it exports.+-- For example, in 0.10.5 it no longer exports Function, fill,+-- function.++module Graphics.UI.Sifflet.LittleGtk+ (+ -- These are just directly re-exported from Gtk:++ -- Attributes+ AttrOp(..) -- for (:=)+ , get+ , set++ , adjustmentNew++ , boxPackEnd+ , boxPackStart+ , boxPackStartDefaults++ , Button+ , buttonNewWithLabel+ , buttonPressEvent++ , ButtonsType(..)++ , ContainerClass+ , castToContainer+ , containerAdd+ , containerChild+ , containerForeach+ , containerGetChildren++ , CursorType(..)+ , customStoreSetColumn++ -- Dialog+ , Dialog+ , dialogNew+ , dialogAddButton+ , dialogGetActionArea+ , dialogGetUpper + , dialogRun+ , dialogSetDefaultResponse+ , toDialog++ , DrawWindow+ , drawWindowInvalidateRect++ , entryCompletionInsertPrefix+ , entryCompletionModel+ , entryCompletionNew+ , entryCompletionSetTextColumn++ , Entry+ , entryGetText+ , entryGetCompletion+ , entryNew+ , entrySetCompletion+ , entrySetText++ , EventMask(..)++ , eventBoxNew++ , Expander+ , expanderNew+ , expanderSetExpanded++ , exposeEvent++ , fileChooserDialogNew+ , fileChooserGetFilename++ , FileChooserAction(..)++ , frameNew+ , frameSetLabel++ , grabAdd+ , grabRemove++ , HBox+ , hBoxNew++ , keyPressEvent++ , Label+ , labelNew+ , labelSetText++ , Layout+ , layoutGetDrawWindow+ , layoutNew+ , layoutPut+ , layoutSetSize++ , listStoreNew++ , makeColumnIdString++ , menuPopup++ , MessageType(..)+ , messageDialogNew++ , on+ , onDestroy+ , onSizeRequest++ , Packing(..)++ , PolicyType(PolicyAutomatic)++ , Rectangle(..)++ , renderWithDrawable++ , Requisition(..)++ , ResponseId(..)++ , ScrolledWindow+ , scrolledWindowNew+ , scrolledWindowSetPolicy++ , Statusbar+ , statusbarGetContextId+ , statusbarNew+ , statusbarPop+ , statusbarPush++ , Table+ , tableNew+ , tableAttachDefaults++ , VBox+ , vBoxNew++ , WidgetClass++ , widgetAddEvents+ , widgetClassPath+ , widgetDestroy+ , widgetGrabFocus+ , widgetSetCanFocus+ , widgetSetDoubleBuffered+ , widgetSetName+ , widgetSetSizeRequest+ , widgetShow+ , widgetShowAll+ , widgetSizeRequest+ , widgetVisible++ , Window+ , windowDeletable+ , windowMove+ , windowNew+ , windowPresent+ , windowSetDefaultSize+ , windowSetPosition+ , windowSetTitle+ , windowTitle++ , WindowPosition(..)+ -- General GTK stuff+ , initGUI+ , mainGUI+ , mainQuit++ -- These are renamed:+ , GtkFrame+ , GtkLayout+ )++where++import Graphics.UI.Gtk++type GtkFrame = Frame+type GtkLayout = Layout
+ Graphics/UI/Sifflet/RPanel.hs view
@@ -0,0 +1,154 @@+-- | "Rowed Panel." +-- Expandable framed panel with a two-dimensional layout+-- (rows of widgets, but not with aligned columns like in a table).++module Graphics.UI.Sifflet.RPanel+ (+ RPanel, newRPanel, rpanelId, rpanelRoot, rpanelContent+ , rpanelAddWidget, rpanelAddWidgets, rpanelNewRow+ , rpanelAddRows+ )++where++import Control.Monad++import Graphics.UI.Sifflet.LittleGtk+import Language.Sifflet.Util++debugTracing :: Bool+debugTracing = False++data RPanel + = RPanel {+ -- Public+ rpId :: String -- ^ text for the expander button+ , rpRoot :: GtkFrame -- ^ use the root to add rpanel to a container+ , rpContent :: [[String]] -- ^ ids of widgets added, in rows++ -- Widgets that make up the RPanel+ , rpFrame :: GtkFrame -- ^ frame, same as root+ , rpExpander :: Expander -- ^ expander+ , rpVBox :: VBox -- ^ vbox to contain the rows+ , rpCurrentRow :: HBox -- ^ next element goes here if it fits++ -- Geometry book-keeping+ , rpCurrentRowFreeWidth :: Int -- ^ free width in current row+ , rpMaxWidth :: Int -- ^ maximum row width+ , rpHPad :: Int -- ^ horizontal padding+ }++rpanelId :: RPanel -> String+rpanelId = rpId++rpanelRoot :: RPanel -> GtkFrame+rpanelRoot = rpRoot++rpanelContent :: RPanel -> [[String]]+rpanelContent = rpContent++newRPanel :: String -> Int -> Int -> Int -> IO RPanel+newRPanel cid hpad vpad maxWidth = do+ {+ frame <- frameNew -- adds a border (not labeled, since the expander is)+ + ; expander <- expanderNew cid+ ; expanderSetExpanded expander True+ ; set frame [containerChild := expander]++ ; vbox <- vBoxNew False vpad -- non-homogeneous heights+ ; widgetSetSizeRequest vbox maxWidth (-1) -- height = don't care+ ; set expander [containerChild := vbox]++ ; hbox <- hBoxNew False hpad -- non-homogeoneous widths+ ; boxPackStart vbox hbox PackNatural 0++ ; return $ RPanel {rpId = cid+ , rpRoot = frame+ , rpFrame = frame+ , rpExpander = expander+ , rpVBox = vbox+ , rpCurrentRow = hbox+ , rpContent = [[]]+ , rpCurrentRowFreeWidth = maxWidth - hpad+ , rpMaxWidth = maxWidth - hpad+ , rpHPad = hpad+ }+ }++-- | Given a list of (name, widget) pairs, add each of the widgets+-- and its name to the rpanel+rpanelAddWidgets :: (WidgetClass widget) =>+ RPanel -> [(String, widget)] -> IO RPanel+rpanelAddWidgets rp pairs = + let addPair rp' (widgetId, widget) = rpanelAddWidget rp' widgetId widget+ in foldM addPair rp pairs++-- | Add a single named widget to the RPanel+rpanelAddWidget :: (WidgetClass widget) =>+ RPanel -> String -> widget -> IO RPanel+rpanelAddWidget rp widgetId widget = do+ {+ Requisition widgetWidth _ <- widgetSizeRequest widget+ ; let freeWidth = rpCurrentRowFreeWidth rp+ freeWidth' = freeWidth - widgetWidth - rpHPad rp+ ; if freeWidth' >= 0 || freeWidth == rpMaxWidth rp+ -- Either there is room enough, OR we're at the start of a row+ -- so starting another won't help -- in fact it would lead to+ -- infinite recursion+ then do+ {+ let content' = insertLastLast (rpContent rp) widgetId+ packMode = -- PackNatural -- to left justify+ PackGrow -- to fill+ -- PackRepel -- to center++ ; boxPackStart (rpCurrentRow rp) widget packMode 0+ -- ; widgetShow widget -- do this here???+ ; when debugTracing $+ putStr (unlines ["Adding " ++ widgetId ++ + " width " ++ show widgetWidth+ , "Free width = " ++ show freeWidth +++ " -> " ++ show freeWidth'+ , "Content -> " ++ show content'])++ ; return $ rp {rpContent = content'+ , rpCurrentRowFreeWidth = freeWidth'}+ }+ else + -- We're out of room, but not at the start of the current row,+ -- so start a new row+ do+ {+ rp' <- rpanelNewRow rp+ ; rpanelAddWidget rp' widgetId widget+ }+ }++-- | Force the RPanel to begin a new row++rpanelNewRow :: RPanel -> IO RPanel+rpanelNewRow rp = do+ {+ hbox <- hBoxNew False (rpHPad rp)+ ; boxPackStart (rpVBox rp) hbox PackNatural 0+ ; return $ rp {rpCurrentRow = hbox+ , rpContent = insertLast (rpContent rp) []+ , rpCurrentRowFreeWidth = rpMaxWidth rp}+ }++-- | Given a list of lists, each sublist representing a row of widgets,+-- add the widgets to the RPanel, preserving the row structure+-- as much as possible.+-- (Row structure will be broken if any intended row is too wide.)+rpanelAddRows :: (WidgetClass widget) =>+ RPanel -> [[(String, widget)]] -> IO RPanel+rpanelAddRows rp rows = foldM rpanelAddRow rp rows++-- | Add a row of widgets to an RPanel.+-- This does not start a new row before the first widget,+-- but after the last, so at the end, the current row will be empty.+rpanelAddRow :: (WidgetClass widget) =>+ RPanel -> [(String, widget)] -> IO RPanel+rpanelAddRow rp row = + rpanelAddWidgets rp row >>= rpanelNewRow
+ Graphics/UI/Sifflet/Tool.hs view
@@ -0,0 +1,585 @@+module Graphics.UI.Sifflet.Tool+ (+ ToolId(..)+ , checkMods+ , functionArgToolSpecs+ , functionTool+ , functionToolsFromLists+ , makeConnectTool+ , makeCopyTool+ , makeDeleteTool+ , makeDisconnectTool+ , makeIfTool+ , makeMoveTool+ , showFunctionEntry+ , showLiteralEntry+ , vpuiSetTool+ , vpuiWindowSetTool++ , vwAddFrame+ , vpuiAddFrame++ -- Statusbar+ , wsPopStatusbar, wsPushStatusbar++ -- frame context menu commands (do these belong elsewhere?)+ , dumpFrame+ , dumpGraph+ , dumpWorkWin+ , clearFrame+ , closeFrame+ )++where++import Control.Monad+import Control.Monad.Trans (liftIO)+import Data.IORef+import Data.List++import Data.Graph.Inductive as G++import Graphics.UI.Gtk.Gdk.EventM++import Data.Sifflet.Functoid+import Data.Sifflet.Geometry+import Data.Sifflet.Tree (putTree, repr)+import Data.Sifflet.TreeGraph (graphToOrderedTreeFrom)+import Data.Sifflet.WGraph++import Language.Sifflet.Expr+import Language.Sifflet.ExprTree+import Language.Sifflet.Parser++import Graphics.UI.Sifflet.Callback+import Graphics.UI.Sifflet.Canvas+import Graphics.UI.Sifflet.Frame+import Graphics.UI.Sifflet.LittleGtk +import Graphics.UI.Sifflet.Types++import Language.Sifflet.Util++data ToolId + = ToolConnect+ | ToolDisconnect+ | ToolIf+ | ToolMove+ | ToolDelete+ | ToolFunction String -- ^ function name+ | ToolLiteral Expr+ | ToolArg String Int -- ^ argument name, no. of inputs+ deriving (Eq, Show)+++functionArgToolSpecs :: Function -> [ArgSpec]+functionArgToolSpecs (Function _mname argTypes _rtype impl) =+ let atspec :: (String, Type) -> ArgSpec+ atspec (name, t) = ArgSpec name (typeInlets t)+ typeInlets :: Type -> Int+ typeInlets (TypeCons "Function" [_t1, t2]) = 1 + typeInlets t2+ typeInlets _ = 0+ argNames =+ case impl of+ Primitive _ -> ["arg" ++ show i | i <- [1 .. length argTypes]]+ Compound anames _body -> anames+ in map atspec (zip argNames argTypes)+ +-- Tools++vpuiSetTool :: ToolId -> WinId -> VPUI -> IO VPUI+vpuiSetTool toolId winId =+ vpuiUpdateWindowIO winId (vpuiWindowSetTool (toolIdToTool toolId))++vpuiWindowSetTool :: Tool -> VPUIWindow -> IO VPUIWindow+vpuiWindowSetTool tool vw =+ case vw of+ VPUIWorkWin ws _ ->+ do+ {+ ; wsPopStatusbar ws+ ; wsPushStatusbar ws ("Tool: " ++ toolName tool)+ ; let canvas' = (wsCanvas ws) {vcTool = Just tool}+ ; canvas'' <- toolActivated tool canvas'+ ; return $ vpuiWindowSetCanvas vw canvas''+ }+ _ -> return vw++toolIdToTool :: ToolId -> Tool+toolIdToTool toolId =+ case toolId of+ ToolConnect -> makeConnectTool+ ToolDisconnect -> makeDisconnectTool+ ToolIf -> makeIfTool+ ToolMove -> makeMoveTool+ ToolDelete -> makeDeleteTool+ ToolFunction funcname -> functionTool funcname+ ToolLiteral e -> makeBoundLiteralTool e+ ToolArg argname n -> makeBoundArgTool argname n+++defaultContextDescription :: String+defaultContextDescription = "default context"++wsPushStatusbar :: Workspace -> String -> IO ()+wsPushStatusbar ws msg = do + {+ let sbar = wsStatusbar ws+ ; contextId <- statusbarGetContextId sbar defaultContextDescription+ ; _ <- statusbarPush sbar contextId msg+ ; return ()+ }++wsPopStatusbar :: Workspace -> IO ()+wsPopStatusbar ws = do+ {+ let sbar = wsStatusbar ws+ ; contextId <- statusbarGetContextId sbar defaultContextDescription+ ; statusbarPop sbar contextId+ }++makeMoveTool :: Tool+makeMoveTool = + let move canv toolContext _mods x y = + case toolContext of+ TCEditFrame frame ->+ let graph = vcGraph canv+ in case pointSelection graph frame (Position x y) of+ sel@(Just (SelectionNode node)) ->+ let dragging = + Dragging {draggingNode = node,+ draggingPosition = Position x y}+ in do+ {+ vcInvalidateFrameWithParent canv graph frame+ ; return $ canv {vcSelected = sel,+ vcDragging = Just dragging}+ }+ _ -> -- no node selected, do nothing+ return canv+ _ ->+ return canv -- not an edit frame, do nothing+ in Tool "MOVE" return (toToolOpVW move)++makeDeleteTool :: Tool+makeDeleteTool = + let del :: CanvasToolOp+ del canv toolContext mods x y =+ case toolContext of+ TCEditFrame frame -> + let graph = vcGraph canv+ in case pointSelection graph frame (Position x y) of+ Just (SelectionNode node) ->+ if checkMods [Shift] mods+ then vcFrameDeleteTree canv frame node+ else vcFrameDeleteNode canv frame node+ _ ->+ return canv -- no node selected, do nothing++ _ -> + return canv -- not an edit frame, do nothing+ + in Tool "DELETE" vcClearSelection (toToolOpVW del)++-- | Check that all required modifiers are in found+checkMods :: [Modifier] -> [Modifier] -> Bool+checkMods required found =+ all (\ r -> elem r found) required++makeConnectTool :: Tool+makeConnectTool = + Tool "CONNECT" vcClearSelection (toToolOpVW (conn connect))+ +makeDisconnectTool :: Tool+makeDisconnectTool = + Tool "DISCONNECT" vcClearSelection (toToolOpVW (conn disconnect))++conn :: (VCanvas -> G.Node -> WEdge -> G.Node -> WEdge -> IO VCanvas)+ -> CanvasToolOp+conn action canvas toolContext _mods x y =+ case toolContext of+ TCEditFrame frame ->+ let oldSel = vcSelected canvas+ graph = vcGraph canvas+ requestRedraw =+ vcInvalidateFrameWithParent canvas graph frame+ in case pointSelection graph frame (Position x y) of+ + -- Overall algorithm: If we're on a port and there's+ -- an opposite port selected, apply the action them+ -- and clear the selection. Otherwise, if we're on+ -- a port, select it. Otherwise do nothing.++ Just sel@(SelectionInlet parent inlet) ->+ -- Case 1: we're on an inlet port, + -- opposite port = outlet+ do + {+ requestRedraw+ ; case oldSel of+ Just (SelectionOutlet child outlet) ->+ do+ {+ canvas' <- action canvas parent + inlet child outlet+ ; return $ canvas' {vcSelected = Nothing}+ }+ _ -> return $ canvas {vcSelected = Just sel}+ }+ Just sel@(SelectionOutlet child outlet) ->+ -- Case 2: we're on an outlet port, + -- opposite port = inlet+ do+ {+ requestRedraw+ ; case oldSel of+ Just (SelectionInlet parent inlet) ->+ do+ {+ canvas' <- action canvas parent + inlet child outlet+ ; return $ canvas' {vcSelected = Nothing}+ }+ _ -> return $ canvas {vcSelected = Just sel}+ }++ _ -> + -- Case 3: we're not on an iolet, do nothing+ return canvas+ _ -> + -- not in an edit frame, do nothing+ return canvas++makeBoundLiteralTool :: Expr -> Tool+makeBoundLiteralTool e =+ let enode node = ENode node EvalUntried+ addLitNode node vw toolContext _mods x y =+ case toolContext of+ TCEditFrame frame ->+ vcFrameAddNode vw frame (enode node) [] x y+ _ ->+ return vw -- Nothing+ mktool node =+ Tool ("Literal: " ++ repr e) + return + (toToolOpVW (addLitNode node))+ in case e of+ EBool b -> mktool (NBool b)+ EChar c -> mktool (NChar c)+ ENumber n -> mktool (NNumber n)+ EString s -> mktool (NString s)+ EList es -> if exprIsLiteral e+ then mktool (NList es)+ else errcats ["makeBoundLiteralTool: ",+ "non-literal list expression",+ show e]+ _ ->+ errcats ["makeBoundLiteralTool: non-literal or",+ "extended expression", show e]++makeBoundArgTool :: String -> Int -> Tool+makeBoundArgTool label n = + let node = ENode (NSymbol (Symbol label)) EvalUntried+ addArgNode vc toolContext _mods x y =+ case toolContext of+ TCEditFrame frame ->+ vcFrameAddNode vc frame node (map show [1..n]) x y+ _ ->+ return vc -- no effect+ in Tool ("Argument: " ++ label ++ "/" ++ show n) + return+ (toToolOpVW addArgNode)++makeIfTool :: Tool+makeIfTool = + let if_ vpui toolContext _mods x y =+ case toolContext of+ TCEditFrame frame ->+ let node = ENode (NSymbol (Symbol "if")) EvalUntried+ labels = ["test", "left", "right"]+ in vcFrameAddNode vpui frame node labels x y+ _ ->+ return vpui -- do nothing+ in Tool "if" return (toToolOpVW if_)++makeCopyTool :: Tool+makeCopyTool = dummyTool "COPY"++dummyTool :: String -> Tool+dummyTool name = + let op vpui _winId _context _mods x y =+ info ("dummyTool", name, x, y) >>+ return vpui+ in Tool ("*" ++ name ++ "*") return op++-- functionTool needs the VPUI in its op,+-- because it needs to get an environment.++functionTool :: String -> Tool+functionTool name =+ let op :: ToolOp+ op vpui winId toolContext _mods x y = + -- Some of these are not used in some cases,+ -- but with lazy evaluation it doesn't hurt to+ -- declare them all up here:+ let env = vpuiGlobalEnv vpui+ func = envGetFunction env name+ in case toolContext of+ TCCallFrame _ -> + return vpui -- do nothing+ TCEditFrame frame ->+ let modify canvas =+ vcFrameAddFunctoidNode canvas frame+ (FunctoidFunc func)+ x y+ in vpuiModCanvasIO vpui winId modify+ TCExprNode ->+ return vpui -- do nothing+ TCWorkspace ->+ case functionImplementation func of+ Primitive _ -> + return vpui -- do nothing+ Compound _ _ -> + -- Add a call frame to the workspace+ vpuiAddFrame vpui winId (FunctoidFunc func) + Nothing CallFrame + env x y 0 Nothing+ in Tool name return op+++-- | Add a frame representing a functoid to the canvas +-- of a particular window, specified by its window id (title).++vpuiAddFrame :: VPUI -> WinId -> Functoid -> Maybe [Value] -> FrameType+ -> Env -> Double -> Double -> Double -> Maybe G.Node + -> IO VPUI+vpuiAddFrame vpui winId functoid mvalues mode prevEnv x y z mparent = + let update vw = + vwAddFrame vw functoid mvalues mode prevEnv x y z mparent+ in vpuiUpdateWindowIO winId update vpui++-- | Add a frame representing a functoid to the canvas +-- of a VPUIWindow (which ought to have a canvas, of course).+-- Otherwise like vcAddFrame.++vwAddFrame :: VPUIWindow -> Functoid -> Maybe [Value] -> FrameType + -> Env -> Double -> Double -> Double -> Maybe G.Node + -> IO VPUIWindow+vwAddFrame vw functoid mvalues mode prevEnv x y z mparent = + let modify canvas = vcAddFrame canvas functoid mvalues mode prevEnv+ x y z mparent+ in vpuiWindowModCanvasIO vw modify++functionToolsFromLists :: [[String]] -> [[Tool]]+functionToolsFromLists = map2 functionTool+ +-- | Open an entry for user input of function name to select a function tool.+-- Returns unaltered VPUI, for convenience in menus and key callbacks.++showFunctionEntry :: WinId -> CBMgr -> VPUI -> IO VPUI+showFunctionEntry winId uimgr vpui = + let env = vpuiGlobalEnv vpui+ fsymbols = (envFunctionSymbols env)+ -- Check whether the name is bound to a function+ checkFunctionName :: String -> SuccFail String+ checkFunctionName name =+ case envLookup env name of+ Nothing -> Fail $ name ++ ": unbound variable"+ Just (VFun _) -> Succ name+ _ -> Fail $ name ++ ": bound to non-function value"+ in showToolEntry winId "Function name" + (Just fsymbols) -- completions+ checkFunctionName -- parser+ -- activateTool -- action+ ToolFunction -- tool type specifier+ uimgr -- state+ vpui++-- | Show an entry for input of a literal value.+-- Returns unaltered VPUI, for convenience in menus and key callbacks.++showLiteralEntry :: WinId -> CBMgr -> VPUI -> IO VPUI+showLiteralEntry winId = + -- Needs uimgr for action when entry is activated+ showToolEntry winId "Literal value" + Nothing -- completions+ parseLiteral -- parser+ -- activateTool -- action+ ToolLiteral -- tool type specifier++-- | New, light replacement for most dialogs++-- | Prompt for input in a text entry.+-- When the user presses Return, attempt to parse the input text.+-- If parse succeeds, apply the toolType to the resulting value+-- to produce a ToolId and set the corresponding tool.+-- If user presses Escape, the input is closed with no action.+-- If mcompletions is (Just comps), comps is a list of possible+-- completions for the entry.+-- +-- Returns the vpui, with NO UPDATE, for convenience in callbacks and menus++-- *** Consider merging this with the other "light dialog"+-- in EditArgsPanel.hs++-- Note: this has been specialized; the action only sets a tool+-- on the window. It can be generalized again+-- to any sort of action if needed.+showToolEntry :: WinId -> String -> Maybe [String] + -> (String -> SuccFail a) -> (a -> ToolId)+ -> CBMgr -> VPUI+ -> IO VPUI+showToolEntry winId prompt mcompletions parser toolType uimgr vpui =+ let vw = vpuiGetWindow vpui winId+ in case vpuiWindowLookupCanvas vw of+ Nothing -> error "showToolEntry: no canvas!"+ Just canvas ->+ let layout = vcLayout canvas+ (xx, yy) = vcMousePos canvas+ in do+ {+ ; root <- eventBoxNew -- needed for Label visibility+ ; frame <- frameNew+ ; frameSetLabel frame prompt+ ; entry <- entryNew+ ; case mcompletions of+ Nothing -> return ()+ Just comps -> addEntryCompletions entry comps++ -- Organize as (eventbox (frame (entry)))+ ; containerAdd frame entry+ ; containerAdd root frame+ ; layoutPut layout root (round xx) (round yy)+ ; widgetShowAll root++ -- grab and handle events+ ; grabAdd entry -- all keyboard and mouse events of the app+ ; widgetGrabFocus entry -- grabs keyboard events, still necessary+ -- Set actions for TAB (entry completion) and ESC (cancel)+ ; _ <- on entry keyPressEvent (entryKeyPress root entry)+ ; uimgr (OnEntryActivate entry+ (entryActivated root winId entry parser toolType))+ ; return vpui+ }++-- | When activated, send a message to set the current tool +entryActivated :: (ContainerClass c) => + c -> WinId -> Entry + -> (String -> SuccFail a) -- parser+ -> (a -> ToolId) -- tool type+ -> IORef VPUI -- state+ -> IO ()+entryActivated container winId entry parser toolType uiref = do+-- This could be rewritten in the form of :: ... -> VPUI -> IO VPUI+-- and transformed in the CBMgr, "lifting" the readIORef/writeIORef,+-- in fact streamlining it to "mutateIORef"+ {+ text <- entryGetText entry+ ; case parser text of+ Fail msg -> info msg+ Succ v -> + grabRemove entry >>+ widgetDestroy container >>+ readIORef uiref >>= + vpuiSetTool (toolType v) winId >>=+ writeIORef uiref+ }++addEntryCompletions :: Entry -> [String] -> IO ()+addEntryCompletions entry comps = do+ {+ -- prepare the model+ model <- listStoreNew comps++ -- make EntryCompletion and set model and column+ ; ec <- entryCompletionNew+ ; set ec [entryCompletionModel := Just model]+ ; customStoreSetColumn model (makeColumnIdString 0) id+ ; entryCompletionSetTextColumn ec (makeColumnIdString 0)++ -- attach EntryCompletion to Entry+ ; entrySetCompletion entry ec+ ; return ()+ }++-- | Set actions for Tab and Escape+entryKeyPress :: (ContainerClass c) => c -> Entry -> EventM EKey Bool+entryKeyPress container entry =+ tryEvent $ do+ {+ kname <- eventKeyName+ ; case kname of+ "Escape" ->+ liftIO $ + grabRemove entry >>+ widgetDestroy container+ "Tab" ->+ -- complete the entry as far as possible+ liftIO $+ entryGetCompletion entry >>=+ entryCompletionInsertPrefix+ _ -> stopEvent+ } ++-- | For debugging (frame context menu command)+dumpFrame :: VPUI -> WinId -> CanvFrame -> IO ()+dumpFrame vpui winId frame = + let vw = vpuiGetWindow vpui winId+ canv = vpuiWindowGetCanvas vw+ graph = vcGraph canv+ frameNode = cfFrameNode frame+ frame' = vcGetFrame canv graph frameNode -- ?? possibly different+ tree = graphToOrderedTreeFrom graph frameNode+ in + info ("frame functoid:", cfFunctoid frame) >>+ info ("frame' functoid:", cfFunctoid frame') >>+ info ("frame' all descendants:", + nodeAllSimpleDescendants graph frameNode) >>+ info "Tree rooted at frame:" >>+ putTree tree++-- | For debugging (frame context menu command)+dumpGraph :: VPUI -> WinId -> IO ()+dumpGraph vpui = + printWGraph . vcGraph . vpuiWindowGetCanvas . vpuiGetWindow vpui++-- | For debugging the window's widget children+dumpWorkWin :: VPUI -> WinId -> IO ()+dumpWorkWin vpui winId =+ case vpuiTryGetWindow vpui winId of+ Nothing -> putStrLn ("dumpWorkWin: no window found with id " ++ winId)+ Just vpuiWindow ->+ let window = vpuiWindowWindow vpuiWindow+ in dumpWidget window >>+ containerForeach window dumpWidget++dumpWidget :: (WidgetClass w) => w -> IO ()+dumpWidget w = do+ {+ (_, path, _) <- widgetClassPath w+ ; cname <- widgetClassName w+ ; vis <- get w widgetVisible+ ; print (path, cname, vis)+ ; when (elem cname ["GtkVBox", "GtkHBox", "GtkLayout"]) $+ containerForeach (castToContainer w) dumpWidget+ }++widgetClassName :: (WidgetClass w) => w -> IO String+widgetClassName w = do+ {+ (_len, _path, rpath) <- widgetClassPath w+ ; return (case elemIndex '.' rpath of+ Nothing -> "Nil"+ Just n -> reverse (take n rpath))+ }++-- | Clear frame indicated by mouse location+clearFrame :: WinId -> CanvFrame -> VPUI -> IO VPUI+clearFrame winId frame vpui =+ let clear canvas = vcClearFrame canvas frame+ in vpuiModCanvasIO vpui winId clear++-- | Close frame (context menu command)+closeFrame :: VPUI -> WinId -> CanvFrame -> IO VPUI+closeFrame vpui winId frame =+ let close canvas = vcCloseFrame canvas frame+ in vpuiModCanvasIO vpui winId close
+ Graphics/UI/Sifflet/Types.hs view
@@ -0,0 +1,301 @@+module Graphics.UI.Sifflet.Types + ( VPUI(..)+ , WinId, VPUIWindow(..)+ , vpuiFileChanged+ , vpuiUserEnvAList++ -- | Operations on a VPUI involving its window+ , vpuiInsertWindow+ , vpuiTryGetWindow+ , vpuiGetWindow+ , vpuiUpdateWindow+ , vpuiReplaceWindow+ , vpuiUpdateWindowIO+ , vpuiRemoveVPUIWindow++ -- | Operations on a window involving its canvas+ , vpuiWindowLookupCanvas, vpuiWindowGetCanvas+ , vpuiWindowSetCanvas, vpuiWindowModCanvas + , vpuiWindowModCanvasIO++ -- | Operation on a VPUI involving the canvas of its window+ , vpuiModCanvas, vpuiModCanvasIO+ + -- | Other operations on a window+ , vpuiWindowWindow++ , VPToolkit(..)++ , Toolbox(..)+ , Tool(..)+ , ToolContext(..)++ , CanvasToolOp+ , ToolOp+ , toToolOpVW ++ , Workspace(..)+ , VCanvas(..)+ , Selection(..)+ , Dragging(..)+ )++where++import Data.Map as Map +import Data.Graph.Inductive as G++import Graphics.UI.Gtk.Gdk.EventM (Modifier(..))++import Data.Sifflet.Geometry+import Data.Sifflet.TreeLayout+import Data.Sifflet.WGraph+import Language.Sifflet.Expr++import Graphics.UI.Sifflet.Frame+import Graphics.UI.Sifflet.LittleGtk+import Graphics.UI.Sifflet.RPanel+import Language.Sifflet.Util+++-- | VPUI: Sifflet (formerly VisiProg) User Interface+-- The initialEnv is apt to contain "builtin" functions;+-- it's preserved here so that when writing to a file,+-- we can skip the functions that were in the initial env.+data VPUI = VPUI {+ vpuiWindows :: Map WinId VPUIWindow, -- ^ all the windows of the program+ vpuiToolkits :: [(String, VPToolkit)], -- ^ ordered association list,+ -- collections of tools+ vpuiFilePath :: Maybe FilePath, -- ^ the file opened or to save+ vpuiStyle :: Style, -- ^ for windows, canvases, editors+ vpuiInitialEnv :: Env, -- ^ initial value of global environment+ vpuiGlobalEnv :: Env, -- ^ the global environment+ vpuiFileEnv :: Env, -- ^ global env as of last file open or save,+ -- used to detect unsaved changes+ vpuiDebugging :: Bool -- ^ include debug commands in context menu?+ }++-- | Tell whether the global environmkent has changed since the+-- last file open or save+vpuiFileChanged :: VPUI -> Bool+vpuiFileChanged vpui = vpuiGlobalEnv vpui /= vpuiFileEnv vpui++-- | Extract from the environment the part defined by the user+-- But you probably want to use Graphics.UI.Sifflet.Window.UserFunctions+-- instead of this.+vpuiUserEnvAList :: VPUI -> [(String, Value)]+vpuiUserEnvAList vpui =+ let env' = vpuiGlobalEnv vpui -- I hope+ env = vpuiInitialEnv vpui+ in if length env == 1 && length env' == 1+ then assocs (Map.difference (head env') (head env))+ else errcats ["vpuiUserEnv: env lengths are not one",+ "|env'|:", show (length env'),+ "|env|:", show (length env)]++-- | Insert a window in the window map+vpuiInsertWindow :: VPUI -> WinId -> VPUIWindow -> VPUI+vpuiInsertWindow vpui winId vw =+ vpui {vpuiWindows = Map.insert winId vw (vpuiWindows vpui)}++-- | Try to get the VPUIWindow with the given window ID,+-- return Just result or Nothing+vpuiTryGetWindow :: VPUI -> WinId -> Maybe VPUIWindow+vpuiTryGetWindow vpui winId = Map.lookup winId (vpuiWindows vpui)++-- | Get the VPUIWindow with the given window ID;+-- it is an error if this fails.+vpuiGetWindow :: VPUI -> WinId -> VPUIWindow+vpuiGetWindow vpui winId = vpuiWindows vpui ! winId++-- | Replace a VPUIWindow with given window ID;+-- it is an error if this fails.+vpuiReplaceWindow :: VPUI -> WinId -> VPUIWindow -> VPUI+vpuiReplaceWindow vpui winId vpuiWin =+ let winMap = vpuiWindows vpui+ winMap' = insert winId vpuiWin winMap+ in vpui {vpuiWindows = winMap'}++-- | Apply an update function to a VPUIWindow with given window ID;+-- it is an error if this fails.+vpuiUpdateWindow :: VPUI -> WinId -> (VPUIWindow -> VPUIWindow) -> VPUI+vpuiUpdateWindow vpui winId updater =+ let winMap = vpuiWindows vpui+ winMap' = adjust updater winId winMap+ in vpui {vpuiWindows = winMap'}++-- | Apply an update IO action to a VPUIWindow with given window ID;+-- it is an error if this fails.+vpuiUpdateWindowIO :: WinId -> (VPUIWindow -> IO VPUIWindow) -> VPUI -> IO VPUI+vpuiUpdateWindowIO winId updater vpui = do+ {+ let winMap = vpuiWindows vpui+ vw = winMap ! winId+ ; vw' <- updater vw+ ; let winMap' = insert winId vw' winMap+ ; return $ vpui {vpuiWindows = winMap'}+ }++-- | Remove a window from the windows map; it has already been destroyed+-- in the GUI+vpuiRemoveVPUIWindow :: WinId -> VPUI -> VPUI+vpuiRemoveVPUIWindow winId vpui =+ let winMap = vpuiWindows vpui+ winMap' = delete winId winMap+ in vpui {vpuiWindows = winMap'}++data VPUIWindow = -- VPUIJustWindow Window + VPUIWorkWin Workspace Window+ | FunctionPadWindow Window [(String, RPanel)]+++vpuiWindowWindow :: VPUIWindow -> Window+vpuiWindowWindow vw =+ case vw of+ VPUIWorkWin _ w -> w+ FunctionPadWindow w _ -> w++-- | Try to find canvas; fail gracefully+vpuiWindowLookupCanvas :: VPUIWindow -> Maybe VCanvas+vpuiWindowLookupCanvas vw =+ case vw of+ VPUIWorkWin ws _ -> Just (wsCanvas ws)+ _ -> Nothing++-- | Find canvas or fail dramatically+vpuiWindowGetCanvas :: VPUIWindow -> VCanvas+vpuiWindowGetCanvas vw =+ case vpuiWindowLookupCanvas vw of+ Nothing -> error "vpuiWindowGetCanvas: no canvas found"+ Just canvas -> canvas++vpuiWindowSetCanvas :: VPUIWindow -> VCanvas -> VPUIWindow+vpuiWindowSetCanvas vw canvas =+ case vw of+ VPUIWorkWin ws w -> VPUIWorkWin (ws {wsCanvas = canvas}) w+ _ -> error "vpuiWindowSetCanvas: not a workspace window"++vpuiWindowModCanvas :: VPUIWindow -> (VCanvas -> VCanvas) -> VPUIWindow+vpuiWindowModCanvas vw f =+ case vpuiWindowLookupCanvas vw of+ Nothing -> error "vpuiWindowModCanvas: plain VPUIWindow"+ Just canvas -> vpuiWindowSetCanvas vw (f canvas)++vpuiWindowModCanvasIO :: VPUIWindow -> (VCanvas -> IO VCanvas) -> IO VPUIWindow+vpuiWindowModCanvasIO vw f =+ case vpuiWindowLookupCanvas vw of+ Nothing -> error "vpuiWindowModCanvas: plain VPUIWindow"+ Just canvas -> + do+ { + canvas' <- f canvas+ ; return $ vpuiWindowSetCanvas vw canvas'+ }++-- | Update the canvas of the specified window, without IO+vpuiModCanvas :: VPUI -> WinId -> (VCanvas -> VCanvas) -> VPUI+vpuiModCanvas vpui winId modCanvas = + let modWindow vw = vpuiWindowModCanvas vw modCanvas+ in vpuiUpdateWindow vpui winId modWindow++-- | Update the canvas of the specified window, with IO+vpuiModCanvasIO :: VPUI -> WinId -> (VCanvas -> IO VCanvas) -> IO VPUI+vpuiModCanvasIO vpui winId modCanvas = + let modWindow vw = vpuiWindowModCanvasIO vw modCanvas+ in vpuiUpdateWindowIO winId modWindow vpui++type WinId = String++data Workspace = + Workspace {wsRootWidget :: VBox, -- ^ container of the rest+ wsCanvas :: VCanvas, -- ^ the canvas+ wsButtonBar :: HBox,+ wsStatusbar :: Statusbar,+ wsArgToolSpecs :: [ArgSpec] -- ^ none if not editing+ }+++-- | Toolkit functions are organized in groups (rows) for presentation+-- in a toolbox+data VPToolkit = VPToolkit {toolkitName :: String,+ toolkitWidth :: Int, -- (-1) = don't care+ toolkitRows :: [[Tool]]}++-- | A Toolbox is a framed VBox with a set of Toolbars attached+data Toolbox = Toolbox {toolboxFrame :: GtkFrame+ , toolboxVBox :: VBox}++-- | ToolOp a is intended for a = VPUIWindow or VCanvas+-- type ToolOp a +-- = VPUI -> a -> ToolContext -> [Modifier] -> Double -> Double -> IO a++type ToolOp + = VPUI -> WinId -> ToolContext -> [Modifier] -> Double -> Double -> IO VPUI++type CanvasToolOp+ = VCanvas -> ToolContext -> [Modifier] -> Double -> Double -> IO VCanvas++data Tool = Tool {toolName :: String, -- the tool's name++ -- what to do when the tool is selected from the toolbox+ toolActivated :: VCanvas -> IO VCanvas,++ -- what to do to apply the tool to a point on the canvas+ toolOp :: ToolOp+ }++-- | A helper for making toolOps from actions on VCanvas++toToolOpVW :: CanvasToolOp -> ToolOp+toToolOpVW vcOp vpui winId toolContext mods x y = do+ {+ let vw = vpuiGetWindow vpui winId+ canv = vpuiWindowGetCanvas vw+ ; canv' <- vcOp canv toolContext mods x y+ ; let vw' = vpuiWindowSetCanvas vw canv'+ ; return $ vpuiReplaceWindow vpui winId vw'+ }+ +-- | ToolContext: The way a tool should be applied depends on +-- where it is being used ++data ToolContext = TCWorkspace + | TCCallFrame CanvFrame + | TCEditFrame CanvFrame+ | TCExprNode -- ???++++-- | A canvas that can display multiple boxes representing +-- expressions or function definitions or calls++data VCanvas = VCanvas {+ vcLayout :: GtkLayout,+ vcStyle :: Style,+ vcGraph :: WGraph,+ vcFrames :: [CanvFrame],+ vcSize :: Size,+ -- vcLocalEnv :: Env, -- only good for function editor, I think? + vcMousePos :: (Double, Double),+ vcTool :: Maybe Tool, -- current tool on this canvas+ vcActive :: Maybe Node, -- active node, if any+ vcSelected :: Maybe Selection, -- selected node(s), if any+ vcDragging :: Maybe Dragging -- what we're dragging, if anything+ }+++data Selection = SelectionNode {selNode :: G.Node}+ | SelectionInlet {selNode :: G.Node,+ selInEdge :: WEdge} -- numbered from 0+ | SelectionOutlet {selNode :: G.Node,+ selOutEdge :: WEdge} -- normally just 0+ deriving (Eq, Read, Show)++-- | A Dragging keeps track of the object (node) being dragged+-- and the current mouse position.++data Dragging = Dragging { draggingNode :: G.Node,+ draggingPosition :: Position+ }+ deriving (Eq, Read, Show)+
+ Graphics/UI/Sifflet/Window.hs view
@@ -0,0 +1,1259 @@+module Graphics.UI.Sifflet.Window+ (+ -- Window utilities+ getOrCreateWindow+ , showWindow+ , newWindowTitled++ , showWorkWin+ , showWorkspaceWindow++ , showFedWin+ , fedWindowTitle++ , getOrCreateFunctionPadWindow+ , showFunctionPadWindow+ , newFunctionDialog++ , openFilePath+ , setWSCanvasCallbacks+ , keyBindingsHelpText+ )++where++-- debug imports+-- import Debug.Trace++-- standard imports++import Control.Monad+import Data.IORef+import Data.List as List+import Data.Map as Map (fromList, keys, lookup)+import Data.Map (Map)+import Data.Maybe++import Data.Graph.Inductive as G+import Data.Version++import Graphics.Rendering.Cairo+import Graphics.UI.Gtk.Gdk.EventM++import System.FilePath++-- sifflet imports++import Data.Sifflet.Functoid+import Data.Sifflet.Geometry+import Data.Sifflet.WGraph++import Language.Sifflet.Export.Exporter+import Language.Sifflet.Export.ToHaskell (defaultHaskellOptions, exportHaskell)+import Language.Sifflet.Export.ToPython (defaultPythonOptions, exportPython)+import Language.Sifflet.Export.ToScheme (SchemeOptions(..), exportScheme)++import Language.Sifflet.Expr+import Language.Sifflet.SiffML++import Graphics.UI.Sifflet.Frame+import Graphics.UI.Sifflet.Canvas+import Graphics.UI.Sifflet.Types+import Graphics.UI.Sifflet.Callback+import Graphics.UI.Sifflet.Tool+import Graphics.UI.Sifflet.Workspace+import Graphics.UI.Sifflet.GtkForeign+import Graphics.UI.Sifflet.GtkUtil+import Graphics.UI.Sifflet.LittleGtk+import Graphics.UI.Sifflet.RPanel++import Language.Sifflet.Util+++import Paths_sifflet_lib as Paths++-- ---------------------------------------------------------------------+-- | Finding, creating, and initializing windows (VPUIWindow)++-- | Find and show a window, if it exists.+-- If not, create the window, put it in the vpui's window map,+-- and initialize it and any auxiliary objects using the initWin function.+-- The 3rd argument of initWin will be the window's title.+-- Always presents the window (shows and raises).+-- Returns a 3-tuple: the VPUIWindow contains the Window,+-- and the Bool value is True if the Window is new+-- (and therefore might need some further initialization).+-- The third tuple element is an IORef to the VPUIWindow;+-- it may be useful for setting up signal and event handling.++showWindow :: WinId -> CBMgr+ -> (VPUI -> Window -> IO VPUIWindow) -- initializes Gtk Window+ -> (VPUI -> WinId -> CBMgr -> IO ()) -- initializes callbacks+ -> VPUI -> IO (VPUI, VPUIWindow, Bool)+showWindow winId uimgr initWin initCB vpui =+ getOrCreateWindow winId uimgr initWin initCB True vpui++-- | Like showWindow, except that making the window visible is optional+getOrCreateWindow :: WinId -> CBMgr+ -> (VPUI -> Window -> IO VPUIWindow) + -- ^ initialize Gtk Window+ -> (VPUI -> WinId -> CBMgr -> IO ())+ -- ^ initialize callbacks+ -> Bool -- ^ make window visible+ -> VPUI -> IO (VPUI, VPUIWindow, Bool)+getOrCreateWindow winId uimgr initWin initCB visible vpui = do+ (vpui', vw, isNew) <- + case vpuiTryGetWindow vpui winId of+ Nothing -> do+ window <- newWindowTitled winId+ widgetSetName window ("Sifflet-" ++ winId)+ vwin <- initWin vpui window+ let vpui' = vpuiInsertWindow vpui winId vwin+ uimgr (OnWindowDestroy window (onWindowDestroy winId))+ return (vpui', vwin, True)+ Just vw ->+ return (vpui, vw, False)+ when isNew (initCB vpui' winId uimgr) -- add callbacks on new window+ when visible $+ -- show it+ let window = vpuiWindowWindow vw+ in do+ widgetShowAll window+ windowPresent window+ return (vpui', vw, isNew)++onWindowDestroy :: WinId -> IORef VPUI -> IO ()+onWindowDestroy winId uiref =+ if (winId == workspaceId) + then + readIORef uiref >>=+ checkForChanges "quit (by closing the workspace window)" True False+ (\ vpui -> do { mainQuit; return vpui }) >>+ return ()+ else modifyIORef uiref (vpuiRemoveVPUIWindow winId)++-- | Default "do-nothing" add-callbacks function+initCBDefault :: VPUI -> WinId -> CBMgr -> IO ()+initCBDefault _vpui _winId _uimgr = return ()++newWindowTitled :: String -> IO Window+newWindowTitled winId = do+ window <- windowNew+ set window [windowTitle := winId]+ widgetSetName window ("Sifflet-" ++ winId)+ return window++-- | Show a workspace window, with a given title, _not_ editing a function++showWorkWin :: VPUI -> WinId -> CBMgr -> IO VPUI+showWorkWin vpui winId uimgr = do+ {+ (vpui', _, _) <- showWorkspaceWindow winId uimgr Nothing vpui+ ; return vpui'+ }++-- | Show a workspace window with a given title and maybe function to edit++showWorkspaceWindow :: WinId -> CBMgr -> Maybe Function -> VPUI+ -> IO (VPUI, VPUIWindow, Bool)+showWorkspaceWindow winId cbmgr mfunc =+ showWindow winId cbmgr (workspaceWindowInit cbmgr winId mfunc) + setWSCanvasCallbacks+++-- | Initialize a Workspace window.+-- Called in sifflet.hs:Main from showWindow called from showWorkspaceWindow.++workspaceWindowInit :: CBMgr -> WinId -> Maybe Function -> VPUI -> Window+ -> IO VPUIWindow+workspaceWindowInit cbmgr winId mfunc vpui window = do+ {+ let style = vpuiStyle vpui+ env = vpuiGlobalEnv vpui+ ; ws <- case mfunc of + Nothing -> workspaceNewDefault style (buildMainMenu cbmgr)+ Just func -> workspaceNewEditing style env func+ ; set window [windowTitle := winId, containerChild := wsRootWidget ws]+ -- this should suppress the window close button,+ -- but doesn't, at least in Fluxbox+ -- windowDeletable := False] -- no close button+ ; widgetShowAll window+ ; windowPresent window++ ; return $ VPUIWorkWin ws window+ }++-- Menu specs here need to coordinate accelerators (shortcuts)+-- with keyBindingsList in WindowManagement.hs++buildMainMenu :: CBMgr -> VBox -> IO ()+buildMainMenu cbmgr vbox = do+ {+ -- menu bar + let mspecs = + [MenuSpec "File"+ [ -- "new" isn't implemented yet+ -- MenuItem "New ..." menuFileNew+ -- , + -- Temporarily disabling file I/O operations+ MenuItem "Open ... (C-o)" (menuFileOpen cbmgr)+ , MenuItem "Save (C-s)" menuFileSave+ , MenuItem "Save as ..." menuFileSaveAs+ , MenuItem "Export to Haskell ..." + menuFileExportHaskell+ , MenuItem "Export to Python3 ..." menuFileExportPython+ , MenuItem "Export to Scheme ..." menuFileExportScheme+ , MenuItem "Save image ..." menuFileSaveImage+ , MenuItem "Quit (C-q)" menuFileQuit]+ , MenuSpec "Functions"+ [MenuItem "New ... (n)"+ (newFunctionDialog "ignore" cbmgr)+ , MenuItem "Function Pad"+ (showFunctionPadWindow cbmgr)]+ , MenuSpec "Help"+ [MenuItem "Help ..." showHelpDialog+ , MenuItem "Complaints and praise ..." showBugs+ , MenuItem "About ..." showAboutDialog]+ ]+ ; menubar <- createMenuBar mspecs cbmgr+ ; boxPackStart vbox menubar PackNatural 0+}++-- | Show a function editor window = a workspace window editing a given function.+-- Use argNames for a new function; ignore them if funcName is bound.++showFedWin :: CBMgr -> String -> [String] -> VPUI -> IO VPUI+showFedWin cbmgr funcName argNames vpui = do+ {+ let initEnv = vpuiGlobalEnv vpui+ function = case envLookupFunction initEnv funcName of+ Nothing -> newUndefinedFunction funcName argNames+ Just func -> func+ winId = fedWindowTitle funcName++ ; (vpui', vw, isNew) <- showWorkspaceWindow winId cbmgr (Just function) vpui++ ; if isNew+ then do+ {+ let canvas = vpuiWindowGetCanvas vw+ ; canvas' <- vcAddFrame canvas (FunctoidFunc function) + Nothing EditFrame+ initEnv 0 0 0 Nothing+ ; canvas'' <- + case vcFrames canvas' of+ [] -> info "showFedWin: ERROR: no frame on canvas" >> + return canvas'+ _:_:_ -> + info "showFedWin: ERROR: too many frames on canvas" >> + return canvas'+ [frame] -> editFunction canvas' frame + ; addFedWinButtons cbmgr winId vpui'+ ; return (vpuiReplaceWindow vpui' winId + (vpuiWindowSetCanvas vw canvas''))+ }+ else return vpui'+ }++fedWindowTitle :: String -> WinId+fedWindowTitle funcName = "Edit " ++ funcName++updateFunctionPadIO :: String -> (RPanel -> IO RPanel) -> VPUI -> IO VPUI+updateFunctionPadIO padName update =+ let updateWindow vw =+ case vw of+ FunctionPadWindow window rpAList ->+ do+ {+ rpAList' <- adjustAListM padName update rpAList+ ; return (FunctionPadWindow window rpAList')+ }+ _ -> return vw+ in vpuiUpdateWindowIO "Function Pad" updateWindow++-- | Show the function pad window; create it if needed+showFunctionPadWindow :: CBMgr -> VPUI -> IO VPUI+showFunctionPadWindow cbmgr vpui = getOrCreateFunctionPadWindow cbmgr True vpui++-- | Create the function pad window if it doesn't exist+getOrCreateFunctionPadWindow :: CBMgr -> Bool -> VPUI -> IO VPUI+getOrCreateFunctionPadWindow cbmgr visible vpui = + let initWindow _vpui window = do+ vbox <- vBoxNew False 0 -- non-homogenous, 0 padding+ set window [containerChild := vbox]+ let rpnames = ["Base", "Examples", "My Functions"]+ rps <- mapM (makeFunctionPadPanel cbmgr vpui) rpnames+ mapM_ (\ rp -> boxPackStart vbox (rpanelRoot rp) PackNatural 0)+ rps+ windowMove window 5 5+ return $ FunctionPadWindow window (zip rpnames rps) + -- maybe need reference only the "My Functions" panel though **+ in do+ (vpui', _, windowIsNew) <- getOrCreateWindow functionPadWinId + cbmgr initWindow initCBDefault visible vpui+ -- "My Functions" default is empty; add any user-defined+ -- functions in the environment to it+ if windowIsNew+ then addUserFunctions cbmgr vpui'+ else return vpui'+++functionPadWinId :: String+functionPadWinId = "Function Pad"++addUserFunctions :: CBMgr -> VPUI -> IO VPUI+addUserFunctions cbmgr vpui =+ let names = map fst (vpuiUserEnvAList vpui)+ update rp = do+ {+ buttons <- mapM (makeToolButton cbmgr . functionTool) names+ ; rp' <- rpanelAddWidgets rp (zip names buttons)+ ; widgetShowAll (rpanelRoot rp')+ ; return rp'+ } + in updateFunctionPadIO "My Functions" update vpui++makeFunctionPadPanel :: CBMgr -> VPUI -> String -> IO RPanel+makeFunctionPadPanel cbmgr vpui name =+ let VPToolkit _ width toolrows = + case List.lookup name (vpuiToolkits vpui) of+ Nothing ->+ errcats ["makeFunctionPadPanel:",+ "can't find toolkit definition:", name]+ Just atoolkit -> atoolkit+ in do+ {+ buttonRows <- makeToolButtonRows cbmgr toolrows+ :: IO [[(String, Button)]]+ ; rp <- newRPanel name 3 3 width+ ; rpanelAddRows rp buttonRows+ }++makeToolButtonRows :: CBMgr -> [[Tool]] -> IO [[(String, Button)]]+makeToolButtonRows cbmgr toolRows = + mapM2 (makeNamedToolButton cbmgr) toolRows++makeNamedToolButton :: CBMgr -> Tool -> IO (String, Button)+makeNamedToolButton cbmgr tool = do+ {+ button <- makeToolButton cbmgr tool+ ; return (toolName tool, button)+ }++makeToolButton :: CBMgr -> Tool -> IO Button+makeToolButton cbmgr tool = do+ {+ button <- buttonNewWithLabel (toolName tool)+ ; cbmgr (AfterButtonClicked button+ (modifyIORefIO (forallWindowsIO (vpuiWindowSetTool tool))))+ ; return button+ }++-- | Add a tool button to the function pad window in a specified panel+addFunctionPadToolButton :: CBMgr -> String -> Tool -> VPUIWindow + -> IO VPUIWindow+addFunctionPadToolButton cbmgr panelId tool vw = + case vw of+ FunctionPadWindow window panelAList ->+ let adjustPanel :: RPanel -> IO RPanel+ adjustPanel rp = do+ {+ -- make the tool button from the tool+ button <- makeToolButton cbmgr tool+ -- add it to the panel+ ; rp' <- rpanelAddWidget rp (toolName tool) button+ ; widgetShowAll (rpanelRoot rp')+ ; return rp'+ }+ in do+ {+ panelAList' <- adjustAListM panelId adjustPanel panelAList+ ; return $ FunctionPadWindow window panelAList'+ }+ _ -> return vw+++-- | Ask user for new function name and arguments,+-- then begin editing the function.++newFunctionDialog :: WinId -> CBMgr -> VPUI -> IO VPUI+newFunctionDialog _winId cbmgr vpui =+ -- _winId is ignored, but needed for use in KeyBindingsList+ let reader :: Reader [String] (String, [String])+ reader inputLines =+ case inputLines of+ [fname, fargs] ->+ return (fname, words fargs)+ _ -> fail "wrong number of lines"+ in do+ {+ inputDialog <- + createEntryDialog "New Function"+ ["Function name", "Argument names (space between)"]+ ["", ""]+ reader+ (-1)+ ; values <- runEntryDialog inputDialog+ ; case values of+ Nothing -> return vpui+ Just (name, args) -> editNewFunction cbmgr name args vpui+ }++-- ------------------------------------------------------------+-- Implementation of menu commands++-- -- | Create a new file, but what does this mean?++-- menuFileNew :: VPUI -> IO VPUI+-- menuFileNew vpui = putStrLn "Not implemented: \"New\"" >> return vpui+-- -- Notes for future implementation:+-- -- checkChangesAndContinue vpui ...+++-- | Quit from Sifflet++menuFileQuit :: VPUI -> IO VPUI+menuFileQuit = checkForChanges "quit" False True vpuiQuit++-- | Open a file (load its function definitions)++menuFileOpen :: CBMgr -> VPUI -> IO VPUI+menuFileOpen cbmgr =+ checkForChanges "open file" True True (continueFileOpen cbmgr)++-- | Offer to save changes, if any, and continue with the continuation.+-- The continuation gets the current vpui if there are no changes+-- or if the offer to save is rejected; otherwise, it gets a+-- vpui which knows it has saved its last changes.+-- If offerCancel is true, there is an option to cancel the operation;+-- this won't work if the user is closing the main (workspace) window.+-- The message, if any, is a confirmation that the file was+-- saved and that we are going on to the next operation --+-- useful for open file, but not for quit.++checkForChanges :: String -> Bool -> Bool -> (VPUI -> IO VPUI) + -> VPUI -> IO VPUI+checkForChanges beforeOperation acknowledge offerCancel continue vpui =+ let mAckIfSaved vpui' = + when (not (vpuiFileChanged vpui') && acknowledge)+ (+ showInfoMessage "Changes saved" + ("Your changes are now saved; " +++ "proceeding to " +++ beforeOperation ++ ".")+ ) + >>+ return vpui'+ choices = [("Save them", + menuFileSave vpui >>= mAckIfSaved >>= continue),+ ("Throw them away", + return vpui >>= continue)] ++ + if offerCancel+ then [("Cancel " ++ beforeOperation, return vpui)]+ else []+ labels = map fst choices+ actions = map snd choices+ -- labels = ["Save them", + -- "Throw them away", + -- ]+ -- actions = [menuFileSave vpui >>= mAckIfSaved >>= continue, -- save+ -- return vpui >>= continue, -- throw away+ -- return vpui] -- cancel+ offerSaveAndContinue = showChoicesDialog "Save changes?"+ ("There are unsaved changes. " +++ "Before you " ++ beforeOperation +++ ", would you ...")+ labels+ actions+ + in if vpuiFileChanged vpui+ then offerSaveAndContinue+ else continue vpui++-- | Continue with opening the file, after having possibly saved changes+continueFileOpen :: CBMgr -> VPUI -> IO VPUI+continueFileOpen cbmgr vpui = do+ mpath <- showDialogFileOpen vpui+ case mpath of+ Nothing -> return vpui+ Just filePath -> openFilePath cbmgr filePath vpui++-- | Now that we have a file path, go ahead and open it,+-- loading the function definitions into Sifflet+-- and adding buttons to the function pad "My Functions" area.++openFilePath :: CBMgr -> FilePath -> VPUI -> IO VPUI+openFilePath cbmgr filePath vpui = do+ loadResult <- loadFile vpui filePath+ case loadResult of+ Fail msg ->+ showErrorMessage msg >> return vpui+ Succ (vpui', functions) -> + let title = "My Functions"+ updatePad rp =+ -- Figure out which functions are new,+ -- i.e., not already on the pad+ let oldNames = concat (rpanelContent rp)+ loadedNames = map functionName functions+ -- use set difference to avoid duplicates+ newNames = loadedNames \\ oldNames+ newTools = map functionTool newNames+ in do + newPairs <- mapM (makeNamedToolButton cbmgr) newTools+ rp' <- rpanelAddWidgets rp newPairs+ widgetShowAll (rpanelRoot rp)+ return rp'++ in do+ vpui'' <- + getOrCreateFunctionPadWindow cbmgr False vpui' >>=+ updateFunctionPadIO title updatePad + return $ vpui'' {vpuiFilePath = Just filePath, + vpuiFileEnv = vpuiGlobalEnv vpui'}++showDialogFileOpen :: VPUI -> IO (Maybe FilePath)+showDialogFileOpen _vpui = do+ chooser <- fileChooserDialogNew+ (Just "Open file ...") -- default title+ Nothing -- transient parent of the dialog+ FileChooserActionOpen+ [("Open", ResponseOk), ("Cancel", ResponseCancel)] -- buttons+ result <- runDialogM (toDialog chooser) chooser fileChooserGetFilename+ return result++loadFile :: VPUI -> FilePath -> IO (SuccFail (VPUI, [Function]))+loadFile vpui filePath = do+ {+ functions <- consumeSiffMLFile xmlToFunctions filePath+ ; case functions of+ [Functions fs] ->+ let vpui' = foldl bindFunction vpui fs+ in return (Succ (vpui', fs))+ _ ->+ return (Fail "file format error")++ }++bindFunction :: VPUI -> Function -> VPUI+bindFunction vpui function =+ let env = vpuiGlobalEnv vpui+ Function (Just name) _argTypes _resType _impl = function+ env' = envIns env name (VFun function)+ in vpui {vpuiGlobalEnv = env'}++-- | Implements File menu "Save" command.++menuFileSave :: VPUI -> IO VPUI+menuFileSave vpui = + case vpuiFilePath vpui of+ Nothing -> menuFileSaveAs vpui+ Just filePath -> saveFile vpui filePath++-- | Implements File menu "Save as" command.+menuFileSaveAs :: VPUI -> IO VPUI+menuFileSaveAs vpui = do+ {+ mFilePath <- chooseOutputFile "Save" vpui+ ; case mFilePath of+ Nothing -> return vpui+ Just filePath -> saveFile vpui filePath+ }++-- | Unconditionally save user functions in SiffML file.+-- Called from menuFileSave and menuFileSaveAs.+-- Updates vpuiFilePath and vpuiFileEnv.+saveFile :: VPUI -> FilePath -> IO VPUI+saveFile vpui filePath =+ produceSiffMLFile (userFunctions vpui) filePath >>+ return vpui {vpuiFilePath = Just filePath, + vpuiFileEnv = vpuiGlobalEnv vpui}++-- | The user-defined functions of the environment+userFunctions :: VPUI -> Functions+userFunctions vpui = + Functions (map (valueFunction . snd) + (vpuiUserEnvAList vpui))++-- | Export user functions to a file,+-- given an exporter and a path,+-- returning the vpui unchanged.+maybeExportUserFunctions :: VPUI -> (opts -> Exporter) + -> Maybe (FilePath, opts) -> IO VPUI+maybeExportUserFunctions vpui export mpathOptions =+ case mpathOptions of+ Nothing -> return vpui + Just (path, options) -> + export options (userFunctions vpui) path >> return vpui++-- | Export user functions to Haskell file+menuFileExportHaskell :: VPUI -> IO VPUI+menuFileExportHaskell vpui =+ chooseOutputFile "Export Haskell" vpui >>=+ maybeDefaultOptions defaultHaskellOptions >>=+ maybeExportUserFunctions vpui exportHaskell++-- | Export user functions to Python file+menuFileExportPython :: VPUI -> IO VPUI+menuFileExportPython vpui =+ chooseOutputFile "Export Python" vpui >>=+ maybeDefaultOptions defaultPythonOptions >>=+ maybeExportUserFunctions vpui exportPython++-- | Export user functions to Scheme file+menuFileExportScheme :: VPUI -> IO VPUI+menuFileExportScheme vpui =+ chooseOutputFile "Export Scheme" vpui >>= + maybeRunSchemeOptionsDialog >>=+ -- maybeExportUserFunctions vpui (exportScheme defaultSchemeOptions) + maybeExportUserFunctions vpui exportScheme++-- | Choose an output file, for file save, save as, and export commands+chooseOutputFile :: String -> VPUI -> IO (Maybe FilePath)+chooseOutputFile verb _vpui = do+ chooser <- fileChooserDialogNew+ (Just (verb ++ " to file ...")) -- title+ Nothing -- transient parent of the dialog+ FileChooserActionSave+ [(verb, ResponseOk), ("Cancel", ResponseCancel)] -- buttons+ result <- runDialogM (toDialog chooser) chooser fileChooserGetFilename+ return result++maybeDefaultOptions :: a -> Maybe FilePath -> IO (Maybe (FilePath, a))+maybeDefaultOptions defaultOptions mpath =+ case mpath of+ Nothing -> return Nothing+ Just path -> return $ Just (path, defaultOptions)++maybeRunSchemeOptionsDialog :: Maybe FilePath + -> IO (Maybe (FilePath, SchemeOptions))+maybeRunSchemeOptionsDialog mpath =+ case mpath of+ Nothing -> return Nothing+ Just path ->+ let result :: Bool -> IO (Maybe (FilePath, SchemeOptions))+ result useLambda =+ return (Just (path, + SchemeOptions {defineWithLambda = useLambda}))+ in showChoicesDialog "Scheme Export Options"+ "Use lambda in function definitions?"+ ["Yes", "No"]+ [result True, result False]+++-- | Save an image of a window in a file++menuFileSaveImage :: VPUI -> IO VPUI+menuFileSaveImage vpui = do+ (windowId, fileExt) <- chooseImageOptions vpui+ mfile <- chooseOutputFile ("Save image of " ++ windowId) vpui+ case mfile of+ Nothing -> return vpui+ Just filePath ->+ saveImageFile vpui windowId filePath fileExt++-- | Returns (WinId, fileExtension),+-- e.g., ("Sifflet Workspace", ".svg"),+-- where fileExtension is ".svg", ".ps", or ".pdf"+chooseImageOptions :: VPUI -> IO (WinId, String)+chooseImageOptions vpui =+ let hasCanvas winId = + isJust (vpuiWindowLookupCanvas (vpuiGetWindow vpui winId))+ -- We can only use Cairo to render a window that has a canvas+ windowChoices = filter hasCanvas (keys (vpuiWindows vpui))+ windowActions = map return windowChoices+ formatChoices = ["SVG", "PS", "PDF"]+ formatActions = map return [".svg", ".ps", ".pdf"]+ in do+ ext <- showChoicesDialog "Save Image" "Select image format"+ formatChoices formatActions+ winId <- if length windowChoices == 1+ then return $ head windowChoices+ else showChoicesDialog "Save Image"+ "Select window to save as image"+ windowChoices windowActions+ return (winId, ext)++-- | Save the window image in the file format specified,+-- adding the right file extension to the file path if it is+-- not already present.+saveImageFile :: VPUI -> WinId -> FilePath -> String -> IO VPUI+saveImageFile vpui winId path ext =+ let vpuiWindow = vpuiGetWindow vpui winId+ canvas = vpuiWindowGetCanvas vpuiWindow + -- Size width height = vcSize canvas -- pixels (Double)+ clipbox@(BBox _ _ width height) = defaultFileSaveClipBox canvas+ -- cliprect = bbToRect clipbox++ render :: Surface -> IO ()+ render surface = + renderWith surface (renderCanvas canvas clipbox True)+ -- (BBox 0 0 width height)+ path' = if takeExtension path == ext+ then path+ else addExtension path ext++ in case ext of+ ".pdf" -> withPDFSurface path' width height render >> return vpui+ ".ps" -> withPSSurface path' width height render >> return vpui+ ".svg" -> withSVGSurface path' width height render >> return vpui+ + -- Cairo can do PNG too, but it is harder:+ -- surfaceWriteToPNG surface path'+ -- (have to get a surface and render to it first?)++ -- Anything else really should not happen, because+ -- chooseImageOptions returns one of the three extensions above.+ -- But just in case:+ _ -> do+ showErrorMessage $+ "Unable to save in this file format " +++ "(" ++ ext ++ ").\n" +++ "Please try a file extension of " +++ ".svg, .ps, or .pdf."+ menuFileSaveImage vpui++++-- | Text shown by the help dialog+helpText :: String+helpText =+ unlines ["Functions menu:",+ " \"New\" enters a dialog to create a new function.",+ " \"Function pad\" raises the function pad window.",+ "Keystroke shortcuts for the menu commands are shown " +++ "using \"C-\" for Control. For example, Quit " +++ "is C-q, meaning Control+Q.",+ "",+ "In a function editor, right-click for the context menu.",+ "",+ "For more help, please visit the Sifflet web site,",+ "http://mypage.iu.edu/~gdweber/software/sifflet/",+ "especially the Sifflet Tutorial:",+ "http://mypage.iu.edu/~gdweber/software/sifflet/doc/tutorial.html"+ ]++-- | Show the help dialog+showHelpDialog :: MenuItemAction+showHelpDialog vpui = showInfoMessage "Sifflet Help" helpText >> return vpui++-- | How to report bugs+bugsText :: String+bugsText =+ unlines ["To report bugs, please send mail to " ++ bugReportAddress,+ "and mention \"Sifflet\" in the Subject header.",+ "To send praise, follow the same procedure.",+ "Seriously, whether you like Sifflet or dislike it,",+ "I'd like to hear from you."+ ]++bugReportAddress :: String+bugReportAddress = concat ["gdweber", at, "iue", punctum, "edu"]+ where at = "@"+ punctum = "."++showBugs :: MenuItemAction+showBugs vpui = showInfoMessage "Reporting bugs" bugsText >> return vpui++-- | Text for the About dialog+aboutText :: String+aboutText =+ unlines ["Sifflet version " ++ showVersion Paths.version,+ "Copyright (C) 2010-2012 Gregory D. Weber",+ "",+ "BSD3 License",+ "",+ "Sifflet home page:",+ "http://mypage.iu.edu/~gdweber/software/sifflet/"+ ]++showAboutDialog :: MenuItemAction+showAboutDialog vpui = showInfoMessage "About Sifflet" aboutText >> return vpui++-- ----------------------------------------------------------------------++-- Moved here (WHY???) from Callbacks.hs:++setWSCanvasCallbacks :: VPUI -> WinId -> CBMgr -> IO ()+setWSCanvasCallbacks vpui winId cbmgr = do+ {+ let vw = vpuiGetWindow vpui winId+ window = vpuiWindowWindow vw+ ; case vpuiWindowLookupCanvas vw of+ Nothing ->+ errcats ["setWSCanvasCallbacks: VPUIWindow is not a VPUIWorkWin",+ "and has no canvas"]+ Just canvas ->+ do+ {+ -- Notice when the window size is changed+ ; cbmgr (OnWindowConfigure window (configuredCallback winId))++ -- Keypress events -- send to canvas window because the Gtk.Layout+ -- cannot receive them (why ever not?)+ ; cbmgr (AfterWindowKeyPress window (keyPressCallback winId cbmgr))++ -- Send remaining events to the Gtk.Layout (why?)+ ; let layout = vcLayout canvas+ ; widgetSetCanFocus layout True+ ; cbmgr (OnLayoutExpose layout (exposedCallback winId))++ -- Mouse events + ; widgetAddEvents layout [PointerMotionMask]+ ; cbmgr (OnLayoutMouseMove layout (mouseMoveCallback winId))+ ; cbmgr (OnLayoutButtonPress layout + (buttonPressCallback winId cbmgr))+ ; cbmgr (OnLayoutButtonRelease layout (buttonReleaseCallback winId))+ }+ }++-- | Context menu command to edit the function displayed in +-- a CallFrame++editFrameFunction :: CBMgr -> CanvFrame -> VPUI -> IO VPUI+editFrameFunction cbmgr frame vpui =+ let func = cfFunctoid frame+ in showFedWin cbmgr (functoidName func) (functoidArgNames func) vpui++-- | Create a new function, add it to the global environment +-- with body undefined, and start editing it in a new window. +-- Also update and show "My Functions" toolbox and+-- update its toolkit.++editNewFunction :: CBMgr -> String -> [String] -> VPUI -> IO VPUI+editNewFunction cbmgr name args vpui = + let updateEnv :: VPUI -> IO VPUI+ updateEnv vpui' =+ let env = vpuiGlobalEnv vpui'+ env' = envIns env name (VFun (newUndefinedFunction name args))+ in return $ vpui' {vpuiGlobalEnv = env'} + in + -- Show window first, with the *old* functions+ showFunctionPadWindow cbmgr vpui >>=+ updateEnv >>=+ vpuiUpdateWindowIO functionPadWinId+ (addFunctionPadToolButton cbmgr "My Functions" + (functionTool name)) >>=+ showFedWin cbmgr name args++configuredCallback :: WinId -> IORef VPUI -> EventM EConfigure Bool+configuredCallback winId uiref =+ tryEvent $ do+ {+ (w, h) <- eventSize+ ; liftIO $ modifyIORef uiref (handleConfigured winId w h)+ -- We *must* "stop the event", forcing the event handler + -- to return False, or else the canvas remains "squeezed in"+ -- -- Weird!!+ ; stopEvent+ }++-- | Handle the Configured event.+handleConfigured :: WinId -> Int -> Int -> VPUI -> VPUI+handleConfigured winId width height vpui = + let vw = vpuiGetWindow vpui winId+ vw' = vpuiWindowModCanvas vw + (atLeastSize (Size (fromIntegral width) (fromIntegral height)))+ in vpuiReplaceWindow vpui winId vw'++exposedCallback :: WinId -> IORef VPUI -> EventM EExpose Bool+exposedCallback winId uiref =+ tryEvent $ do+ {+ cliprect <- eventArea+ ; liftIO (readIORef uiref >>= handleExposed winId cliprect)+ }++-- | Handle the Exposed event, should be called only for a window+-- with a canvas+handleExposed :: WinId -> Rectangle -> VPUI -> IO ()+handleExposed winId cliprect vpui = + let vw = vpuiGetWindow vpui winId -- error if not found+ in case vpuiWindowLookupCanvas vw of+ Nothing -> info "handleExposed: no canvas found!"+ Just canvas -> drawCanvas canvas cliprect ++data KeyBinding = KeyBinding {kbGtkKeyName :: String,+ kbAltKeyName :: Maybe String, -- for humans+ kbRequiredModifiers :: [Modifier],+ kbDescription :: String,+ kbAction :: KeyAction}++data KeyAction + = KeyActionST (WinId -> VPUI -> IO VPUI) -- ^ set a tool+ | KeyActionDG (WinId -> CBMgr -> VPUI -> IO VPUI) -- ^ start a dialog+ | KeyActionModIO (CBMgr -> VPUI -> IO VPUI) -- ^ modify VPUI with IO+ | KeyActionHQ (VPUI -> IO ()) -- ^ help or quit++-- | Key bindings map. This is derived from keyBindingsList.+keyBindingsMap :: Map String KeyBinding+keyBindingsMap = Map.fromList [(kbGtkKeyName kb, kb) | kb <- keyBindingsList]++-- | KeyBinding list for workspace and function editor windows.++keyBindingsList :: [KeyBinding]+keyBindingsList = + [+ -- Bindings to set tools+ KeyBinding "c" Nothing [] "connect" + (KeyActionST (vpuiSetTool ToolConnect))+ , KeyBinding "d" Nothing [] "disconnect" + (KeyActionST (vpuiSetTool ToolDisconnect))+ , KeyBinding "i" Nothing [] "if" (KeyActionST (vpuiSetTool ToolIf))+ , KeyBinding "m" Nothing [] "move" (KeyActionST (vpuiSetTool ToolMove))+ , KeyBinding "KP_Delete" (Just "Keypad-Del") [] "delete" + (KeyActionST (vpuiSetTool ToolDelete))++ -- Bindings to start dialogs+ , KeyBinding "n" Nothing [] "new function" (KeyActionDG newFunctionDialog)+ , KeyBinding "f" Nothing [] "function" (KeyActionDG showFunctionEntry)+ , KeyBinding "l" Nothing [] "literal" (KeyActionDG showLiteralEntry)++ -- Help and quit++ , KeyBinding "question" (Just "?") [] "help" (KeyActionHQ vpuiKeyHelp)++ -- Shortcuts for menu commands (GTK "accelerators", but not done+ -- in the standard GTK way).+ -- These need to be coordinated with buildMainMenu,+ -- in WindowManagement.hs++-- Oops! Binding Ctrl+F here interferes with binding just plain f above.+-- , KeyBinding "f" (Just "Control-f") [Control] "function-pad"+-- (KeyActionModIO showFunctionPadWindow)++ , KeyBinding "o" (Just "Control-o") [Control] "open"+ (KeyActionModIO menuFileOpen)+ , KeyBinding "s" (Just "Control-s") [Control] "save"+ (KeyActionModIO (\ _cbmgr -> menuFileSave))+ , KeyBinding "q" (Just "Control-q") [Control] "quit" + (KeyActionHQ (\ vpui -> menuFileQuit vpui >> return ()))+ ]++-- | Unused argument needed for key bindings+vpuiKeyHelp :: VPUI -> IO ()+vpuiKeyHelp _vpui = putStrLn keyBindingsHelpText++-- | Help text built from key bindings+keyBindingsHelpText :: String+keyBindingsHelpText = + let add :: String -> KeyBinding -> String+ add result (kb@KeyBinding {kbAltKeyName = mkey}) =+ concat [result, " ", + case mkey of + Nothing -> kbGtkKeyName kb+ Just akey -> akey, + " = ", kbDescription kb, "\n"]+ in foldl add "" keyBindingsList++-- | What to do when a key is pressed+keyPressCallback :: WinId -> CBMgr -> IORef VPUI -> EventM EKey Bool+keyPressCallback winId cbmgr uiref =+ tryEvent $ do+ {+ kname <- eventKeyName+ ; mods <- eventModifier+ -- ; liftIO $ print mods++ ; let giveUp = + -- liftIO (info ("Unrecognized key: " ++ kname)) >> + stopEvent++ ; case Map.lookup kname keyBindingsMap of+ Nothing -> + giveUp+ Just keyBinding -> + if checkMods (kbRequiredModifiers keyBinding) mods+ then liftIO $ + case kbAction keyBinding of+ KeyActionModIO f0 ->+ -- update with IO+ modifyIORefIO (f0 cbmgr) uiref+ KeyActionST f1 ->+ -- update with IO and window ID+ modifyIORefIO (f1 winId) uiref+ KeyActionDG f2 ->+ -- update with IO and cbmgr to set further callbacks+ modifyIORefIO (f2 winId cbmgr) uiref+ KeyActionHQ f3 ->+ -- no update, no cbmgr, no further callbacks+ readIORef uiref >>= f3+ else giveUp+ }++buttonPressCallback :: WinId -> CBMgr -> IORef VPUI -> EventM EButton Bool+buttonPressCallback winId cbmgr uiref =+ tryEvent $ do+ {+ ; (x, y) <- eventCoordinates+ ; mouseButton <- eventButton+ ; mods <- eventModifier+ ; timestamp <- eventTime+ ; let updateAction = + handleButtonPress winId cbmgr mouseButton x y mods timestamp+ ; liftIO (modifyIORefIO updateAction uiref)+ }++mouseMoveCallback :: WinId -> IORef VPUI -> EventM EMotion Bool+mouseMoveCallback winId uiref =+ tryEvent $ do+ {+ (x, y) <- eventCoordinates+ ; mods <- eventModifier+ ; liftIO (modifyIORefIO (handleMouseMove winId x y mods) uiref)+ }++buttonReleaseCallback :: WinId -> IORef VPUI -> EventM EButton Bool+buttonReleaseCallback winId uiref =+ tryEvent $ do+ {+ mouseButton <- eventButton+ ; liftIO (modifyIORefIO (handleButtonRelease winId mouseButton) uiref)+ }++-- | Handle the ButtonPress event. Should be called only for a window+-- with a canvas.+handleButtonPress :: WinId -> CBMgr -> MouseButton + -> Double -> Double -- x, y+ -> [Modifier] -> TimeStamp -- timestamp not needed?+ -> VPUI ->IO VPUI+handleButtonPress winId cbmgr mouseButton x y mods timestamp vpui =+ let vw = vpuiGetWindow vpui winId+ in case vpuiWindowLookupCanvas vw of+ Nothing -> info "handleButtonPress: no canvas found!" >>+ return vpui+ Just canvas ->+ case whichFrame canvas x y of+ Nothing ->+ case vcTool canvas of+ Nothing -> return vpui+ Just tool -> toolOp tool vpui winId TCWorkspace mods x y + Just frame -> + frameButtonPressed winId cbmgr vw+ frame mods (x, y)+ mouseButton timestamp vpui++-- | Handles button pressed in a frame+frameButtonPressed :: WinId -> CBMgr -> VPUIWindow -> CanvFrame + -> [Modifier] -> (Double, Double) -> MouseButton + -> TimeStamp + -> VPUI+ -> IO VPUI+frameButtonPressed winId cbmgr vw frame mods (x, y) mouseButton timestamp vpui =+ let retWrap :: VPUIWindow -> IO VPUI+ retWrap = return . vpuiReplaceWindow vpui winId+ in case mouseButton of+ LeftButton ->+ if cfPointInHeader frame x y + then beginFrameDrag vw frame x y >>= retWrap+ else if cfPointInFooter frame x y+ then leftButtonPressedInFrameFooter vw frame >>= retWrap+ else frameBodyButtonPressed vpui winId frame + mouseButton mods x y+ MiddleButton -> return vpui+ RightButton -> do+ offerContextMenu winId cbmgr frame RightButton timestamp + (vpuiDebugging vpui)+ return vpui+ OtherButton _ -> return vpui++-- | Handles button pressed in the body of a frame+-- frameBodyButtonPressed needs VPUIWindow because it calls a toolOp.+-- mb (mouse button) is unused, but might be used later.+frameBodyButtonPressed :: VPUI -> WinId -> CanvFrame + -> MouseButton -> [Modifier] -> Double -> Double + -> IO VPUI+frameBodyButtonPressed vpui winId frame _mb mods x y = do+ {+ let vw = vpuiGetWindow vpui winId+ canvas = vpuiWindowGetCanvas vw+ mnode = vcanvasNodeAt canvas (Position x y)+ ; case mnode of+ Nothing -> + case vcTool canvas of+ Nothing -> return vpui+ Just tool -> toolOp tool vpui winId (cfContext frame) mods x y+ Just node -> + do+ {+ vw' <- openNode vw node+ ; return $ vpuiReplaceWindow vpui winId vw'+ }+ }++-- | Handles left button pressed in the footer of a frame+leftButtonPressedInFrameFooter ::+ VPUIWindow -> CanvFrame -> IO VPUIWindow+leftButtonPressedInFrameFooter vw frame = + let canvas = vpuiWindowGetCanvas vw+ in case frameType frame of+ CallFrame -> + -- request argument values and evaluate call+ if cfEvalReady frame+ then do+ canvas' <- vcEvalDialog canvas frame+ return $ vpuiWindowSetCanvas vw canvas'+ else return vw+ EditFrame ->+ -- ignore+ return vw++-- | Handles beginning of mouse-drag+beginFrameDrag :: VPUIWindow -> CanvFrame -> Double -> Double + -> IO VPUIWindow+beginFrameDrag vw frame x y = + let canvas = vpuiWindowGetCanvas vw+ window = vpuiWindowWindow vw+ dragging = Dragging {draggingNode = cfFrameNode frame,+ draggingPosition = Position x y}+ canvas' = canvas {vcDragging = Just dragging}+ in setCursor window Fleur >> + (return $ vpuiWindowSetCanvas vw canvas')++-- | Handle mouse move event+handleMouseMove :: WinId -> Double -> Double -> [Modifier] -> VPUI -> IO VPUI+handleMouseMove winId x y mods vpui =+-- Needs to be in IO because of drawWindowInvalidateRect+ let vw = vpuiGetWindow vpui winId+ in case vpuiWindowLookupCanvas vw of+ Nothing -> + info "SQUAWK! No canvas! Shouldn't happen!" >>+ return vpui -- shouldn't happen+ Just canvas -> + do+ {+ -- Highlight the active node, if any+ let active = vcActive canvas+ active' = vcanvasNodeAt canvas (Position x y)++ invalidate :: DrawWindow -> Maybe G.Node -> IO ()+ invalidate win mnode =+ case mnode of+ Nothing -> return ()+ Just node -> + drawWindowInvalidateRect win + (vcanvasNodeRect canvas node) False++ ; when (active /= active') $+ do+ {+ win <- layoutGetDrawWindow (vcLayout canvas)+ ; invalidate win active+ ; invalidate win active'+ }+ -- if dragging, continue drag+ ; canvas' <- continueDrag (canvas {vcActive = active', + vcMousePos = (x, y)}) + mods x y+ ; let vw' = vpuiWindowSetCanvas vw canvas'+ ; return $ vpuiReplaceWindow vpui winId vw'+ }++continueDrag :: VCanvas -> [Modifier] -> Double -> Double -> IO VCanvas+continueDrag canvas mods x y =+ case vcDragging canvas of+ Nothing -> return canvas+ Just dragging -> + let graph = vcGraph canvas+ dnode = draggingNode dragging+ wnode = wlab graph dnode+ Position oldX oldY = draggingPosition dragging+ (dx, dy) = (x - oldX, y - oldY)+ in+ case wnode of+ WSimple _ -> + continueDragSimple canvas dragging dnode mods x y dx dy + WFrame frameNode -> + continueDragFrame canvas dragging frameNode x y dx dy ++continueDragSimple :: VCanvas -> Dragging -> G.Node -> [Modifier] + -> Double -> Double -> Double -> Double -> IO VCanvas+continueDragSimple canvas dragging simpleNode mods x y dx dy =+ let graph = vcGraph canvas+ frame = nodeContainerFrame canvas graph simpleNode+ dragging' = dragging {draggingPosition = Position x y}+ translateSelection = if checkMods [Shift] mods+ then translateTree+ else translateNode+ graph' = translateSelection dx dy graph simpleNode+ canvas' = canvas {vcGraph = graph'}+ in vcInvalidateFrameWithParent canvas graph frame >>+ return (canvas' {vcDragging = Just dragging'})++continueDragFrame :: + VCanvas -> Dragging -> G.Node -> + Double -> Double -> Double -> Double -> IO VCanvas+continueDragFrame canvas dragging frameNode x y dx dy =+ let graph = vcGraph canvas+ frame = vcGetFrame canvas graph frameNode+ frame' = translateFrame frame dx dy+ graph' = grTranslateFrameNodes graph frame dx dy+ canvas' = vcUpdateFrameAndGraph canvas frame' graph'+ dragging' = Just dragging {draggingPosition = Position x y}+ in + -- Tell the GUI about the changes so they will be redrawn+ -- Mark the frame changed so it will be redrawn+ frameChanged canvas graph frame graph' frame' >>+ -- Also, any frames opened from nodes of this frame+ mapM_ (\f -> frameChanged canvas graph f graph' f)+ (vcFrameSubframes canvas frame) >>+ -- Return the modified canvas+ return (canvas' {vcDragging = dragging'})++handleButtonRelease :: WinId -> MouseButton -> VPUI -> IO VPUI+handleButtonRelease winId mouseButton vpui =+ case mouseButton of+ LeftButton -> + -- End drag+ let vw = vpuiGetWindow vpui winId+ canvas = vpuiWindowGetCanvas vw+ window = vpuiWindowWindow vw+ vw' = vpuiWindowSetCanvas vw (canvas {vcDragging = Nothing})+ vpui' = vpuiReplaceWindow vpui winId vw'+ in setCursor window LeftPtr >>+ return vpui'+ _ -> return vpui++-- | Show a context menu for mouse click in a frame.+offerContextMenu :: WinId -> CBMgr -> CanvFrame + -> MouseButton -> TimeStamp -> Bool -> IO ()+offerContextMenu winId cbmgr frame button timestamp debugging = do+ -- Needs CBMgr to specify menu actions.+ {+ let menuSpec = + MenuSpec "Context Menu" + (contextMenuOptions winId cbmgr frame debugging)+ ; menu <- createMenu menuSpec cbmgr+ ; widgetShowAll menu+ ; menuPopup menu (Just (button, timestamp))+ }++-- | Options for context menu that depend on the frame type.++contextMenuOptions :: WinId -> CBMgr -> CanvFrame -> Bool -> [MenuItemSpec]+contextMenuOptions winId cbmgr frame debugging =+ let typeDependentOptions :: [MenuItemSpec]+ typeDependentOptions =+ case frameType frame of+ CallFrame -> + [MenuItem "Edit" (editFrameFunction cbmgr frame)+ , MenuItem "Close" (\ vpui -> closeFrame vpui winId frame)]+ EditFrame -> + [+ -- The next items duplicate parts of keyBindingsList+ MenuItem "CONNECT (c)" (vpuiSetTool ToolConnect winId)+ , MenuItem "DISCONNECT (d)" (vpuiSetTool ToolDisconnect winId)+ , MenuItem "IF (i)" (vpuiSetTool ToolIf winId)+ , MenuItem "FUNCTION (f)" (showFunctionEntry winId cbmgr)+ , MenuItem "LITERAL (l)" (showLiteralEntry winId cbmgr)+ -- , ("CLEAR (not implemented)", clearFrame winId frame)+ , MenuItem "MOVE (m)" (vpuiSetTool ToolMove winId)+ , MenuItem "DELETE (KP-Del)" (vpuiSetTool ToolDelete winId)+ ]+ in typeDependentOptions +++ if debugging+ then [MenuItem "Dump frame (debug)"+ (\ vpui -> dumpFrame vpui winId frame >> return vpui)+ , MenuItem "Dump graph (debug)"+ (\ vpui -> dumpGraph vpui winId >> return vpui)+ ]+ else []
+ Graphics/UI/Sifflet/Workspace.hs view
@@ -0,0 +1,597 @@+{- Currently, this module contains functions for VPUI, VPUIWindow,+Workspace, VCanvas, and more. A reorganization seems called for,+but for now I will just keep adding functions here. -}++module Graphics.UI.Sifflet.Workspace+ (+ -- VPUI and Workspace+ vpuiNew+ , workspaceNewDefault+ , workspaceNewEditing+ , addFedWinButtons+ , defineFunction+ , workspaceId+ , openNode++ -- Quitting:+ , removeWindow+ , vpuiQuit++ -- Windows:+ , forallWindowsIO++ , baseFunctionsRows+ )++where++-- for debugging+-- import System.IO.Unsafe++import Control.Monad++import Data.IORef+import Data.List+import Data.Map ((!), keys)+import qualified Data.Map as Map (empty)+import Data.Maybe++import Data.Graph.Inductive as G++import Data.Sifflet.Functoid+import Data.Sifflet.Geometry+import Data.Sifflet.TreeGraph+import Data.Sifflet.TreeLayout+import Data.Sifflet.WGraph++import Language.Sifflet.Expr+import Language.Sifflet.ExprTree++import Graphics.UI.Sifflet.Callback+import Graphics.UI.Sifflet.Canvas+import Graphics.UI.Sifflet.EditArgsPanel+import Graphics.UI.Sifflet.Frame+import Graphics.UI.Sifflet.GtkUtil++import Graphics.UI.Sifflet.LittleGtk+-- FIX!!+-- import Graphics.UI.Gtk (widgetSizeRequest, windowResize)++import Graphics.UI.Sifflet.Tool+import Graphics.UI.Sifflet.Types++import Language.Sifflet.Util++-- | Create a new VPUI.+-- This used to set up the basic "q to quit" and "on exposed" callbacks,+-- but now does not even do that. +-- The 'init' function argument+-- may perform additional initialization;+-- if there is none, simply use 'return'.++-- The following comment is out of date,+-- but may explain some bizarre features historically:++-- Note that if you want to set up callbacks,+-- there is some trickiness: the vpui contains the workspace,+-- and the layout (which is on the workspace) needs to have callbacks+-- which know the uiref. So, create the workspace, vpui, and uiref,+-- in that order, and then set up the callbacks.+++vpuiNew :: Style -> Env -> Bool -> IO VPUI+vpuiNew style env debugging =+ return VPUI {vpuiWindows = Map.empty,+ vpuiToolkits = [],+ vpuiFilePath = Nothing,+ vpuiStyle = style,+ vpuiInitialEnv = env,+ vpuiGlobalEnv = env,+ vpuiFileEnv = env,+ vpuiDebugging = debugging+ }++++-- | Create a new "main" workspace window, with a given style.+-- The second argument should set up a menu bar and place it on the vbox, +-- or do nothing if no menu is wanted.+workspaceNewDefault :: Style -> (VBox -> IO ()) -> IO Workspace+workspaceNewDefault style = + workspaceNew style (Size 3600.0 2400.0) (Just (Size 900.0 600.0)) []++workspaceNewEditing :: Style -> Env -> Function -> IO Workspace+workspaceNewEditing style initEnv func = do+ {+ ; let funcFrame = fedFuncFrame style func initEnv -- throw-away+ Size fwidth fheight = bbSize (cfBox funcFrame)+ canvSize = Size (max fwidth 300) (max fheight 300)+ mViewSize = Nothing+ specs = functionArgToolSpecs func+ addNoMenu _ = return () + ; workspaceNew style canvSize mViewSize specs addNoMenu+ }++-- Add the buttons for argument tools, apply, close, etc.,+-- to a function editor window++addFedWinButtons :: CBMgr -> WinId -> VPUI -> IO ()+addFedWinButtons cbmgr winId vpui =+ case vpuiGetWindow vpui winId of+ VPUIWorkWin ws window -> + let bbar = wsButtonBar ws+ argSpecs = wsArgToolSpecs ws+ applyFrame :: VPUI -> IO VPUI+ applyFrame vpui' = + let frames = vcFrames + (vpuiWindowGetCanvas + (vpuiGetWindow vpui' winId))+ in case frames of+ [] -> info "applyFrame: no frame found on canvas" >>+ return vpui'+ _:_:_ -> info ("applyFrame: more than one frame " +++ "found on canvas " +++ show frames) >>+ return vpui'+ [frame] -> defineFunction winId frame vpui'++ editParametersOK :: ArgSpecAction+ editParametersOK newSpecs = + cbmgr (WithUIRef (editParametersOK' newSpecs))+ editParametersOK' newSpecs uiref =+ do+ stripButtonBar+ dressButtonBar newSpecs+ vpui' <- readIORef uiref+ -- Maybe it should update more than just one window+ -- here, in view of issues 36 and 31+ -- VVV+ let vpui'' = vpuiUpdateWindow vpui' + winId + (updateEditWindowArgSpecs newSpecs)+ -- Should it also resize (smaller)+ -- since the panel is being removed?+ writeIORef uiref vpui''+ -- redrawing must be done AFTER writing the IORef above.+ redrawCanvas uiref winId++ editParameters :: VPUI -> IO VPUI+ editParameters vpui' =+ do+ {+ let argSpecs' = vpuiWindowArgSpecs vpui' winId+ ; argPanel <- + makeEditArgsPanel cbmgr argSpecs' editParametersOK+ -- attach it to the Gtk Layout at top left+ ; let canv = wsCanvas ws+ layout = vcLayout canv+ panelRoot = editArgsPanelRoot argPanel+ ; layoutPut layout panelRoot 1 1++ -- Make the layout big enough to see the whole panel+ ; expandToFit layout panelRoot++ ; return vpui'+ }+ addButton :: String -> CBMgrAction -> IO ()+ addButton label action = do+ {+ button <- buttonNewWithLabel label+ ; boxPackEnd bbar button PackNatural 3+ ; widgetShow button+ ; cbmgr (AfterButtonClicked button action)+ }++ dressButtonBar :: [ArgSpec] -> IO ()+ dressButtonBar argSpecs' = do+ {+ mapM_ (addArgToolButton cbmgr winId bbar) argSpecs'+ ; addButton "Close" (\ _uiref -> widgetDestroy window)+ ; addButton "Parameters" (modifyIORefIO editParameters)+ ; addButton "Apply" (modifyIORefIO applyFrame)+ } ++ stripButtonBar :: IO ()+ stripButtonBar = do+ {+ -- the buttons should be the only children+ children <- containerGetChildren bbar+ ; mapM_ widgetDestroy children+ }++ in dressButtonBar argSpecs++ FunctionPadWindow _ _ -> return () -- should not happen++vpuiWindowArgSpecs :: VPUI -> WinId -> [ArgSpec]+vpuiWindowArgSpecs vpui winId =+ case vpuiGetWindow vpui winId of+ VPUIWorkWin ws _ -> wsArgToolSpecs ws+ FunctionPadWindow _ _ -> [] -- should not happen++-- | Update an Edit Function window after the "Inputs" dialog,+-- which might have given it some new ArgSpec's.+-- In the resulting VPUIWindow, no node inlets or inputs than +-- its new arity.++updateEditWindowArgSpecs :: [ArgSpec] -> VPUIWindow -> VPUIWindow+updateEditWindowArgSpecs newSpecs vwin =+ case vwin of + VPUIWorkWin ws0 window ->+ let canv0 = wsCanvas ws0+ graph0 = vcGraph canv0+ argNames = map argName newSpecs+ in case vcFrames canv0 of+ [frame0] ->+ let foid0 = cfFunctoid frame0+ fnodes0 = fpNodes foid0 :: [G.Node]++ -- Filter out symbol nodes of any removed arguments+ validNames = + "if" : (argNames ++ envSymbols (cfEnv frame0))+ validNode' :: G.Node -> Bool+ validNode' = validNode graph0 validNames++ fnodes1 = filter validNode' fnodes0+ foid1 = foid0 {fpNodes = fnodes1,+ fpArgs = argNames} -- redundant args here+ frame1 = frame0 {cfVarNames = argNames, -- and here+ cfFunctoid = foid1}+ graph1 = nfilter validNode' graph0++ -- Eliminate excess inlets and edges + -- due to reduced arity of functional arguments+ style = vcStyle canv0+ graph2 = wLimitOuts style newSpecs graph1+ -- Frame node adopts orphaned nodes so they are+ -- not invisible+ orphans = filter (isWSimple . fromJust . lab graph2)+ (graphOrphans graph2)+ graph3 = + adoptChildren graph2 (cfFrameNode frame1) orphans++ -- plug back into larger structures+ canv1 = canv0 {vcGraph = graph3, vcFrames = [frame1]}+ ws1 = ws0 {wsArgToolSpecs = newSpecs, wsCanvas = canv1}+ in VPUIWorkWin ws1 window+ _ -> vwin -- shouldn't happen+ FunctionPadWindow _ _ -> vwin -- shouldn't happen+++-- | Given a WGraph and a list of valid symbols, a node is valid+-- if it is an ExprNode ... labeled with one of the valid symbols+--+-- (This function is only used above, in updateEditWindowArgSpecs)+validNode :: WGraph -> [String] -> G.Node -> Bool+validNode g validNames n =+ case G.lab g n of+ Nothing -> False+ Just (WFrame _) -> True+ Just (WSimple loNode) ->+ let ENode nodeLabel _ = gnodeValue (nodeGNode loNode)+ in case nodeLabel of+ NSymbol (Symbol name) -> name `elem` validNames+ _ -> True+++-- | Require a node to have <= n inlets in the node+-- and <= n successors in the graph, where+-- n is specified in a list of ArgSpec++wLimitOut :: Style -> [ArgSpec] -> WGraph -> Node -> WGraph+wLimitOut style specs g v =+ let ordered :: Adj WEdge -> Adj WEdge+ ordered = sortBy compareAdj+ compareAdj (WEdge i, _nodei) (WEdge j, _nodej) = compare i j+ in case match v g of+ (Nothing, _) -> g+ (Just (ins, _v, wnode, outs), g') -> + -- wnode is the graph-node's "label"+ case wnode of+ WFrame _ -> g+ WSimple lonode@(LayoutNode {nodeGNode = gnode}) ->+ let ENode eLabel _ = gnodeValue gnode -- :: ExprNode+ in case eLabel of+ NSymbol (Symbol name) ->+ case aspecsLookup name specs of+ Nothing -> g+ Just n -> + let (inlets, _) = + makeIolets style + (gnodeNodeBB gnode)+ (n, 1)+ gnode' = gnode {gnodeInlets = inlets}+ wnode' = + WSimple (lonode {nodeGNode = gnode'})+ in (ins, v, wnode', take n (ordered outs)) & + g'+ _ -> g++-- | Like wLimitOut, but applies to a whole graph+wLimitOuts :: Style -> [ArgSpec] -> WGraph -> WGraph+wLimitOuts style specs g = foldl (wLimitOut style specs) g (nodes g)++-- | Redraw the entire canvas of a given window+redrawCanvas :: IORef VPUI -> WinId -> IO ()+redrawCanvas uiref winId = do+ {+ vpui <- readIORef uiref+ ; let mcanvas = vpuiTryGetWindow vpui winId >>= vpuiWindowLookupCanvas + ; case mcanvas of+ Nothing -> return ()+ Just canvas ->+ case vcFrames canvas of+ [] -> return ()+ frame:_ -> vcInvalidateBox canvas (cfBox frame)+ }++addArgToolButton :: CBMgr -> WinId -> HBox -> ArgSpec -> IO ()+addArgToolButton cbmgr winId buttonBox (ArgSpec label n) = do+ {+ button <- buttonNewWithLabel label+ ; boxPackStart buttonBox button PackNatural 3 -- spacing between buttons+ ; widgetShow button+ ; cbmgr (AfterButtonClicked button + (modifyIORefIO (vpuiSetTool (ToolArg label n) winId)))+ ; return ()+ }+++-- | fedFuncFrame generates a throw-away frame for the sole purplse+-- of obtaining its measurements before initializing the canvas++fedFuncFrame :: Style -> Function -> Env -> CanvFrame+fedFuncFrame style func prevEnv = + fst (frameNewWithLayout style (Position 0 0) 0 + (FunctoidFunc func) Nothing+ CallFrame -- mode may change below+ 0 prevEnv Nothing)++-- | If mViewSize is Nothing, no scrollbars are put on the canvas,+-- and its display size request is its natural size.+-- If mViewSize = Just viewSize, then scrollbars are wrapped around+-- the canvas, and its displayed size request is viewSize.+-- addMenuBar is an action which, if desired, adds a menu bar;+-- if you don't want one, just pass (\ _ -> return ()).++workspaceNew :: Style -> Size -> Maybe Size -> [ArgSpec]+ -> (VBox -> IO ()) + -> IO Workspace+workspaceNew style canvSize mViewSize argSpecs addMenuBar = do+ {+ ; let Size dcWidth dcHeight = canvSize -- Double, Double+ (icWidth, icHeight) = (round dcWidth, round dcHeight)++ scrolled :: GtkLayout -> Size -> IO ScrolledWindow+ scrolled layout viewSize = do+ {+ let Size dvWidth dvHeight = viewSize -- Double, Double+ (iViewWidth, iViewHeight) = (round dvWidth, round dvHeight)+ -- Wrap layout directly in a ScrolledWindow .+ -- Adjustments: value lower upper stepIncr pageIncr pageSize+ ; xAdj <- adjustmentNew 0.0 0.0 dcWidth 10.0 dvWidth dvWidth+ ; yAdj <- adjustmentNew 0.0 0.0 dcHeight 10.0 dvHeight dvHeight+ ; scrollWin <- scrolledWindowNew (Just xAdj) (Just yAdj)+ -- show scrollbars? (never, always, or if needed)+ ; scrolledWindowSetPolicy scrollWin PolicyAutomatic PolicyAutomatic+ -- request view size for _layout_+ ; widgetSetSizeRequest layout iViewWidth iViewHeight+ ; set scrollWin [containerChild := layout]+ ; return scrollWin+ }++ bare :: GtkLayout -> IO GtkLayout+ bare layout = do+ {+ -- request canvas size for _layout_+ ; widgetSetSizeRequest layout icWidth icHeight -- new+ ; return layout+ }++ -- The canvas itself+ ; vcanvas <- vcanvasNew style dcWidth dcHeight+ ; let layout = vcLayout vcanvas+ -- Set the actual size of the canvas layout, which may be more+ -- than is displayed if scrollbars are used+ ; layoutSetSize layout icWidth icHeight++ -- An empty HBox for buttons (or it may remain empty)+ ; buttonBar <- hBoxNew False 3++ -- The Statusbar+ ; statusBar <- statusbarNew+ + -- All together in a VBox+ ; vbox <- vBoxNew False 0 -- not equal space allotments, 0 spacing++ ; addMenuBar vbox++ ; let packGrow :: WidgetClass w => w -> IO ()+ packGrow w = boxPackStart vbox w PackGrow 0+ ; case mViewSize of+ Nothing -> bare layout >>= packGrow+ Just viewSize -> scrolled layout viewSize >>= packGrow++ ; boxPackStart vbox buttonBar PackNatural 0+ ; boxPackStart vbox statusBar PackNatural 0+ ; return (Workspace vbox vcanvas buttonBar statusBar argSpecs)+}+++vpuiQuit :: VPUI -> IO VPUI+vpuiQuit vpui = do+ {+ -- This should also check for unsaved changes? ***+ vpui' <- foldM (\ vp winId -> removeWindow vp True winId)+ vpui+ (vpuiAllWindowKeys vpui)+ ; mainQuit+ ; return vpui'+ }++-- | List of all window ids of the vpui, ++vpuiAllWindowKeys :: VPUI -> [WinId]+vpuiAllWindowKeys = keys . vpuiWindows++-- | Perform action on all windows+-- (actually (WinId, VPUIWindow) pairs.+-- Returns updated VPUI (in case any windows are changed).++forallWindowsIO :: (VPUIWindow -> IO VPUIWindow) -> VPUI -> IO VPUI+forallWindowsIO action vpui = + let loop ks vpui' =+ case ks of+ [] -> return vpui'+ k : ks' -> + let w = vpuiGetWindow vpui' k+ in do + {+ w' <- action w+ ; loop ks' (vpuiReplaceWindow vpui' k w')+ }+ in loop (vpuiAllWindowKeys vpui) vpui+++-- | This function is called either when a window *has been* destroyed,+-- with destroy = False,+-- or when you *want to* destroy a window, with destroy = True.++-- WOULD BE BETTER to have two functions, windowRemoved and removeWindow???++-- | removeWindow actually *closes* the window if destroy = True,+-- as well as removing it from the vpui's windows map.+removeWindow :: VPUI -> Bool -> WinId -> IO VPUI+removeWindow vpui destroy winId = do+ {+ -- Remove the window from vpui;+ -- if destroy is true, also destroy it.+ -- It is an error if the window id does not exist.+ let vwMap = vpuiWindows vpui+ ; when destroy $ widgetDestroy (vpuiWindowWindow (vwMap ! winId))+ ; return $ vpuiRemoveVPUIWindow winId vpui+ }++-- | Context menu command to apply the function definition+-- of an EditFrame.++-- | "Execute" the definition currently represented in the frame,+-- i.e., bind the function name in the global environment+-- to the function definition found in the frame.++defineFunction :: WinId -> CanvFrame -> VPUI -> IO VPUI+defineFunction winId frame vpui = + case frameType frame of+ CallFrame ->+ showErrorMessage "Software error\nNot in an edit frame!"+ >> return vpui+ EditFrame ->+ case cfFunctoid frame of+ FunctoidFunc _function ->+ return vpui+ fparts@FunctoidParts {} ->+ let env = vpuiGlobalEnv vpui+ vw = vpuiGetWindow vpui winId+ canv = vpuiWindowGetCanvas vw+ graph = vcGraph canv+ frameNode = cfFrameNode frame+ in case functoidToFunction fparts graph frameNode env of+ Fail errmsg -> + showErrorMessage errmsg >>+ return vpui+ Succ function ->+ let BBox x y _ _ = cfBox frame+ z = cfLevel frame+ fname = functionName function+ env' = envSet env fname (VFun function)+ vpui' = vpui {vpuiGlobalEnv = env'}+ in do + {+ ; canv' <- vcCloseFrame canv frame+ -- can this use vpuiAddFrame? ***+ ; canv'' <- + vcAddFrame canv' (FunctoidFunc function) + Nothing + EditFrame+ env' x y z Nothing+ ; let vw' = vpuiWindowSetCanvas vw canv''+ vpui'' = vpuiReplaceWindow vpui' winId vw'+ ; vpuiUpdateCallFrames vpui'' fname+ }++workspaceId :: String+workspaceId = "Sifflet Workspace"++-- | In the workspace window, update each frame calling the named function +-- to reflect the current function definition+vpuiUpdateCallFrames :: VPUI -> String -> IO VPUI+vpuiUpdateCallFrames vpui fname = + case vpuiTryGetWindow vpui workspaceId of+ Nothing -> return vpui+ Just w -> do+ {+ ; let canvas = vpuiWindowGetCanvas w+ env = vpuiGlobalEnv vpui+ frames = callFrames canvas fname+ update canv frame = canvasUpdateCallFrame canv frame fname env+ ; canvas' <- foldM update canvas frames + ; let w' = vpuiWindowSetCanvas w canvas'+ ; return $ vpuiReplaceWindow vpui workspaceId w'+ }++-- | In the canvas, update a call frame with the current function+-- definition from the environment, returning a new canvas.+-- Root call frames are torn down and rebuilt with the new function definition.+-- Call frames that are called by other call frames are simply torn down.+canvasUpdateCallFrame :: VCanvas -> CanvFrame -> String -> Env -> IO VCanvas+canvasUpdateCallFrame canvas frame fname env = do+ {+ -- Tear down old frame+ canvas' <- vcCloseFrame canvas frame+ ; case cfParent frame of+ Nothing -> + -- root frame; build up new frame+ let Position x y = bbPosition (cfBox frame)+ z = cfLevel frame+ functoid = FunctoidFunc {fpFunc = envGetFunction env fname}+ in vcAddFrame canvas' functoid Nothing CallFrame env x y z Nothing+ Just _ -> + -- frame with a parent; finished+ return canvas'+ }++openNode :: VPUIWindow -> G.Node -> IO VPUIWindow+openNode vw node = do+ let canvas = vpuiWindowGetCanvas vw+ graph = vcGraph canvas+ if not (nodeIsSimple graph node)+ then return vw -- WFrame node -- is this possible?+ else if nodeIsOpen graph node+ then info "Already open" >> return vw+ else let frame = nodeContainerFrame canvas graph node+ in case nodeCompoundFunction graph frame node of+ Nothing -> + info "Cannot be opened" >> return vw+ Just function ->+ case nodeInputValues graph node of+ EvalOk (VList values) ->+ let env = extendEnv (functionArgNames function)+ values (cfEnv frame)+ Position x y = + frameOffset (vcStyle canvas) frame+ z = succ (cfLevel frame)+ in vwAddFrame vw + (FunctoidFunc function) + (Just values) CallFrame+ env x y z (Just node)+ EvalOk x ->+ errcats ["openNode: non-VList result:", show x]+ _ -> + info "Cannot be opened: lacking input values" >>+ return vw++baseFunctionsRows :: [[String]]+baseFunctionsRows = [["+", "-", "*", "div", "mod", "add1", "sub1", "/"],+ ["==", "/=", "<", ">", "<=", ">="],+ ["zero?", "positive?", "negative?"],+ ["null", "head", "tail", ":"]]
+ ISSUES view
@@ -0,0 +1,403 @@+ISSUES+======++Issues for sifflet and sifflet-lib++This is the ISSUES file for both sifflet and sifflet-lib.+If you are editing it, be sure you are editing the original+and not the copies in the app and lib subdirectories.++O = date opened+R = reporter+P = priority+C = closed++OPEN ISSUES+-----------++41 next issue bug number++40 Duplicate arguments in "Edit Arguments"++ Adding a duplicate argument in the "Edit Arguments" dialog+ is possible (for example, two arguments named "f").+ It should not be.+ O: 2012 July 2+ R: gdw+ P: medium++39 No lambda tool++ There is no lambda tool, and since partial application doesn't work+ (issue 32), this means there is no way to have a function return a function.+ O: 2012 jul 2+ R: gdw+ P: medium++38 Working directory++ Sifflet should have some sense of its working directory, + for convenience of opening and saving files+ O: 2012 july 2+ R: gdw+ P: medium++37 Changing helper function definition does not affect displayed function call++ See f2 and testF2 in ../examples/map.sfl+ When testF2 is called on the workspace, and you edit and chagne f2,+ apply the new definition, the displayed call is not re-evaluated+ using the new definition. In fact if you use the displayed+ testF2 frame to call again, it still uses the old definition --+ it seems permanently bound to it. To use the new definition+ of f2, you have to place a new copy of testF2 on the workspace.++ O: 2012 Jun 27+ R: gdw+ P: low++36 Cascade effect of changing kind of arguments to a function.++ When we change the number of type (kind) of the arguments of+ a function, we also change the function's type (or kind); and if+ it's used in the definitions of other functions, then they may+ become invalid in turn -- what should be done in this case?+ O: 2012 Jun 27+ R: gdw+ P: medium++33 testMapSumFromZero (see ../examples/map.sfl)+ In testMapSumFromZero n, with n = [1,2],+ opening the sumFromZero node displays bogus error messages+ as though it is a call to sumFromZero with n = [1,2].+ In fact, the node has no input, so there is no call + to sumFromZero until we get into map.+ The problem doesn't occur with recursive functions.+ It must be because the node has an inlet that expects n,+ and finds n in the enclosing environment although it is not called.+ O: 2012 June 22+ R: gdw+ P: low++32 Partial application does not work+ (e.g., calling the + function with one argument)+ Comment: See also #39.+ O: 2012 Jun 21+ R: gdw+ P: medium++31 Edit function inputs: when an argument tool is selected,+ and you change the arity of that argument, the next click+ of the tool puts the argument down on the canvas with its+ *old* arity; should be the new arity.+ Comment: A work-around is to reselect the tool to get the right arity.+ O: 2012 Jun 21+ R: gdw+ P: medium++26 EditArgsPanel: TAB fails to move the cursor to the next field.+ O: 2011 Jun 23+ R: gdw+ P: medium-high++25 Nodes lose unused inlets when applying a function definition.+ - Suppose the f node has two inlets, but none of them are+ used by any f nodes in the definition. Then when you apply the+ definition, all the f nodes lose both of their inlets.+ On the other hand, if one f node uses none of its two inlets+ and another uses both, then when you apply the definition,+ both of them keep both of their inlets.+ O: 2011 Jun 23+ R: gdw+ P: lowest.+ Comment:+ This seems not really to be a problem. If NONE of the f nodes+ use their inlets, then why do they have inlets, anyway?+ Besides, it should become irrelevant when a new UI is+ developed without the circular inlets, as in issue 24.+ Comment: The inlets will reappear if you reopen the "inputs"+ dialog and then click "OK".++24 Arguments are shifted when applying a function definition.+ - If a node has 3 inlets and connections on inlets 1 and 2,+ but not on 0, then when "apply" is clicked the connections are+ shifted to inlets 0 and 1, leaving 2 open.+ O: 2011 Jun 23+ R: gdw+ P: lowest.+ This should become irrelevant when and if I develop a new+ UI that does away with the circular inlets. Why not just+ let the user make as many connections as they please? And+ that would get rid of the need for a dialog that lets you+ change the number of inlets too.++23 typeToXml fails for (TypeCons Function)+ O: 2011 Jun 23+ R: gdw+ P: medium++22 When you connect an input to the second (or generally not first)+ input port of a node, and leave the first "open", then click on+ apply, the input is reconnected to the first port instead of the+ second.+ -- Is this actually a bug? + O: 2011 Jun 2+ R: gdw+ P: ???++14 If two frames overlap, then clicks on the top frame+ sometimes are interpreted as clicks on the lower (hidden) frame.+ O: 2010 aug 16+ P: low++13 If you change any of the *example* functions and quit or open,+ Sifflet prompts you to save changes, but it does not save+ these particular changes, since they are not in "My Functions".+ Probably need to re-design the whole organization of examples+ and my functions, introduce modules associated with files,+ and show each in its own panel.+ Opened 2010 May 27.+ Priority: low++11 When function f, which calls function g, is on the workspace,+ and you edit g, the changes do not propagate back to f, so that+ calling f results in calling the *old* g.+ Example: open test-champ.siff, place two-lovely on the workspace,+ call it with any argument, edit lovely, apply, call two-lovely+ again.+ Opened 2010 May 20. + Priority: medium++* * *++CLOSED ISSUES+-------------+++1 After using the literal tool, etc., the program stops responding+ to key presses in the workspace.+ Opened ??+ Closed 2009 Oct 5.++2 Fix resize frame+ Opened ??+ Closed 2009 Oct 13.++3 Quitting (q and File/quit) should actually quit!+ Opened ??+ Closed 2009 Oct 13.++4 vpTypeOf fails for [].+ Opened ??+ Closed 2009 Oct 30.++5 Deleting a node leaves its children orphans.+ Opened ??+ Closed 2009 Nov 4.++6 After closing "My functions" window and re-opening it,+ it should "remembers" the function tools that it had when closed,+ instead of showing up as a blank window.+ Opened ??+ Closed 2009 Nov 11++7 If function pad window is closed, then creating a + new function results in a "key not found" error.+ Opened ??+ Closed 2010 March 1++8 Opening a file twice put duplicate tools in+ "My Functions" window (and toolkit, presumably).+ Opened ??+ Closed 2010 May 13++9 After the dialog suggesting save changes before quitting or+ opening a new file, the program crashes due to a bug in+ Gtk2hs 0.10.0 and 0.10.1 (but fixed in gtk2hs darcs).+ Opened ??+ Closed 2010 May 20.++10 Evaluating a call frame with zero arguments+ opens an unnecessary dialog (there are not arguments,+ so no values needed); moreover, it does not evaluate+ the function call.++15 In the test function list_of_2, evaluating [] results in+ "error: unbound variable: []". Ditto repeat_a_b.+ O: 2010 aug 16+ P: high+ C: 2010 aug 16 -- fixed.++16 Editing a function, if you enter the literal ["a", "b"],+ the program crashes with this message:+ sifflet-new-expr: makeFixedLiteralTool: + non-empty list expression EList [EString "a",EString "b"]+ O: 2010 aug 16+ P: high+ C: 2010 aug 16 -- fixed.+++17 There is an error in reading a non-empty list element from+ a SiffML file, e.g., + <literal>+ <list>+ <string>a</string>+ <string>b</string>+ </list>+ </literal>+ due to the fact that the children of the <list> element+ directly represent values, not expressions, so they are+ not read correctly (not read at all?) by xmlToExpr.+ I think I need to use xmlToValue here, but how to connect+ the two is not easy.+ In the long run, it is desirable to change the XML doctype,+ dropping the <literal> element so that strings would be+ e.g. just <string>a</string>+ and lists would be just e.g. + <list>+ <string>a</string>+ <string>b</string>+ </list>+ O: 2010 aug 17+ P: highest+ C: 2010 aug 17 -- fixed.++19 Closing all program windows leaves executable still running+ but no way to kill it (Ctrl+C is a no-op).*+ O: 2010 nov 3+ R: Andrew Coppin andrewcoppin@btinternet.com+ P: HIGH+ C: 2010 dec 9 (actually fixed dec 3): closing the workspace window+ now "safely" quits from the application.++20 Amend documentation/home page to note that installation on+ Windows without curl is now definitely feasible+ O: 2010 nov 3+ R: Andrew Coppin andrewcoppin@btinternet.com+ P: medium+ C: 2010 dec 9 (actually fixed dec 3)++21 Define adds list =+ if null list+ then 0+ else if head list+ then + list 1+ else tail (head list)+ Call the function with list = [1]+ Sifflet crashes with this error message:+ evalTreeWithLimit (if): unexpected test result+ O: 2010 dec 9+ R: Charles Turner III (turnchan@iue.edu)+ P: high+ C: 2010 dec 9++29 typedValue (in Parser.hs) has two conditions that result in+ run-time errors. For testing higher-order functions, it would+ be good to be able to type in the name of a function in the global+ environment, but we don't have access to the environment here,+ do we? Also in case of failure, it should fail with an error+ message but not crash sifflet.+ O: 2012 Jun 15+ R: gdw+ P: high+ Crashes program.+ C: 2012 June 21 -- fixed.++30 typeToXml: program crashes when saving the map function definition,+ with message:+ sifflet-devel: typeToXml: TypeCons Function cannot be converted to XML.+ Notes: + 1. typeToXml is fixed, but now I also need to fix the corresponding+ part of xmlToType so the file can be read back in.+ 2. Maybe better still:+ Why do we need the types in the XML file anyway, now that+ we have type inference?+ 3. Also revise schema/DTD for SiffML files.+ O: 2012 Jun 21+ R: gdw+ P: highest - program crash+ C: 2012 June 22, fixed.++28 EditArgsPanel should change inlets for existing nodes when the arg's+ kind is changed.+ - Suppose you change the number of parameters for argument f.+ Then any existing f nodes on the drawing should have their+ number of inlets changed correspondingly.+ Currently this works if the number is increased, but+ fails if it is decreased and there are now too many+ connected children in the graph --> error in graphRenderTree.+ Unfortunately, this is not the place where that can be fixed:+ even if I reduce nto or nfrom in this context,+ there are still the contexts in the connected children to+ consider.+ - Also, when extra inputs are disconnected (as they should be)+ they are sometimes also deleted from the canvas (shouldn't be).+ O: 2011 Jun 23+ R: gdw+ P: highest. Crashes program.+ C: 2012 Jun 27, fixed.+++18 Sifflet crashes when applying a function if some connections+ are missing.++ "PS. I presume you know that you can crash the whole of Sifflet by+ something as trivial as hitting the "apply" button when some+ connections are missing, and that closing all of the program windows+ leaves the executable still running but with no way to kill it. (I'm+ not sure why Ctrl+C is no-op...)"+ -- email from Andrew Coppin, 2010 Nov 01.++ O: 2010 nov 3+ R: Andrew Coppin andrewcoppin@btinternet.com+ P: HIGH+ C: 2012 Jun 27 -- unable to duplicate either part of this at this time;+ must have been fixed earlier. Though it might be that the+ "no way to kill it" issue persists on MS Windows.++35 vcCloseFrame does not remove the frame's subgraph from the canvas graph.++ vcCloseFrame removes only the WFrame node, not the WSimple nodes+ displayed in the frame. It should remove all of them, otherwise+ clutter builds up every time a function definition is applied,+ since the apply command calls vcCloseFrame and then vcAddFrame.+ O: 2012 Jun 27+ R: gdw+ P: high+ C: 2012 Jun 27 fixed.++12 Four tests fail in testall -u.+ These seem to all involve issues of text node size+ and are pretty minor, so maybe the tests need to be + fine-tuned to allow for variations in what Cairo says+ is the text's dimensions.+ Opened 2010 May 24+ Priority: medium+ C: 2012 Jul 2, previously fixed but not closed; all unit tests now pass.++34 In edit function / edit arguments: the edit window sometimes+ does not sufficiently increase in size, + so the edit arguments buttons (OK, cancel) + are hidden by the edit function buttons (args, apply, inputs, close).+ Comment: see darcs change:+ Tue Jun 7 12:40:24 EDT 2011 gdweber@iue.edu+ * Crude window resizing to fit as the EditArgsPanel grows+ - crude because it does not take into account the button bar+ below the panel+ Comment: duplicate of #27+ O: 2012 June 26+ R: gdw+ P: top+ C: 2012 jul 2, fixed++27 EditArgsPanel: window resizing is bad.+ - Sometimes the window does not expand enough to show the + panel's buttons.+ - Sometimes it shrinks back too much after the panel is closed.+ - Comment: lib/Graphics/UI/Sifflet/EditArgsPanel.hs+ Workspace.hs : 138+ + O: 2011 Jun 23+ R: gdw+ P: top+ C: 2012 jul 2, fixed
+ Language/Sifflet/Examples.hs view
@@ -0,0 +1,277 @@+module Language.Sifflet.Examples + (exampleFunctions+ , exampleFunctionNames+ , exampleEnv + , foo, eFoo -- used in Testing.Unit.ExprTests+ , eMax -- ditto+ , eFact -- ditto+ , getExampleFunction+ )++where++import Language.Sifflet.Expr++-- TEST COMPOUND FUNCTIONS++-- | grossProfit salesA salesB = 0.12 salesA + 0.25 salesB++grossProfit :: Function+grossProfit = + Function (Just "grossProfit") [typeNum, typeNum] typeNum+ (Compound ["salesA", "salesB"]+ (eCall "+" [eCall "*" [eFloat 0.12, eSymbol "salesA"],+ eCall "*" [eFloat 0.25, eSymbol "salesB"]]))++-- | bonus1 profit = if profit > 100000 +-- then 1000 + 0.0012 * profit+-- else 0+bonus1 :: Function+bonus1 =+ Function (Just "bonus1") [typeNum] typeNum+ (Compound ["profit"]+ (eIf (eCall ">" [eSymbol "profit", eInt 100000])+ (eCall "+" [eInt 1000,+ eCall "*" [eFloat 0.0012, eSymbol "profit"]])+ (eInt 0)))++-- | bonus2 salesA salesB = bonus1 (grossProfit salesA salesB)+bonus2 :: Function+bonus2 =+ Function (Just "bonus2") [typeNum, typeNum] typeNum+ (Compound ["salesA", "salesB"]+ (eCall "bonus1" [eCall "grossProfit" [eSymbol "salesA",+ eSymbol "salesB"]]))++-- | foo a b = 2 * a + b+foo :: Function+foo = Function (Just "foo") [typeNum, typeNum] typeNum+ (Compound ["a", "b"]+ (eCall "+" [eCall "*" [eInt 2, eSymbol "a"],+ eSymbol "b"])+ )++-- | An expression representing a call to foo+eFoo :: Expr -> Expr -> Expr+eFoo e1 e2 = eCall "foo" [e1, e2]++-- | max x y = if x > y then x else y+max :: Function+max = let ex = eSymbol "x"+ ey = eSymbol "y"+ in Function (Just "max") [typeNum, typeNum] typeNum+ (Compound ["x", "y"] (eIf (eGt ex ey) ex ey))++-- | An expression representing a call to max+eMax :: Expr -> Expr -> Expr+eMax e1 e2 = eCall "max" [e1, e2]++-- | fact n = if n == 0 then 1 else n * (fact (n - 1))+fact :: Function+fact = let en = eSymbol "n" in+ Function (Just "fact") [typeNum] typeNum+ (Compound ["n"] + (eIf (eZerop en) + (eInt 1)+ (eTimes en (eFact (eSub1 en)))))++-- | An expression representing a call to fact+eFact :: Expr -> Expr+eFact e1 = eCall "fact" [e1]++-- | sum of the integers 0..n+-- Lewis and Loftus, Jave Software Solutions, 6th. ed. (they call it "sum")+sumFromZero :: Function+sumFromZero = let en = eSymbol "n" in+ Function (Just "sumFromZero") [typeNum] typeNum+ (Compound ["n"]+ (eIf (eZerop en)+ (eInt 0)+ (ePlus en (eSumFromZero (eSub1 en)))))++buggySumFromZero :: Function+buggySumFromZero = + let body = ePlus (eSym "n")+ (eCall "buggySumFromZero"+ [eMinus (eSym "n") (eInt 1)])+ in Function (Just "buggySumFromZero") [typeNum] typeNum + (Compound ["n"] body)++eFib1 :: Expr -> Expr+eFib1 en = eCall "fib1" [en]++fib1 :: Function+fib1 = let en = eSymbol "n"+ one = eInt 1+ two = eInt 2+ in Function (Just "fib1") [typeNum] typeNum+ (Compound ["n"]+ (eIf (eEq en one)+ one+ (eIf (eEq en two)+ one+ (ePlus (eFib1 (eMinus en two))+ (eFib1 (eMinus en one))))))++-- implying that there should be fib2 ...++eSumFromZero :: Expr -> Expr+eSumFromZero en = eCall "sumFromZero" [en]++-- | rmul: multiplication by repeated addition.+-- The "multiply" function in Hanly and Koffman,+-- "Problem Solving and Program Design in C", 5th ed.++rmul :: Function+rmul = let em = eSymbol "m"+ en = eSymbol "n"+ in Function (Just "rmul") [typeNum, typeNum] typeNum+ (Compound ["m", "n"]+ (eIf (eZerop en)+ (eInt 0)+ (ePlus em (eRmul em (eSub1 en)))))++-- | An expression representing a call to rmul+eRmul :: Expr -> Expr -> Expr+eRmul em en = eCall "rmul" [em, en]++eGcd :: Expr -> Expr -> Expr+eGcd em en = eCall "gcd" [em, en]++gcd :: Function+gcd = let em = eSymbol "m"+ en = eSymbol "n"+ in Function (Just "gcd") [typeNum, typeNum] typeNum+ (Compound ["m", "n"]+ (eIf (eZerop (eMod em en))+ en+ (eGcd en (eMod em en))))+++-- | Even and odd, the slow way.+-- Springer and Friedman, Scheme and the Art of Programming (??).+-- Rubio-Sanchez, Urquiza-Fuentes, and Pareja-Flores,+-- "A Gentle Introduction to Mutual Recursion",+-- in ITiCSE 2008: The 13th SIGCSE Conference on Innovation and+-- Technology in Computer Science Education, 2008.++eEvenp, eOddp :: Expr -> Expr+eEvenp en = eCall "even?" [en]+eOddp en = eCall "odd?" [en]++evenp, oddp :: Function++evenp = let en = eSymbol "n"+ in Function (Just "even?") [typeNum] typeBool+ (Compound ["n"]+ (eIf (eZerop en)+ eTrue+ (eOddp (eSub1 en))))++oddp = let en = eSymbol "n"+ in Function (Just "odd?") [typeNum] typeBool+ (Compound ["n"]+ (eIf (eZerop en)+ eFalse+ (eEvenp (eSub1 en))))++-- | Fibonacci series through mutual recursion.+-- Rubio-Sanchez, Urquiza-Fuentes, and Pareja-Flores, "Gentle Introduction"++eRabbitBabies, eRabbitAdults :: Expr -> Expr+eRabbitBabies en = eCall "rabbitBabies" [en]+eRabbitAdults en = eCall "rabbitAdults" [en]++rabbitTotal, rabbitAdults, rabbitBabies :: Function++rabbitTotal = let m = eSymbol "month"+ in Function (Just "rabbitTotal") [typeNum] typeNum+ (Compound ["month"]+ (ePlus (eRabbitBabies m) (eRabbitAdults m)))++rabbitAdults = let m = eSymbol "month"+ zero = eInt 0+ one = eInt 1+ in Function (Just "rabbitAdults") [typeNum] typeNum + (Compound ["month"]+ (eIf (eEq m one)+ zero+ (ePlus+ (eRabbitAdults (eSub1 m)) -- all adults survive+ (eRabbitBabies (eSub1 m))))) -- all babies grow up++rabbitBabies = let m = eSymbol "month" + one = eInt 1+ in Function (Just "rabbitBabies") [typeNum] typeNum + (Compound ["month"]+ (eIf (eEq m one)+ one+ (eRabbitAdults (eSub1 m)))) -- all adults reproduce+ +buggyLength :: Function+buggyLength = let xs = eSymbol "xs"+ one = eInt 1+ in Function (Just "buggyLength") + [typeList (TypeVar "e1")] typeNum+ (Compound ["xs"]+ (eIf (eCall "null" [xs])+ one -- base case off by one, should be zero+ (ePlus one + (eCall "buggyLength"+ [eCall "tail" [xs]]))))++listLength :: Function+listLength = let xs = eSymbol "xs"+ one = eInt 1+ zero = eInt 0+ in Function (Just "length") + [typeList (TypeVar "e1")] typeNum+ (Compound ["xs"]+ (eIf (eCall "null" [xs])+ zero+ (ePlus one + (eCall "length"+ [eCall "tail" [xs]]))))++listSum :: Function+listSum = + Function (Just "sum") [typeList typeNum] typeNum + (Compound ["xs"] sumbody)++sumbody :: Expr+sumbody = + eIf (eCall "null" [eSym "xs"])+ (eInt 0)+ (ePlus (eCall "head" [eSym "xs"])+ (eCall "sum" [eCall "tail" [eSym "xs"]]))++buggySum :: Function+buggySum = let xs = eSymbol "xs"+ in Function (Just "buggySum")+ [typeList typeNum] typeNum+ (Compound ["xs"]+ -- missing "if" and base case+ (ePlus (eCall "head" [xs])+ (eCall "buggySum" [eCall "tail" [xs]])))++exampleFunctions :: [Function]+exampleFunctions = [grossProfit, bonus1, bonus2+ , foo, Language.Sifflet.Examples.max+ , fact, sumFromZero, rmul, fib1+ , Language.Sifflet.Examples.gcd, evenp, oddp+ , rabbitBabies, rabbitAdults, rabbitTotal+ , buggyLength, listLength, listSum, buggySum+ , buggySumFromZero]++exampleFunctionNames :: [String]+exampleFunctionNames = map functionName exampleFunctions++exampleEnv :: Env+exampleEnv = + envInsertL baseEnv exampleFunctionNames (map VFun exampleFunctions)++-- | This function will be in error if the function name is not+-- found in exampleEnv.+getExampleFunction :: String -> Function+getExampleFunction = envGetFunction exampleEnv+
+ Language/Sifflet/Export/Exporter.hs view
@@ -0,0 +1,192 @@+module Language.Sifflet.Export.Exporter + (Exporter+ , simplifyExpr+ , commonRuleHigherPrec+ , commonRuleAtomic+ , commonRuleLeftToRight+ , commonRuleAssocRight+ , commonRuleFuncOp+ , commonRulesForSimplifyingExprs+ , ruleIfRight+ , ruleRightToLeft+ , applyFirstMatch+ , findFixed+ ) ++where++import Language.Sifflet.Expr++-- | The type of a function to export (user) functions to a file.+type Exporter = Functions -> FilePath -> IO ()++-- | Simplify an expression by applying rules +-- top-down throughout the expression+-- tree and repeatedly until there is no change.+-- This is intended for removing extra parentheses,+-- but could be used for other forms of simplification.+-- +-- Should each rule also know the level in the original expr tree,+-- with 0 = top level (root)?+-- That would require additional arguments.++simplifyExpr :: [Expr -> Expr] -> Expr -> Expr+simplifyExpr rules expr = + findFixed (topDown (applyFirstMatch rules)) expr++-- | Repeatedly apply a function to an object until there is no change,+-- that is, until reaching a fixed point of the function, a point +-- where f x == x.++findFixed :: (Eq a) => (a -> a) -> a -> a+findFixed f x =+ let x' = f x+ in if x' == x then x else findFixed f x'+++-- | Common rules for simplifying parentheses.++-- | Remove ()'s around a higher precedence operator: e.g., +-- (a * b) + c ==> a * b + c+-- a + (b * c) ==> a + b * c++commonRuleHigherPrec :: Expr -> Expr+commonRuleHigherPrec e =+ case e of+ EOp op1 (EGroup (EOp op2 subleft subright)) right ->+ -- left side+ if opPrec op2 > opPrec op1+ then EOp op1 (EOp op2 subleft subright) right+ else e+ EOp op1 left (EGroup (EOp op2 subleft subright)) ->+ -- right side+ if opPrec op2 > opPrec op1+ then EOp op1 left (EOp op2 subleft subright)+ else e+ _ -> e++-- | Remove ()'s around an atomic expression -- a variable,+-- literal, or list++commonRuleAtomic :: Expr -> Expr+commonRuleAtomic e =+ case e of+ EGroup e' ->+ if exprIsAtomic e' + then e'+ else e+ _ -> e++-- | Remove ()'s in the case of (a op1 b) op2 c,+-- if op1 and op2 have the same precedence, and+-- both group left to right, since+-- left to right evaluation makes them unnecessary.++commonRuleLeftToRight :: Expr -> Expr+commonRuleLeftToRight e =+ case e of+ EOp op2 (EGroup (EOp op1 a b)) c ->+ if opPrec op1 == opPrec op2 && + opGrouping op1 == GroupLtoR &&+ opGrouping op2 == GroupLtoR+ then EOp op2 (EOp op1 a b) c+ else e+ _ -> e++-- | Remove ()'s in the case of a op (b op c)+-- if op groups right to left, and note that+-- it is the same operator op in both places+-- (though I don't know if that restriction is necessary).+-- This applies to (:) in Haskell, for example:+-- x : y : zs == x : (y : zs)++ruleRightToLeft :: Expr -> Expr+ruleRightToLeft e =+ case e of+ EOp op1 a (EGroup (EOp op2 b c)) ->+ if op1 == op2 && opGrouping op1 == GroupRtoL+ then EOp op1 a (EOp op2 b c)+ else e+ _ -> e++-- Associativity on the right+-- x + (y + z) --> x + y + z+-- for + and all other associative operators.+-- We could add, the left-hand rule+-- (x + y) + z --> x + y + z+-- but do not need it,+-- because it is already covered by the left to right rule+-- for operators of equal precedence.+-- It must be the SAME operator on both sides, of course!++commonRuleAssocRight :: Expr -> Expr+commonRuleAssocRight e =+ case e of+ EOp op1 a (EGroup (EOp op2 b c)) -> + if op1 == op2 && opAssoc op1+ then EOp op1 a (EOp op2 b c)+ else e+ _ -> e++-- An if expression as the right operand can be unparenthesized.+-- but not so on the left (at least in Haskell):+-- x + (if ...) --> x + if ...+-- but NOT+-- (if ...) + x --> if ... + x (NOT!)++ruleIfRight :: Expr -> Expr+ruleIfRight e =+ case e of+ EOp op a (EGroup i@(EIf _ _ _)) -> EOp op a i+ _ -> e++-- In Haskell, a function application has precedence over all+-- operators. This applies in both the left and right operands.++commonRuleFuncOp :: Expr -> Expr+commonRuleFuncOp e =+ case e of+ EOp op a (EGroup c@(ECall _ _)) -> EOp op a c+ EOp op (EGroup c@(ECall _ _)) b -> EOp op c b+ _ -> e++-- | A list of common rules for simplifying expressions.+-- Does *not* include ruleIfRight, since that works+-- for Haskell but not Python.++commonRulesForSimplifyingExprs :: [Expr -> Expr]+commonRulesForSimplifyingExprs =+ [commonRuleHigherPrec+ , commonRuleAtomic+ , commonRuleLeftToRight+ , commonRuleAssocRight+ , commonRuleFuncOp]++-- | Try the first rule in a list to see if it changes an expression,+-- returning the new expression if it does; otherwise, try the next rule,+-- and so on; if no rule changes the expression, then return the expression.+-- (Note that (applyFirstMatch rules) is itself a rule.)++applyFirstMatch :: [Expr -> Expr] -> Expr -> Expr+applyFirstMatch [] e = e+applyFirstMatch (r:rs) e = + let e' = r e+ in if e' /= e+ then e'+ else applyFirstMatch rs e++-- | Apply a rule top-down to all levels of an expression.+-- Normally, the "rule" would be a value of (applyFirstMatch rules).+topDown :: (Expr -> Expr) -> Expr -> Expr+topDown f e =+ let tdf = topDown f+ e' = f e+ in case e' of+ EIf c a b -> EIf (tdf c) (tdf a) (tdf b)+ EList xs -> EList (map tdf xs)+ ELambda x body -> ELambda x (tdf body)+ ECall fsym args -> ECall fsym (map tdf args)+ EOp op left right -> EOp op (tdf left) (tdf right)+ EGroup e'' -> EGroup (tdf e'')+ _ -> e'+
+ Language/Sifflet/Export/Haskell.hs view
@@ -0,0 +1,159 @@+-- | Abstract syntax tree and pretty-printing for Haskell 98.+-- This is only a small subset of the Haskell 98 syntax,+-- so we do not need to pull in haskell-src and all its complexity.+-- Moreover, haskell-src gives too little control over the format+-- of pretty-printed text output.++module Language.Sifflet.Export.Haskell+ (HsPretty(..)+ , Module(..)+ , ExportSpec(..)+ , ImportDecl(..)+ , Decl(..)+ , operatorTable+ )++where++import Data.List (intercalate)+import qualified Data.Map as M++import Language.Sifflet.Expr+import Text.Sifflet.Pretty++class HsPretty a where++ hsPretty :: a -> String++ hsPrettyList :: String -> String -> String -> [a] -> String+ hsPrettyList pre tween post xs =+ pre ++ intercalate tween (map hsPretty xs) ++ post++instance HsPretty Symbol where+ hsPretty = pretty++instance HsPretty Operator where+ hsPretty = pretty++-- | A Haskell module; moduleDecls are functions and variables.++data Module = Module {moduleName :: String+ , moduleExports :: Maybe ExportSpec+ , moduleImports :: ImportDecl+ , moduleDecls :: [Decl]+ }+ deriving (Eq, Show)++instance HsPretty Module where+ hsPretty m = + let pmod = "module " ++ moduleName m+ pexports = case moduleExports m of+ Nothing -> ""+ Just exports -> hsPretty exports+ pimports = hsPretty (moduleImports m)+ pdecls = sepLines2 (map hsPretty (moduleDecls m))+ in unlines [pmod ++ " where",+ pexports,+ pimports,+ pdecls]++-- | A Haskell module's export spec: a list of function and +-- variable identifiers+newtype ExportSpec = ExportSpec [String]+ deriving (Eq, Show)++instance HsPretty ExportSpec where+ hsPretty (ExportSpec exports) = + "(" ++ sepCommaSp exports ++ ")"++-- | A Haskell modules import decls: a list of module identifiers.+-- No support for "qualified" or "as" or for selecting only some+-- identifiers from the imported modules.++newtype ImportDecl = ImportDecl [String]+ deriving (Eq, Show)++instance HsPretty ImportDecl where+ hsPretty (ImportDecl modnames) = + let idecl modname = "import " ++ modname+ in unlines (map idecl modnames)++-- | Wrap a string in parentheses+par :: String -> String+par s = "(" ++ s ++ ")"++-- | A Haskell function or variable declaration.+-- An explicit type declaration is optional.+-- Thus we have just enough for +-- name :: type+-- name [args] = expr.+-- Of course [args] would be empty if it's just a variable.+data Decl = Decl {declIdent :: String+ , declType :: Maybe [String]+ , declParams :: [String]+ , declExpr :: Expr+ }+ deriving (Eq, Show)++instance HsPretty Decl where+ hsPretty decl =+ let ptypeDecl = "" -- to be improved **+ pparams = case declParams decl of+ [] -> ""+ params -> " " ++ sepSpace params+ pbody = hsPretty (declExpr decl)+ in ptypeDecl ++ + declIdent decl ++ pparams ++ " =\n" +++ " " ++ pbody++-- | HsPretty expressions. This is going to be like in Python.hs.+instance HsPretty Expr where+ hsPretty pexpr =+ case pexpr of+ EUndefined -> "undefined"+ EChar c -> show c+ ENumber n -> show n+ EBool b -> show b+ EString s -> show s+ ESymbol sym -> hsPretty sym+ EList xs -> hsPrettyList "[" ", " "]" xs+ EIf c a b -> + unwords ["if", hsPretty c, "then", hsPretty a, "else", hsPretty b]+ EGroup e -> par (hsPretty e)+ ELambda (Symbol x) body ->+ unwords ["\\", x, "->", hsPretty body]+ EApp fexpr argexpr ->+ hsPretty fexpr ++ " " ++ hsPretty argexpr+ ECall fexpr argExprs -> + hsPretty fexpr ++ " " ++ hsPrettyList "" " " "" argExprs+ EOp op left right -> + unwords [hsPretty left, hsPretty op, hsPretty right]++-- | The Haskell operators.+-- Now what about the associativity of (:)?+-- It really doesn't even make sense to ask if (:) is+-- associative in the usual sense, +-- since (x1 : x2) : xs == x1 : (x2 : xs)+-- is not only untrue, but the left-hand side is+-- a type error, except maybe in some very special cases+-- (and then the right-hand side would probably be a type error).+-- Is (:) what is called a "right-associative" operator?+-- And do I need to expand my Operator type to+-- include this? And then what about (-) and (/)???+-- Does this affect their relationship with (+) and (-)?++operatorTable :: M.Map String Operator+operatorTable = + M.fromList (map (\ op -> (opName op, op)) + [ Operator "*" 7 True GroupLtoR -- times+ , Operator "+" 6 True GroupLtoR -- plus+ , Operator "-" 6 False GroupLtoR -- minus+ , Operator ":" 5 False GroupRtoL -- cons+ , Operator "==" 4 False GroupNone -- eq+ , Operator "/=" 4 False GroupNone -- ne+ , Operator ">" 4 False GroupNone -- gt+ , Operator ">=" 4 False GroupNone -- ge+ , Operator "<" 4 False GroupNone -- lt+ , Operator "<=" 4 False GroupNone -- le+ ])+
+ Language/Sifflet/Export/Python.hs view
@@ -0,0 +1,198 @@+-- | Abstract syntax tree and pretty-printing for Python.+-- Works for Python 2 and 3.+-- A lot of the data structures are inspired by the language-python package;+-- I have chosen not to have language-python as a dependency of sifflet-lib,+-- however, because it would be overkill and still allows to little control+-- over pretty-printing of Python expressionsw.++module Language.Sifflet.Export.Python+ (PyPretty(..)+ , PModule(..)+ , PStatement(..)+ , alterParens+ , ret+ , condS+ , var+ , ident+ , char+ , fun+ , operatorTable+ )++where++import Data.List (intercalate)+import qualified Data.Map as M++import Language.Sifflet.Expr+import Text.Sifflet.Pretty++class PyPretty a where++ pyPretty :: a -> String++ pyPrettyList :: String -> String -> String -> [a] -> String+ pyPrettyList pre tween post xs =+ pre ++ intercalate tween (map pyPretty xs) ++ post++pyPrettyParens :: (PyPretty a) => [a] -> String+pyPrettyParens = pyPrettyList "(" ", " ")"++instance PyPretty Symbol where+ pyPretty = pretty++instance PyPretty Operator where+ pyPretty = pretty++-- | Python module -- essentially a list of statements;+-- should it also have a name?+newtype PModule = PModule [PStatement]+ deriving (Eq, Show)++instance PyPretty PModule where+ pyPretty (PModule ss) = sepLines2 (map pyPretty ss)++-- | Python statement+data PStatement = PReturn Expr+ | PImport String -- ^ import statement+ | PCondS Expr + PStatement + PStatement -- ^ if condition action alt-action+ | PFun Symbol + [Symbol]+ PStatement -- ^ function name, formal parameters, body+ deriving (Eq, Show)++instance PyPretty PStatement where+ pyPretty s =+ case s of+ PReturn e -> "return " ++ pyPretty e+ PImport modName -> "import " ++ modName+ PCondS c a b ->+ sepLines ["if " ++ pyPretty c ++ ":",+ indentLine 4 (pyPretty a),+ "else:",+ indentLine 4 (pyPretty b)]+ PFun fid params body ->+ sepLines ["def " ++ pyPretty fid ++ + pyPrettyParens params ++ ":",+ indentLine 4 (pyPretty body)]++-- | Expr as an instance of PyPretty.+-- This instance is only for Exprs as Python exprs,+-- for export to Python! It will conflict with the+-- one in ToHaskell.hs (or Haskell.hs).+--+-- The EOp case needs work to deal with precedences+-- and avoid unnecessary parens.+-- Note that this instance declaration is for *Python* Exprs.+-- Haskell Exprs of course should not be pretty-printed+-- the same way!+instance PyPretty Expr where+ pyPretty pexpr =+ case pexpr of+ EUndefined -> "undefined"+ EChar _ -> error ("Python pyPretty of Expr: " +++ "EChar should have been converted to " +++ "EString")+ EList _ -> error ("Python pyPretty of Expr: " +++ "EList should have been converted to " +++ "ECall li ...")+ EIf c a b -> + unwords [pyPretty a, "if", pyPretty c, "else", pyPretty b]+ EGroup e -> pyPrettyParens [e]+ ESymbol vid -> pyPretty vid+ ENumber n -> show n+ EBool b -> show b+ EString s -> show s+ ELambda (Symbol x) body -> + unwords ["lambda", show x, ":", pyPretty body]+ EApp fexpr argExpr ->+ concat [pyPretty fexpr, pyPrettyParens [argExpr]]+ ECall fexpr argExprs -> + concat [pyPretty fexpr, pyPrettyParens argExprs]+ EOp op left right -> + unwords [pyPretty left, pyPretty op, pyPretty right]++alterParens :: (Expr -> Expr) -> PStatement -> PStatement+alterParens t s =+ case s of+ PReturn e -> PReturn (t e)+ PCondS c a b -> PCondS (t c) (alterParens t a) (alterParens t b)+ PFun fid params b -> PFun fid params (alterParens t b)+ _ -> s+++-- | Python return statement+ret :: Expr -> PStatement+ret pexpr = PReturn pexpr++-- | Python if STATEMENT++-- This is the if STATEMENT:+-- if c:+-- a+-- else:+-- b+--+-- But do I need this at all?++condS :: Expr -> Expr -> Expr -> PStatement+condS c a b = PCondS c (ret a) (ret b)++-- PExpr smart constructors++-- | Python variable+var :: String -> Expr+var name = ESymbol (Symbol name)++-- | Python identifier+ident :: String -> Symbol+ident s = Symbol s++-- | Python character expression = string expression with one character+char :: Char -> Expr+char c = EString [c]++-- | Python function formal parameter+param :: String -> Symbol+param name = Symbol name++-- | Defines function definition+fun :: String -> [String] -> Expr -> PStatement+fun fname paramNames bodyExpr = + PFun (ident fname) (map param paramNames) (ret bodyExpr)++-- | Binary operators+-- Precedence levels are rather *informally* described in+-- The Python Language Reference,+-- http://docs.python.org/reference/.+-- I am adopting the infixr levels from Haskell,+-- which seem to be consistent with Python,+-- at least for the operators that Sifflet uses.+--+-- | Operator information+-- Arithmetic operators: +-- + and - have lower precedence than *, /, //, %+-- | Comparison operators have precedence lower than any arithmetic+-- operator. Here, I've specified associative = False,+-- because association doesn't even make sense (well, it does in Python+-- but not in other languages);+-- (a == b) == c is in general not well typed.++operatorTable :: M.Map String Operator+operatorTable = + M.fromList (map (\ op -> (opName op, op)) + [ (Operator "*" 7 True GroupLtoR) -- times+ , (Operator "//" 7 False GroupLtoR) -- int div+ , (Operator "/" 7 False GroupLtoR) -- float div+ , (Operator "%" 7 False GroupLtoR) -- mod+ , (Operator "+" 6 True GroupLtoR) -- plus+ , (Operator "-" 6 False GroupLtoR) -- minus+ , (Operator "==" 4 False GroupNone) -- eq+ , (Operator "!=" 4 False GroupNone) -- ne+ , (Operator ">" 4 False GroupNone) -- gt+ , (Operator ">=" 4 False GroupNone) -- ge+ , (Operator "<" 4 False GroupNone) -- lt+ , (Operator "<=" 4 False GroupNone) -- le+ ])
+ Language/Sifflet/Export/ToHaskell.hs view
@@ -0,0 +1,136 @@+-- | Exports Sifflet to Haskell+-- Requires haskell-src package.++module Language.Sifflet.Export.ToHaskell+ (+ HaskellOptions(..)+ , defaultHaskellOptions+ , exportHaskell+ , functionsToHsModule+ , functionToHsDecl+ , exprToHsExpr+ )+where++import Data.Char (toUpper)+import qualified Data.Map as M+import System.FilePath (dropExtension, takeFileName)++import Language.Sifflet.Export.Exporter+import Language.Sifflet.Export.Haskell+import Language.Sifflet.Expr+import Language.Sifflet.Util+++-- Main types and functions++-- | User configurable options for export to Haskell.+-- Currently these options are unused.+-- The line width options should probably go somewhere else,+-- maybe as PrettyOptions.+data HaskellOptions = + HaskellOptions {optionsSoftMaxLineWidth :: Int+ , optionsHardMaxLineWidth :: Int+ }+ deriving (Eq, Show)++-- | The default options for export to Haskell.+defaultHaskellOptions :: HaskellOptions+defaultHaskellOptions = HaskellOptions {optionsSoftMaxLineWidth = 72,+ optionsHardMaxLineWidth = 80}++-- | Export functions with specified options to a file+exportHaskell :: HaskellOptions -> Exporter+exportHaskell _options functions path =+ let header = "-- File: " ++ path +++ "\n-- Generated by the Sifflet->Haskell exporter.\n\n"+ in writeFile path (header ++ + hsPretty (functionsToHsModule + (pathToModuleName path)+ functions))+++pathToModuleName :: FilePath -> String+pathToModuleName path =+ case dropExtension (takeFileName path) of+ [] -> "Test"+ c : cs -> toUpper c : cs++-- ------------------------------------------------------------------------++-- | Converting Sifflet to Haskell syntax tree++-- | Create a module from a module name and Functions.+functionsToHsModule :: String -> Functions -> Module+functionsToHsModule modname (Functions fs) =+ Module {moduleName = modname+ , moduleExports = Nothing+ , moduleImports = ImportDecl ["Data.Number.Sifflet"]+ , moduleDecls = map functionToHsDecl fs+ }++-- | Create a declaration from a Function.+-- Needs work: infer and declare the type of the function.+-- Minimally parenthesized.+functionToHsDecl :: Function -> Decl+functionToHsDecl (Function mname _atypes _rtype impl) =+ case (mname, impl) of+ (Nothing, _) -> error "functionToHsDecl: function has no name"+ (_, Primitive _) -> error "functionToHsDecl: function is primitive"+ (Just fname, Compound args body) ->+ Decl {declIdent = fname+ , declType = Nothing -- to be improved later+ , declParams = args+ , declExpr = (simplifyExpr haskellRules)+ (exprToHsExpr body)}++haskellRules :: [Expr -> Expr]+haskellRules = commonRulesForSimplifyingExprs ++ + [ruleIfRight, ruleRightToLeft]++-- | Converts a Sifflet Expr to a fully parenthesized Haskell Expr+exprToHsExpr :: Expr -> Expr+exprToHsExpr expr =+ case expr of+ EUndefined -> ESymbol (Symbol "undefined")+ ESymbol _ -> expr+ EBool _ -> expr+ EChar _ -> expr+ ENumber _ -> expr+ EString _ -> expr++ EIf c a b -> EIf (exprToHsExpr c) (exprToHsExpr a) (exprToHsExpr b)+ EList es -> EList (map exprToHsExpr es)+ ELambda x body -> ELambda x (EGroup body)+ ECall (Symbol fname) args -> + case nameToHaskell fname of+ Left op -> -- operator+ case args of+ [left, right] -> + EOp op (EGroup (exprToHsExpr left))+ (EGroup (exprToHsExpr right))+ _ -> error + "exprToHsExpr: operation does not have 2 operands"+ Right funcName -> -- function+ ECall (Symbol funcName) (map (EGroup . exprToHsExpr) args)+ _ -> errcats ["exprToHsExpr: extended expr:", show expr]++-- | Map Sifflet names to Haskell names.+-- Returns a Left Operator for Haskell operators,+-- which always have the same name as their corresponding Sifflet +-- functions, or a Right String for Haskell function and variable names.+nameToHaskell :: String -> Either Operator String+nameToHaskell name =+ case M.lookup name operatorTable of+ Just op -> Left op+ Nothing ->+ -- Most names would have the same names in Haskell,+ -- but there are a few special cases.+ Right (case name of+ "zero?" -> "eqZero"+ "positive?" -> "gtZero"+ "negative?" -> "ltZero"+ "add1" -> "succ"+ "sub1" -> "pred"+ _ -> name)+
+ Language/Sifflet/Export/ToPython.hs view
@@ -0,0 +1,171 @@+-- | Sifflet to abstract syntax tree for Python.+-- Use Python module's pyPretty to pretty-print the result.++module Language.Sifflet.Export.ToPython+ (+ PythonOptions(..)+ , defaultPythonOptions+ , exprToPExpr+ , nameToPython+ , fixIdentifierChars+ , functionToPyDef+ , defToPy+ , functionsToPyModule+ , functionsToPrettyPy+ , exportPython+ )++where++import Data.Char (isAlpha, isDigit, ord)+import Control.Monad (unless)+import Data.Map ((!))+import System.Directory (copyFile, doesFileExist)+import System.FilePath (replaceFileName)++import Language.Sifflet.Export.Exporter+import Language.Sifflet.Export.Python+import Language.Sifflet.Expr+-- import Text.Sifflet.Pretty+import Language.Sifflet.Util++import Paths_sifflet_lib -- Cabal-generated paths module++-- | Options for Python export.+-- Should probably include choices like Python2 or Python3;+-- if statement or if expression. Right now, just a placeholder.++data PythonOptions = PythonOptions+ deriving (Eq, Show)+++defaultPythonOptions :: PythonOptions+defaultPythonOptions = PythonOptions++-- A lot of these are "pass-through" -- simplify: ***+exprToPExpr :: Expr -> Expr+exprToPExpr expr =+ case expr of+ EUndefined -> EUndefined -- was var "undefined"+ ESymbol _ -> expr++ EBool _ -> expr+ EChar c -> EString [c] -- Python does not distinguish char from str+ ENumber _ -> expr+ EString _ -> expr++ EIf cond action altAction ->+ -- EIf here represents a Python if *expression*:+ -- value if test else altvalue+ -- not the familiar if *statement*!+ EIf (exprToPExpr cond) + (exprToPExpr action)+ (exprToPExpr altAction)+ EList exprs -> + ECall (Symbol "li") (map exprToPExpr exprs)+ ELambda x body -> ELambda x (EGroup body)+ ECall (Symbol fname) args -> + -- Python distinguishes between functions and operators+ case nameToPython fname of+ Left op ->+ case args of+ [left, right] -> + EOp op (EGroup (exprToPExpr left))+ (EGroup (exprToPExpr right))+ _ -> error "exprToPExpr: operation does not have 2 operands"+ Right pname ->+ -- I don't think we need (a) for each arg a+ -- since they are separated by commas+ ECall (Symbol pname) (map exprToPExpr args)+ _ -> errcats ["exprToPExpr: extended expr:", show expr]++-- | Convert Sifflet name (of a function) to Python operator (Left)+-- or function name (Right)+nameToPython :: String -> Either Operator String+nameToPython name =+ let oper oname = Left $ operatorTable ! oname+ in case name of + "+" -> oper "+"+ "-" -> oper "-"+ "*" -> oper "*"+ "div" -> oper "//"+ "mod" -> oper "%"+ "/" -> oper "/" -- invalid for integers in Python 2!+ "==" -> oper "=="+ "/=" -> oper "!="+ ">" -> oper ">"+ ">=" -> oper ">="+ "<" -> oper "<"+ "<=" -> oper "<="+ "add1" -> Right "add1"+ "sub1" -> Right "sub1"+ "zero?" -> Right "eqZero"+ "positive?" -> Right "gtZero"+ "negative?" -> Right "ltZero"+ "null" -> Right "null"+ "head" -> Right "head"+ "tail" -> Right "tail"+ ":" -> Right "cons"+ _ -> Right (fixIdentifierChars name)++-- | Remove characters that are not valid in a Python identifier,+-- and in some cases, insert other characters to show what's missing++fixIdentifierChars :: String -> String+fixIdentifierChars =+ let fix s =+ case s of+ [] -> []+ c:cs ->+ if isAlpha c || isDigit c || c == '_'+ then c : fix cs+ else case c of+ '?' -> "_QUESTION_" ++ fix cs+ -- other cases can be inserted here+ _ -> "_CHR" ++ show (ord c) ++ "_" ++ fix cs+++ in fix++-- | Create a Python def statement from a Sifflet function.+-- Minimally parenthesized.+functionToPyDef :: Function -> PStatement+functionToPyDef = defToPy . functionToDef++defToPy :: FunctionDefTuple -> PStatement+defToPy (fname, paramNames, _, _, body) =+ fun (fixIdentifierChars fname) + paramNames + ((simplifyExpr pyRules) (exprToPExpr body))++pyRules :: [Expr -> Expr]+pyRules = commonRulesForSimplifyingExprs++functionsToPyModule :: Functions -> PModule+functionsToPyModule (Functions fs) = PModule (map functionToPyDef fs)++functionsToPrettyPy :: Functions -> String+functionsToPrettyPy = pyPretty . functionsToPyModule++exportPython :: PythonOptions -> Exporter+exportPython _options funcs path = + let header = "# File: " ++ path +++ "\n# Generated by the Sifflet->Python exporter.\n\n" +++ "from sifflet import *\n\n"+ libDest = replaceFileName path "sifflet.py"+ in do+ {+ libDestExists <- doesFileExist libDest+ ; unless libDestExists + (do+ -- copy sifflet.py to the same directory as path+ {+ libSrc <- pythonLibSiffletPath + ; copyFile libSrc libDest+ }+ )+ ; writeFile path (header ++ (functionsToPrettyPy funcs))+ }++pythonLibSiffletPath :: IO FilePath+pythonLibSiffletPath = getDataFileName "sifflet.py"
+ Language/Sifflet/Export/ToScheme.hs view
@@ -0,0 +1,321 @@+module Language.Sifflet.Export.ToScheme+ (SExpr(..)+ , Atom(..)+ , Indent+ , Exporter+ , SchemeOptions(..)+ , defaultSchemeOptions+ , exprToSExpr+ , functionNameToSchemeName+ , valueToSExpr+ , exprToSchemeRepr+ , exprToSchemePretty+ , exprToScheme+ , inl+ , sepLines2+ , functionsToSExprs+ , functionsToPrettyScheme+ , defToSExpr+ , exportScheme+ )++where++import Paths_sifflet_lib -- generated by Cabal++import Data.Number.Sifflet+import Language.Sifflet.Export.Exporter+import Language.Sifflet.Expr+import Text.Sifflet.Repr+import Text.Sifflet.Pretty+import Language.Sifflet.Util++-- Scheme S-exprs+-- --------------++-- Names beginning with S are generally Scheme things,+-- and of course, SExpr also stands for Symbolic Expression.++data SExpr = SAtom Atom | SList [SExpr]+ deriving (Eq, Show)++data Atom = SFloat Double+ | SInt Integer+ | SSymbol String+ | SString String+ | SChar Char+ | SBool Bool+ | SFunction Function+ deriving (Eq, Show)++type Indent = Int++data SchemeOptions = + SchemeOptions { defineWithLambda :: Bool + -- ^ use explicit lambda in function definitions,+ -- (define f (lambda (a b) ...)+ }+ deriving (Eq, Show)++defaultSchemeOptions :: SchemeOptions+defaultSchemeOptions = SchemeOptions {defineWithLambda = False}++-- | An SExpr is "flattish" if it is an atom+-- or is a list containing only atoms, +-- where the empty list () is an atom.++flattish :: [SExpr] -> Bool+flattish sexprs =+ case sexprs of+ [] -> True+ x:xs -> atom x && flattish xs++atom :: SExpr -> Bool+atom sexpr =+ case sexpr of+ SAtom _ -> True+ SList [] -> True+ _ -> False++-- Converting Sifflet Exprs to SExprs++exprToSExpr :: Expr -> SExpr+exprToSExpr expr =+ case expr of+ EUndefined -> + SAtom (SSymbol "*sifflet-undefined*")+ ESymbol (Symbol str) -> + SAtom (SSymbol (functionNameToSchemeName str))++ EBool b -> valueToSExpr (VBool b)+ EChar c -> valueToSExpr (VChar c)+ ENumber n -> valueToSExpr (VNumber n)+ EString s -> valueToSExpr (VString s)++ EIf cond action altAction ->+ SList [SAtom (SSymbol "if"), exprToSExpr cond,+ exprToSExpr action, exprToSExpr altAction]+ EList exprs -> + -- This case is not likely to be used,+ -- but if it is, the exprs might need to be evaluated,+ -- so we have to use list instead of quote+ SList (SAtom (SSymbol "list") : (map exprToSExpr exprs))+ ELambda (Symbol x) body ->+ SList [SAtom (SSymbol "lambda"), SList [SAtom (SSymbol x)], + exprToSExpr body] + ECall fsym args -> + SList (exprToSExpr (ESymbol fsym) : map exprToSExpr args) + _ -> errcats ["exprToSExpr: extended expr:", show expr]++-- Convert Sifflet function names to corresponding Scheme function names.+-- There are a few special cases; otherwise, the names are the same.+-- In particular, all of these have the same names in Sifflet+-- as in standard (R5RS) Scheme:+-- +, -, *, +-- All of these are defined in the library sifflet.scm, +-- with the prefix "sifflet-" (e.g., sifflet-div):+-- div, add1, sub1, /, not-equal?+-- Notes: 1+ and 1- are commonly found in Scheme implementations,+-- but not standard.++functionNameToSchemeName :: String -> String+functionNameToSchemeName name =+ case name of+ "mod" -> "modulo"+ "add1" -> "sifflet-add1"+ "sub1" -> "sifflet-sub1"+ "==" -> "equal?"+ "/=" -> "sifflet-not-equal?"+ "null" -> "null?"+ "head" -> "car"+ "tail" -> "cdr"+ ":" -> "cons"+ _ -> name++-- Converting Sifflet Values to SExprs++valueToSExpr :: Value -> SExpr+valueToSExpr value =+ case value of+ VList vs -> + SList [SAtom (SSymbol "quote"), SList (map valueToSExpr vs)]+ _ ->+ SAtom (case value of+ VBool b -> SBool b+ VChar c -> SChar c+ VNumber (Exact i) -> SInt i+ VNumber (Inexact x) -> SFloat x+ VString s -> SString s+ VFun f -> SFunction f+ VList _ -> + error ("valueToSExpr: Impossible! " +++ "We can't have VList here!")+ )++-- Converting Exprs to Strings of Scheme code++exprToSchemeRepr :: Expr -> String+exprToSchemeRepr = repr . exprToSExpr++exprToSchemePretty :: Expr -> String+exprToSchemePretty = pretty . exprToSExpr++exprToScheme :: Expr -> String+exprToScheme = exprToSchemePretty++-- Converting SExprs to Strings of Scheme code++instance Repr SExpr where+ repr sexpr =+ case sexpr of+ SAtom satom ->+ case satom of+ SFloat x -> show x+ SInt i -> show i+ SSymbol name -> name -- without ""+ SString str -> show str -- with ""+ SChar char -> show char+ SBool False -> "#f"+ SBool True -> "#t"+ -- SFunction: is this case really needed?+ -- Isn't the conversion to Scheme function names+ -- done in exprToSExpr? + SFunction (Function mname _ _ _) ->+ case mname of+ Nothing -> + error "SExpr/repr: cannot repr unnamed function"+ Just name -> + functionNameToSchemeName name+ SList exprs ->+ "(" ++ unwords (map repr exprs) ++ ")"++instance Pretty SExpr where+ pretty = prettyLoop 0++prettyLoop :: Indent -> SExpr -> String+prettyLoop ind sexpr =+ case sexpr of+ SAtom _ -> repr sexpr+ SList xs ->+ if flattish xs+ then repr sexpr+ else+ case xs of+ [] -> error "prettyLoop: non-flattish xs cannot be []."+ [SAtom (SSymbol "if"), _, _, _] ->+ displayList2 ind (ind + 4) xs+ [SAtom (SSymbol "define"), _, _] ->+ displayList2 ind (ind + 4) xs+ [SAtom (SSymbol "lambda"), _, _] ->+ displayList2 ind (ind + 4) xs+ SAtom (SSymbol name) : args ->+ -- If it starts as a symbol, it's probably a function call,+ -- so put function name and first argument on one line,+ -- and indent everything following to first argument.+ case args of+ [] ->+ -- no argument+ displayList1 ind (ind + length name + 2) xs+ _ ->+ -- at least one argument, so at least two elements+ -- in the list+ displayList2 ind (ind + length name + 2) xs+ _ -> displayList1 ind (ind + 1) xs+++-- | Newline and indent+inl :: Int -> String+inl ind = "\n" ++ replicate ind ' '++-- displayList1 "shows" the first list element on the first line+-- and then the rest on succeeding lines, so it must have at+-- least one element+displayList1 :: Indent -> Indent -> [SExpr] -> String+displayList1 ind ind' xs =+ case xs of+ [] -> error "displayList1: empty list"+ x:xs' -> "(" ++ + prettyLoop ind x ++ + displayTail ind' xs'++-- Like displayList1 but "shows" the first *two* list elements+-- on the first line.+displayList2 :: Indent -> Indent -> [SExpr] -> String+displayList2 ind ind' xs =+ case xs of+ x0:x1:xs' -> "(" ++ prettyLoop ind x0 +++ " " ++ prettyLoop ind' x1 +++ displayTail ind' xs'+ _ -> error "displayList2: list is too short"++-- displayTail "shows" the tail of an SExpr which is a list,+-- it assumes the initial "(" and first element have already+-- been "shown"+displayTail :: Indent -> [SExpr] -> String+displayTail ind xs =+ case xs of+ [] -> ")"+ -- to prevent final ")" from being on a line by itself+ x:[] -> inl ind ++ prettyLoop ind x ++ ")"+ x:xs' -> inl ind ++ prettyLoop ind x ++ displayTail ind xs'++++-- Converting Sifflet definitions to Scheme definitions.++-- | Convert Sifflet Functions to Scheme SExprs+functionsToSExprs :: SchemeOptions -> Functions -> [SExpr]+functionsToSExprs options (Functions fs) =+ map (defToSExpr options . functionToDef) fs++-- | Convert Sifflet Functions to pretty Scheme+functionsToPrettyScheme :: SchemeOptions -> Functions -> String+functionsToPrettyScheme options = + sepLines2 . map pretty . functionsToSExprs options++-- | Convert a FunctionDefTuple to a Scheme SExpr.+-- Use the form (define (name . args) body)+-- except when there are zero arguments, which becomes a+-- Scheme constant rather than a function,+-- use (define name expr).++defToSExpr :: SchemeOptions -> FunctionDefTuple -> SExpr+defToSExpr options (name, args, _atypes, _rtype, body) =+ let asym = SAtom . SSymbol+ sdefine = asym "define"+ sname = asym name+ sbody = exprToSExpr body+ in case args of+ [] -> SList [sdefine, sname, sbody]+ _:_ ->+ let argAtoms = map asym args+ in if defineWithLambda options+ then let slambda = asym "lambda"+ sargs = SList argAtoms+ slambdaArgsBody = SList [slambda, sargs, sbody]+ in SList [sdefine, sname, slambdaArgsBody]+ else let snameArgs = SList (sname : argAtoms)+ in SList [sdefine, snameArgs, sbody]++-- | Export functions to a Scheme file.++-- This, too, could use an extra "explicit lambda" argument,+-- like defToSExpr.++exportScheme :: SchemeOptions -> Exporter+exportScheme options functions path = do+ {+ let header = ";;; File: " ++ path +++ "\n;;; Generated by the Sifflet->Scheme exporter."+ ; lib <- schemeLibSiffletPath >>= readFile+ ; writeFile path + (sepLines2 [header,+ functionsToPrettyScheme options functions,+ lib])+ }++-- | The path to the "sifflet.scm" file.+schemeLibSiffletPath :: IO FilePath++-- getDataFileName is provided by Cabal+schemeLibSiffletPath = getDataFileName "sifflet.scm"
+ Language/Sifflet/Expr.hs view
@@ -0,0 +1,1021 @@+module Language.Sifflet.Expr+ (+ ArgSpec(..)+ , aspecsLookup+ , EvalResult, EvalRes(EvalOk, EvalError, EvalUntried)+ , exprToValue, valueToLiteral, valueToLiteral'+ , Symbol(..)+ , OStr, OBool, OChar+ , Expr(..), eSymbol, eSym, eInt, eString, eChar, eFloat+ , toLambdaExpr+ , callToApp, mapply+ , appToCall, mcall+ , exprIsAtomic+ , exprIsCompound+ , eBool, eFalse, eTrue, eIf+ , eList, eCall+ , exprIsLiteral+ , exprSymbols, exprVarNames+ , Operator(..)+ , Precedence+ , OperatorGrouping(..)+ , Value(..), valueFunction+ , Functions(..)+ , Function(..), functionName, functionNArgs+ , functionArgSpecs+ , functionArgTypes, functionResultType, functionArgResultTypes+ , functionType+ , functionArgNames, functionBody, functionImplementation+ , FunctionDefTuple, functionToDef, functionFromDef+ , FunctionImpl(..)+ , TypeVarName, TypeConsName, Type(..)+ , typeBool, typeChar, typeNum, typeString+ , typeList, typeFunction+ , Env, emptyEnv, makeEnv, extendEnv, envInsertL, envPop+ , envIns, envSet, envGet+ , envGetFunction, envLookup, envLookupFunction+ , envSymbols, envFunctionSymbols, envFunctions+ , eval, evalWithLimit, stackSize, apply+ , newUndefinedFunction+ , ePlus, eTimes, eMinus, eDiv, eMod+ , eAdd1, eSub1+ , eEq, eNe, eGt, eGe, eLt, eLe+ , eZerop, ePositivep, eNegativep+ , baseEnv)++where++-- drop this after debugging:+-- import System.IO.Unsafe(unsafePerformIO)++import Data.Map as Map hiding (filter, foldl, map, null)+import Data.List as List++import Data.Number.Sifflet++import Data.Sifflet.Tree as T++import Text.Sifflet.Pretty+import Text.Sifflet.Repr ()++import Language.Sifflet.Util++-- | Transform a numerical expression into its negation,+-- e.g., 5 --> (-5).+-- Fails if the expression is not an ENumber.++eNegate :: Expr -> SuccFail Expr+eNegate expr = + case expr of+ ENumber n -> Succ $ ENumber (negate n)+ _ -> Fail $ "eNegate: cannot handle" ++ show expr++-- Symbols have names, and may or may not have values,+-- but the value is stored in an environment, not in the symbol itself.++newtype Symbol = Symbol String+ deriving (Eq, Read, Show)++instance Pretty Symbol where+ pretty (Symbol s) = s++instance Repr Symbol where repr (Symbol s) = s++-- The Haskell representations of V's primitive data types.+-- Data.Number.Sifflet.Number represents exact and inexact numbers.+type OStr = String+type OBool = Bool+type OChar = Char++-- | A more highly "parsed" type of expression+--+-- Function calls have two kinds:+-- 1. ECall:+-- restricted to the case where the function expression+-- is just a symbol, since otherwise it will be hard to visualize.+-- 2. EApp: allows any expression to be the function,+-- but is applied to only one argument.+-- For now, the type checker will convert ECall expressions to+-- EApp expressions. Ultimately, the two variants ought to be+-- unified.+--+-- The constructors EOp and EGroup are not used in Sifflet itself,+-- but they are needed for export to Python, Haskell, and similar languages;+-- they allow a distinction between operators and functions, and+-- wrapping expressions in parentheses.+-- EGroup e represents parentheses used for grouping: (e);+-- it is not used for other cases of parentheses, e.g.,+-- around the argument list in a function call.] ++data Expr = EUndefined+ | ESymbol Symbol + | EBool Bool+ | EChar Char+ | ENumber Number+ | EString String+ | EIf Expr Expr Expr -- ^ if test branch1 branch2+ | EList [Expr]+ | ELambda Symbol Expr + | EApp Expr Expr -- ^ apply function to argument+ | ECall Symbol [Expr] -- ^ function name, arglist+ | EOp Operator Expr Expr -- ^ binary operator application+ | EGroup Expr -- ^ grouping parentheses+ deriving (Eq, Show)++instance Repr Expr where+ repr e =+ case e of+ EUndefined -> "*undefined*"+ ESymbol s -> repr s+ EBool b -> repr b+ EChar c -> repr c+ ENumber n -> repr n+ EString s -> show s+ EIf t a b -> par "if" (map repr [t, a, b])+ EList xs -> if exprIsLiteral e+ then reprList "[" ", " "]" xs+ else error ("Expr.repr: EList expression is non-literal: " + ++ show e)+ -- check *** was: par "EList" (map repr items)+ ELambda x body -> par "lambda" [repr x , "->", repr body]+ EApp f arg -> par (repr f) [repr arg]+ ECall (Symbol fname) args -> par fname (map repr args)+ EOp op left right -> unwords [repr left, opName op, repr right]+ EGroup e' -> "(" ++ repr e' ++ ")"++-- | Try to convert the arguments and body of a function to a lambda expression.+-- Fails if there are no arguments, since a lambda expression requires one.+-- If there are multiple arguments, then we get a nested lambda expression.++toLambdaExpr :: [String] -> Expr -> SuccFail Expr+toLambdaExpr args body =+ case args of+ [] -> Fail "toLambdaExpr: no arguments; at least one needed"+ a:[] -> Succ $ ELambda (Symbol a) body+ a:as -> do+ {+ expr <- toLambdaExpr as body+ ; Succ $ ELambda (Symbol a) expr+ }+ +-- | Convert an ECall expression to an EApp expression++callToApp :: Expr -> Expr+callToApp expr =+ case expr of+ ECall fsym args -> mapply (ESymbol fsym) args+ _ -> error "callToApp: requires ECall expression"++-- | Helper for callToApp, but may have other uses.+-- Creates an EApp expression representing a function call+-- with possibly many arguments.+mapply :: Expr -> [Expr] -> Expr+mapply fexpr args =+ case args of+ [] -> error "mapply: no argument, cannot happen"+ arg:[] -> EApp fexpr arg+ arg:args' -> mapply (EApp fexpr arg) args'+++-- | Convert an EApp expression to an ECall expression++appToCall :: Expr -> Expr+appToCall expr =+ case expr of+ EApp f arg -> mcall f [arg]+ _ -> error "appToCall: requires an EApp expression"+++-- | Helper for appToCall, but may have other uses.+-- Creates an ECall expression.++mcall :: Expr -> [Expr] -> Expr+mcall f args =+ case f of+ ESymbol fsym -> ECall fsym args+ EApp g arg -> mcall g (arg:args)+ _ ->+ error "mcall: function must be represented as a symbol or an EApp"++-- | An Expr is "extended" if it uses the extended constructors+-- EOp or EGroup. In pure Sifflet, no extended Exprs are used.++exprIsExtended :: Expr -> Bool+exprIsExtended e =+ case e of+ EOp _ _ _ -> True+ EGroup _ -> True+ EIf t a b -> exprIsExtended t ||+ exprIsExtended a ||+ exprIsExtended b+ EList xs -> any exprIsExtended xs+ ELambda _ body -> exprIsExtended body+ ECall (Symbol _) args -> any exprIsExtended args+ _ -> False++-- | Is an Expr a literal? A literal is a boolean, character, number, string,+-- or list of literals. We (should) only allow user input expressions+-- to be literal expressions.++exprIsLiteral :: Expr -> Bool+exprIsLiteral e =+ case e of + EBool _ -> True+ EChar _ -> True+ ENumber _ -> True+ EString _ -> True+ EList es -> all exprIsLiteral es+ -- Shouldn't we say that + -- EGroup e' *not* a literal, even if e' is a literal?+ -- But consider carefully the effect on exprIsAtomic and ()'s removal.+ EGroup _e' -> True -- or False, or exprIsLiteral e' ???+ _ -> False++-- | Is an expression atomic?+-- Atomic expressions do not need parentheses in any reasonable language,+-- because there is nothing to be grouped (symbols, literals)+-- or in the case of lists, they already have brackets+-- which separate them from their neighbors.+--+-- All lists are atomic, even if they are not literals,+-- because (for example) we can remove parentheses+-- from ([a + b, 7])++exprIsAtomic :: Expr -> Bool+exprIsAtomic e =+ case e of+ ESymbol _ -> True+ EList _ -> True+ _ -> exprIsLiteral e++-- | Compound = non-atomic+exprIsCompound :: Expr -> Bool+exprIsCompound = not . exprIsAtomic++eSymbol, eSym :: String -> Expr+eSymbol = ESymbol . Symbol+eSym = eSymbol++eInt :: Integer -> Expr+eInt = ENumber . Exact++eString :: OStr -> Expr+eString = EString++eChar :: OChar -> Expr+eChar = EChar++eFloat :: Double -> Expr+eFloat = ENumber . Inexact++eBool :: Bool -> Expr+eBool = EBool++eFalse, eTrue :: Expr+eFalse = eBool False+eTrue = eBool True++eIf :: Expr -> Expr -> Expr -> Expr+eIf = EIf++eList :: [Expr] -> Expr+eList = EList++-- | Example:+-- ePlus_2_3 = eCall "+" [eInt 2, eInt 3]+eCall :: String -> [Expr] -> Expr+eCall = ECall . Symbol++-- | An operator, such as * or ++-- An operator is associative, like +, if (a + b) + c == a + (b + c).+-- Its grouping is left to right if (a op b op c) means (a op b) op c;+-- right to left if (a op b op c) means a op (b op c).+-- Most operators group left to right.+data Operator = Operator {opName :: String+ , opPrec :: Precedence+ , opAssoc :: Bool -- ^ associative?+ , opGrouping :: OperatorGrouping+ }+ deriving (Eq, Show)++instance Pretty Operator where+ pretty = opName++-- | Operator priority, normally is > 0 or >= 0, +-- but does that really matter? I think not.+type Precedence = Int++-- | Operator grouping: left to right or right to left,+-- or perhaps not at all+data OperatorGrouping = GroupLtoR | GroupRtoL | GroupNone+ deriving (Eq, Show)++-- VALUES AND EVALUATION++data Value = VBool OBool+ | VChar OChar+ | VNumber Number+ | VString OStr+ | VFun Function+ | VList [Value] + deriving (Eq, Show)+ -- no Read for Function++instance Repr Value where+ repr (VBool b) = show b+ repr (VChar c) = show c+ repr (VNumber n) = show n+ repr (VString s) = show s+ repr (VFun f) = repr f+ repr (VList vs) = reprList "[" ", " "]" vs++valueFunction :: Value -> Function+valueFunction value =+ case value of+ VFun function -> function+ _ -> error "valueFunction: non-function value"++-- Evaluation results (or non-results)++type EvalResult = EvalRes Value++data EvalRes e = EvalOk e | EvalError String | EvalUntried+ deriving (Eq, Show)++instance Monad EvalRes where+ EvalOk value >>= f = f value+ EvalError e >>= _f = EvalError e+ EvalUntried >>= _f = EvalUntried+ return = EvalOk+ fail = EvalError++-- | The value of an expression in the base environment.++exprToValue :: Expr -> SuccFail Value+exprToValue expr = + case eval expr baseEnv of + EvalOk value -> Succ value+ EvalError msg -> Fail msg+ EvalUntried -> error "exprToValue: eval resulted in EvalUntried"++valueToLiteral :: Value -> SuccFail Expr+valueToLiteral v = + case v of+ VBool b -> Succ $ EBool b+ VChar c -> Succ $ EChar c+ VNumber n -> Succ $ ENumber n+ VString s -> Succ $ EString s+ -- VList [] -> Succ $ EList []+ -- VV Should this be fixed? VV+ -- VList _ -> Fail "cannot convert non-empty list to literal expression"+ VList vs -> mapM valueToLiteral vs >>= Succ . EList+ VFun _f -> Fail "cannot convert function to literal expression"++valueToLiteral' :: Value -> Expr+valueToLiteral' v = case valueToLiteral v of+ Fail msg -> error ("valueToLiteral: " ++ msg)+ Succ e -> e++-- | Convert a literal expression to the value it represents.+-- It is an error if the expression is non-literal.+-- See exprIsLiteral. +literalToValue :: Expr -> Value+literalToValue e =+ case e of+ EBool b -> VBool b+ EChar c -> VChar c+ ENumber n -> VNumber n+ EString s -> VString s+ EList es -> if exprIsLiteral e+ then VList (map literalToValue es)+ else errcats ["literalToValue: ",+ "non-literal list expression: ",+ show e]+ _ -> errcats ["literalToValue: non-literal or extended expression: " , + show e]++-- | Type variable name +type TypeVarName = String++-- | Type constructor name+type TypeConsName = String++-- | A Type is either a type variable or a constructed type+-- with a constructor and a list of type parameters++data Type = TypeVar TypeVarName -- named type variable+ | TypeCons TypeConsName [Type] -- constructed type+ deriving (Eq)++instance Show Type where+ show (TypeVar vname) = vname+ show (TypeCons ctor ts) =+ case ts of+ [] -> ctor+ (t:ts') ->+ case ctor of+ "List" -> "[" ++ show t ++ "]"+ "Function" -> "(" ++ show t ++ " -> " +++ show (head ts') ++ ")"+ _ -> "(" ++ ctor ++ (unwords (map show ts)) ++ ")"+++typeToArity :: Type -> Int+typeToArity atype =+ case atype of + TypeCons "Function" [_, next] -> 1 + typeToArity next+ _ -> 0++-- Primitive types++primType :: TypeConsName -> Type+primType tname = TypeCons tname []++typeBool, typeChar, typeNum, typeString :: Type+typeBool = primType "Bool"+typeChar = primType "Char"+typeNum = primType "Num"+typeString = primType "String"++-- Built-in compound types++typeList :: Type -> Type+typeList t = TypeCons "List" [t]++-- | The type of a function, from its argument types and result type,+-- where (a -> b) is represented as TypeCons "Function" [a, b].+-- Note that for n-ary functions, n > 2 implies nested function types: +-- (a -> b -> c) is represented as+-- TypeCons "Function" [a, TypeCons "Function" [b, c]], etc.++typeFunction :: [Type] -> Type -> Type+typeFunction atypes rtype =+ let func a b = TypeCons "Function" [a, b]+ in case atypes of+ [] -> error "typeFunction: empty argument type list"+ atype:[] -> func atype rtype+ atype:atypes' -> func atype (typeFunction atypes' rtype)++-- | A collection of functions, typically to be saved or exported+-- or read from a file++newtype Functions = Functions [Function]+ deriving (Eq, Show)++-- | A function may have a name and always has an implementation+data Function = Function (Maybe String) -- function name+ [Type] -- argument types+ Type -- result type+ FunctionImpl -- implementation+ deriving (Show)+++data ArgSpec = ArgSpec {argName :: String, -- argument name+ argInlets :: Int -- number of inputs+ }+ deriving (Eq, Show)++-- | Try to find the number of inlets for an argument+-- from a list of ArgSpec+aspecsLookup :: String -> [ArgSpec] -> Maybe Int+aspecsLookup name specs =+ case specs of + [] -> Nothing+ s:ss ->+ if argName s == name+ then Just (argInlets s)+ else aspecsLookup name ss++functionArgSpecs :: Function -> [ArgSpec]+functionArgSpecs f@(Function _ argTypes _ _) = + [ArgSpec {argName = n, argInlets = typeToArity t} |+ (n, t) <- zip (functionArgNames f) argTypes]++data FunctionImpl = Primitive ([Value] -> EvalResult) -- a Haskell function+ | Compound [String] Expr -- arguments, body++instance Show FunctionImpl where+ show (Primitive _) = "<primitive function>"+ show (Compound args body) = + concat ["Compound function, args = " ++ show args ++ + "; body = " ++ show body]++instance Read FunctionImpl where+ readsPrec _ _ = error "readsPrec not implemented for FunctionImpl"++instance Repr Function where+ repr f =+ let name = functionName f+ in case functionImplementation f of+ Primitive _ -> "<primfunc " ++ name ++ ">"+ Compound _args _body -> "<func " ++ name ++ ">"++newUndefinedFunction :: String -> [String] -> Function+newUndefinedFunction name argnames =+ let (atypes, rtype) = undefinedTypes argnames+ impl = Compound argnames EUndefined+ in Function (Just name) atypes rtype impl++undefinedTypes :: [String] -> ([Type], Type)+undefinedTypes argnames =+ let atypes = [TypeVar ('_' : name) | name <- argnames]+ rtype = TypeVar "_result"+ in (atypes, rtype)++functionName :: Function -> String+functionName (Function mname _ _ _) = + case mname of+ Just name -> name+ Nothing -> "(unnamed)"++functionNArgs :: Function -> Int+functionNArgs = length . functionArgTypes++functionArgTypes :: Function -> [Type]+functionArgTypes (Function _ argtypes _ _) = argtypes++functionResultType :: Function -> Type+functionResultType (Function _ _ rtype _) = rtype++-- | Type type of a function, a tuple of (arg types, result type)+functionArgResultTypes :: Function -> ([Type], Type) -- (args., result type)+functionArgResultTypes f = (functionArgTypes f, functionResultType f)++-- | The type of a function, +-- where (a -> b) is represented as TypeCons "Function" [a, b]+functionType :: Function -> Type+functionType (Function _ argTs resultT _) = typeFunction argTs resultT++functionImplementation :: Function -> FunctionImpl+functionImplementation (Function _ _ _ impl) = impl++functionArgNames :: Function -> [String]+functionArgNames f = case functionImplementation f of+ Primitive _ -> + ["dummy" | _t <- functionArgTypes f]+ Compound args _body -> args++type FunctionDefTuple = (String, [String], [Type], Type, Expr)++functionToDef :: Function -> FunctionDefTuple+functionToDef (Function mname argTypes resType impl) = + case impl of+ Primitive _ -> error "functionToDef: primitive function"+ Compound argNames body ->+ case mname of+ Nothing -> error "functionToDef: unnamed function"+ Just name -> (name, argNames, argTypes, resType, body)++functionFromDef :: FunctionDefTuple -> Function+functionFromDef (name, argNames, argTypes, resType, body) =+ Function (Just name) argTypes resType (Compound argNames body)++functionBody :: Function -> Expr+functionBody f = case functionImplementation f of+ Primitive _fp -> + errcats ["functionBody:",+ "no body available for primitive function"]+ Compound _args body -> body++-- | We need to be able to say functions are equal (or not) in order+-- to tell if environments are equal or not, in order to know whether+-- there are unsaved changes. This is tricky since the primitive+-- function implementations do not instantiate Eq, so if it's+-- primitive == primitive? we go by the names alone (there's nothing+-- else to go by). Otherwise all the parts must be equal.+instance Eq Function where+ f1 == f2 =+ let Function mname1 atypes1 anames1 impl1 = f1+ Function mname2 atypes2 anames2 impl2 = f2+ in case (impl1, impl2) of+ (Primitive _, Primitive _) -> mname1 == mname2+ (Compound args1 body1, Compound args2 body2 ) -> + mname1 == mname2 &&+ atypes1 == atypes2 &&+ anames1 == anames2 &&+ args1 == args2 &&+ body1 == body2+ _ -> False++-- | An Environment contains variable bindings and may be linked to +-- a next environment+--+-- Perhaps it may also be used to generate Vp type variables (with int id's)++type EnvFrame = Map String Value+type Env = [EnvFrame] -- should be NON-empty+type Binding = (String, Value)++emptyEnv :: Env+emptyEnv = makeEnv [] []++makeEnv :: [String] -> [Value] -> Env +makeEnv names values = extendEnv names values []++extendEnv :: [String] -> [Value] -> Env -> Env+extendEnv names values env = fromList (zip names values) : env++-- | Insert names and values from lists into an environment+envInsertL :: Env -> [String] -> [Value] -> Env+envInsertL env names values =+ case env of+ [] -> error "envInsertL: empty list"+ f : fs ->+ let ins :: EnvFrame -> Binding -> EnvFrame+ ins frame (name, value) = Map.insert name value frame+ in foldl ins f (zip names values) : fs++envIns :: Env -> String -> Value -> Env+envIns env name value =+ case env of+ [] -> error "envIns: empty list"+ f : fs -> Map.insert name value f : fs++envSet :: Env -> String -> Value -> Env+envSet env name value = + -- If name is bound in some map in the environment, update the binding+ -- in that map; otherwise insert it into the "front" map+ let loop :: Env -> Maybe Env+ loop env1 =+ case env1 of+ [] -> Nothing+ f:fs ->+ case Map.lookup name f of+ Just _ -> Just (envIns env1 name value)+ Nothing ->+ do -- in the Maybe monad:+ fs' <- loop fs+ return (f:fs')++ in case loop env of+ Just result -> result+ Nothing -> envIns env name value++-- | Get the value of a variable from an environment+envGet :: Env -> String -> Value+envGet env name = case envLookup env name of+ Just value -> value+ Nothing -> errcats ["envGet: unbound variable:", name]++envGetFunction :: Env -> String -> Function+envGetFunction env name = func + where VFun func = envGet env name++envLookup :: Env -> String -> Maybe Value+envLookup env name =+ case env of+ [] -> Nothing+ f:fs ->+ case Map.lookup name f of+ Just value -> Just value+ Nothing -> envLookup fs name++envLookupFunction :: Env -> String -> Maybe Function+envLookupFunction env name = + case envLookup env name of+ Nothing -> Nothing+ Just value ->+ case value of+ VFun function -> Just function+ _ -> Nothing++-- | List of all symbols bound in the environment+envSymbols :: Env -> [String]+envSymbols env =+ case env of+ [] -> []+ f : fs -> keys f ++ envSymbols fs++-- | List of all symbols bound to functions in the environment+envFunctionSymbols :: Env -> [String]+envFunctionSymbols env =+ let isFunction s = case envGet env s of+ VFun _ -> True+ _ -> False+ in [s | s <- envSymbols env, isFunction s]++-- | All the functions in the environment+envFunctions :: Env -> Functions+envFunctions env = + Functions (map (envGetFunction env) + (envFunctionSymbols env))++-- | Return to the environment prior to an extendEnv+envPop :: Env -> Env+envPop env =+ case env of+ [] -> error "envPop: empty list"+ _f:fs -> fs+ +unbound :: String -> Env -> Bool+unbound name env = envLookup env name == Nothing++-- EVALUATING EXPRESSIONS++-- Limit the stack size for recursion, since we are helping+-- novice programmers to learn++stackSize :: Int+stackSize = 1000++eval :: Expr -> Env -> EvalResult+eval expr env = evalWithLimit expr env stackSize++evalWithLimit :: Expr -> Env -> Int -> EvalResult++-- Evaluate an expression in an environment with a limited stack++evalWithLimit expr env stacksize =+ if stacksize <= 0+ then EvalError "stack overflow"+ else+ let stacksize' = pred stacksize in+ case expr of+ EUndefined -> EvalError "undefined"+ ESymbol (Symbol name) ->+ case envLookup env name of+ Nothing -> EvalError $ "unbound variable: " ++ name+ Just value -> EvalOk value++ EBool b -> EvalOk (VBool b)+ EChar c -> EvalOk (VChar c)+ ENumber n -> EvalOk (VNumber n)+ EString n -> EvalOk (VString n)++ EIf t a b ->+ case evalWithLimit t env stacksize' of+ EvalOk (VBool True) -> evalWithLimit a env stacksize'+ EvalOk (VBool False) -> evalWithLimit b env stacksize'+ result -> result++ ELambda _ _ -> error "evalWithLimit: not implemented for lambda expr"+ ECall fsym args ->+ -- evaluating a function call+ -- I assume that call expressions have *symbols* for the + -- functions.+ -- To relax this assumption: change the definition of ECall,+ -- but how will you visualize it?++ case evalWithLimit (ESymbol fsym) env stacksize' of+ EvalOk f -> + case mapM (\ a -> evalWithLimit a env stacksize') args of+ EvalOk argvalues -> apply f argvalues env stacksize'+ -- why doesn't this work? err -> err+ EvalError e -> EvalError e+ EvalUntried -> EvalUntried+ err -> err++ EList elist ->+ case mapM (\ elt -> evalWithLimit elt env stacksize') elist of+ EvalOk values -> EvalOk (VList values)+ EvalError e -> EvalError e+ EvalUntried -> EvalUntried+ _ -> errcats ["evalWithLimit: extended expression not supported",+ show expr]+ +-- | Apply a function fvalue to a list of actual arguments args+-- in an environment env and with a limited stack size stacksize+apply :: Value -> [Value] -> Env -> Int -> EvalResult+apply fvalue args env stacksize =+ case fvalue of+ VFun f ->+ case functionImplementation f of+ Primitive pf -> pf args+ Compound formalArgs body ->+ evalWithLimit body (extendEnv formalArgs args env) stacksize+ not_a_function ->+ EvalError ("apply: first arg is not a function: " ++ + show not_a_function)++-- Shortcuts for making expressions that call the primitive functions+ePlus :: Expr -> Expr -> Expr+ePlus e1 e2 = eCall "+" [e1, e2]++eTimes :: Expr -> Expr -> Expr+eTimes e1 e2 = eCall "*" [e1, e2]++eMinus, eDiv, eMod :: Expr -> Expr -> Expr+eMinus e1 e2 = eCall "-" [e1, e2]+eDiv e1 e2 = eCall "div" [e1, e2]+eMod e1 e2 = eCall "mod" [e1, e2]++eAdd1, eSub1 :: Expr -> Expr+eAdd1 e1 = eCall "add1" [e1]+eSub1 e1 = eCall "sub1" [e1]++eEq, eNe, eGt, eGe, eLt, eLe :: Expr -> Expr -> Expr+eEq e1 e2 = eCall "==" [e1, e2]+eNe e1 e2 = eCall "/=" [e1, e2]+eGt e1 e2 = eCall ">" [e1, e2]+eGe e1 e2 = eCall ">=" [e1, e2]+eLt e1 e2 = eCall "<" [e1, e2]+eLe e1 e2 = eCall "<=" [e1, e2]+++eZerop, ePositivep, eNegativep :: Expr -> Expr+eZerop e1 = eCall "zero?" [e1]+ePositivep e1 = eCall "positive?" [e1]+eNegativep e1 = eCall "negative?" [e1]++-- A good base environment to get started with ++primitiveFunctions :: [Function]+primitiveFunctions = [+ -- Arithmetic+ primN2N "+" (+), -- Number (+)+ primN2N "-" (-),+ primN2N "*" (*),+ primIntDiv,+ primIntMod,+ primFloatDiv,++ primN1N "add1" succ,+ primN1N "sub1" pred,++ -- Comparison+ primN2B "==" (==),+ primN2B "/=" (/=),+ primN2B ">" (>),+ primN2B ">=" (>=),+ primN2B "<" (<),+ primN2B "<=" (<=),++ primN1B "zero?" eqZero,+ primN1B "positive?" gtZero,+ primN1B "negative?" ltZero,++ -- List operations++ -- null xs tells if xs is an empty list+ prim "null" [typeList (TypeVar "a")] + typeBool primNull,++ prim "head" [typeList (TypeVar "b")] + (TypeVar "b")+ primHead,+ prim "tail" [typeList (TypeVar "c")] + (typeList (TypeVar "c"))+ primTail,+ prim ":" [TypeVar "d", typeList (TypeVar "d")]+ (typeList (TypeVar "d"))+ primCons+ ]++type PFun = [Value] -> EvalResult++-- Primitive functions of arbitrary type+prim :: String -> [Type] -> Type -> PFun -> Function+prim name atypes rtype = Function (Just name) atypes rtype . Primitive++-- Primitive arithmetic functions++-- | Integer div and mod operations, for exact integers only.+-- Using an inexact (floating point) argument is an error,+-- even if the argument is "equal" to an integer (e.g., 5.0).+-- Division (div or mod) by zero is an error.+primIntDivMod :: String -> (Number -> Number -> Number) -> Function+primIntDivMod name oper =+ let func args =+ let err msg = EvalError $ concat [name, ": ", msg, + " (", show args, ")"]+ in case args of+ [VNumber a, VNumber b] ->+ if b == 0+ then err "zero divisor"+ else if isExact a && isExact b+ then EvalOk $ VNumber (oper a b)+ else err "arguments must be exact numbers"+ _ -> error "wrong type or number of arguments"+ in prim name [typeNum, typeNum] typeNum func++primIntDiv, primIntMod :: Function+primIntDiv = primIntDivMod "div" div+primIntMod = primIntDivMod "mod" mod++-- | Floating point division.+-- Integer arguments are coerced to floating point,+-- and the result is always floating point.+-- operands are ints. +-- x / 0 is NaN if x == 0, Infinity if x > 0, -Infinity if x < 0.+primFloatDiv :: Function+primFloatDiv =+ let divide args =+ case args of+ [VNumber x, VNumber y] -> EvalOk $ VNumber (x / y)+ _ -> EvalError $ "/: invalid args: " ++ show args+ in prim "/" [typeNum, typeNum] typeNum divide++-- Primitive functions for lists++primArgCountError :: String -> EvalResult+primArgCountError name = + errcat [name, ": wrong number of arguments in primitive function"]++-- Some of the type-checking in these primitive functions+-- shouldn't be necessary, if Sifflet knew the types of the+-- functions and could do type inference and check input values.++primNull :: PFun+primNull args =+ case args of+ [VList list] -> EvalOk $ VBool (List.null list)+ [_] -> EvalError "null: not a list"+ _ -> primArgCountError "primNull"++primHead :: PFun+primHead args =+ case args of+ [VList (x : _xs)] -> EvalOk x+ [VList []] -> EvalError "head: empty list"+ [_] -> EvalError "head: not a list"+ _ -> primArgCountError "primHead"++primTail :: PFun+primTail args =+ case args of+ [VList (_x : xs)] -> EvalOk $ VList xs+ [VList []] -> EvalError "tail: empty list"+ [_] -> EvalError "tail: not a list"+ _ -> primArgCountError "primTail"++primCons :: PFun+primCons args =+ case args of+ [x, VList xs] -> EvalOk $ VList (x:xs)+ [_, _] -> EvalError "cons: second argument not a list"+ _ -> primArgCountError "primCons"++-- Functions for constructing Functions of common types++-- | Primitive function with 2 number arguments yield an number value+-- fn = Number function to implement for Number operands.+primN2N :: String -> (Number -> Number -> Number) -> Function+primN2N name fn =+ let impl args =+ case args of+ [VNumber x, VNumber y] -> EvalOk $ VNumber (fn x y)+ _ -> EvalError $ name ++ ": invalid args: " ++ show args+ in prim name [typeNum, typeNum] typeNum impl++-- | Primitive unary functions number to number+primN1N :: String -> (Number -> Number) -> Function+primN1N name fn = + let impl args =+ case args of+ [VNumber x] -> EvalOk $ VNumber (fn x)+ _ -> EvalError $ name ++ ": invalid args: " ++ show args+ in prim name [typeNum] typeNum impl++-- Primitive frunctions with 2 number args and a boolean result+primN2B :: String -> (Number -> Number -> OBool) -> Function+primN2B name fn =+ let impl args =+ case args of+ [VNumber x, VNumber y] -> EvalOk $ VBool (fn x y)+ _ -> EvalError $ name ++ ": invalid args: " ++ show args+ in prim name [typeNum, typeNum] typeBool impl+++-- Primitive unary functions number to boolean+primN1B :: String -> (Number -> Bool) -> Function+primN1B name fn = + let impl args =+ case args of+ [VNumber x] -> EvalOk $ VBool (fn x)+ _ -> EvalError $ name ++ ": invalid args: " ++ show args+ in prim name [typeNum] typeBool impl++baseEnv :: Env+baseEnv = + makeEnv (map functionName primitiveFunctions)+ (map VFun primitiveFunctions)++-- | Given an expression, return the list of names of variables+-- occurring in the expression+exprSymbols :: Expr -> [Symbol]+exprSymbols expr = + nub $ case expr of+ EUndefined -> [] -- is *not* a variable+ ESymbol s -> [s]+ EIf t a b -> nub $ concat [exprSymbols t,+ exprSymbols a,+ exprSymbols b]+ ELambda x body -> nub (x : exprSymbols body)+ ECall f args -> + case args of+ [] -> [f]+ a:as -> nub $ concat [exprSymbols a,+ exprSymbols (ECall f as)]+ EList items -> nub $ concatMap exprSymbols items+ _ -> if exprIsExtended expr+ then errcats ["exprSymbols: extended expr not supported:",+ show expr]+ else [] -- literal types bool, char, number, string++-- | exprVarNames expr returns the names of variables in expr+-- that are UNBOUND in the base environment. This may not be ideal,+-- but it's a start.++exprVarNames :: Expr -> [String]+exprVarNames expr = [name | (Symbol name) <- exprSymbols expr,+ unbound name baseEnv]
+ Language/Sifflet/ExprTree.hs view
@@ -0,0 +1,227 @@+module Language.Sifflet.ExprTree+ (+ ExprTree, ExprNode(..), ExprNodeLabel(..)+ , exprNodeIoletCounter -- needs work ****** get rid of it???+ , exprToTree, treeToExpr, exprToReprTree+ , evalTree, unevalTree+ )++where++import Data.Number.Sifflet++import Data.Sifflet.Tree as T+import Data.Sifflet.TreeLayout (IoletCounter)++import Language.Sifflet.Expr++import Text.Sifflet.Repr ()++import Language.Sifflet.Util+++-- | +-- EXPRESSION TREES+-- For pure Sifflet, so not defined for extended expressions.++type ExprTree = Tree ExprNode+data ExprNode = ENode ExprNodeLabel EvalResult+ deriving (Eq, Show)++data ExprNodeLabel = NUndefined + | NSymbol Symbol + |+ -- formerly NLit Value+ NBool Bool | NChar Char | NNumber Number + | NString String+ | NList [Expr] -- ???+ deriving (Eq, Show)++instance Repr ExprNode where+ reprl (ENode label evalRes) =+ case label of+ NUndefined ->+ case evalRes of+ EvalUntried -> ["undefined"]+ EvalError e -> ["undefined", "error: " ++ e]+ EvalOk _ -> + errcats ["reprl of ExprNode: NUndefined with EvalOk",+ "should not happen!"]+ NSymbol s ->+ case evalRes of+ EvalOk v -> [repr s, repr v]+ EvalError e -> [repr s, "error: " ++ e]+ EvalUntried -> reprl s++ -- NLit l -> reprl l+ NBool b -> reprl b+ NChar c -> reprl c+ NNumber n -> reprl n+ NString s -> [show s]+ NList es -> reprl (EList es) -- check ***++-- | Counts the number of inlets and outlets for an ExprNode+exprNodeIoletCounter :: Env -> [ArgSpec] -> IoletCounter ExprNode+exprNodeIoletCounter env aspecs (ENode nodeLabel _nodeResult) =+ case nodeLabel of+ NUndefined -> (0, 1)+ NSymbol (Symbol "if") -> (3, 1) + NSymbol (Symbol s) -> + case envLookup env s of+ Nothing -> -- probably a parameter of the function+ case aspecsLookup s aspecs of+ Nothing -> (0, 1)+ Just i -> (i, 1)+ Just value ->+ case value of+ VFun function -> (functionNArgs function, 1)+ _ -> (0, 1) -- symbol bound to non-function value+ _ -> (0, 1)++exprToTree :: Expr -> ExprTree+exprToTree expr =+ let leafnode :: ExprNodeLabel -> T.Tree ExprNode+ leafnode e = node e []+ node :: ExprNodeLabel -> [T.Tree ExprNode] -> T.Tree ExprNode+ node e ts = T.Node (ENode e EvalUntried) ts+ errext = error ("exprToTree: extended expr: " ++ show expr)+ in case expr of+ -- EUndefined, ESymbol, and literals map directly + -- to NUndefined, NSymbol, E(literal-type) + EUndefined -> leafnode NUndefined+ ESymbol s -> leafnode (NSymbol s)++ -- Literals+ EBool b -> leafnode (NBool b)+ EChar c -> leafnode (NChar c)+ ENumber n -> leafnode (NNumber n)+ EString s -> leafnode (NString s)++ -- EIf maps to symbol "if" at the root, 3 subtrees+ EIf t a b -> node (NSymbol (Symbol "if")) (map exprToTree [t, a, b])++ ELambda _x _body -> error "exprToTree: not implemented for lambda expr"+ EApp f arg -> node (NSymbol (Symbol "@")) (map exprToTree [f, arg])+ -- ECall maps to symbol f (function name) at the root,+ -- each argument forms a subtree+ ECall f args -> node (NSymbol f) (map exprToTree args)+ EList xs -> leafnode (NList xs)+ -- Extended Exprs not supported!+ EGroup _ -> errext+ EOp _ _ _ -> errext++-- | Convert an expression tree (back) to an expression+-- It will not give back the *same* expression in the case of an EList.+treeToExpr :: ExprTree -> SuccFail Expr+treeToExpr (T.Node (ENode label _) trees) =+ let lit e = if null trees then Succ e+ else Fail "literal node with non-empty subtrees"+ in case label of+ NUndefined -> Succ EUndefined+ NBool b -> lit (EBool b)+ NChar c -> lit (EChar c)+ NNumber n -> lit (ENumber n)+ NString s -> lit (EString s)+ NList xs -> lit (EList xs)+ NSymbol (Symbol "@") ->+ case trees of+ [f, arg] -> + do+ {+ f' <- treeToExpr f+ ; arg' <- treeToExpr arg+ ; Succ $ EApp f' arg'+ }+ _ -> Fail "'@' node with /= 2 subtrees"+ NSymbol (Symbol "if") ->+ case trees of+ [q, a, b] -> + do+ {+ q' <- treeToExpr q+ ; a' <- treeToExpr a+ ; b' <- treeToExpr b+ ; Succ $ EIf q' a' b'+ }+ _ -> Fail ("An 'if' node has the wrong number of subtrees" +++ " (should be 3)")+ NSymbol s ->+ -- VVV Do I really need to distinguish these two cases?+ if null trees + then + -- s = terminal symbol+ Succ $ ESymbol s + else -- s = function symbol in function call+ do+ {+ trees' <- mapM treeToExpr trees+ ; Succ $ ECall s trees'+ }++-- Convert an expression to a repr tree (of string elements)+-- (Why?)++exprToReprTree :: Expr -> Tree String+exprToReprTree = fmap repr . exprToTree+++-- Evaluate an expression tree showing the evaluation at each node.+-- There's a lot of redundancy in this computation, but does it matter?++evalTree :: ExprTree -> Env -> ExprTree+evalTree atree env = evalTreeWithLimit atree env stackSize++evalTreeWithLimit :: ExprTree -> Env -> Int -> ExprTree+evalTreeWithLimit atree env stacksize =++ let T.Node root subtrees = atree+ ss' = pred stacksize+ in case root of+ ENode (NSymbol (Symbol "if")) _ ->+ case subtrees of+ [tt, ta, tb] ->+ let tt' = evalTreeWithLimit tt env ss'+ ENode _ testResult = rootLabel tt'+ subEval subtree =+ let subtree' = evalTreeWithLimit subtree env ss'+ ENode _ subresult = rootLabel subtree'+ in (subresult, subtree')+ ifNode result = ENode (NSymbol (Symbol "if")) result+ in case testResult of+ EvalOk (VBool True) -> + let (taValue, ta') = subEval ta in+ T.Node (ifNode taValue) [tt', ta', tb]++ EvalOk (VBool False) -> + let (tbValue, tb') = subEval tb in+ T.Node (ifNode tbValue) [tt', ta, tb']++ EvalOk weirdValue ->+ -- This shouldn't happen with proper type+ -- checking!+ let msg = "if: non-boolean condition value: " +++ repr weirdValue+ in T.Node (ifNode (EvalError msg)) [tt', ta, tb]++ EvalError msg ->+ T.Node (ifNode (EvalError msg)) [tt', ta, tb]++ _ -> errcats ["evalTreeWithLimit (if):",+ "unexpected test result"]++ _ -> error "evalTreeWithLimit: if: wrong number of subtrees"++ ENode rootOper _ ->+ let evalResult =+ case treeToExpr atree of+ Succ expr -> evalWithLimit expr env ss'+ Fail msg -> EvalError msg+ in T.Node (ENode rootOper evalResult)+ [evalTreeWithLimit s env ss' | s <- subtrees]++-- remove the values from the ExprNodes+-- "inverse" of evalTree +unevalTree :: ExprTree -> ExprTree+unevalTree atree = + let unevalNode (ENode oper _) = ENode oper EvalUntried+ in fmap unevalNode atree
+ Language/Sifflet/Parser.hs view
@@ -0,0 +1,319 @@+-- | A parser for Sifflet input values.+-- This is not a parser for all Sifflet expressions,+-- but just those that might be input in textual form+-- through the function call dialog that asks for the argument values.+-- So, it is limited (deliberately) to "data" types of expressions:+-- that is, Exprs using the constructors:+-- ELit+-- EList+-- That means excluding Exprs constructed with EUndefined,+-- ESymbol, EIf, ELambda, and ECall.++module Language.Sifflet.Parser+ (parseExpr+ , parseValue+ , parseLiteral+ , parseTest+ , parseSuccFail+ , parseTypedInput2, parseTypedInputs2+ , parseTypedInput3, parseTypedInputs3+ , nothingBut+ , expr, list+ , value, typedValue+ , bool, qchar, qstring, integer, double+ , number+ )++where++import Text.ParserCombinators.Parsec++import Data.Number.Sifflet+import Language.Sifflet.Expr+import Language.Sifflet.Util++-- | Parse a Sifflet data literal (number, string, char, bool, or list),+-- returning an Expr+parseExpr :: String -> SuccFail Expr+parseExpr = parseSuccFail expr++-- | Parse a Sifflet literal expression and return its Value+parseValue :: String -> SuccFail Value+parseValue s =+ -- take a shortcut here?+ -- case parseExpr s of -- stringToExpr s of+ -- Succ expr -> exprToValue expr+ -- Fail errmsg -> Fail errmsg+ parseLiteral s >>= exprToValue++parseLiteral :: String -> SuccFail Expr+parseLiteral s = + -- parseValue s >>= valueToLiteral+ case parseExpr s of+ Succ e -> if exprIsLiteral e+ then Succ e+ else Fail $ + "parseLiteral: expr is non-literal" ++ show e+ Fail errmsg -> Fail errmsg++parseSuccFail :: Parser a -> String -> SuccFail a+parseSuccFail p s =+ case parse p "user input" s of+ Left perr -> Fail (show perr)+ Right v -> Succ v++-- | Try to parse an input value of a specific type+parseTypedInput2 :: (String, Type) -> SuccFail Value+parseTypedInput2 (str, vartype) =+ parseSuccFail (nothingBut (typedValue vartype)) str++-- | Try to parse input values of specific types+parseTypedInputs2 :: [String] -- ^ input strings+ -> [Type] -- ^ expected types+ -> SuccFail [Value]+parseTypedInputs2 strs vartypes = + mapM parseTypedInput2 (zip strs vartypes)++-- | Try to parse an input value for a named variable of a specific type+parseTypedInput3 :: (String, String, Type) -> SuccFail Value+parseTypedInput3 (s, varname, vartype) =+ case parseSuccFail (nothingBut (typedValue vartype)) s of+ Fail msg -> Fail ("For variable " ++ varname ++ ":\n" ++ msg)+ Succ v -> Succ v++-- | Try to parse input values for named variables of specific types+parseTypedInputs3 :: [String] -- ^ inputs+ -> [String] -- ^ variable names+ -> [Type] -- ^ variable types+ -> SuccFail [Value]+parseTypedInputs3 strs varnames vartypes =+ mapM parseTypedInput3 (zip3 strs varnames vartypes)++-- | Like expr, but consumes the entire input,+-- so there must not be any extraneous characters after the Expr.+input :: Parser Expr+input = nothingBut expr++-- | 'nothingBut p is like 'p', but consumes the entire input,+-- so there must be no extraneous characters (except space)+-- after whatever 'p' parses.+nothingBut :: Parser a -> Parser a+nothingBut p = (many space >> p) `prog1` (many space >> eof)++prog1 :: (Monad m) => m a -> m b -> m a+prog1 m1 m2 = m1 >>= (\ r -> m2 >> return r)++-- | Parse a Sifflet data expression -- actually only a literal+-- or a list of literals.+expr :: Parser Expr+expr = -- (try (list expr >>= return . EList)) <|>+ (bool >>= return . EBool) <|>+ (qchar >>= return . EChar) <|>+ (qstring >>= return . EString) <|>+ try (double >>= return . ENumber . Inexact) <|>+ (integer >>= return . ENumber . Exact) <|>+ (list expr >>= return . EList)+ +list :: Parser a -> Parser [a]+list element = + let sep = try (skipMany space >> char ',' >> skipMany space)+ in (char '[' >> many space >> sepBy element sep)+ `prog1`+ (many space >> char ']')+ -- do I need (...) above?+ <?> "list" -- ???++++ +-- | Parser for a Value of any primitive or (concrete) list type++value :: Parser Value+value = (bool >>= return . VBool) <|>+ (qchar >>= return .VChar) <|>+ (qstring >>= return . VString) <|>+ try (double >>= return . VNumber . Inexact) <|>+ (integer >>= return . VNumber . Exact) <|>+ (list value >>= return . VList)++-- | Parser for a value with a specific primitive or concrete list type +-- expected.++typedValue :: Type -> Parser Value+typedValue t = + (case t of+ TypeCons "Bool" [] -> bool >>= return . VBool+ TypeCons "Char" [] -> qchar >>= return . VChar+ TypeCons "String" [] -> qstring >>= return . VString+ TypeCons "Num" [] -> number >>= return . VNumber+ TypeCons "List" [e] -> list (typedValue e) >>= return . VList+ TypeCons "Function" _argTypes -> + fail $ "Sorry, but you cannot input a function here.\n\n" +++ "Note to developer: typedValue needs access to the " +++ "global environment in order to look up function names."+ TypeCons _ _ -> + fail $ "Sorry, but you cannot input that type of value here.\n\n" +++ "Note to developer: typedValue needs to implement " +++ "the type " ++ show t+ TypeVar _ -> value -- can't check, so just accept anything+ )+ <?> typeName t++-- | A name for the type, for use in parser error reporting+typeName :: Type -> String+typeName t =+ let cerr cname = + error ("typeName: improper " ++ cname ++ " type construction")+ primitive tname args =+ case args of+ [] -> tname+ _ -> cerr tname+ in case t of + TypeVar tvn -> tvn -- could be more specific!+ TypeCons "Bool" ts -> primitive "boolean" ts+ TypeCons "Char" ts -> + primitive "character" ts -- "character (in single quotes)"+ TypeCons "Num" ts -> primitive "number" ts+ TypeCons "String" ts -> + primitive "string" ts -- "string (in double quotes)"+ TypeCons "List" [e] -> "list of " ++ typeName e+ TypeCons "List" _ -> cerr "List"+ TypeCons "Function" [_, _] -> "function" -- ???+ TypeCons "Function" _ -> cerr "Function"+ TypeCons tname texprs -> tname ++ " " ++ show (map typeName texprs)++bool :: Parser Bool+bool = (try (string "True" >> return True) <|>+ (string "False" >> return False))+ <?> typeName typeBool+++-- quoted character 'c'+qchar :: Parser Char+qchar = + let sq = '\'' -- single quote character+ in (((char sq <?> "opening single quote") >> + (try escapedChar <|> noneOf [sq])) + `prog1`+ (char sq <?> "closing single quote")+ )+ <?> typeName typeChar+ +-- quoted string "c..."++qstring :: Parser String+qstring = + let dq = '\"' -- double quote character+ in (char dq >> + many (escapedChar <|> noneOf [dq] <?> "")) + `prog1` + (char dq <?> "close of quotation")+ -- Do I need (...) above?+ <?> typeName typeString++-- escapedChar recognizes the following escape sequences:+-- \t = tab+-- \n = newline+-- \r = carriage return+-- \\ = backslash+-- Anything else that begins with \ is an error.++escapedChar :: Parser Char+escapedChar = + let bs = '\\' -- backslash character+ in char bs >> + (oneOf "ntr\\" <?> "n, t, r, or \\ to follow \\") >>=+ (\ c ->+ return (case c of+ 'n' -> '\n'+ 't' -> '\t'+ 'r' -> '\r'+ '\\' -> '\\'+ _ -> error "escapedChar: c MUST be n, t, r, or \\"+ )+ )++data Sign = Minus | Plus++-- Integer ::= (+|-)? digit+++integer :: Parser Integer -- sign, digits+integer = do { s <- optSign;+ u <- unsignedInteger;+ return (applySign s u)+ }+ <?> "integer"++unsignedInteger :: Parser Integer+unsignedInteger = many1 digit >>= return . read++-- An optional + or - defaulting to +++optSign :: Parser Sign -- 1: negative; 0: non-negative+optSign = try ( char '-' >> return Minus ) <|>+ try ( char '+' >> return Plus ) <|>+ return Plus++applySign :: (Num n) => Sign -> n -> n+applySign s x =+ case s of + Minus -> (- x)+ Plus -> x++-- A double (float) may begin with a sign (+ or -) and must contain+-- a decimal point along with at least one digit before and/or after+-- the decimal point.+-- So there are three cases:+-- [sign] digits '.' digits+-- [sign] digits '.'+-- [sign] '.' digits++double :: Parser Double++-- Double FAILS if there is a decimal point.+-- It succeeds in the following cases:++double = + let digits1 = many1 digit+ point = char '.'+ -- wpf: whole-part point fraction-part+ wpf = do { dd <- digits1;+ dd' <- point >> digits1;+ return (dd, dd')+ }+ -- wp: whole-part point+ wp = do { dd <- digits1 `prog1` point;+ return (dd, "0")+ }+ -- pf: point fraction-part+ pf = do { dd' <- point >> digits1;+ return ("0", dd')+ }+ -- optional trailing exponent notation e.g. e-4+ scale = do { i <- oneOf "eE" >> integer;+ return (10 ** fromIntegral i)+ }+ <|> return 1++ in do { sign <- optSign+ ; (whole, frac) <- (try wpf <|>+ try wp <|>+ try pf)+ ; m <- scale;+ ; let w = read (whole ++ ".0") -- whole part as number+ f = read ("0." ++ frac) -- frac part as number+ ; return (m * applySign sign (w + f))+ }+ <?> "real number"++-- A number is a Sifflet Number, which is exact unless it contains+-- a decimal point.+-- To avoid consuming "123" from "123." and interpreting it as an exact+-- number, we MUST try to parse double before integer.+number :: Parser Number+number = (try (double >>= return . Inexact) <|> + (integer >>= return . Exact))+ <?> typeName typeNum+++
+ Language/Sifflet/SiffML.hs view
@@ -0,0 +1,474 @@+-- | SiffML : Sifflet Markup Language.+-- An XML application for storing and retrieving Sifflet programs+-- and libraries.++module Language.Sifflet.SiffML+ (+ ToXml(..)+ , produceSiffMLFile+ , consumeSiffMLFile+ , xmlToFunctions+ -- for testing+ , testFromXml+-- , consumeString+ )++where++import Text.XML.HXT.Core++import Data.Number.Sifflet+import Language.Sifflet.Expr+import Language.Sifflet.Util++class ToXml a where+ toXml :: a -> XMLProducer++-- | An XMLProducer produces XML+type XMLProducer = IOSLA (XIOState ()) XmlTree XmlTree++-- | An XMLConsumer consumes XML+type XMLConsumer a b = IOSLA (XIOState ()) a b++defaultOptions :: SysConfigList+defaultOptions = [withIndent yes, withValidate no ]++produceSiffMLFile :: (ToXml a) => a -> FilePath -> IO ()+produceSiffMLFile src path = + let arrow :: XMLProducer+ arrow = toXml src+ options = defaultOptions+ in do+ {+ putStrLn ""+ ; [rc] <- runX (root [] [arrow] >>>+ writeDocument options path >>>+ getErrStatus)+ ; putStrLn (case rc of+ 0 -> "Okay"+ _ -> "Failed")++ }+consumeSiffMLFile :: XMLConsumer XmlTree a -> FilePath -> IO [a]+consumeSiffMLFile fromXml filePath =+ let options = defaultOptions+ in runX (readDocument options filePath >>> fromXml)++-- | Symbols++instance ToXml Symbol where+ toXml = symbolToXml++symbolToXml :: Symbol -> XMLProducer+symbolToXml (Symbol name) =+ selem "symbol" [txt name]+++-- | Expr++instance ToXml Expr where+ toXml = exprToXml++exprToXml :: Expr -> XMLProducer+exprToXml expr =+ let literal label text = + -- future: (omit label arg.): selem label [txt text]+ selem "literal" [selem label [txt text]]+ in case expr of+ EUndefined -> + eelem "undefined"+ ESymbol (Symbol name) -> + selem "symbol" [txt name]++ -- "Literals"+ -- New way: duplicates parts of valueToXml (bad) ***+ EBool b -> + -- future: selem "bool" [eelem (show b)]+ selem "literal" [selem "bool" [eelem (show b)]]+ EChar c ->+ -- future: selem "char" [txt [c]]+ literal "char" [c]+ ENumber (Exact i) ->+ -- future: selem "int" [txt (show i)]+ literal "int" (show i)+ ENumber (Inexact x) ->+ -- future: selem "float" [txt (show x)]+ literal "float" (show x)+ EString s ->+ -- future: selem "string" [txt s]+ literal "string" s++ EIf e1 e2 e3 -> + selem "if" [toXml e1, toXml e2, toXml e3]+ EList xs -> + -- I predict that this is going to be troublesome! ***+ -- No checking for whether the list elements are literals!+ selem "literal" [selem "list" + (map (toXml . literalToValue) xs)]+ -- future: selem "list" (map toXml xs)+ ELambda _ _ -> error "exprToXml: not implemented for lambda expr"+ ECall (Symbol name) xs -> + selem "call" + (selem "symbol" [txt name] :+ map toXml xs)+ _ -> errcats ["exprToXml: extended expr:", show expr]++-- | Convert a literal expression to a value.+-- It is an error if the expr is not a literal.+-- Compare exprToValue in Expr.hs+literalToValue :: Expr -> Value+literalToValue e =+ if exprIsLiteral e+ then case e of+ EBool b -> VBool b+ EChar c -> VChar c+ ENumber n -> VNumber n+ EString s -> VString s+ EList es -> VList (map literalToValue es)+ EGroup e' -> literalToValue e'+ _ -> error "literalToValue: expr is literal, but not literal?"+ else error ("literalToValue: expr is not a literal: " ++ show e)++xmlToExpr :: XMLConsumer XmlTree Expr+xmlToExpr = + isElem >>>+ (+ (hasName "undefined" >>> constA EUndefined) <+>+ (hasName "symbol" >>> getChildren >>> isText >>> getText >>>+ arr (ESymbol . Symbol)) <+>++ -- future: remove extra level "literal"+ (hasName "literal" >>> getChildren >>> xmlToExpr) <+>++ -- boolean values+ (hasName "True" >>> constA (EBool True)) <+>+ (hasName "False" >>> constA (EBool False)) <+>++ -- chars+ (hasName "char" >>> getChildren >>> isText >>> getText >>>+ -- VVV head dangerous ???+ arr (EChar . head)) <+>++ -- numbers -- why not use parser instead of read???+ (hasName "int" >>> getChildren >>> isText >>> getText >>>+ arr (ENumber . Exact . read)) <+> -- read dangerous?+ (hasName "float" >>> getChildren >>> isText >>> getText >>>+ arr (ENumber . Inexact . read)) <+> -- read dangerous?+ + -- strings+ (hasName "string" >>> getChildren >>> isText >>> getText >>> + arr EString) <+>+++ (hasName "if" >>> listA (getChildren >>> xmlToExpr) >>> + -- sometimes I get bogus run-time errors here about+ -- this pattern [a, b, c] being non-exhaustive.+ -- Of course, it *is* non-exhaustive; but it is+ -- never violated in practice+ arr (\ [a, b, c] -> EIf a b c)) <+>+ -- This is very awkward, but needed for compatibility with the+ -- present SiffML doctype:+ (hasName "list" >>> + -- future?: listA (getChildren >>> xmlToExpr) >>> + -- Anyway, *why* does this not work???+ listA (getChildren >>> xmlToExpr) >>> + -- past?:+ -- listA (getChildren >>> xmlToValue >>> arr valueToLiteral') >>> + arr EList) <+>+ -- VVV Would be less awkward if ECall :: Symbol -> [Expr] -> Expr+ -- were changed to ECall :: Expr -> [Expr] -> Expr+ (hasName "call" >>> listA (getChildren >>> xmlToExpr) >>>+ arr (\ (ESymbol symf : args) -> ECall symf args))+ )++-- | Values+-- Still used in exprToXml in the EList case :-(++instance ToXml Value where+ toXml = valueToXml++-- Is this still needed? ***+valueToXml :: Value -> XMLProducer+valueToXml value =+ case value of+ VBool b ->+ -- <True/> or <False/> + -- complicate? selem "bool" [txt (show b)]+ eelem (show b)+ VChar c ->+ selem "char" [txt [c]]+ VString s ->+ selem "string" [txt s]+ VNumber (Exact i) ->+ selem "int" [txt (show i)]+ VNumber (Inexact x) ->+ selem "float" [txt (show x)]+ -- Are VFun and VList needed???+ VFun f ->+ selem "function" [toXml f]+ VList vs ->+ selem "list" (map toXml vs)++-- xmlToValue: still needed? ***+xmlToValue :: XMLConsumer XmlTree Value+xmlToValue = + isElem >>>+ ((hasName "True" >>> constA (VBool True)) <+>+ (hasName "False" >>> constA (VBool False)) <+>+ (hasName "char" >>> getChildren >>> isText >>> getText >>>+ arr (VChar . head)) <+>+ (hasName "string" >>> getChildren >>> isText >>> getText >>> + arr VString) <+>+ (hasName "int" >>> getChildren >>> isText >>> getText >>>+ arr (VNumber . Exact . read)) -- dangerous?+ <+>+ (hasName "float" >>> getChildren >>> isText >>> getText >>>+ arr (VNumber . Inexact . read)) -- dangerous?++ <+>+ (hasName "function" >>> getChildren >>> xmlToFunction >>> arr VFun) ++ <+>+ -- listA arr collects the results of arr into a list, so to speak;+ -- note that listA (arr1 >>> arr2) + -- does not equal listA arr1 >>> listA arr2+ -- and probably will not even have a well-defined type.+ -- In particular:+ -- getChildren --> [child1]+ -- listA getChildren --> [childi for i = 1 to n]+ -- listA getChildren >>> xmlToValue+ -- --> [child1] if child1 passes xmlToValue (it does not)+ -- listA (getChildren >>> xmlToValue)+ -- --> [childi for i = 1 to n if childi passes xmlToValue]+ (hasName "list" >>> listA (getChildren >>> xmlToValue) >>>+ arr VList)+ )++-- | Types++instance ToXml Type where+ toXml = typeToXml++typeToXml :: Type -> XMLProducer+typeToXml vtype =+ case vtype of+ TypeVar typeVarName -> selem "type-variable" [txt typeVarName]+ TypeCons "String" [] -> eelem "string-type"+ TypeCons "Char" [] -> eelem "char-type"+ TypeCons "Num" [] -> eelem "num-type"+ TypeCons "Bool" [] -> eelem "bool-type"+ TypeCons "List" [eltType] -> selem "list-type" [typeToXml eltType]+ TypeCons "Function" [argT, resultT] -> + selem "function-type" [typeToXml argT, typeToXml resultT]+ -- error "typeToXml: TypeCons Function cannot be converted to XML"+ TypeCons _ _ ->+ errcats ["typeToXml:", show vtype, "cannot be converted to XML"]++xmlToType :: XMLConsumer XmlTree Type+xmlToType =+ isElem >>> + ((hasName "string-type" >>> constA typeString) <+>+ (hasName "char-type" >>> constA typeChar) <+>+ (hasName "num-type" >>> constA typeNum) <+>+ (hasName "bool-type" >>> constA typeBool) <+>+ (hasName "list-type" >>> getChildren >>> xmlToType >>> arr typeList) <+>+ (hasName "function-type" >>> + listA (getChildren >>> xmlToType) >>>+ -- there must be exactly two children, but I'm not checking+ arr (\ ts -> TypeCons "Function" ts)) <+>+ (hasName "type-variable" >>> getChildren >>>+ isText >>> getText >>> arr TypeVar)+ )++-- | Functions++instance ToXml Function where+ toXml = functionToXml++functionToXml :: Function -> XMLProducer+functionToXml (Function mName argTypes retType impl) =+ case impl of+ Primitive _ ->+ -- shouldn't happen+ errcats ["functionToXml:",+ "primitive functions cannot be exported to XML",+ show (mName, argTypes, retType)]+ Compound argNames body ->+ selem "compound-function"+ (let name s = selem "name" [txt s]+ rest = + [selem "return-type" [typeToXml retType],+ selem "arg-types" (map typeToXml argTypes),+ selem "arg-names" (map name argNames),+ selem "body" [toXml body]]+ in case mName of+ Nothing -> rest+ Just fName -> name fName : rest+ )++xmlToFunction :: XMLConsumer XmlTree Function+xmlToFunction = + let getChildElem :: XMLConsumer XmlTree XmlTree+ getChildElem = getChildren >>> isElem++ getFuncName :: XMLConsumer XmlTree String+ getFuncName = hasName "name" >>> getChildren >>> isText >>> getText++ getReturnType :: XMLConsumer XmlTree Type+ getReturnType = hasName "return-type" >>> getChildren >>> xmlToType++ getArgTypes :: XMLConsumer XmlTree [Type]+ getArgTypes = hasName "arg-types" >>> + listA (getChildren >>> xmlToType)++ getArgNames :: XMLConsumer XmlTree [String]+ getArgNames = hasName "arg-names" >>> + listA (getChildElem >>> getFuncName)++ getBody :: XMLConsumer XmlTree Expr+ getBody = hasName "body" >>> getChildren >>> xmlToExpr ++ in + isElem >>> hasName "compound-function" >>> + -- NOTE:+ -- If arr1 "produces" a, and arr2 "produces" b,+ -- then (arr1 &&& arr2) "produces" (a, b).+ (+ ( -- function name is optional, though it *should* be in the XML file+ listA (getChildElem >>> getFuncName)) &&& + (getChildElem >>> getReturnType) &&&+ (getChildElem >>> getArgTypes) &&&+ (getChildElem >>> getArgNames) &&&+ (getChildElem >>> getBody)+ )+ >>>+ (arr (\ (names, (returnType, (argTypes, (argNames, body)))) -> + Function (case names of + [] -> Nothing+ (fname : _) -> (Just fname)+ )+ argTypes+ returnType (Compound argNames body)))++functionsToXml :: Functions -> XMLProducer+functionsToXml (Functions fs) =+ selem "functions" (map toXml fs)++xmlToFunctions :: XMLConsumer XmlTree Functions+xmlToFunctions =+ isElem >>> -- document root+ getChildren >>>+ hasName "functions" >>>+ listA (getChildren >>> xmlToFunction) >>>+ arr Functions++instance ToXml Functions where+ toXml = functionsToXml++-- -- | Examples and tests++-- exampleFunction :: Function+-- exampleFunction = +-- let esym = ESymbol . Symbol+-- in Function (Just "cincr") +-- [typeBool, typeNum, typeNum]+-- typeNum+-- (Compound ["incr", "a", "b"]+-- (EIf (esym "incr")+-- (ECall (Symbol "+") [esym "a", esym "b"])+-- (esym "a")))++-- -- | This is for testing, when I don't know the type +-- -- of the result I'm getting+-- xmlToX :: XMLConsumer XmlTree Functions -- [Function] -- XmlTree+-- xmlToX = +-- isElem -- document root+-- >>>+-- getChildren+-- >>>+-- hasName "functions" +-- >>>+-- listA (getChildren >>> xmlToFunction) +-- >>>+-- arr Functions++-- -- | Tests++-- testOut :: IO ()+-- testOut = +-- produceStdout (Functions [exampleFunctions !! 0, exampleFunctions !! 1])+++-- testFromFile :: (Show a) => XMLConsumer XmlTree a -> FilePath -> IO ()+-- testFromFile fromXml filePath = do+-- {+-- results <- consumeSiffMLFile fromXml filePath+-- ; putStrLn ""+-- ; print (length results)+-- ; print results+-- ; putStrLn ""+-- }++-- testIn :: (Show a) => XMLConsumer XmlTree a -> IO ()+-- testIn fromXml = testFromFile fromXml "-"++-- UNUSED:++-- | testFromXml :: (ToXml a, Show a) => a -> XMLConsumer XmlTree a -> IO ()+-- VVV This type generalization (a, a to a, b) is for debugging, undo it later:+testFromXml :: (ToXml a, Show b) => Int -> a -> XMLConsumer XmlTree b -> IO ()+testFromXml traceLevel src consumer = do+ {+ produceSiffMLFile src "test.xml"+ ; results <- runX (readDocument + (defaultOptions ++ [withTrace traceLevel])+ "test.xml" >>>+ isElem >>> -- document root+ getChildren >>>+ consumer)+ ; case results of+ [] -> putStrLn "Failed"+ result : _ -> print result+ }++-- testToXmlAndBack :: (ToXml a, Show a) => a -> XMLConsumer XmlTree a -> IO ()+-- testToXmlAndBack = testFromXml+++-- xmlToSymbol :: XMLConsumer XmlTree Symbol+-- xmlToSymbol = +-- isElem >>> hasName "symbol" >>> -- symbol element+-- getChildren >>> isText >>> -- text element+-- getText >>> -- String+-- arr Symbol -- quasi (return . Symbol)++-- testXmlToSymbol :: Symbol -> IO ()+-- testXmlToSymbol sym = testFromXml sym xmlToSymbol++++-- produceStdout :: (ToXml a) => a -> IO ()+-- produceStdout src = produceSiffMLFile src "-"+++-- produceXmlTrees :: (ToXml a) => a -> IO [XmlTree]+-- produceXmlTrees src = +-- let arrow :: XMLProducer+-- arrow = toXml src+-- options = defaultOptions+-- in do+-- {+-- putStrLn ""+-- ; docs <- runX (root [] [arrow] >>> writeDocument options "-")+-- ; case docs of+-- [] -> putStrLn "Failed"+-- doc : _ ->+-- print doc+-- ; return docs++-- }++++-- consumeStdin :: XMLConsumer XmlTree a -> IO [a]+-- consumeStdin fromXml = consumeSiffMLFile fromXml "-"
+ Language/Sifflet/TypeCheck.hs view
@@ -0,0 +1,590 @@+module Language.Sifflet.TypeCheck+ (valueType, typeCheckValues+ , typeVarsIn+ , Subst+ , subst, substComp, idSubst, deltaSubst+ , extend, unify+ , TypeScheme(..), schematicsTS, unknownsTS, substTS+ , TypeEnv, emptyTypeEnv, install, unknownsTE, substTE+ , NameSupply(..), newNameSupply, nameSupplyNext, nameSupplyTake+ , tcExpr, tcExprs+ , tcVar, newInstance+ , arrow+ , envToTypeEnv, baseTypeEnv+ , fromLambdaType, decideTypes+ )++where++-- drop this after debugging:+import System.IO.Unsafe(unsafePerformIO)++import Control.Monad (foldM)++import qualified Data.List as AList (lookup)++import Data.Map as Map hiding (filter, foldr, lookup, map, null)+import qualified Data.Map as Map (map, lookup)++import Language.Sifflet.Expr+import Text.Sifflet.Repr+import Language.Sifflet.Util+++-- | Determine the type of a value.+-- May result in a type variable.++valueType :: Value -> SuccFail Type+valueType v =+ case v of+ VBool _ -> Succ typeBool+ VChar _ -> Succ typeChar+ VNumber _ -> Succ typeNum+ VString _ -> Succ typeString+ VFun (Function _ atypes rtype _) -> Succ $ typeFunction atypes rtype++ VList [] -> Succ $ typeList $ TypeVar "list_element"+ VList (x:xs) -> + do+ xtype <- valueType x+ xstypes <- mapM valueType xs+ if filter (/= xtype) xstypes == []+ then Succ $ typeList xtype+ else Fail "list with diverse element types"++-- | Check whether the values agree with the types (which may be abstract)+--+-- This is *probably* too lenient in the case of type variables:+-- it can pass a mixed-type list.++typeCheckValues :: [String] -> [Type] -> [Value] -> SuccFail [Value]+typeCheckValues names types values =+ let check :: (Map String Type) -> [String] -> [Type] -> [Value] + -> SuccFail [Value]+ check _ [] [] [] = Succ []+ check env (n:ns) (t:ts) (v:vs) = + case typeMatch t v env of+ Succ env' -> check env' ns ts vs >>= Succ . (v:)+ Fail msg -> Fail $ "For variable " ++ n ++ ":\n" ++ msg+ check _ _ _ _ = error "typeCheckValues: mismatched list lengths"+ in check empty names types values+ +++-- | Try to match a single type and value,+-- may result in binding a type variable in a new environment+-- or just the old environment+-- (Note that this tries to produce a map of type variable names to Types,+-- instead of TypeSchemes as with TypeEnv below)+typeMatch :: Type -> Value -> (Map String Type) -> SuccFail (Map String Type)+typeMatch atype value env = + let sorry x etype =+ Fail $ repr x ++ ": " ++ etype ++ " expected"+ in case (atype, value) of+ -- easy cases+ (TypeCons "Bool" [], VBool _) -> Succ env+ (TypeCons "Bool" [], x) -> sorry x "True or False"+ (TypeCons "Char" [], VChar _) -> Succ env+ (TypeCons "Char" [], x) -> sorry x "character"+ (TypeCons "Num" [], VNumber _) -> Succ env+ (TypeCons "Num" [], x) -> sorry x "number"+ (TypeCons "String" [], VString _) -> Succ env+ (TypeCons "String" [], x) -> sorry x "string"+ -- VV Harder+ -- VV Are the avalues below supposed to be equal to the value above?+ (TypeVar name, avalue) -> + case Map.lookup name env of+ Nothing -> + -- bind type variable+ valueType avalue >>= \ vtype -> + Succ $ Map.insert name vtype env+ Just concreteType -> typeMatch concreteType avalue env+ (TypeCons "List" [etype], VList lvalues) ->+ case lvalues of+ [] -> Succ env+ v:vs -> + typeMatch etype v env >>= + typeMatch (typeList etype) (VList vs)+ (TypeCons "Function" [_, _], _) ->+ -- this will require matching type variables with type variables!+ error "typeMatch: unimplemented case for TypeCons Function"+ _ -> Fail $ "type mismatch: " ++ show (atype, value)+++-- The rest of this is based (loosely and conceptually) on the type checker+-- chapters 8-9 of Simon L. Peyton Jones, The Implementation of Functional+-- Programming Languages, Prentice Hall, 1987.+-- Out of print but available on the web at+-- http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/+--+-- Chapter 8: Polymorphic Type Checking, by Peter Hancock.+-- Chapter 9: A Type-Checker, by Peter Hancock.++-- 9.2 Representation of Type Expressions+-- ('a -> b' means the textbook notation 'a' corresponds (at least roughly)+-- to the Sifflet code 'b'.)+-- tvname -> Language.Sifflet.Expr.TypeVarName+-- type_exp -> Language.Sifflet.Expr.Type+-- tvars_in -> typeVarsIn++typeVarsIn :: Type -> [TypeVarName]+typeVarsIn atype =+ let tvarsIn atype' result =+ case atype' of+ TypeVar vname -> vname : result+ TypeCons _ctor ts -> foldr tvarsIn result ts+ -- TypeList t -> foldr tvarsIn result [t]+ -- TypeFunction ats rt -> foldr tvarsIn result (rt:ats)+ -- _ -> []+ in tvarsIn atype []++-- 9.3 Success and Failure+-- reply -> Language.Sifflet.Util.SuccFail++-- 9.4 Solving Equations+-- 9.4.1 Substitutions+-- subst -> Subst+-- sub_type -> subst+-- scomp -> substComp+-- id_subst -> idSubst+-- delta -> deltaSubst++-- | The type of a substitution function, which maps type variables to types++type Subst = TypeVarName -> Type++-- | Apply a substitution function to a type (possibly a type with variables)++subst :: Subst -> Type -> Type+subst phi typeExpr =+ case typeExpr of+ TypeVar tvn -> phi tvn+ -- TypeList etype -> TypeList (subst phi etype)+ -- TypeFunction atypes rtype -> + -- TypeFunction (map (subst phi) atypes) (subst phi rtype)+ TypeCons ctor atypes ->+ TypeCons ctor (map (subst phi) atypes)++-- | Composing two substitutions++substComp :: Subst -> Subst -> Subst+substComp sub2 sub1 tvn = subst sub2 (sub1 tvn)++-- | Identity substitution++idSubst :: Subst+idSubst tvn = TypeVar tvn++-- | A delta substitution is one that affects just a single variable.++deltaSubst :: TypeVarName -> Type -> Subst+deltaSubst tvn typeExpr tvn' =+ if tvn == tvn'+ then typeExpr+ else TypeVar tvn'++-- 9.4.2. Unification+-- extend -> extend+-- unify -> unify+-- unifyl -> unifyList++-- | Try to extend a substitution by adding a single variable-value pair++extend :: Subst -> TypeVarName -> Type -> SuccFail Subst+extend phi tvn t =+ if t == TypeVar tvn+ then Succ phi+ else if tvn `elem` typeVarsIn t+ then Fail ("occurs check: " ++ tvn ++ " is in " ++ (show t))+ else Succ (deltaSubst tvn t `substComp` phi)++-- extend is untested; we can wait until unify to test++unify :: Subst -> (Type, Type) -> SuccFail Subst+unify phi (TypeVar tvn, t) =+ if phi tvn == TypeVar tvn+ then extend phi tvn (subst phi t)+ else unify phi (phi tvn, subst phi t)+unify phi (TypeCons tcn ts, TypeVar tvn) =+ unify phi (TypeVar tvn, TypeCons tcn ts)+unify phi types@(TypeCons tcn ts, TypeCons tcn' ts') =+ if tcn == tcn'+ then unifyList phi (zip ts ts')+ else Fail ("cannot unify " ++ show (fst types) ++ + " with " ++ show (snd types))++unifyList :: Subst -> [(Type, Type)] -> SuccFail Subst+unifyList = foldM unify++-- 9.5 Keeping track of types+-- 9.5.2 Look to the variables+-- type_scheme -> TypeScheme+-- SCHEME -> TypeScheme+-- unknowns_scheme -> unknownsTS+-- sub_scheme -> substTS++-- | A TypeScheme (TypeScheme schematicVariables typeExpression)+-- is a sort of type template, in which+-- schematic variables (i.e., type parameters or "generics")+-- are made explicit; any other type variables+-- in the type expression are unknowns++data TypeScheme = TypeScheme [TypeVarName] Type+ deriving (Eq, Show)++schematicsTS :: TypeScheme -> [TypeVarName]+schematicsTS (TypeScheme schVars _) = schVars++unknownsTS :: TypeScheme -> [TypeVarName]+unknownsTS (TypeScheme schVars t) =+ [tv | tv <- typeVarsIn t, not (tv `elem` schVars)]++-- | Apply a substitution to a type scheme, taking care to affect+-- only the unknowns, not the schematic variables++substTS :: Subst -> TypeScheme -> TypeScheme+substTS phi (TypeScheme schVars t) =+ let phi' :: Subst+ phi' tvn =+ -- phi' is like phi but excludes the schematic variables+ if tvn `elem` schVars+ then TypeVar tvn+ else phi tvn+ in TypeScheme schVars (subst phi' t)++-- 9.5.3 Type environments+-- I'll use maps instead of association lists+-- type_env -> TypeEnv+-- unknowns_te -> unknownsTE+-- sub_te -> substTE++-- | A type environment maps type variable names to type schemes.+-- Oh, is it really type variable names, or just names?+-- It seems to me that in envToTypeEnv, it's function names,+-- instead of type variable names.++type TypeEnv = Map TypeVarName TypeScheme++emptyTypeEnv :: TypeEnv+emptyTypeEnv = Map.empty++-- -- | The domain of a type environment++-- dom :: TypeEnv -> [TypeVarName]+-- dom = keys++-- | The range of a type environment++rng :: TypeEnv -> [TypeScheme]+rng = elems++-- -- | The type scheme value of a type variable name+-- -- This will fail, of course, if the variable name is not+-- -- bound in the type environment.+-- val :: TypeEnv -> TypeVarName -> TypeScheme+-- val = (!)++-- | Insert a new type variable and its type scheme value+install :: TypeEnv -> TypeVarName -> TypeScheme -> TypeEnv+install te tvn ts = insert tvn ts te++-- | The unknowns of a type environment++unknownsTE :: TypeEnv -> [TypeVarName]+unknownsTE gamma = concatMap unknownsTS (rng gamma)++substTE :: Subst -> TypeEnv -> TypeEnv+substTE phi gamma = Map.map (substTS phi) gamma++-- 9.6 New variables+-- I diverge from the "easy" way given in the book++-- | A source of variable names++data NameSupply = NameSupply TypeVarName Int -- prefix, count+ deriving (Eq, Show)++-- | Creates a name supply++newNameSupply :: TypeVarName -> NameSupply+newNameSupply prefix = NameSupply prefix 1++-- | Produces next variable from a name supply++nameSupplyNext :: NameSupply -> (TypeVarName, NameSupply)+nameSupplyNext (NameSupply prefix count) =+ (prefix ++ show count, NameSupply prefix (count + 1))++-- | Produces next several variables from a name supply++nameSupplyTake :: NameSupply -> Int -> ([TypeVarName], NameSupply)+nameSupplyTake (NameSupply prefix count) n =+ ([prefix ++ show i | i <- [count .. count+n-1]],+ NameSupply prefix (count + n))++-- 9.7 The type checker+-- tc -> tcExpr+-- tcl -> tcExprs (type checks a list of Expr)+-- +-- Additional functions (?) tcExprTree ??++-- | Type check an expression++tcExpr :: TypeEnv -> NameSupply -> Expr -> SuccFail (Subst, Type, NameSupply)+tcExpr te ns expr =+ let prim ptype = Succ (idSubst, ptype, ns)+ in case expr of+ EUndefined -> + let (tvn, ns') = nameSupplyNext ns+ in Succ (idSubst, TypeVar tvn, ns')++ EBool _ -> prim typeBool+ EChar _ -> prim typeChar+ ENumber _ -> prim typeNum+ EString _ -> prim typeString++ ESymbol (Symbol name) -> tcVar te ns name+ EIf c a b -> tcIf te ns c a b+ EList exprs -> tcList te ns exprs+ ELambda (Symbol x) body -> tcLambda te ns x body+ EApp f arg -> tcApp te ns f arg+ ECall _ _ -> tcExpr te ns (callToApp expr)++ -- EOp and EGroup are not needed in Sifflet itself,+ -- only for export+ EOp _op _ _ -> Fail "tcExpr: not implemented for EOp"+ EGroup e -> tcExpr te ns e++-- 9.7.1 Type checking lists of expressions+-- tcl -> tcExprs+-- tcl1 -> tcExprs1+-- tcl2 -> tcExprs2+-- This occurs in "two stages":+-- 1. Type check each expression in the list, building up the +-- type environment as we scan through it.+-- 2. Cumulatively compose all the substitutions found,+-- and make sure each type found is a fixed point of the+-- final substitution.+-- NOTE: This is to check a list of Exprs, not an Expr of the type EList.+-- See tcList.++tcExprs :: TypeEnv -> NameSupply -> [Expr] + -> SuccFail (Subst, [Type], NameSupply)+tcExprs _te ns [] = Succ (idSubst, [], ns)+tcExprs te ns (e:es) = do+ {+ (phi, t, ns') <- tcExpr te ns e+ ; tcExprs1 te ns' es phi t+ }++tcExprs1 :: TypeEnv -> NameSupply -> [Expr] -> Subst -> Type+ -> SuccFail (Subst, [Type], NameSupply)+tcExprs1 te ns es phi t = do+ {+ (psi, ts, ns') <- tcExprs (substTE phi te) ns es+ ; tcExprs2 phi t psi ts ns'+ }++tcExprs2 :: Subst -> Type -> Subst -> [Type] -> NameSupply+ -> SuccFail (Subst, [Type], NameSupply)+tcExprs2 phi t psi ts ns =+ Succ (substComp psi phi, subst psi t : ts, ns)++-- 9.7.2 Type checking variables+-- tcvar -> tcVar+-- newinstance -> newInstance+-- al_to_subst -> alistToSubst++-- | Type check a variable (actually the variable name).+-- Find the type scheme associated with the variable in the type environment.+-- Return a "new instance" of the type scheme, in which its schematic variables+-- are replaced by new variables, and the unknowns are left as they are.++tcVar :: TypeEnv -> NameSupply -> String -> SuccFail (Subst, Type, NameSupply)+tcVar te ns name =+ case Map.lookup name te of+ Nothing -> + Fail ("BUG in type checker: tcVar: unable to find " ++ name ++ + " in type environment")+ Just ntype ->+ let (t, ns') = newInstance ns ntype+ in Succ (idSubst, t, ns')++newInstance :: NameSupply -> TypeScheme -> (Type, NameSupply)+newInstance ns (TypeScheme schVars t) =+ let (names, ns') = nameSupplyTake ns (length schVars)+ alist = zip schVars names+ phi = alistToSubst alist+ in (subst phi t, ns')++-- | Convert an association list of type variable names into a substitution++alistToSubst :: [(TypeVarName, TypeVarName)] -> Subst+alistToSubst alist tvn =+ case AList.lookup tvn alist of+ Just tvn' -> TypeVar tvn'+ Nothing -> TypeVar tvn++-- 9.7.3 Type checking application+-- tcap -> tcApp+-- a $arrow b -> a `arrow` b = TypeCons "Function" [a, b]++tcApp :: TypeEnv -> NameSupply -> Expr -> Expr+ -> SuccFail (Subst, Type, NameSupply)+tcApp te ns e1 e2 = + let (tvn, ns') = nameSupplyNext ns+ in do+ {+ (phi, [t1, t2], ns'') <- tcExprs te ns' [e1, e2]+ ; phi' <- unify phi (t1, t2 `arrow` (TypeVar tvn))+ ; Succ (phi', phi' tvn, ns'')+ }++arrow :: Type -> Type -> Type+arrow a b = TypeCons "Function" [a, b]+ ++-- 9.7.4 Type checking lambda abstractions+-- +-- LAMBDA -> ELambda++tcLambda :: TypeEnv -> NameSupply -> String -> Expr+ -> SuccFail (Subst, Type, NameSupply)+tcLambda te ns x e =+ let (tvn, ns') = nameSupplyNext ns+ te' = install te x (TypeScheme [] (TypeVar tvn))+ in do+ {+ (phi, t, ns'') <- tcExpr te' ns' e+ ; Succ (phi, phi tvn `arrow` t, ns'')+ } ++-- 9.7.5 Type checking let expressions+-- Not needed, at least for the present ++-- 9.7.6 Type checking letrec expressions+-- Not needed, at least for the present ++++-- Extras (not in the textbook)++-- | Decide the type of a function -- called by the function editor+-- when the Apply button is clicked.+-- decideTypes tries to find the argument types and return type+-- of an expression considered as the body of a function,+-- at the same time checking for consistency of inputs and+-- outputs between the parts of the expression.+-- It returns Succ (argtypes, returntype) if successful;+-- Fail errormessage otherwise.++decideTypes :: String -> Expr -> [String] -> Env -> SuccFail([Type], Type)+decideTypes name body args env =+ let te = envToTypeEnv env+ -- extend te to bind name to a type variable that will not be used+ te' = install te name (TypeScheme [] (TypeVar "u"))+ result :: SuccFail (Type, ([Type], Type))+ result = do+ {+ expr <- toLambdaExpr args body+ ; (_, t, _) <- tcExpr te' (newNameSupply "t") expr+ -- ; fromLambdaType t+ ; res <- fromLambdaType t+ ; Succ (t, res)+ }+ in unsafePerformIO $ do + {+ putStrLn "decideTypes:"+ ; print (args, body)+ ; putStrLn "::"+ -- ; print result+ -- ; return result+ ; case result of+ Succ (t, res) -> do { print t ; return (Succ res) }+ Fail msg -> do { putStrLn msg; return (Fail msg) }+ }++fromLambdaType :: Type -> SuccFail ([Type], Type)+fromLambdaType t = + case t of+ TypeCons "Function" [t1, t2] -> + case t2 of+ TypeCons "Function" _ ->+ do+ {+ (atypes, rtype) <- fromLambdaType t2+ ; return (t1:atypes, rtype)+ }+ _ ->+ Succ ([t1], t2)+ TypeCons "Function" _ -> + error ("fromLambdaType: invalid function type " ++ show t)+ _ -> error ("fromLambdaType: non-function type " ++ show t)++-- | Type check an IF expression+-- *** Failure messages need improvement ***++tcIf :: TypeEnv -> NameSupply -> Expr -> Expr -> Expr + -> SuccFail (Subst, Type, NameSupply)+tcIf te ns expr1 expr2 expr3 = do+ {+ (phi, ts, ns') <- tcExprs te ns [expr1, expr2, expr3]+ ; -- assert ts has length 3+ phi' <- unifyList phi [(head ts, typeBool), (ts !! 1, ts !! 2)]+ ; return (phi', subst phi' (ts !! 1), ns')+ }++-- | Type check a list expression.+-- NOTE: This is to check a single Expr with the EList type, +-- in which all elements must be of the same type,+-- not a general list of Exprs of possibly mixed type. +-- See tcExprs.+--+-- *** Failure messages need improvement ***+--+-- IDEA: can this be reduced to [] and application of (:)?++tcList :: TypeEnv -> NameSupply -> [Expr]+ -> SuccFail (Subst, Type, NameSupply)+tcList te ns exprs =+ case exprs of+ [] -> + let (tvn, ns') = nameSupplyNext ns+ in Succ (idSubst, typeList (TypeVar tvn), ns')+ e:es ->+ do+ {+ (phi, ts, ns') <- tcExprs te ns [e, EList es]+ ; -- assert ts has length 2+ phi' <- unify phi (typeList (head ts), ts !! 1)+ ; return (phi', subst phi' (ts !! 1), ns')+ }++++-- | Create a TypeEnv from an Env+-- Bindings on the left replace bindings of the same name on the right,+-- if any.++envToTypeEnv :: Env -> TypeEnv+envToTypeEnv env =+ let frameTE frame = Map.map vftype frame+ in Map.unions (Prelude.map frameTE env)++-- | The base type environment, derived from the base environment+-- of the Sifflet language (built-in functions)+++baseTypeEnv :: TypeEnv+baseTypeEnv = Map.map vftype (head baseEnv)++vftype :: Value -> TypeScheme+vftype (VFun f) = ftype f+vftype _ = error "vftype: non-function value type"++ftype :: Function -> TypeScheme+ftype f = + let ft = uncurry typeFunction (functionArgResultTypes f)+ -- treat /all/ type variables in ft as schematic+ scvars = typeVarsIn ft+ in TypeScheme scvars ft
+ Language/Sifflet/Util.hs view
@@ -0,0 +1,135 @@+module Language.Sifflet.Util (+ -- | Parser Utilities+ SuccFail(Succ, Fail)+ , parsef, parseInt, parseDouble, parseVerbatim+ -- | String Utilities+ , par+ -- Output Utilities+ , putCatsLn+ , putCatLn+ , info+ , fake+ , stub++ -- | Error Reporting+ , errcat+ , errcats++ -- | List Utilities+ , map2, mapM2+ , adjustAList, adjustAListM+ , insertLastLast, insertLast+ )++where++import Control.Monad()++-- SuccFail: the result of an attempt, succeeds or fails+data SuccFail a = Succ a -- value+ | Fail String -- error message+ deriving (Eq, Read, Show)++instance Monad SuccFail where+ Succ val >>= f = f val+ Fail err >>= _f = Fail err+ return = Succ+ fail = Fail++-- PARSER UTILITIES++parsef :: (Read a) => String -> String -> String -> SuccFail a+parsef typeName inputLabel input =+ case reads input of+ [(value, "")] -> Succ value+ [(_, more)] -> + Fail $ inputLabel ++ ": extra characters after " ++ + typeName ++ ": " ++ more+ _ -> Fail $ inputLabel ++ ": cannot parse as " ++ + typeName ++ ": " ++ input++parseInt :: String -> String -> SuccFail Int+parseInt = parsef "integer"++parseDouble :: String -> String -> SuccFail Double+parseDouble = parsef "real number"++parseVerbatim :: String -> String -> SuccFail String+parseVerbatim _label = Succ++-- | Enclose in parentheses, like a Lisp function call.+-- Example: par "foo" ["x", "y"] = "(foo x y)"++par :: String -> [String] -> String+par f xs = "(" ++ unwords (f:xs) ++ ")"++-- | Write a list of words, separated by spaces+putCatsLn :: [String] -> IO ()+putCatsLn = putStrLn . unwords++-- | Write a list of words, not separated by spaces+putCatLn :: [String] -> IO ()+putCatLn = putStrLn . concat++info :: (Show t) => t -> IO ()+info = print++fake :: String -> IO ()+fake what = putStrLn $ "Faking " ++ what ++ "..."++stub :: String -> IO ()+stub name = putStrLn $ "Stub for " ++ name++-- ERROR REPORTING++-- | Signal an error using a list of strings to be concatenated+errcat :: [String] -> a+errcat = error . concat++-- | Signal an error using a list of strings to be concatenated+-- with spaces between (unwords).+errcats :: [String] -> a+errcats = error . unwords++-- LIST UTILITIES++-- | Generalization of map to lists of lists+map2 :: (a -> b) -> [[a]] -> [[b]]+map2 f rows = -- map (\ row -> map f row) rows+ map (map f) rows++-- | Generalization of mapM to lists of lists+mapM2 :: (Monad m) => (a -> m b) -> [[a]] -> m [[b]]+mapM2 f rows = -- mapM (\ row -> mapM f row) rows+ mapM (mapM f) rows++-- | Insert an item into a list of lists of items,+-- making it the last element in the last sublist+insertLastLast :: [[a]] -> a -> [[a]]+insertLastLast xss x = init xss ++ [insertLast (last xss) x]++-- | Insert an item in a list of items, making it the last element+insertLast :: [a] -> a -> [a]+insertLast xs x = xs ++ [x]++-- | Update a value at a given key by applying a function.+-- Similar to Data.Map.adjust.++-- This implementation, using map, could be inefficient+-- if the key to be updated is near the front of a long list.+adjustAList :: (Eq k) => k -> (v -> v) -> [(k, v)] -> [(k, v)]+adjustAList key f alist =+ map (\ (k, v) -> if k == key then (k, f v) else (k, v)) + alist++-- | Monadic generalization of adjustAList ++-- Same caution re. inefficiency+adjustAListM :: (Eq k, Monad m) => + k -> (v -> m v) -> [(k, v)] -> m [(k, v)]+adjustAListM key f alist =+ mapM (\ (k, v) -> + if k == key + then do { v' <- f v; return (k, v') }+ else return (k, v))+ alist
+ RELEASE-NOTES view
@@ -0,0 +1,159 @@+% RELEASE NOTES++Release notes for sifflet and sifflet-lib++This is the RELEASE-NOTES file for both sifflet and sifflet-lib.+If you are editing it, be sure you are editing the original+and not the copies in the app and lib subdirectories.++Release 2.0.0.0, 2012 July 5+----------------------------++- Reorganized hierarchical modules to conform to+ http://www.haskell.org/haskellwiki/Hierarchical_module_names+ - Sifflet.Data.X is now Data.Sifflet.X+ for X in [Functoid, Geometry, Tree, TreeGraph,+ TreeLayout, WGraph]+ - Sifflet.Foreign.X is now Language.Sifflet.Export.X+ for X in [Exporter, Haskell, Python, ToHaskell, ToPython, ToScheme]+ - Sifflet.Language.X is now Language.Sifflet.X+ for X in [Expr, Parser, SiffML]+ - Sifflet.Rendering.X is now Graphics.Rendering.Sifflet.X+ for X in [Draw, DrawTreeGraph]+ - Sifflet.Text.X is now Text.Sifflet.X+ for X in [Pretty, Repr]+ - Sifflet.UI.X is now Graphics.UI.Sifflet.X+ for X in [Callback, Canvas, Frame, GtkForeign, GtkUtil, LittleGtk,+ RPanel, Tool, Types, Window, Workspace]+ - Sifflet.UI is now Graphics.UI.Sifflet+ - Sifflet.Util is now Language.Sifflet.Util, and+ Sifflet.Examples is now Language.Sifflet.Examples.+ (not clear that this is really the best place for it;+ maybe it hsould be split into pieces, such as + Control.Monad.SuccFail, ...)+ - Sifflet.Examples is now Language.Sifflet.Examples.++- File menu command to save the image of the Workspace or any+ open editor window, in SVG, PS, or PDF format.++- Implemented type checking with type inference.+ Type inference can be used for export to languages+ that require type declarations.+ Type checking error messages are *not* friendly+ for novice programmers; this needs improvement.++- Editing a function can now change its type.+ - It is now possible to change the arguments of a function+ (add, rename, or remove arguments, or for functional+ arguments, change the kind or arity).++- Bug fixes:+ - Fixed bug where the program crashed from incomplete 'if' trees+ - Various small bug fixes.++- Added RELAX NG schema for siffml version 2.0 (in datafiles).+ This is an upwards-compatible extension of siffml 1.0,+ which adds FunctionType to the alternatives for SiffletType.++- Added RELAX NG schema for siffml version 1.0 (in datafiles)++Release 1.2.5.1, 2012 Jun 14 (or later)+---------------------------------------++- Compatibility with mtl 2.1.1++Release 1.2.5, 2012 Jun 1+-------------------------++- Compatibility with GHC 7.4.1++Release 1.2.4, 2011 Mar 18+--------------------------++- Compatibility with HXT > 9.0++Release 1.2.3, 2011 Feb 18+--------------------------++- Compatibility with GHC 7 and Haskell Platform 2011.2++Release 1.2.2, 2010 Dec 3+-------------------------++- Offers to save changes before quitting++Release 1.2.1, 2010 Nov 19+--------------------------++- Compatibility with Haskell GTK packages 0.12++Release 1.2, 2010 Oct 30+------------------------++- Requires HXT 9.0+- Avoids indirect dependency on curl++Release 1.1, 2010 Aug 24+------------------------++- Improved Haskell exporter now removes most unnecessary+ parentheses.+- Exported Haskell code now uses the module Data.Number.Sifflet,+ formerly Sifflet.Data.Number.+- Sifflet now builds with slightly newer versions of the+ Haskell "Gtk2hs" libraries (cairo, gtk, glib),+ should work for versions >= 0.11.0 and < 0.12.++Release 1.0, 2010 Aug 11+------------------------++- Sifflet now has exporters to Scheme, Python, and Haskell and a small+ supporting "library" for each of those languages.+ This is intended for beginning students who are learning these+ languages, not for "industrial-strength" code production.++- Although the exporters apparently generate correct code,+ there are at present too many parentheses -- a few extra in Python,+ many extra in Haskell; no extra parentheses in Scheme, of course.++- Sifflet has a new number type, Sifflet.Data.Number.Number, which allows+ numbers to be exact or inexact, almost as in Scheme.+ Originally intended for the Scheme exporter, it is now+ also used internally by Sifflet's interpreter.++- The code is reorganized. There is now a separate library package,+ sifflet-lib, in addition to the sifflet package which provides+ only the executable. At present, the library's API is highly+ unstable.++- Sifflet has a new parser for user inputs, made with Parsec, + allowing more intuitive input+ of numbers (liberation from Haskell number syntax:+ for example, 0.0 can now be entered as 0.0, 0., or .0)+ and providing more intelligible error messages+ for some kinds of invalid input.++- Sifflet requires Gtk2hs packages (gtk, glib, cairo) version 0.11;+ support for the 0.10 Gtk2hs packages is dropped.++Release 0.1.7, 2010 June 10+---------------------------++- Tightened upper bounds on package dependency versions, in anticipation of + incompatible changes in the fgl API.+- No bug fixes or new features.+- Users who are staying with fgl == 5.4.2.2 will not benefit from upgrading + to Sifflet 0.1.7.++Release 0.1.6, 2010 May 27+--------------------------++- Sifflet offers to save your work if you give the quit or + open file command and have unsaved changes.+- Sifflet now builds with Gtk2hs 0.11.+- Some bug fixes and improved error checking.++Release 0.1.5, 2010 May 13+--------------------------++First public release.
− Sifflet/Data/Functoid.hs
@@ -1,98 +0,0 @@-module Sifflet.Data.Functoid- (Functoid(..)- , functoidName, functoidArgNames, functoidHeader- , FunctoidLayout(..), flayout- , flayoutBBox, flayoutSize, flayoutWidth, flayoutBottom- , flayoutWiden)--where--import Data.Graph.Inductive as G--import Sifflet.Data.Geometry-import Sifflet.Language.Expr-import Sifflet.Data.TreeLayout---- | A Functoid is either a FunctoidParts, representing a partially--- defined function, or a (completely defined) Function.--- A FunctoidParts represents an incompletely defined function,--- which has a function name, argument names, and a list of graph nodes --- representing the function body which might not yet form--- a complete expression tree, e.g., nodes might--- be unconnected.--data Functoid = FunctoidParts {fpName :: String,- fpArgs :: [String],- fpNodes :: [G.Node]}- | FunctoidFunc {fpFunc :: Function}- deriving (Show)--functoidName :: Functoid -> String-functoidName (FunctoidParts {fpName = name}) = name-functoidName (FunctoidFunc function) = functionName function--functoidArgNames :: Functoid -> [String]-functoidArgNames (FunctoidParts {fpArgs = args}) = args-functoidArgNames (FunctoidFunc function) = functionArgNames function--functoidHeader :: Functoid -> String-functoidHeader f = unwords (functoidName f : functoidArgNames f)---- | What is a FunctoidLayout? You can think of it as a --- tree layout for a Frame,--- or for a Functoid, or as a Forest of Tree Layouts!--- For an Edit frame, it must be a forest, since the nodes are not--- yet necessarily connected into a tree.--- For a call frame, it will be a single TreeLayout (singleton list).--data FunctoidLayout = FLayoutTree (TreeLayout ExprNode)- | FLayoutForest [TreeLayout ExprNode] BBox---- | Graphically lay out the Functoid.--- mvalues: Nothing implies an uncalled frame,--- Just values implies a called frame, even with 0 arguments.--flayout :: Style -> Functoid -> Env -> Maybe [Value] -> FunctoidLayout-flayout style functoid env mvalues =- case functoid of- FunctoidParts {} ->- -- STUB: [] and bbox ***- -- WELL: is this supposed to CREATE the tlo or RETRIEVE the tlo?- -- Either way, it won't work without access to the WGraph!- FLayoutForest [] (BBox (hpad style) (vpad style) 300 300)- FunctoidFunc function ->- let expr = functionBody function- exprTree = case mvalues of- Nothing -> exprToTree expr- Just _values -> evalTree (exprToTree expr) env- tlo = treeLayout style (exprNodeIoletCounter env) exprTree- in FLayoutTree tlo--flayoutBBox :: FunctoidLayout -> BBox-flayoutBBox aflayout =- case aflayout of- FLayoutTree t -> layoutTreeBB t- FLayoutForest _ bbox -> bbox--flayoutSize :: FunctoidLayout -> Size-flayoutSize = bbSize . flayoutBBox--flayoutWidth :: FunctoidLayout -> Double-flayoutWidth = bbWidth . flayoutBBox--flayoutBottom :: FunctoidLayout -> Double-flayoutBottom = bbBottom . flayoutBBox--flayoutWiden :: FunctoidLayout -> Double -> FunctoidLayout -flayoutWiden aflayout minWidth =- case aflayout of- FLayoutTree t -> FLayoutTree (treeLayoutWiden t minWidth)- FLayoutForest f bbox -> FLayoutForest f bbox -- STUB ***--instance Translate FunctoidLayout where- translate dx dy fl =- case fl of- FLayoutTree t -> - FLayoutTree (translate dx dy t)- FLayoutForest f b ->- FLayoutForest (translate dx dy f) (translate dx dy b)
− Sifflet/Data/Geometry.hs
@@ -1,162 +0,0 @@-module Sifflet.Data.Geometry- (Position(..), - positionDelta, positionDistance,- positionDistanceSquared, positionCloseEnough,- Circle(..), pointInCircle,- Size(..),- BBox(..), - bbX, bbY, bbWidth, bbSetWidth, bbHeight, bbPosition, bbSize,- bbToRect, bbFromRect, bbCenter, bbLeft, bbXCenter, bbRight,- bbTop, bbYCenter, bbBottom,- bbMerge, bbMergeList, pointInBB,- Widen(widen),- Translate(..)- )--where--import Data.Tree as T-import Graphics.UI.Gtk(Rectangle(Rectangle))---- A Position may be interpreted either absolutely, as a point (x, y);--- or relatively, as an offset (dx, dy)--data Position = Position {posX :: Double, posY :: Double} -- x, y- deriving (Eq, Read, Show)--positionDelta :: Position -> Position -> (Double, Double)-positionDelta (Position x1 y1) (Position x2 y2) = (x2 - x1, y2 - y1)--positionDistance :: Position -> Position -> Double-positionDistance p1 p2 = sqrt (positionDistanceSquared p1 p2)--positionDistanceSquared :: Position -> Position -> Double-positionDistanceSquared (Position x1 y1) (Position x2 y2) =- (x1 - x2) ** 2 + (y1 - y2) ** 2--positionCloseEnough :: Position -> Position -> Double -> Bool-positionCloseEnough p1 p2 radius =- -- Essentially asks if p1 and p2 are nearly intersecting,- -- i.e., if p1 is within a circle with center p2 and the given radius- positionDistanceSquared p1 p2 <= radius ** 2----data Circle = Circle {circleCenter :: Position,- circleRadius :: Double}- deriving (Eq, Read, Show)--pointInCircle :: Position -> Circle -> Bool-pointInCircle point (Circle center radius) =- positionCloseEnough point center radius--data Size = Size {sizeW :: Double, sizeH :: Double} -- width, height- deriving (Eq, Read, Show)---- | BBox x y width height; (x, y) is the top left corner--data BBox = BBox Double Double Double Double- deriving (Eq, Read, Show)---- | BBox accessors and utilities--bbX, bbY, bbWidth, bbHeight :: BBox -> Double-bbX (BBox x _y _w _h) = x--bbY (BBox _x y _w _h) = y-bbWidth (BBox _x _y w _h) = w-bbHeight (BBox _x _y _w h) = h--bbPosition :: BBox -> Position-bbPosition (BBox x y _w _h) = Position x y--bbSize :: BBox -> Size-bbSize (BBox _x _y w h) = Size w h--bbCenter :: BBox -> Position-bbCenter (BBox x y w h) = Position (x + w / 2) (y + h / 2)--bbSetWidth :: BBox -> Double -> BBox-bbSetWidth (BBox x y _w h) nwidth = BBox x y nwidth h--bbLeft, bbXCenter, bbRight :: BBox -> Double-bbLeft = bbX-bbXCenter (BBox x _y w _h) = x + w / 2-bbRight (BBox x _y w _h) = x + w--bbTop, bbYCenter, bbBottom :: BBox -> Double-bbTop = bbY-bbYCenter (BBox _x y _w h) = y + h / 2-bbBottom (BBox _x y _w h) = y + h--bbToRect :: BBox -> Rectangle-bbToRect (BBox x y w h) = - Rectangle (round x) (round y) (round w) (round h)--bbFromRect :: Rectangle -> BBox-bbFromRect (Rectangle x y w h) =- BBox (fromIntegral x) (fromIntegral y)- (fromIntegral w) (fromIntegral h)---- | Form a new BBox which encloses two bboxes-bbMerge :: BBox -> BBox -> BBox-bbMerge bb1 bb2 =- let f1 ! f2 = f1 (f2 bb1) (f2 bb2)- bottom = max ! bbBottom -- i.e., max (bbBottom bb1) (bbBottom bb2)- top = min ! bbTop- left = min ! bbLeft- right = max ! bbRight- in BBox left top (right - left) (bottom - top)--bbMergeList :: [BBox] -> BBox-bbMergeList [] = error "bbMergeList: empty list"-bbMergeList (b:bs) = foldl bbMerge b bs- --- Test whether a point (e.g., from mouse click) is within a--- bounding box-pointInBB :: Position -> BBox -> Bool-pointInBB (Position x y) (BBox x1 y1 w h) =- x >= x1 &&- x <= x1 + w &&- y >= y1 &&- y <= y1 + h--class Widen a where- -- | Make an object have at least a specified minimum width;- -- does nothing if it's already at least that wide- widen :: a -> Double -> a--instance Widen BBox where- widen bb@(BBox x y w h) minWidth =- if w >= minWidth- then bb- else BBox x y minWidth h---- | A Translate is a thing that can be repositioned by--- delta x and delta y--class Translate a where- translate :: Double -- ^ delta X- -> Double -- ^ delta Y- -> a -- ^ thing in old position- -> a -- ^ thing in new position--instance (Translate e) => Translate [e] where- translate dx dy = map (translate dx dy)--instance (Translate e) => Translate (Tree e) where- translate dx dy t = - T.Node (translate dx dy (rootLabel t))- (translate dx dy (subForest t))---instance Translate BBox where- translate dx dy (BBox x y w h) = BBox (x + dx) (y + dy) w h--instance Translate Position where- translate dx dy (Position x y) = Position (x + dx) (y + dy)--instance Translate Circle where- translate dx dy (Circle center radius) =- Circle (translate dx dy center) radius-
− Sifflet/Data/Tree.hs
@@ -1,67 +0,0 @@--- Tree.hs--- General (not binary) trees--module Sifflet.Data.Tree - (T.Tree(..), tree, leaf, isLeaf, treeSize, treeDepth- , Repr(..)- -- Since String is not an instance of Repr,- -- we need to convert (Tree String) to (Tree Name)- , Name(..), nameTree- , putTree, putTreeR, putTreeRs, putTreeS- , drawTreeShow- )--where--import Data.Tree as T-import Sifflet.Text.Repr---- Makers--tree :: e -> Forest e -> T.Tree e-tree root subtrees = T.Node {rootLabel = root, subForest = subtrees}--leaf :: e -> T.Tree e-leaf x = tree x []--isLeaf :: T.Tree e -> Bool-isLeaf (T.Node _root []) = True-isLeaf _ = False--treeSize :: T.Tree e -> Int-treeSize (T.Node _root subtrees) = 1 + sum (map treeSize subtrees)--treeDepth :: T.Tree e -> Int-treeDepth (T.Node _root []) = 1-treeDepth (T.Node _root subtrees) = 1 + maximum (map treeDepth subtrees)--{- tree_map or treeMap:- removed; use (Functor) fmap instead--}--{- tree_mapM:- removed, use Data.Traversable.mapM instead.--}--nameTree :: T.Tree String -> T.Tree Name-nameTree = fmap Name--putTree :: (Show e) => T.Tree e -> IO ()-putTree = putTreeS---- putTreeR -- using repr (first part)-putTreeR :: (Repr e) => T.Tree e -> IO()-putTreeR = putStrLn . drawTree . fmap repr---- putTreeRs -- using reprs (all parts)-putTreeRs :: (Repr e) => T.Tree e -> IO()-putTreeRs = putStrLn . drawTree . fmap reprs----- putTreeS -- using show-putTreeS :: (Show e) => T.Tree e -> IO ()-putTreeS = putStrLn . drawTreeShow--drawTreeShow :: (Show e) => T.Tree e -> String-drawTreeShow = drawTree . fmap show-
− Sifflet/Data/TreeGraph.hs
@@ -1,250 +0,0 @@-module Sifflet.Data.TreeGraph - (LayoutGraph, - flayoutToGraph, treeLayoutToGraph,- -- treeToGraph, - orderedTreeToGraph, - treeGraphNodesTree, graphToTreeOriginal,- graphToTreeStructure, - flayoutToGraphRoots,- graphToOrderedTree, graphToOrderedTreeFrom,- orderedChildren, adjCompareEdge,- nextNodes, -- exported for testing; any other reason?- grTranslateNode, grTranslateSubtree, grTranslateGraph,-- -- moved from Workspace.hs:- functoidToFunction, graphToExprTree,-- )--where--import Data.List (sort, sortBy)--import Data.Graph.Inductive as G--import Sifflet.Data.Functoid-import Sifflet.Data.Geometry-import Sifflet.Data.Tree as T-import Sifflet.Data.TreeLayout-import Sifflet.Data.WGraph-import Sifflet.Language.Expr-import Sifflet.Util--type LayoutGraph n e = Gr (LayoutNode n) e--flayoutToGraph :: FunctoidLayout -> WGraph-flayoutToGraph tlo = - case tlo of - FLayoutTree t -> treeLayoutToGraph t- FLayoutForest ts _bbox -> - foldl grAddGraph wgraphNew (map treeLayoutToGraph ts)--treeLayoutToGraph :: TreeLayout ExprNode -> WGraph-treeLayoutToGraph = orderedTreeToGraph . fmap WSimple----- flayoutToGraphRoots returns a list of graph nodes (Ints)--- corresponding to the root of the tree, or the roots of--- the trees in the forest.--- The list is ordered in the same sense as the graph nodes.--flayoutToGraphRoots :: FunctoidLayout -> [G.Node]-flayoutToGraphRoots (FLayoutTree _t) = [1]-flayoutToGraphRoots (FLayoutForest trees _bbox) =- let loop _ [] res = reverse res- loop next (t:ts) res =- loop (next + treeSize t) ts (next:res)- in loop 1 trees []----- {-# DEPRECATED treeToGraph "use ??? instead" #-}---- treeToGraph :: Tree e -> Gr e ()--- treeToGraph (T.Node root subtrees) = --- -- Convert an ordered tree to a graph.--- -- The graph edge labels from parent to child --- -- are integers corresponding to the order of the children.--- -- So, to recover the ordered list of children, --- -- sort by the edge label.--- let g0 = empty :: Gr e ()--- g1 = insNode (1, root) g0---- grow :: Gr e () -> [(Tree e, G.Node)] -> G.Node -> Gr e ()--- -- grow graph [(tree, parent)] node--- grow g [] _ = g--- grow g ((t, p):tps) n =--- -- insert t forming g', insert ts into g'--- let edge = ((), p) -- predecessors, or both?--- g' = ([edge], n, (rootLabel t), []) & g -- assume pred only--- in grow g' (tps ++ [(s, n) | s <- subForest t])--- (succ n)---- in grow g1 [(s, 1) | s <- subtrees] 2--sprout :: G.Node -> Tree e -> [(G.Node, WEdge, Tree e)]-sprout parent (T.Node _ subtrees) =- -- create triples (parent graph node, edge, subtree)- let m = length subtrees - 1- in [(parent, WEdge e, s) | (e, s) <- zip [0..m] subtrees]---orderedTreeToGraph :: Tree e -> Gr e WEdge-orderedTreeToGraph otree = - -- Convert an ordered tree to a graph.- -- The graph edge labels from parent to child - -- are integers corresponding to the order of the children.- -- So, to recover the ordered list of children, - -- sort by the edge label.- let g0 = empty :: Gr e WEdge- g1 = insNode (1, rootLabel otree) g0-- grow :: Gr e WEdge -> [(G.Node, WEdge, Tree e)] -> G.Node -> Gr e WEdge- -- grow graph [(parent, edgeLabel, subtree), ...] node- grow g [] _ = g- grow g ((p, e, t):pets) n =- -- insert pet forming g', insert pets into g';- -- n is the node id for the root of this subtree- let adj = (e, p) -- to parent (priority, node)- g' = ([adj], n, (rootLabel t), []) & g - n' = succ n- in grow g' (pets ++ - sprout n t- -- [(s, n) | s <- subForest t])- )- n'-- in grow g1 (sprout 1 otree) 2---- And what about this (e, Node) type? Is it some sort of monad?--treeGraphNodesTree :: Tree e -> Tree Node-treeGraphNodesTree atree =- -- returns a tree of Nodes of the Graph (treeToGraph atree)- let gnTree :: Tree e -> Node -> Node -> (Tree Node, Node)- gnTree (T.Node _root subtrees) rootNode next =- -- rootNode = Node (number) for the root,- -- next = next unused Node- let (nNodes, next') = nextNodes subtrees next- (subtrees', next'') = gnSubtrees subtrees nNodes next'- in (T.Node rootNode subtrees', next'')-- gnSubtrees :: [Tree e] -> [Node] -> Node -> ([Tree Node], Node)- gnSubtrees [] [] next = ([], next)- gnSubtrees (t:ts) (n:ns) next = - let (t', next') = gnTree t n next- (ts', next'') = gnSubtrees ts ns next'- in ((t' : ts'), next'')- gnSubtrees _ _ _ = error "gnSubtrees: list lengths do not match"-- in fst (gnTree atree 1 2)--nextNodes :: [e] -> Node -> ([Node], Node)-nextNodes items next = - -- next is the next unused Node (number).- -- Returns list of new nodes, and a new "next" node- -- E.g., nextNodes [a, b] 3 = ([3, 4], 5)- -- nextNodes [c, d, e] 5 = ([5, 6, 7], 8)- -- nextNodes [] 8 = ([], 8)- let n = length items- next' = next + n- in ([next .. (next' - 1)], next')---- When a tree is converted to a graph,--- each tree node's ordered children get graph node numbers--- in ascending order. Therefore, when reconstructing the tree,--- sorting the graph nodes restores the order of the children--- as in the tree.--graphToOrderedTree :: Gr e WEdge -> Tree e--- inverse of orderedTreeToGraph-graphToOrderedTree g = graphToOrderedTreeFrom g 1--graphToOrderedTreeFrom :: Gr e WEdge -> G.Node -> Tree e-graphToOrderedTreeFrom g n =- case lab g n of- Just label -> - T.Node label (map (graphToOrderedTreeFrom g) (orderedChildren g n))- Nothing -> - errcats ["missing label for node", show n]---- | List of the nodes children, ordered by edge number-orderedChildren :: Gr e WEdge -> G.Node -> [G.Node]-orderedChildren g = map fst . sortBy adjCompareEdge . lsuc g--adjCompareEdge :: (Node, WEdge) -> (Node, WEdge) -> Ordering-adjCompareEdge (_n1, e1) (_n2, e2) = compare e1 e2--{-# DEPRECATED graphToTreeOriginal "use ??? instead" #-}--graphToTreeOriginal :: Gr e () -> G.Node -> Tree e--- (\g -> graphToTreeOriginal g 1) is the inverse of treeToGraph-graphToTreeOriginal g n =- case lab g n of- Just label -> T.Node label (map (graphToTreeOriginal g) - (sort (suc g n)))- _ -> errcats ["missing label for node", show n]--{-# DEPRECATED graphToTreeStructure "use ??? instead" #-}--graphToTreeStructure :: Gr n e -> G.Node -> Tree G.Node--- This is *not* an inverse of treeToGraph.--- Rather, graphToTreeStructure (treeToGraph t 1) is a tree t' of Nodes--- (i.e., integer identifiers of nodes in the graph)--- which parallels the structure of t.--graphToTreeStructure g n = T.Node n (map (graphToTreeStructure g)- (sort (suc g n)))--grTranslateNode :: - Node -> Double -> Double -> LayoutGraph n e -> LayoutGraph n e-grTranslateNode node dx dy graph = - grUpdateNodeLabel graph node (translate dx dy)--grTranslateSubtree :: - Node -> Double -> Double -> LayoutGraph n e -> LayoutGraph n e-grTranslateSubtree root dx dy graph = - let trSubtrees :: [Node] -> LayoutGraph n e -> LayoutGraph n e- trSubtrees [] g = g- trSubtrees (r:rs) g = trSubtrees (rs ++ suc g r)- (grTranslateNode r dx dy g)- in trSubtrees [root] graph--grTranslateGraph :: Double -> Double -> LayoutGraph n e -> LayoutGraph n e-grTranslateGraph dx dy graph = nmap (translate dx dy) graph--grUpdateNodeLabel :: (DynGraph g) => g a b -> Node -> (a -> a) -> g a b-grUpdateNodeLabel graph node updater =- case match node graph of- (Nothing, _) -> error "no such node"- (Just (preds, jnode, label, succs), graph') ->- -- jnode == node- (preds, jnode, updater label, succs) & graph'--functoidToFunction :: - Functoid -> WGraph -> G.Node -> Env -> SuccFail Function-functoidToFunction functoid graph frameNode env =- case functoid of- FunctoidFunc f -> Succ f- FunctoidParts {fpName = name, fpArgs = args} ->- let roots = suc graph frameNode- expr = treeToExpr $ graphToExprTree graph (head roots)- impl = Compound args expr- in - -- Check whether the frame contains a single tree- if length roots /= 1- then Fail "The graph structure is not a tree!"- else case decideTypes expr args env of- Left errmsg -> Fail errmsg- Right (atypes, rtype) -> - Succ (Function (Just name) atypes rtype impl)---graphToExprTree :: WGraph -> G.Node -> Tree ExprNode-graphToExprTree g root =- let extractExprNode wnode =- case wnode of- WSimple layoutNode -> gnodeValue (nodeGNode layoutNode)- WFrame _ -> error "graphToExprTreeFrom: unexpected WFrame node"- in fmap extractExprNode (graphToOrderedTreeFrom g root)--
− Sifflet/Data/TreeLayout.hs
@@ -1,660 +0,0 @@-module Sifflet.Data.TreeLayout - (VColor(..), white, cream, black, lightBlue, lightBlueGreen- , lightGray, mediumGray, darkGray- , yellow, darkBlueGreen, blueGreen- , Style(..), VFont(..), defaultStyle, style0, style1, style2, style3- , wstyle- , styleIncreasePadding- , FontTextExtents(..), setFont, measureText, styleTextExtents, ftExtents- , TextBox(..), makeTextBox, tbWidth, tbHeight, tbBottom- , tbCenter, tbTextCenter, tbBoxCenter, offsetTextBoxCenters- , tbSetWidth- , treeSizes- , GNode(..), treeGNodes, gnodeText, gnodeTextBB- , Iolet(..), ioletCenter, makeIolets, makeIoletsRow- , pointInIolet- , IoletCounter, zeroIoletCounter- , makeGNode- , TreeLayout, LayoutNode(..)- , layoutNodeSource, layoutRootBB, layoutTreeBB- , treeLayout- , treeLayoutPaddedSize, treeLayoutSize, treeLayoutWidth- , layoutTreeMoveCenterTo- , pointInLayoutNode, pointInGNode, findRect- , treeLayoutWiden- )--where--import System.IO.Unsafe---- All this VVV is needed just to measure a piece of text;--- maybe font and text should be split out to a new module?--import Graphics.Rendering.Cairo (FontExtents(..),- FontSlant(..), FontWeight(..),- Format(..), Render,- TextExtents(..),- fontExtents,- renderWith,- textExtents,- selectFontFace, setFontSize,- withImageSurface)--import Graphics.UI.Gtk (Rectangle)-import Data.Traversable () -- imports only instance declarations--import Sifflet.Data.Geometry-import Sifflet.Data.Tree as T hiding (tree)-import Sifflet.Text.Repr ()-import Sifflet.Util---- Do colors really belong here???--data VColor = ColorRGB Double Double Double |- ColorRGBA Double Double Double Double--black, white, cream, lightBlue, lightBlueGreen :: VColor--black = ColorRGB 0 0 0-white = ColorRGB 1 1 1-cream = ColorRGB 0.94902 0.94902 0.82745-lightBlue = ColorRGB 0.43529 0.43922 0.95686-lightBlueGreen = ColorRGB 0 1 1--lightGray, mediumGray, darkGray :: VColor-lightGray = ColorRGBA 0.75 0.75 0.75 0.5-mediumGray = ColorRGB 0.5 0.5 0.5-darkGray = ColorRGB 0.25 0.25 0.25--yellow, darkBlueGreen, blueGreen :: VColor--yellow = ColorRGB 0.9 0.9 0-darkBlueGreen = ColorRGB 0 0.3 0.3-blueGreen = ColorRGB 0 0.4 0.4---- darkRed = ColorRGB 0.5 0 0, -- dark red--data VFont = VFont {vfontFace :: String, vfontSlant :: FontSlant,- vfontWeight :: FontWeight,- vfontSize :: Double}--data Style = - Style {styleFont :: VFont,- lineWidth :: Double,- textMargin :: Double, -- in box on all four sides- hpad, vpad :: Double, -- internode (interbox) spacing- exomargin :: Double, -- margin of the tree within the window- vtinypad :: (Double, Double), -- node-edge spacing (above, below)- styleFramePad :: Double, -- pad between frames- styleNormalTextColor, styleNormalFillColor, - styleNormalEdgeColor,- styleActiveTextColor, styleActiveFillColor,- styleActiveEdgeColor,- styleSelectedTextColor, styleSelectedFillColor, - styleSelectedEdgeColor,- styleTetherColor :: VColor,- -- auxiliary text style, e.g., the value of an ExprNode- styleAuxOffset :: Position, -- center-to-center offset dx dy- styleAuxColor :: VColor,- styleAuxFont :: VFont,- styleIoletRadius :: Double, -- could extend to other shapes ***- styleShowNodeBoxes :: Bool, -- draw a box around expr nodes- styleShowNodePorts :: Bool -- draw the I/O ports of expr nodes- }--styleIncreasePadding :: Style -> Double -> Style--- increase hpad, vpad of style by multiplication-styleIncreasePadding style factor =- style {hpad = factor * hpad style,- vpad = factor * vpad style}--style0, style1, style2, style3, defaultStyle :: Style--style0 = - let green = ColorRGB 0.1 0.9 0.1- veryDarkGray = ColorRGB 0.1 0.1 0.1- brighterGreen = ColorRGB 0.5 0.95 0.5- in Style {styleFont = VFont "serif" FontSlantNormal FontWeightNormal 18, - lineWidth = 2, - textMargin = 18.0, -- text within its box- hpad = 27, vpad = 36, -- inter-node separation- exomargin = 0,- vtinypad = (4.5, 4.5), -- node-edge separation- styleFramePad = 35, -- padding between frames- -- Foreground and background colors- styleNormalTextColor = green,- styleNormalEdgeColor = green,- styleNormalFillColor = veryDarkGray,- styleActiveTextColor = brighterGreen,- styleActiveEdgeColor = brighterGreen,- styleActiveFillColor = mediumGray,- styleSelectedTextColor = blueGreen,- styleSelectedEdgeColor = blueGreen,- styleSelectedFillColor = darkGray,- styleTetherColor = white,- styleAuxOffset = Position 0.0 (-20.0),- styleAuxColor = lightGray,- styleAuxFont = - VFont "serif" FontSlantItalic FontWeightNormal 12,- styleIoletRadius = 5,- styleShowNodeBoxes = True, -- draw a box around expr nodes- styleShowNodePorts = True -- draw the I/O ports of expr nodes- }--style1 = - let veryDarkBlue = ColorRGB 0 0 0.1- pinkIHope = ColorRGB 1 0.9 0.9- in style0 {styleNormalTextColor = veryDarkBlue,- styleNormalEdgeColor = veryDarkBlue,- styleNormalFillColor = lightGray,- styleActiveTextColor = pinkIHope,- styleActiveEdgeColor = pinkIHope- }--style2 = - let darkPink = ColorRGB 1 0.5 0.5- in style1 {styleNormalFillColor = white,- styleActiveTextColor = darkPink,- styleActiveEdgeColor = darkPink}--style3 = - let semiTranspBlue = ColorRGBA 0 0.2 0.9 0.5- in style0 {exomargin = 10,- styleNormalTextColor = yellow,- styleNormalEdgeColor = yellow,- styleNormalFillColor = darkBlueGreen,- styleActiveTextColor = yellow,- styleActiveEdgeColor = yellow,- styleActiveFillColor = blueGreen,- styleAuxColor = semiTranspBlue- } --defaultStyle = style3--wstyle :: Style-wstyle = - style3 { - -- smaller to allow multiple frames on one canvas- styleFont = VFont "serif" FontSlantNormal FontWeightNormal 14,- textMargin = 8.0,- hpad = 10.0, vpad = 24.0, - vtinypad = (0.0, 0.0),- exomargin = 10.0, -- between tree and window edges- styleFramePad = 15,-- -- colors- styleNormalTextColor = yellow,- styleNormalEdgeColor = mediumGray,- styleNormalFillColor = darkBlueGreen,-- styleActiveTextColor = yellow,- styleActiveEdgeColor = mediumGray,- styleActiveFillColor = blueGreen,-- styleSelectedTextColor = black,- styleSelectedEdgeColor = lightBlueGreen,- styleSelectedFillColor = lightBlueGreen,-- styleTetherColor =- ColorRGBA 0.7 0.7 0.7 0.3, -- light gray- - -- node values- styleAuxOffset = Position 0 (-16),- styleAuxFont = VFont "serif" FontSlantItalic FontWeightNormal 12,- styleAuxColor = ColorRGBA 0.9 0.5 0.5 1.0,- -- ColorRGBA 1 0.9 0.9 0.75 - styleIoletRadius = 5- }- --pointInLayoutNode :: Position -> LayoutNode e -> Bool-pointInLayoutNode point = pointInGNode point . nodeGNode--pointInGNode :: Position -> GNode e -> Bool-pointInGNode point = pointInBB point . gnodeNodeBB---- UNUSED: findNode (except in findRect)--- find the node, if any, containing a point --- might need to adjust this to take iolets into account ******-findNode :: Position -> TreeLayout e -> Maybe (LayoutNode e)-findNode point (T.Node node@(LayoutNode _rootGNode treeBB) sublayouts) =- if pointInBB point treeBB- then if pointInLayoutNode point node- then Just node- else findInSubs point sublayouts- else Nothing-- where findInSubs :: Position -> [TreeLayout e] -> Maybe (LayoutNode e)- findInSubs _p [] = Nothing- findInSubs p (l:ls) = - let found = findNode p l in- case found of- (Just _) -> found- Nothing -> findInSubs p ls---- UNUSED: findRect-findRect :: Position -> TreeLayout e -> Maybe Rectangle-findRect point tlo = - case findNode point tlo of- Nothing -> Nothing- Just node -> Just (bbToRect (gnodeNodeBB (nodeGNode node)))--setFont :: VFont -> Render ()-setFont (VFont face slant weight size) = do- selectFontFace face slant weight- setFontSize size--data FontTextExtents = FontTextExtents {- fontAscent::Double, fontDescent::Double, fontHeight::Double,- fontMaxXadvance::Double, fontMaxYadvance::Double,- textXbearing::Double, textYbearing::Double,- extTextWidth::Double, extTextHeight::Double,- textXadvance::Double, textYadvance::Double}- deriving (Eq, Read, Show)--measureText :: Style -> String -> Size-measureText style str =- let extents = styleTextExtents style str- in Size (textXadvance extents)- (fontHeight extents + fontDescent extents)---- Rationale for unsafePerformIO:--- font extents of a string will not change unless the font itself--- changes, which is extremely unlikely--styleTextExtents :: Style -> String -> FontTextExtents-styleTextExtents style str = - unsafePerformIO $ - withImageSurface FormatARGB32 0 0 $ \ surface ->- renderWith surface $ - setFont (styleFont style) >>- ftExtents str---- | ftExtents: used for what?-ftExtents :: String -> Render FontTextExtents-ftExtents text = do- FontExtents asc desc fheight maxxadv maxyadv <- fontExtents- TextExtents xbear ybear twidth theight xadv yadv <- textExtents text- return (FontTextExtents asc desc fheight maxxadv maxyadv- xbear ybear twidth theight xadv yadv)--data TextBox = TextBox {tbText :: String,- tbTextBB :: BBox, -- BBox of text without margins- tbBoxBB :: BBox -- of text with margins- }- deriving (Eq, Read, Show)--makeTextBox :: Style -> String -> TextBox-makeTextBox style text =- let extents = styleTextExtents style text- Size textW textH = measureText style text- -- textW = textXadvance extents- -- textH = fontHeight extents + fontDescent extents- margin = textMargin style-- boxW = textW + 2.0 * margin -- max (textW + 2.0 * margin) minWidth- boxH = textH + 2.0 * margin-- boxX = 0- boxY = 0-- textX = (boxW - textW) / 2.0- -- raise text slightly, by a fraction of its font descent- -- (why? because it just looks better!)- -- Maybe this should be made an aspect of style?- raise = 0.5 * fontDescent extents- textY = fontHeight extents + (boxH - textH) / 2.0 - raise-- textBB = BBox textX textY textW textH- boxBB = BBox boxX boxY boxW boxH-- in TextBox text textBB boxBB--instance Widen TextBox where- widen tb@(TextBox _text textBB boxBB) minWidth =- let w = bbWidth boxBB- in if w >= minWidth- then tb- else let dw = minWidth - w - in tb {tbTextBB = translate (dw / 2) 0 textBB,- tbBoxBB = widen boxBB minWidth}--instance Translate TextBox where- translate dx dy (TextBox text textBB boxBB) = - TextBox text (translate dx dy textBB) (translate dx dy boxBB)---tbWidth :: TextBox -> Double-tbWidth = bbWidth .tbBoxBB--tbSetWidth :: TextBox -> Double -> TextBox-tbSetWidth tbox w = - let TextBox text textBB boxBB = tbox- boxBB' = bbSetWidth boxBB w- (dx, dy) = positionDelta (bbCenter boxBB) (bbCenter boxBB')- in TextBox text (translate dx dy textBB) boxBB'--tbHeight :: TextBox -> Double-tbHeight = bbHeight . tbBoxBB--tbBottom :: TextBox -> Double-tbBottom = bbBottom . tbBoxBB---- The geometric "center" of a TextBox is the center of its "BoxBB"-tbCenter :: TextBox -> Position-tbCenter = tbBoxCenter- -tbBoxCenter :: TextBox -> Position-tbBoxCenter = bbCenter . tbBoxBB---- and the center of its text should have the same x coordinate,--- but due to the way text is positioned, maybe not the same y.--- But do we ever need this?-tbTextCenter :: TextBox -> Position-tbTextCenter = bbCenter . tbTextBB--offsetTextBoxCenters :: Position -> TextBox -> TextBox -> TextBox-offsetTextBoxCenters offset anchor floater =- -- Position the floater text box so that its center differs- -- from the anchor text box by the given offset.- -- (The offset argument comes first, like dx dy in translate.)- let Position ax ay = tbBoxCenter anchor- Position fx fy = tbBoxCenter floater- Position ox oy = offset- -- translate floater by (dx, dy), where fx + dx = ax + ox, - -- so dx = ax + ox - fx; and dy similarly- dx = ax + ox - fx- dy = ay + oy - fy- in translate dx dy floater----- GNode = graphical node, suitable for drawing-data GNode e = GNode {gnodeValue :: e, - gnodeTextBoxes :: [TextBox], -- one or two only- gnodeNodeBB :: BBox, -- encloses all textboxes- gnodeInlets :: [Iolet], -- input connectors- gnodeOutlets :: [Iolet] -- output connectors- }- deriving (Eq)--gnodeText :: GNode e -> String-gnodeText = tbText . head . gnodeTextBoxes--gnodeTextBB :: GNode e -> BBox-gnodeTextBB = tbTextBB . head . gnodeTextBoxes--instance (Show e) => Show (GNode e) where- show (GNode v tbs nodeBB inlets outlets) = - par "GNode" [show v, show tbs, show nodeBB, show inlets, show outlets]--instance Translate (GNode e) where- translate dx dy (GNode value textboxes nodeBB inlets outlets) = - GNode value - (map (translate dx dy) textboxes)- (translate dx dy nodeBB)- (translate dx dy inlets)- (translate dx dy outlets)----- An IoletCounter returns (no. of inlets, no. of outlets)-type IoletCounter e = e -> (Int, Int)---- A sort of "default" IoletCounter returning zeroes-zeroIoletCounter :: IoletCounter e-zeroIoletCounter _node = (0, 0)---- An IoletsMaker ???--- type IoletsMaker e = Style -> e -> ([Position], [Position])--makeGNode :: (Repr e) => Style -> IoletCounter e -> e -> GNode e-makeGNode style countIolets value =- -- The function countIolets creates a tuple (# of inlets, # of outlets)- -- for the node, of course being (0, 0) if we don't want any.- let textboxes1 = map (makeTextBox style) (reprl value)- textboxes2 = case textboxes1 of- [_tb] -> textboxes1- [tb1, tb2] -> - [tb1, - offsetTextBoxCenters (styleAuxOffset style) tb1 tb2]- _ -> wrong textboxes1- nodeBB = case textboxes2 of- [tb] -> tbBoxBB tb- [tb1, tb2] -> bbMerge (tbBoxBB tb1) (tbBoxBB tb2)- _ -> wrong textboxes2- wrong tbs = - errcats ["makeGNode: wrong no. of text boxes;",- "expected 1 or 2, but got", show (length tbs)]- (inlets, outlets) = makeIolets style nodeBB (countIolets value)- in GNode value textboxes2 nodeBB inlets outlets--makeIolets :: Style -> BBox -> (Int, Int) -> ([Iolet], [Iolet])-makeIolets style bbox (nin, nout) =- -- make the (inlets, outlets)- (makeIoletsRow style (bbXCenter bbox) (bbBottom bbox) nin,- makeIoletsRow style (bbXCenter bbox) (bbTop bbox) nout)--makeIoletsRow :: Style -> Double -> Double -> Int -> [Iolet]-makeIoletsRow style cx cy n =- -- make a row of n Iolets, centered at (cx, cy)- let radius = styleIoletRadius style- diam = 2 * radius -- diameter- w = fromIntegral n * diam- x1 = cx - w / 2 + radius -- the center of the first iolet- x i = x1 + fromIntegral (i - 1) * diam- make i = Iolet (Circle (Position (x i) cy) radius)- in map make [1..n]---- | An Iolet is a circular port.--- Other shapes could be added.-data Iolet = Iolet Circle- deriving (Eq, Read, Show)--instance Translate Iolet where- translate dx dy (Iolet circle) = Iolet (translate dx dy circle)--ioletCenter :: Iolet -> Position-ioletCenter (Iolet circle) = circleCenter circle--pointInIolet :: Position -> Iolet -> Bool-pointInIolet point (Iolet circle) = pointInCircle point circle---- Build a Tree of node Sizes parallel to the Repr tree,--- each node in the Size tree is the size of --- the corresponding node in the repr tree.---- treeSizes: given a tree of *node* sizes, for the root nodes of the tree--- and its subtrees, returns a tree of *tree* sizes--treeSizes :: Style -> Tree (GNode e) -> Tree Size-treeSizes style gTree = - let subtreeSizes = map (treeSizes style) (subForest gTree)- (BBox _ _ rootWidth rootHeight) = gnodeNodeBB (rootLabel gTree)- treeWidth = max rootWidth (paddedWidth style subtreeSizes)- treeHeight = rootHeight + paddedHeight style subtreeSizes- in T.Node (Size treeWidth treeHeight) subtreeSizes---- (paddedWidth style subtrees) and (paddedHeight style subtrees)--- compute the total size of all subtrees--- of of a Size tree, including padding.-paddedWidth :: Style -> [Tree Size] -> Double-paddedWidth _style [] = 0-paddedWidth style subtrees =- sum [w | (T.Node (Size w _h) _) <- subtrees] +- hpad style * fromIntegral (length subtrees - 1)---- UNUSED:--- -- | pairSum is unused; should it be?--- pairSum :: (Double, Double) -> Double--- pairSum (a, b) = a + b--paddedHeight :: Style -> [Tree Size] -> Double-paddedHeight _style [] = 0-paddedHeight style subtrees = - vpad style + maximum [h | (T.Node (Size _w h) _) <- subtrees]---- A TreeLayout is a Tree of LayoutNodes.--- The root node in a TreeLayout identifies the root of the source Tree --- (nodeSource) and the node's bounding box (nodeNodeBB).--- It also tells the bounding box of the whole tree (nodeTreeBB).--- The subtrees of the TreeLayout are the TreeLayouts --- of the subtrees of the source Tree.--type TreeLayout e = Tree (LayoutNode e)--data LayoutNode e = LayoutNode {nodeGNode :: GNode e,- nodeTreeBB :: BBox}- deriving (Eq)--layoutNodeSource :: LayoutNode e -> e-layoutNodeSource = gnodeValue . nodeGNode--instance (Show e) => Show (LayoutNode e) where- show (LayoutNode gnode treebb) = par "LayoutNode" [show gnode, show treebb]--instance Translate (LayoutNode e) where- translate dx dy (LayoutNode gnode treeBB) = - LayoutNode (translate dx dy gnode)- (translate dx dy treeBB)--instance Widen (LayoutNode e) where- widen node@(LayoutNode gNode treeBB) minWidth =- let dw = bbWidth treeBB - minWidth- in if dw <= 0- then node- else LayoutNode (translate (dw / 2) 0 gNode)- (widen treeBB minWidth)---- Try this, it's tough!--- instance (Draw n, Widen n) = Widen (Tree n) where ...--- But why not just--- instance (Widen n) = Widen (Tree n) where ...----- UNUSED--- -- | layoutRootSource: unused--- layoutRootSource :: TreeLayout e -> e--- layoutRootSource tree = layoutNodeSource (rootLabel tree)--layoutRootBB :: TreeLayout e -> BBox-layoutRootBB = gnodeNodeBB . nodeGNode . rootLabel--layoutTreeBB :: TreeLayout e -> BBox-layoutTreeBB = nodeTreeBB . rootLabel--treeLayout :: (Repr e) => Style -> IoletCounter e -> Tree e -> TreeLayout e-treeLayout style counter tree = - let t1 = treeGNodes style counter tree- t2 = treeSizes style t1- start = Position (hpad style) (vpad style)- t3 = treeLayout2 style start tree t1 t2- in treeLayoutAddMargin t3 (exomargin style)----- treeGNodes produces a tree of (GNode e),--- based on a top left corner at (0, 0) which, of course, will need--- to be translated in the final tlo.--treeGNodes :: Repr e => - Style -> IoletCounter e -> Tree e -> Tree (GNode e)-treeGNodes style counter tree = fmap (makeGNode style counter) tree--treeLayout2 :: (Repr e) =>- Style -- ^ tree style- -> Position -- ^ top left after padding- -> Tree e -- ^ the tree being laid out - -- (actually we don't need the tree as an argument;- -- it's implied by the GNode tree)- -> Tree (GNode e) -- ^ "node sizes"- -> Tree Size -- ^ tree Sizes- -> TreeLayout e--treeLayout2 style- (Position startX startY)- (T.Node _root subtrees)- (T.Node gnode subGNodes) - (T.Node (Size treeWidth treeHeight) subTreeSizes) - = - let - nodeHeight = bbHeight (gnodeNodeBB gnode)- -- center the root in its level- -- UNUSED:- -- nodeCenterX = startX + treeWidth / 2- -- nodeCenterY = startY + nodeHeight-- -- center the subtrees (may be wider or narrower than the root)- subtreesTotalWidth = paddedWidth style subTreeSizes- subX = startX + (treeWidth - subtreesTotalWidth) / 2 -- first subtree- subY = startY + nodeHeight + vpad style-- sublayouts :: (Repr e) =>- Double -> [Tree e] -> [Tree (GNode e)] -> [Tree Size] -> - [TreeLayout e]- sublayouts _ [] [] [] = []- sublayouts x (t:ts) (g:gs) (s:ss) = - treeLayout2 style (Position x subY) t g s :- sublayouts (x + sizeW (rootLabel s) + hpad style) ts gs ss- sublayouts _ _ _ _ = error "treeLayout2: mismatched list lengths"-- in T.Node (LayoutNode -- root node- (centerGNode gnode startX startY treeWidth nodeHeight) -- node- (BBox startX startY treeWidth treeHeight)) -- whole tree- (sublayouts subX subtrees subGNodes subTreeSizes)---- | Center a GNode in a rectangular area-centerGNode :: GNode e -> Double -> Double -> Double -> Double -> GNode e-centerGNode gnode startx starty awidth aheight =- let cx = startx + awidth / 2 -- desired center- cy = starty + aheight / 2- Position cgx cgy = bbCenter (gnodeNodeBB gnode)- in translate (cx - cgx) (cy - cgy) gnode- -treeLayoutPaddedSize :: Style -> TreeLayout e -> Size-treeLayoutPaddedSize style tlo =- let Size w h = treeLayoutSize tlo- in Size (w + 2.0 * hpad style) (h + 2.0 * vpad style)--treeLayoutSize :: TreeLayout e -> Size-treeLayoutSize tlo = - let BBox _x _y w h = layoutTreeBB tlo in Size w h--layoutTreeMoveCenterTo :: - Double -> Double -> TreeLayout e -> TreeLayout e-layoutTreeMoveCenterTo newX newY layoutTree =- let Position oldX oldY = bbCenter (nodeTreeBB (rootLabel layoutTree))- in translate (newX - oldX) (newY - oldY) layoutTree--treeLayoutAddMargin :: TreeLayout e -> Double -> TreeLayout e-treeLayoutAddMargin tree margin =- let LayoutNode {nodeGNode = rootGNode, nodeTreeBB = treeBB} =- rootLabel tree- subtrees = subForest tree- BBox x y w h = treeBB- treeBB' = BBox (x - margin) (y - margin) - (w + 2 * margin) (h + 2 * margin)- root' = translate margin margin (LayoutNode rootGNode treeBB')- subtrees' = translate margin margin subtrees- in T.Node root' subtrees'-- --treeLayoutWidth :: TreeLayout e -> Double-treeLayoutWidth = bbWidth . layoutTreeBB--treeLayoutWiden :: TreeLayout e -> Double -> TreeLayout e-treeLayoutWiden tlo minWidth =- let w = treeLayoutWidth tlo- in- if w >= minWidth- then tlo- else let dw = minWidth - w- T.Node root subs = translate (dw / 2) 0 tlo- LayoutNode rootGNode treeBB = root- root' = LayoutNode rootGNode - (translate (-dw / 2) - 0 - (widen treeBB minWidth))- in T.Node root' subs---
− Sifflet/Data/WGraph.hs
@@ -1,410 +0,0 @@-module Sifflet.Data.WGraph - (WNode(..), WEdge(..), WGraph, wgraphNew, grInsertNode, grRemoveNode- , connectToFrame- , grConnect, grDisconnect- , grAddGraph- , grExtractExprTree, grExtractLayoutNode, grExtractLayoutTree- - , wlab, llab, nodeExprNode, nodeText, nodeValue- , nodeBBox, nodePosition, nodeInputValues- , nodeAllChildren, nodeSimpleChildren, nodeFrameChildren- , nodeAllSimpleDescendants, nodeProperSimpleDescendants- , nodeIsSimple, nodeIsOpen, nodeContainerFrameNode- , nodeParent-- , grUpdateFLayout, grUpdateTreeLayout- , translateNodes- , translateNode, grRelabelNode- , translateTree- , functoidParts, functionToParts- )--where --import Data.List (sort)-import Data.Maybe (fromJust)--import Data.Graph.Inductive as G--import Sifflet.Data.Functoid-import Sifflet.Data.Geometry-import Sifflet.Data.Tree as T -- exports Data.Tree.Tree(Node), etc.-import Sifflet.Data.TreeLayout-import Sifflet.Language.Expr-import Sifflet.Text.Repr ()-import Sifflet.Util---- | A WGraph consists of WNodes with (sort of) Int-labled edges;--- the edge labels serve to order the children of a node.--type WGraph = Gr WNode WEdge--data WEdge = WEdge Int- deriving (Eq, Read, Show)--instance Ord WEdge where- compare (WEdge i) (WEdge j) = compare i j---- | Two kinds of WNodes:--- A WSimple node represents a node in an expression tree, e.g., "if", "+"--- A WFrame node represents a panel or frame that displays an expression tree,--- function call, or something similar.--data WNode = WSimple (LayoutNode ExprNode) - | WFrame G.Node- deriving (Eq, Show)--instance Repr WNode where- repr (WSimple lnode) = repr (gnodeValue (nodeGNode lnode))- repr (WFrame _) = "<frame>"--wgraphNew :: WGraph-wgraphNew = empty---- | Insert new node with given label into graph,--- without any new edges;--- return the new graph and the new node (number)-grInsertNode :: (DynGraph g) => g n e -> n -> (g n e, G.Node)-grInsertNode graph label =- let (_, hi) = nodeRange graph- newNode = succ hi- nodeContext = ([], newNode, label, [])- graph' = nodeContext & graph- in (graph', newNode)---- | Remove a node from the graph; return the updated graph.-grRemoveNode :: (DynGraph g) => g n e -> G.Node -> (g n e)-grRemoveNode graph node = - let (mctx, g') = match node graph- in case mctx of- Nothing -> errcats ["grRemoveNode: node not found:", show node]- Just _ -> g'---- | Connect parent to child, using inlet as the order of the child--- (0, 1, ...). outlet is ignored, since there is only outlet 0.--- As rendered, the parent's inlet-th inlet will have a line--- to the child's outlet-th outlet.--- This is achieved by inserting a labeled edge (parent, child, inlet)--- and clearing any incompatible edge. The incompatibles are:--- a. from same parent on same inlet to a different child.--- b. from the same parent on a different inlet to the same child.--- c. from same child (on same outlet) to a different parent.------ NOTE: This is confusing, because, from the data flow perspective,--- data flows OUT of the child INTO the parent, but from the--- "tree in graph" perspective, links are directed OUT of the parent--- INTO the child. So beware!--grConnect :: WGraph -> G.Node -> WEdge -> G.Node -> WEdge -> WGraph-grConnect g parent inlet child _outlet =- let (mPcontext, g') = match parent g- in case mPcontext of- Nothing -> error "grConnect: parent not found"- -- should parent below be same as parent above?- Just (pins, jparent, plabel, pouts) ->- -- jparent == parent- let pouts' = filter (edgeNotTo child)- (filter (edgeNotEqual inlet) pouts)- (mCcontext, g'') = match child g'- in case mCcontext of- Nothing -> error "grConnect: child not found"- Just (_cins, jchild, clabel, couts) ->- -- jchild == child- -- remove ANY links into the jchild (c)- let cins' = []- in - (pins, jparent, plabel, (inlet, jchild) : pouts') & - ((cins', jchild, clabel, couts) & g'')--edgeNotEqual :: WEdge -> (WEdge, G.Node) -> Bool-edgeNotEqual edge pair = edge /= fst pair--edgeNotTo :: G.Node -> (WEdge, G.Node) -> Bool-edgeNotTo node pair = node /= snd pair---- | Removes a link between parent and child--- where the edge was labeled inlet (order of child).--- Ignores outlet, which should always be 0.--- If child is not the inlet-th child of parent,--- well, this is an error, but grDisconnect ignores it.--- If toFrameP is true, the child node is--- reconnected as a child to its frame-grDisconnect :: WGraph -> G.Node -> WEdge -> G.Node -> WEdge -> Bool -> WGraph-grDisconnect g parent inlet child _outlet toFrameP =- let (mcontext, g') = match parent g- g'' = case mcontext of- Nothing -> error "grDisconnect: parent not found"- Just (ins, jparent, label, outs) ->- -- jparent == parent- let outs' = filter (/= (inlet, child)) outs- in (ins, jparent, label, outs') & g'- in if toFrameP- then connectToFrame child (nodeContainerFrameNode g parent) g''- else g''--connectToFrame :: G.Node -> G.Node -> WGraph -> WGraph-connectToFrame child frameNode g =- insEdge (frameNode, child, WEdge (outdeg g frameNode)) g--grAddGraph :: (DynGraph g) => g n e -> g n e -> g n e-grAddGraph g1 g2 =- let (_, hi1) = nodeRange g1- (lo2, _) = nodeRange g2- diff = hi1 - lo2 + 1-- adjIncr :: Adj e -> Adj e- adjIncr adj = [(x, node + diff) | (x, node) <- adj]-- ctxIncr :: Context n e -> Context n e- ctxIncr (adjFrom, node, label, adjTo) = - (adjIncr adjFrom, node + diff, label, adjIncr adjTo)-- loop :: (DynGraph g) => g n e -> g n e -> g n e- loop ga gb =- if isEmpty gb- then ga- else let (acontext, gb') = matchAny gb in- ctxIncr acontext & loop ga gb'- in loop g1 g2---- | Extract from a graph the expression with root node n, --- returning a Tree of ExprNode.--- Use only the WSimple nodes of the graph (and n had better be one).-grExtractExprTree :: WGraph -> G.Node -> Tree ExprNode-grExtractExprTree g = fmap layoutNodeSource . grExtractLayoutTree g---- --------------------------------------------------------------- | Finding characteristics of the WNodes in a graph--- It is an implicit error if there is no label for the node---- | wlab is like lab with no Maybe: the node *must* have a label-wlab :: WGraph -> Node -> WNode-wlab g n = fromJust (lab g n)---- | llab is the tree layout node of a WSimple node-llab :: WGraph -> Node -> LayoutNode ExprNode-llab g n = - case wlab g n of- WSimple lnode -> lnode- WFrame _fnode ->- errcat ["llab: node is not simple",- "\nnode ", (show n),- "\n in graph\n",- (show g)]---- | The ExprNode represented by the graph node-nodeExprNode :: WGraph -> Node -> ExprNode-nodeExprNode g n = gnodeValue (nodeGNode (llab g n))---- | The repr of the node's value-nodeText :: WGraph -> Node -> String-nodeText g n = tbText (head (gnodeTextBoxes (nodeGNode (llab g n))))---- | The result of an evaluated node in an expression tree-nodeValue :: WGraph -> Node -> EvalResult-nodeValue g n = mvalue- where ENode _ mvalue = nodeExprNode g n---- | The node's BBox-nodeBBox :: WGraph -> Node -> BBox-nodeBBox g n = gnodeNodeBB (nodeGNode (llab g n))--nodePosition :: WGraph -> Node -> Position-nodePosition g = bbPosition . nodeBBox g--nodeInputValues :: WGraph -> Node -> EvalResult-nodeInputValues graph node = - mapM (nodeValue graph) (nodeSimpleChildren graph node) >>= - EvalOk . VList- --- | Finding the children (nodes, numbers) of a node in a graph :--- all children, only WSimple-labeled children, only WFrame-labeled children--- When constructing the graph, ordered children of a tree node --- get graph node numbers in ascending order; therefore,--- sorting the graph nodes gives back the original order of--- children in the tree (plus WFrames that are added later,--- and those should always be after the simple children)--nodeAllChildren :: WGraph -> Node -> [Node]-nodeAllChildren g n = sort (suc g n) --nodeSimpleChildren :: WGraph -> Node -> [Node]-nodeSimpleChildren g n = filter (nodeIsSimple g) (nodeAllChildren g n)--nodeAllSimpleDescendants :: WGraph -> Node -> [Node]-nodeAllSimpleDescendants g n = - n : concatMap (nodeAllSimpleDescendants g) (nodeSimpleChildren g n)--nodeProperSimpleDescendants :: WGraph -> Node -> [Node]-nodeProperSimpleDescendants g n = tail (nodeAllSimpleDescendants g n)--nodeFrameChildren :: WGraph -> Node -> [Node]-nodeFrameChildren g n = filter (not . nodeIsSimple g) (nodeAllChildren g n)- -nodeIsSimple :: WGraph -> Node -> Bool-nodeIsSimple g n = - -- is node n of graph g a WSimple node?- case lab g n of- Just (WSimple _) -> True- _ -> False---- | An open node has a WFrame-labeled child-nodeIsOpen :: WGraph -> Node -> Bool-nodeIsOpen graph node = nodeFrameChildren graph node /= []---- The root node of the tree in which node occurs.--- This is a WSimple node, it may not back up to a WFrame node.------ THIS CODE IS UNTESTED, AND CURRENTLY UNUSED (2009/2/4)---- -- nodeTreeRoot: unused--- nodeTreeRoot :: WGraph -> Node -> Node--- nodeTreeRoot g n = findRoot n--- where findRoot node = --- case pre g node of--- [] -> (error "nodeTreeRoot: no simple root found")--- [parent] -> --- if not (nodeIsSimple g parent)--- then node--- else findRoot parent--- _ -> error "nodeTreeRoot: node has multiple parents"---- -- The root (a WSimple node) of the tree in which a node occurs--- -- Huh, don't need this!---- -- nodeSimpleRoot: unused--- nodeSimpleRoot :: WGraph -> Node -> Node--- nodeSimpleRoot = undefined---- | The graph node of the frame that contains the given node-nodeContainerFrameNode :: WGraph -> Node -> Node-nodeContainerFrameNode g n = - let findFrame node =- if not (nodeIsSimple g node)- then node- else case pre g node of- [parent] -> findFrame parent- [] -> err "has no parent but is not a frame node" node- _:_ -> err "has multiple parents" node- err msg node =- errcat["nodeContainerFrameNode: node ",- show node, " ", msg, "\n",- "in graph\n", show g]- - in findFrame n---- | The parent (if any) of a node-nodeParent :: WGraph -> Node -> Maybe Node-nodeParent g n = - case pre g n of- [] -> Nothing- [parent] -> Just parent- _ -> error "nodeParent: multiple parents"---grUpdateFLayout :: WGraph -> [G.Node] -> FunctoidLayout -> WGraph-grUpdateFLayout gr ns tlo =- case tlo of- FLayoutTree t -> - case ns of- [rootNode] -> grUpdateTreeLayout gr rootNode t- _ -> error "grUpdateFLayout: tree tlo, but not a single root"- FLayoutForest f _b ->- let accum g (n, t) = grUpdateTreeLayout g n t- in foldl accum gr (zip ns f)---- | Replace the tree embedded in graph g with root n, with a new tree.-grUpdateTreeLayout :: WGraph -> G.Node -> TreeLayout ExprNode -> WGraph-grUpdateTreeLayout gr n0 t0 = updateTree gr n0 t0- where updateTree g n (T.Node root subtrees) =- case match n g of- (Just (adjFrom, jn, WSimple _, adjTo), g1) ->- -- jn == n- let g2 = (adjFrom, jn, WSimple root, adjTo) & g1- in updateForest g2 (nodeSimpleChildren g jn) subtrees- (Just (_adjFrom, _n, _, _adjTo), _) ->- error "grUpdateTreeLayout: root node is not a WSimple"- (Nothing, _) -> - errcats ["grUpdateTreeLayout: no such node:", show n]-- updateForest g [] [] = g- updateForest g (n:ns) (t:ts) = - case lab g n of- Just (WSimple _) -> updateForest (updateTree g n t) ns ts- Just (WFrame _) -> updateForest g ns (t:ts) -- shouldn't happen!- Nothing -> error "grUpdateTreeLayout: no label for node"-- updateForest _g _ [] = error "too many ns"- updateForest _g [] _ = error "too many ts"---- | Extract just the single tree layout node of the given graph node-grExtractLayoutNode :: WGraph -> G.Node -> LayoutNode ExprNode-grExtractLayoutNode g n =- case lab g n of- Just (WSimple lnode) -> lnode- _ -> errcats ["grExtractLayoutNode:",- "no label for node, or node is not WSimple:",- "node", show n]---- | Extract the tree layout (tree) descended from the given root node-grExtractLayoutTree :: WGraph -> G.Node -> TreeLayout ExprNode-grExtractLayoutTree g n =- T.Node (grExtractLayoutNode g n)- (map (grExtractLayoutTree g) (nodeSimpleChildren g n))----- | Translate the nodes forming a tree with the given root-translateTree :: Double -> Double -> WGraph -> G.Node -> WGraph-translateTree dx dy wgraph root = - grUpdateTreeLayout wgraph root - (translate dx dy (grExtractLayoutTree wgraph root))---translateNodes :: Double -> Double -> WGraph -> [G.Node] -> WGraph-translateNodes dx dy = foldl (translateNode dx dy)--translateNode :: Double -> Double -> WGraph -> G.Node -> WGraph-translateNode dx dy wg node =- case match node wg of- (Nothing, _) ->- errcats ["translateNode: no such node:", show node]- (Just (adjFrom, jnode, WSimple layoutNode, adjTo), g') ->- -- jnode == node- (adjFrom, jnode, WSimple (translate dx dy layoutNode), adjTo) & g'- (Just _, _) ->- errcats ["translateNode: node is not a WSimple:", show node]----- | Replace the label of a node in a graph-grRelabelNode :: (DynGraph g) => g a b -> G.Node -> a -> g a b-grRelabelNode g n newLabel =- case match n g of- (Just (adjFrom, jn, _oldLabel, adjTo), g') ->- -- jn == n- (adjFrom, jn, newLabel, adjTo) & g'- (Nothing, _) -> errcats ["grRelabelNode: no such node:", show n]---- | Get the parts of a Functoid.--- See note on functionToParts (just below).--- Seems to be unused ***--functoidParts :: Functoid -> WGraph -> G.Node -> Functoid-functoidParts functoid graph frameNode =- case functoid of- fp@FunctoidParts {} -> fp- FunctoidFunc function -> functionToParts function graph frameNode---- | Convert a function to its parts.--- COULDN'T THIS BE DONE USING the function's implementation,--- and not need to use the graph? Then this could go in Functoid.hs--- without circular import between it and WGraph-functionToParts :: Function -> WGraph -> G.Node -> Functoid-functionToParts (Function mname _atypes _rtype impl) graph frameNode =- case impl of - Primitive _ -> error "functionToParts: function is primitive"- Compound argnames _body ->- FunctoidParts {fpName = case mname of- Nothing -> "unnamed function"- Just jname -> jname,- fpArgs = argnames, - fpNodes = nodeProperSimpleDescendants graph frameNode}
− Sifflet/Examples.hs
@@ -1,277 +0,0 @@-module Sifflet.Examples - (exampleFunctions- , exampleFunctionNames- , exampleEnv - , foo, eFoo -- used in Testing.Unit.ExprTests- , eMax -- ditto- , eFact -- ditto- , getExampleFunction- )--where--import Sifflet.Language.Expr---- TEST COMPOUND FUNCTIONS---- | grossProfit salesA salesB = 0.12 salesA + 0.25 salesB--grossProfit :: Function-grossProfit = - Function (Just "grossProfit") [VpTypeNum, VpTypeNum] VpTypeNum- (Compound ["salesA", "salesB"]- (eCall "+" [eCall "*" [eFloat 0.12, eSymbol "salesA"],- eCall "*" [eFloat 0.25, eSymbol "salesB"]]))---- | bonus1 profit = if profit > 100000 --- then 1000 + 0.0012 * profit--- else 0-bonus1 :: Function-bonus1 =- Function (Just "bonus1") [VpTypeNum] VpTypeNum- (Compound ["profit"]- (eIf (eCall ">" [eSymbol "profit", eInt 100000])- (eCall "+" [eInt 1000,- eCall "*" [eFloat 0.0012, eSymbol "profit"]])- (eInt 0)))---- | bonus2 salesA salesB = bonus1 (grossProfit salesA salesB)-bonus2 :: Function-bonus2 =- Function (Just "bonus2") [VpTypeNum, VpTypeNum] VpTypeNum- (Compound ["salesA", "salesB"]- (eCall "bonus1" [eCall "grossProfit" [eSymbol "salesA",- eSymbol "salesB"]]))---- | foo a b = 2 * a + b-foo :: Function-foo = Function (Just "foo") [VpTypeNum, VpTypeNum] VpTypeNum- (Compound ["a", "b"]- (eCall "+" [eCall "*" [eInt 2, eSymbol "a"],- eSymbol "b"])- )---- | An expression representing a call to foo-eFoo :: Expr -> Expr -> Expr-eFoo e1 e2 = eCall "foo" [e1, e2]---- | max x y = if x > y then x else y-max :: Function-max = let ex = eSymbol "x"- ey = eSymbol "y"- in Function (Just "max") [VpTypeNum, VpTypeNum] VpTypeNum- (Compound ["x", "y"] (eIf (eGt ex ey) ex ey))---- | An expression representing a call to max-eMax :: Expr -> Expr -> Expr-eMax e1 e2 = eCall "max" [e1, e2]---- | fact n = if n == 0 then 1 else n * (fact (n - 1))-fact :: Function-fact = let en = eSymbol "n" in- Function (Just "fact") [VpTypeNum] VpTypeNum- (Compound ["n"] - (eIf (eZerop en) - (eInt 1)- (eTimes en (eFact (eSub1 en)))))---- | An expression representing a call to fact-eFact :: Expr -> Expr-eFact e1 = eCall "fact" [e1]---- | sum of the integers 0..n--- Lewis and Loftus, Jave Software Solutions, 6th. ed. (they call it "sum")-sumFromZero :: Function-sumFromZero = let en = eSymbol "n" in- Function (Just "sumFromZero") [VpTypeNum] VpTypeNum- (Compound ["n"]- (eIf (eZerop en)- (eInt 0)- (ePlus en (eSumFromZero (eSub1 en)))))--buggySumFromZero :: Function-buggySumFromZero = - let body = ePlus (eSym "n")- (eCall "buggySumFromZero"- [eMinus (eSym "n") (eInt 1)])- in Function (Just "buggySumFromZero") [VpTypeNum] VpTypeNum - (Compound ["n"] body)--eFib1 :: Expr -> Expr-eFib1 en = eCall "fib1" [en]--fib1 :: Function-fib1 = let en = eSymbol "n"- one = eInt 1- two = eInt 2- in Function (Just "fib1") [VpTypeNum] VpTypeNum- (Compound ["n"]- (eIf (eEq en one)- one- (eIf (eEq en two)- one- (ePlus (eFib1 (eMinus en two))- (eFib1 (eMinus en one))))))---- implying that there should be fib2 ...--eSumFromZero :: Expr -> Expr-eSumFromZero en = eCall "sumFromZero" [en]---- | rmul: multiplication by repeated addition.--- The "multiply" function in Hanly and Koffman,--- "Problem Solving and Program Design in C", 5th ed.--rmul :: Function-rmul = let em = eSymbol "m"- en = eSymbol "n"- in Function (Just "rmul") [VpTypeNum, VpTypeNum] VpTypeNum- (Compound ["m", "n"]- (eIf (eZerop en)- (eInt 0)- (ePlus em (eRmul em (eSub1 en)))))---- | An expression representing a call to rmul-eRmul :: Expr -> Expr -> Expr-eRmul em en = eCall "rmul" [em, en]--eGcd :: Expr -> Expr -> Expr-eGcd em en = eCall "gcd" [em, en]--gcd :: Function-gcd = let em = eSymbol "m"- en = eSymbol "n"- in Function (Just "gcd") [VpTypeNum, VpTypeNum] VpTypeNum- (Compound ["m", "n"]- (eIf (eZerop (eMod em en))- en- (eGcd en (eMod em en))))----- | Even and odd, the slow way.--- Springer and Friedman, Scheme and the Art of Programming (??).--- Rubio-Sanchez, Urquiza-Fuentes, and Pareja-Flores,--- "A Gentle Introduction to Mutual Recursion",--- in ITiCSE 2008: The 13th SIGCSE Conference on Innovation and--- Technology in Computer Science Education, 2008.--eEvenp, eOddp :: Expr -> Expr-eEvenp en = eCall "even?" [en]-eOddp en = eCall "odd?" [en]--evenp, oddp :: Function--evenp = let en = eSymbol "n"- in Function (Just "even?") [VpTypeNum] VpTypeBool- (Compound ["n"]- (eIf (eZerop en)- eTrue- (eOddp (eSub1 en))))--oddp = let en = eSymbol "n"- in Function (Just "odd?") [VpTypeNum] VpTypeBool- (Compound ["n"]- (eIf (eZerop en)- eFalse- (eEvenp (eSub1 en))))---- | Fibonacci series through mutual recursion.--- Rubio-Sanchez, Urquiza-Fuentes, and Pareja-Flores, "Gentle Introduction"--eRabbitBabies, eRabbitAdults :: Expr -> Expr-eRabbitBabies en = eCall "rabbitBabies" [en]-eRabbitAdults en = eCall "rabbitAdults" [en]--rabbitTotal, rabbitAdults, rabbitBabies :: Function--rabbitTotal = let m = eSymbol "month"- in Function (Just "rabbitTotal") [VpTypeNum] VpTypeNum- (Compound ["month"]- (ePlus (eRabbitBabies m) (eRabbitAdults m)))--rabbitAdults = let m = eSymbol "month"- zero = eInt 0- one = eInt 1- in Function (Just "rabbitAdults") [VpTypeNum] VpTypeNum - (Compound ["month"]- (eIf (eEq m one)- zero- (ePlus- (eRabbitAdults (eSub1 m)) -- all adults survive- (eRabbitBabies (eSub1 m))))) -- all babies grow up--rabbitBabies = let m = eSymbol "month" - one = eInt 1- in Function (Just "rabbitBabies") [VpTypeNum] VpTypeNum - (Compound ["month"]- (eIf (eEq m one)- one- (eRabbitAdults (eSub1 m)))) -- all adults reproduce- -buggyLength :: Function-buggyLength = let xs = eSymbol "xs"- one = eInt 1- in Function (Just "buggyLength") - [VpTypeList (VpTypeVar "e1")] VpTypeNum- (Compound ["xs"]- (eIf (eCall "null" [xs])- one -- base case off by one, should be zero- (ePlus one - (eCall "buggyLength"- [eCall "tail" [xs]]))))--listLength :: Function-listLength = let xs = eSymbol "xs"- one = eInt 1- zero = eInt 0- in Function (Just "length") - [VpTypeList (VpTypeVar "e1")] VpTypeNum- (Compound ["xs"]- (eIf (eCall "null" [xs])- zero- (ePlus one - (eCall "length"- [eCall "tail" [xs]]))))--listSum :: Function-listSum = - Function (Just "sum") [VpTypeList VpTypeNum] VpTypeNum - (Compound ["xs"] sumbody)--sumbody :: Expr-sumbody = - eIf (eCall "null" [eSym "xs"])- (eInt 0)- (ePlus (eCall "head" [eSym "xs"])- (eCall "sum" [eCall "tail" [eSym "xs"]]))--buggySum :: Function-buggySum = let xs = eSymbol "xs"- in Function (Just "buggySum")- [VpTypeList VpTypeNum] VpTypeNum- (Compound ["xs"]- -- missing "if" and base case- (ePlus (eCall "head" [xs])- (eCall "buggySum" [eCall "tail" [xs]])))--exampleFunctions :: [Function]-exampleFunctions = [grossProfit, bonus1, bonus2- , foo, Sifflet.Examples.max- , fact, sumFromZero, rmul, fib1- , Sifflet.Examples.gcd, evenp, oddp- , rabbitBabies, rabbitAdults, rabbitTotal- , buggyLength, listLength, listSum, buggySum- , buggySumFromZero]--exampleFunctionNames :: [String]-exampleFunctionNames = map functionName exampleFunctions--exampleEnv :: Env-exampleEnv = - envInsertL baseEnv exampleFunctionNames (map VFun exampleFunctions)---- | This function will be in error if the function name is not--- found in exampleEnv.-getExampleFunction :: String -> Function-getExampleFunction = envGetFunction exampleEnv-
− Sifflet/Foreign/Exporter.hs
@@ -1,191 +0,0 @@-module Sifflet.Foreign.Exporter - (Exporter- , simplifyExpr- , commonRuleHigherPrec- , commonRuleAtomic- , commonRuleLeftToRight- , commonRuleAssocRight- , commonRuleFuncOp- , commonRulesForSimplifyingExprs- , ruleIfRight- , ruleRightToLeft- , applyFirstMatch- , findFixed- ) --where--import Sifflet.Language.Expr---- | The type of a function to export (user) functions to a file.-type Exporter = Functions -> FilePath -> IO ()---- | Simplify an expression by applying rules --- top-down throughout the expression--- tree and repeatedly until there is no change.--- This is intended for removing extra parentheses,--- but could be used for other forms of simplification.--- --- Should each rule also know the level in the original expr tree,--- with 0 = top level (root)?--- That would require additional arguments.--simplifyExpr :: [Expr -> Expr] -> Expr -> Expr-simplifyExpr rules expr = - findFixed (topDown (applyFirstMatch rules)) expr---- | Repeatedly apply a function to an object until there is no change,--- that is, until reaching a fixed point of the function, a point --- where f x == x.--findFixed :: (Eq a) => (a -> a) -> a -> a-findFixed f x =- let x' = f x- in if x' == x then x else findFixed f x'----- | Common rules for simplifying parentheses.---- | Remove ()'s around a higher precedence operator: e.g., --- (a * b) + c ==> a * b + c--- a + (b * c) ==> a + b * c--commonRuleHigherPrec :: Expr -> Expr-commonRuleHigherPrec e =- case e of- EOp op1 (EGroup (EOp op2 subleft subright)) right ->- -- left side- if opPrec op2 > opPrec op1- then EOp op1 (EOp op2 subleft subright) right- else e- EOp op1 left (EGroup (EOp op2 subleft subright)) ->- -- right side- if opPrec op2 > opPrec op1- then EOp op1 left (EOp op2 subleft subright)- else e- _ -> e---- | Remove ()'s around an atomic expression -- a variable,--- literal, or list--commonRuleAtomic :: Expr -> Expr-commonRuleAtomic e =- case e of- EGroup e' ->- if exprIsAtomic e' - then e'- else e- _ -> e---- | Remove ()'s in the case of (a op1 b) op2 c,--- if op1 and op2 have the same precedence, and--- both group left to right, since--- left to right evaluation makes them unnecessary.--commonRuleLeftToRight :: Expr -> Expr-commonRuleLeftToRight e =- case e of- EOp op2 (EGroup (EOp op1 a b)) c ->- if opPrec op1 == opPrec op2 && - opGrouping op1 == GroupLtoR &&- opGrouping op2 == GroupLtoR- then EOp op2 (EOp op1 a b) c- else e- _ -> e---- | Remove ()'s in the case of a op (b op c)--- if op groups right to left, and note that--- it is the same operator op in both places--- (though I don't know if that restriction is necessary).--- This applies to (:) in Haskell, for example:--- x : y : zs == x : (y : zs)--ruleRightToLeft :: Expr -> Expr-ruleRightToLeft e =- case e of- EOp op1 a (EGroup (EOp op2 b c)) ->- if op1 == op2 && opGrouping op1 == GroupRtoL- then EOp op1 a (EOp op2 b c)- else e- _ -> e---- Associativity on the right--- x + (y + z) --> x + y + z--- for + and all other associative operators.--- We could add, the left-hand rule--- (x + y) + z --> x + y + z--- but do not need it,--- because it is already covered by the left to right rule--- for operators of equal precedence.--- It must be the SAME operator on both sides, of course!--commonRuleAssocRight :: Expr -> Expr-commonRuleAssocRight e =- case e of- EOp op1 a (EGroup (EOp op2 b c)) -> - if op1 == op2 && opAssoc op1- then EOp op1 a (EOp op2 b c)- else e- _ -> e---- An if expression as the right operand can be unparenthesized.--- but not so on the left (at least in Haskell):--- x + (if ...) --> x + if ...--- but NOT--- (if ...) + x --> if ... + x (NOT!)--ruleIfRight :: Expr -> Expr-ruleIfRight e =- case e of- EOp op a (EGroup i@(EIf _ _ _)) -> EOp op a i- _ -> e---- In Haskell, a function application has precedence over all--- operators. This applies in both the left and right operands.--commonRuleFuncOp :: Expr -> Expr-commonRuleFuncOp e =- case e of- EOp op a (EGroup c@(ECall _ _)) -> EOp op a c- EOp op (EGroup c@(ECall _ _)) b -> EOp op c b- _ -> e---- | A list of common rules for simplifying expressions.--- Does *not* include ruleIfRight, since that works--- for Haskell but not Python.--commonRulesForSimplifyingExprs :: [Expr -> Expr]-commonRulesForSimplifyingExprs =- [commonRuleHigherPrec- , commonRuleAtomic- , commonRuleLeftToRight- , commonRuleAssocRight- , commonRuleFuncOp]---- | Try the first rule in a list to see if it changes an expression,--- returning the new expression if it does; otherwise, try the next rule,--- and so on; if no rule changes the expression, then return the expression.--- (Note that (applyFirstMatch rules) is itself a rule.)--applyFirstMatch :: [Expr -> Expr] -> Expr -> Expr-applyFirstMatch [] e = e-applyFirstMatch (r:rs) e = - let e' = r e- in if e' /= e- then e'- else applyFirstMatch rs e---- | Apply a rule top-down to all levels of an expression.--- Normally, the "rule" would be a value of (applyFirstMatch rules).-topDown :: (Expr -> Expr) -> Expr -> Expr-topDown f e =- let tdf = topDown f- e' = f e- in case e' of- EIf c a b -> EIf (tdf c) (tdf a) (tdf b)- EList xs -> EList (map tdf xs)- ECall fsym args -> ECall fsym (map tdf args)- EOp op left right -> EOp op (tdf left) (tdf right)- EGroup e'' -> EGroup (tdf e'')- _ -> e'-
− Sifflet/Foreign/Haskell.hs
@@ -1,155 +0,0 @@--- | Abstract syntax tree and pretty-printing for Haskell 98.--- This is only a small subset of the Haskell 98 syntax,--- so we do not need to pull in haskell-src and all its complexity.--- Moreover, haskell-src gives too little control over the format--- of pretty-printed text output.--module Sifflet.Foreign.Haskell- (HsPretty(..)- , Module(..)- , ExportSpec(..)- , ImportDecl(..)- , Decl(..)- , operatorTable- )--where--import Data.List (intercalate)-import qualified Data.Map as M--import Sifflet.Language.Expr-import Sifflet.Text.Pretty--class HsPretty a where-- hsPretty :: a -> String-- hsPrettyList :: String -> String -> String -> [a] -> String- hsPrettyList pre tween post xs =- pre ++ intercalate tween (map hsPretty xs) ++ post--instance HsPretty Symbol where- hsPretty = pretty--instance HsPretty Operator where- hsPretty = pretty---- | A Haskell module; moduleDecls are functions and variables.--data Module = Module {moduleName :: String- , moduleExports :: Maybe ExportSpec- , moduleImports :: ImportDecl- , moduleDecls :: [Decl]- }- deriving (Eq, Show)--instance HsPretty Module where- hsPretty m = - let pmod = "module " ++ moduleName m- pexports = case moduleExports m of- Nothing -> ""- Just exports -> hsPretty exports- pimports = hsPretty (moduleImports m)- pdecls = sepLines2 (map hsPretty (moduleDecls m))- in unlines [pmod ++ " where",- pexports,- pimports,- pdecls]---- | A Haskell module's export spec: a list of function and --- variable identifiers-data ExportSpec = ExportSpec [String]- deriving (Eq, Show)--instance HsPretty ExportSpec where- hsPretty (ExportSpec exports) = - "(" ++ sepCommaSp exports ++ ")"---- | A Haskell modules import decls: a list of module identifiers.--- No support for "qualified" or "as" or for selecting only some--- identifiers from the imported modules.--data ImportDecl = ImportDecl [String]- deriving (Eq, Show)--instance HsPretty ImportDecl where- hsPretty (ImportDecl modnames) = - let idecl modname = "import " ++ modname- in unlines (map idecl modnames)---- | Wrap a string in parentheses-par :: String -> String-par s = "(" ++ s ++ ")"---- | A Haskell function or variable declaration.--- An explicit type declaration is optional.--- Thus we have just enough for --- name :: type--- name [args] = expr.--- Of course [args] would be empty if it's just a variable.-data Decl = Decl {declIdent :: String- , declType :: Maybe [String]- , declParams :: [String]- , declExpr :: Expr- }- deriving (Eq, Show)--instance HsPretty Decl where- hsPretty decl =- let ptypeDecl = "" -- to be improved **- pparams = case declParams decl of- [] -> ""- params -> " " ++ sepSpace params- pbody = hsPretty (declExpr decl)- in ptypeDecl ++ - declIdent decl ++ pparams ++ " =\n" ++- " " ++ pbody---- | HsPretty expressions. This is going to be like in Python.hs.-instance HsPretty Expr where- hsPretty pexpr =- case pexpr of- EUndefined -> "undefined"- EChar c -> show c- ENumber n -> show n- EBool b -> show b- EString s -> show s- ESymbol sym -> hsPretty sym- EList xs -> hsPrettyList "[" ", " "]" xs- EIf c a b -> - unwords ["if", hsPretty c, "then", hsPretty a, "else", hsPretty b]- EGroup e -> par (hsPretty e)- ECall fexpr argExprs -> - hsPretty fexpr ++ " " ++ hsPrettyList "" " " "" argExprs- EOp op left right -> - unwords [hsPretty left, hsPretty op, hsPretty right]---- | The Haskell operators.--- Now what about the associativity of (:)?--- It really doesn't even make sense to ask if (:) is--- associative in the usual sense, --- since (x1 : x2) : xs == x1 : (x2 : xs)--- is not only untrue, but the left-hand side is--- a type error, except maybe in some very special cases--- (and then the right-hand side would probably be a type error).--- Is (:) what is called a "right-associative" operator?--- And do I need to expand my Operator type to--- include this? And then what about (-) and (/)???--- Does this affect their relationship with (+) and (-)?--operatorTable :: M.Map String Operator-operatorTable = - M.fromList (map (\ op -> (opName op, op)) - [ Operator "*" 7 True GroupLtoR -- times- , Operator "+" 6 True GroupLtoR -- plus- , Operator "-" 6 False GroupLtoR -- minus- , Operator ":" 5 False GroupRtoL -- cons- , Operator "==" 4 False GroupNone -- eq- , Operator "/=" 4 False GroupNone -- ne- , Operator ">" 4 False GroupNone -- gt- , Operator ">=" 4 False GroupNone -- ge- , Operator "<" 4 False GroupNone -- lt- , Operator "<=" 4 False GroupNone -- le- ])-
− Sifflet/Foreign/Python.hs
@@ -1,194 +0,0 @@--- | Abstract syntax tree and pretty-printing for Python.--- Works for Python 2 and 3.--- A lot of the data structures are inspired by the language-python package;--- I have chosen not to have language-python as a dependency of sifflet-lib,--- however, because it would be overkill and still allows to little control--- over pretty-printing of Python expressionsw.--module Sifflet.Foreign.Python- (PyPretty(..)- , PModule(..)- , PStatement(..)- , alterParens- , ret- , condS- , var- , ident- , char- , fun- , operatorTable- )--where--import Data.List (intercalate)-import qualified Data.Map as M--import Sifflet.Language.Expr-import Sifflet.Text.Pretty--class PyPretty a where-- pyPretty :: a -> String-- pyPrettyList :: String -> String -> String -> [a] -> String- pyPrettyList pre tween post xs =- pre ++ intercalate tween (map pyPretty xs) ++ post--pyPrettyParens :: (PyPretty a) => [a] -> String-pyPrettyParens = pyPrettyList "(" ", " ")"--instance PyPretty Symbol where- pyPretty = pretty--instance PyPretty Operator where- pyPretty = pretty---- | Python module -- essentially a list of statements;--- should it also have a name?-data PModule = PModule [PStatement]- deriving (Eq, Show)--instance PyPretty PModule where- pyPretty (PModule ss) = sepLines2 (map pyPretty ss)---- | Python statement-data PStatement = PReturn Expr- | PImport String -- ^ import statement- | PCondS Expr - PStatement - PStatement -- ^ if condition action alt-action- | PFun Symbol - [Symbol]- PStatement -- ^ function name, formal parameters, body- deriving (Eq, Show)--instance PyPretty PStatement where- pyPretty s =- case s of- PReturn e -> "return " ++ pyPretty e- PImport modName -> "import " ++ modName- PCondS c a b ->- sepLines ["if " ++ pyPretty c ++ ":",- indentLine 4 (pyPretty a),- "else:",- indentLine 4 (pyPretty b)]- PFun fid params body ->- sepLines ["def " ++ pyPretty fid ++ - pyPrettyParens params ++ ":",- indentLine 4 (pyPretty body)]---- | Expr as an instance of PyPretty.--- This instance is only for Exprs as Python exprs,--- for export to Python! It will conflict with the--- one in ToHaskell.hs (or Haskell.hs).------ The EOp case needs work to deal with precedences--- and avoid unnecessary parens.--- Note that this instance declaration is for *Python* Exprs.--- Haskell Exprs of course should not be pretty-printed--- the same way!-instance PyPretty Expr where- pyPretty pexpr =- case pexpr of- EUndefined -> "undefined"- EChar _ -> error ("Python pyPretty of Expr: " ++- "EChar should have been converted to " ++- "EString")- EList _ -> error ("Python pyPretty of Expr: " ++- "EList should have been converted to " ++- "ECall li ...")- EIf c a b -> - unwords [pyPretty a, "if", pyPretty c, "else", pyPretty b]- EGroup e -> pyPrettyParens [e]- ESymbol vid -> pyPretty vid- ENumber n -> show n- EBool b -> show b- EString s -> show s- ECall fexpr argExprs -> - concat [pyPretty fexpr, pyPrettyParens argExprs]- EOp op left right -> - unwords [pyPretty left, pyPretty op, pyPretty right]--alterParens :: (Expr -> Expr) -> PStatement -> PStatement-alterParens t s =- case s of- PReturn e -> PReturn (t e)- PCondS c a b -> PCondS (t c) (alterParens t a) (alterParens t b)- PFun fid params b -> PFun fid params (alterParens t b)- _ -> s----- | Python return statement-ret :: Expr -> PStatement-ret pexpr = PReturn pexpr---- | Python if STATEMENT---- This is the if STATEMENT:--- if c:--- a--- else:--- b------ But do I need this at all?--condS :: Expr -> Expr -> Expr -> PStatement-condS c a b = PCondS c (ret a) (ret b)---- PExpr smart constructors---- | Python variable-var :: String -> Expr-var name = ESymbol (Symbol name)---- | Python identifier-ident :: String -> Symbol-ident s = Symbol s---- | Python character expression = string expression with one character-char :: Char -> Expr-char c = EString [c]---- | Python function formal parameter-param :: String -> Symbol-param name = Symbol name---- | Defines function definition-fun :: String -> [String] -> Expr -> PStatement-fun fname paramNames bodyExpr = - PFun (ident fname) (map param paramNames) (ret bodyExpr)---- | Binary operators--- Precedence levels are rather *informally* described in--- The Python Language Reference,--- http://docs.python.org/reference/.--- I am adopting the infixr levels from Haskell,--- which seem to be consistent with Python,--- at least for the operators that Sifflet uses.------ | Operator information--- Arithmetic operators: --- + and - have lower precedence than *, /, //, %--- | Comparison operators have precedence lower than any arithmetic--- operator. Here, I've specified associative = False,--- because association doesn't even make sense (well, it does in Python--- but not in other languages);--- (a == b) == c is in general not well typed.--operatorTable :: M.Map String Operator-operatorTable = - M.fromList (map (\ op -> (opName op, op)) - [ (Operator "*" 7 True GroupLtoR) -- times- , (Operator "//" 7 False GroupLtoR) -- int div- , (Operator "/" 7 False GroupLtoR) -- float div- , (Operator "%" 7 False GroupLtoR) -- mod- , (Operator "+" 6 True GroupLtoR) -- plus- , (Operator "-" 6 False GroupLtoR) -- minus- , (Operator "==" 4 False GroupNone) -- eq- , (Operator "!=" 4 False GroupNone) -- ne- , (Operator ">" 4 False GroupNone) -- gt- , (Operator ">=" 4 False GroupNone) -- ge- , (Operator "<" 4 False GroupNone) -- lt- , (Operator "<=" 4 False GroupNone) -- le- ])
− Sifflet/Foreign/ToHaskell.hs
@@ -1,135 +0,0 @@--- | Exports Sifflet to Haskell--- Requires haskell-src package.--module Sifflet.Foreign.ToHaskell- (- HaskellOptions(..)- , defaultHaskellOptions- , exportHaskell- , functionsToHsModule- , functionToHsDecl- , exprToHsExpr- )-where--import Data.Char (toUpper)-import qualified Data.Map as M-import System.FilePath (dropExtension, takeFileName)--import Sifflet.Foreign.Exporter-import Sifflet.Foreign.Haskell-import Sifflet.Language.Expr-import Sifflet.Util----- Main types and functions---- | User configurable options for export to Haskell.--- Currently these options are unused.--- The line width options should probably go somewhere else,--- maybe as PrettyOptions.-data HaskellOptions = - HaskellOptions {optionsSoftMaxLineWidth :: Int- , optionsHardMaxLineWidth :: Int- }- deriving (Eq, Show)---- | The default options for export to Haskell.-defaultHaskellOptions :: HaskellOptions-defaultHaskellOptions = HaskellOptions {optionsSoftMaxLineWidth = 72,- optionsHardMaxLineWidth = 80}---- | Export functions with specified options to a file-exportHaskell :: HaskellOptions -> Exporter-exportHaskell _options functions path =- let header = "-- File: " ++ path ++- "\n-- Generated by the Sifflet->Haskell exporter.\n\n"- in writeFile path (header ++ - hsPretty (functionsToHsModule - (pathToModuleName path)- functions))---pathToModuleName :: FilePath -> String-pathToModuleName path =- case dropExtension (takeFileName path) of- [] -> "Test"- c : cs -> toUpper c : cs---- ---------------------------------------------------------------------------- | Converting Sifflet to Haskell syntax tree---- | Create a module from a module name and Functions.-functionsToHsModule :: String -> Functions -> Module-functionsToHsModule modname (Functions fs) =- Module {moduleName = modname- , moduleExports = Nothing- , moduleImports = ImportDecl ["Data.Number.Sifflet"]- , moduleDecls = map functionToHsDecl fs- }---- | Create a declaration from a Function.--- Needs work: infer and declare the type of the function.--- Minimally parenthesized.-functionToHsDecl :: Function -> Decl-functionToHsDecl (Function mname _atypes _rtype impl) =- case (mname, impl) of- (Nothing, _) -> error "functionToHsDecl: function has no name"- (_, Primitive _) -> error "functionToHsDecl: function is primitive"- (Just fname, Compound args body) ->- Decl {declIdent = fname- , declType = Nothing -- to be improved later- , declParams = args- , declExpr = (simplifyExpr haskellRules)- (exprToHsExpr body)}--haskellRules :: [Expr -> Expr]-haskellRules = commonRulesForSimplifyingExprs ++ - [ruleIfRight, ruleRightToLeft]---- | Converts a Sifflet Expr to a fully parenthesized Haskell Expr-exprToHsExpr :: Expr -> Expr-exprToHsExpr expr =- case expr of- EUndefined -> ESymbol (Symbol "undefined")- ESymbol _ -> expr- EBool _ -> expr- EChar _ -> expr- ENumber _ -> expr- EString _ -> expr-- EIf c a b -> EIf (exprToHsExpr c) (exprToHsExpr a) (exprToHsExpr b)- EList es -> EList (map exprToHsExpr es)- ECall (Symbol fname) args -> - case nameToHaskell fname of- Left op -> -- operator- case args of- [left, right] -> - EOp op (EGroup (exprToHsExpr left))- (EGroup (exprToHsExpr right))- _ -> error - "exprToHsExpr: operation does not have 2 operands"- Right funcName -> -- function- ECall (Symbol funcName) (map (EGroup . exprToHsExpr) args)- _ -> errcats ["exprToHsExpr: extended expr:", show expr]---- | Map Sifflet names to Haskell names.--- Returns a Left Operator for Haskell operators,--- which always have the same name as their corresponding Sifflet --- functions, or a Right String for Haskell function and variable names.-nameToHaskell :: String -> Either Operator String-nameToHaskell name =- case M.lookup name operatorTable of- Just op -> Left op- Nothing ->- -- Most names would have the same names in Haskell,- -- but there are a few special cases.- Right (case name of- "zero?" -> "eqZero"- "positive?" -> "gtZero"- "negative?" -> "ltZero"- "add1" -> "succ"- "sub1" -> "pred"- _ -> name)-
− Sifflet/Foreign/ToPython.hs
@@ -1,170 +0,0 @@--- | Sifflet to abstract syntax tree for Python.--- Use Python module's pyPretty to pretty-print the result.--module Sifflet.Foreign.ToPython- (- PythonOptions(..)- , defaultPythonOptions- , exprToPExpr- , nameToPython- , fixIdentifierChars- , functionToPyDef- , defToPy- , functionsToPyModule- , functionsToPrettyPy- , exportPython- )--where--import Data.Char (isAlpha, isDigit, ord)-import Control.Monad (unless)-import Data.Map ((!))-import System.Directory (copyFile, doesFileExist)-import System.FilePath (replaceFileName)--import Sifflet.Foreign.Exporter-import Sifflet.Foreign.Python-import Sifflet.Language.Expr--- import Sifflet.Text.Pretty-import Sifflet.Util--import Paths_sifflet_lib -- Cabal-generated paths module---- | Options for Python export.--- Should probably include choices like Python2 or Python3;--- if statement or if expression. Right now, just a placeholder.--data PythonOptions = PythonOptions- deriving (Eq, Show)---defaultPythonOptions :: PythonOptions-defaultPythonOptions = PythonOptions---- A lot of these are "pass-through" -- simplify: ***-exprToPExpr :: Expr -> Expr-exprToPExpr expr =- case expr of- EUndefined -> EUndefined -- was var "undefined"- ESymbol _ -> expr-- EBool _ -> expr- EChar c -> EString [c] -- Python does not distinguish char from str- ENumber _ -> expr- EString _ -> expr-- EIf cond action altAction ->- -- EIf here represents a Python if *expression*:- -- value if test else altvalue- -- not the familiar if *statement*!- EIf (exprToPExpr cond) - (exprToPExpr action)- (exprToPExpr altAction)- EList exprs -> - ECall (Symbol "li") (map exprToPExpr exprs)- ECall (Symbol fname) args -> - -- Python distinguishes between functions and operators- case nameToPython fname of- Left op ->- case args of- [left, right] -> - EOp op (EGroup (exprToPExpr left))- (EGroup (exprToPExpr right))- _ -> error "exprToPExpr: operation does not have 2 operands"- Right pname ->- -- I don't think we need (a) for each arg a- -- since they are separated by commas- ECall (Symbol pname) (map exprToPExpr args)- _ -> errcats ["exprToPExpr: extended expr:", show expr]---- | Convert Sifflet name (of a function) to Python operator (Left)--- or function name (Right)-nameToPython :: String -> Either Operator String-nameToPython name =- let oper oname = Left $ operatorTable ! oname- in case name of - "+" -> oper "+"- "-" -> oper "-"- "*" -> oper "*"- "div" -> oper "//"- "mod" -> oper "%"- "/" -> oper "/" -- invalid for integers in Python 2!- "==" -> oper "=="- "/=" -> oper "!="- ">" -> oper ">"- ">=" -> oper ">="- "<" -> oper "<"- "<=" -> oper "<="- "add1" -> Right "add1"- "sub1" -> Right "sub1"- "zero?" -> Right "eqZero"- "positive?" -> Right "gtZero"- "negative?" -> Right "ltZero"- "null" -> Right "null"- "head" -> Right "head"- "tail" -> Right "tail"- ":" -> Right "cons"- _ -> Right (fixIdentifierChars name)---- | Remove characters that are not valid in a Python identifier,--- and in some cases, insert other characters to show what's missing--fixIdentifierChars :: String -> String-fixIdentifierChars =- let fix s =- case s of- [] -> []- c:cs ->- if isAlpha c || isDigit c || c == '_'- then c : fix cs- else case c of- '?' -> "_QUESTION_" ++ fix cs- -- other cases can be inserted here- _ -> "_CHR" ++ show (ord c) ++ "_" ++ fix cs--- in fix---- | Create a Python def statement from a Sifflet function.--- Minimally parenthesized.-functionToPyDef :: Function -> PStatement-functionToPyDef = defToPy . functionToDef--defToPy :: FunctionDefTuple -> PStatement-defToPy (fname, paramNames, _, _, body) =- fun (fixIdentifierChars fname) - paramNames - ((simplifyExpr pyRules) (exprToPExpr body))--pyRules :: [Expr -> Expr]-pyRules = commonRulesForSimplifyingExprs--functionsToPyModule :: Functions -> PModule-functionsToPyModule (Functions fs) = PModule (map functionToPyDef fs)--functionsToPrettyPy :: Functions -> String-functionsToPrettyPy = pyPretty . functionsToPyModule--exportPython :: PythonOptions -> Exporter-exportPython _options funcs path = - let header = "# File: " ++ path ++- "\n# Generated by the Sifflet->Python exporter.\n\n" ++- "from sifflet import *\n\n"- libDest = replaceFileName path "sifflet.py"- in do- {- libDestExists <- doesFileExist libDest- ; unless libDestExists - (do- -- copy sifflet.py to the same directory as path- {- libSrc <- pythonLibSiffletPath - ; copyFile libSrc libDest- }- )- ; writeFile path (header ++ (functionsToPrettyPy funcs))- }--pythonLibSiffletPath :: IO FilePath-pythonLibSiffletPath = getDataFileName "sifflet.py"
− Sifflet/Foreign/ToScheme.hs
@@ -1,318 +0,0 @@-module Sifflet.Foreign.ToScheme- (SExpr(..)- , Atom(..)- , Indent- , Exporter- , SchemeOptions(..)- , defaultSchemeOptions- , exprToSExpr- , functionNameToSchemeName- , valueToSExpr- , exprToSchemeRepr- , exprToSchemePretty- , exprToScheme- , inl- , sepLines2- , functionsToSExprs- , functionsToPrettyScheme- , defToSExpr- , exportScheme- )--where--import Paths_sifflet_lib -- generated by Cabal--import Data.Number.Sifflet-import Sifflet.Foreign.Exporter-import Sifflet.Language.Expr-import Sifflet.Text.Repr-import Sifflet.Text.Pretty-import Sifflet.Util---- Scheme S-exprs--- ------------------ Names beginning with S are generally Scheme things,--- and of course, SExpr also stands for Symbolic Expression.--data SExpr = SAtom Atom | SList [SExpr]- deriving (Eq, Show)--data Atom = SFloat Double- | SInt Integer- | SSymbol String- | SString String- | SChar Char- | SBool Bool- | SFunction Function- deriving (Eq, Show)--type Indent = Int--data SchemeOptions = - SchemeOptions { defineWithLambda :: Bool - -- ^ use explicit lambda in function definitions,- -- (define f (lambda (a b) ...)- }- deriving (Eq, Show)--defaultSchemeOptions :: SchemeOptions-defaultSchemeOptions = SchemeOptions {defineWithLambda = False}---- | An SExpr is "flattish" if it is an atom--- or is a list containing only atoms, --- where the empty list () is an atom.--flattish :: [SExpr] -> Bool-flattish sexprs =- case sexprs of- [] -> True- x:xs -> atom x && flattish xs--atom :: SExpr -> Bool-atom sexpr =- case sexpr of- SAtom _ -> True- SList [] -> True- _ -> False---- Converting Sifflet Exprs to SExprs--exprToSExpr :: Expr -> SExpr-exprToSExpr expr =- case expr of- EUndefined -> - SAtom (SSymbol "*sifflet-undefined*")- ESymbol (Symbol str) -> - SAtom (SSymbol (functionNameToSchemeName str))-- EBool b -> valueToSExpr (VBool b)- EChar c -> valueToSExpr (VChar c)- ENumber n -> valueToSExpr (VNumber n)- EString s -> valueToSExpr (VString s)-- EIf cond action altAction ->- SList [SAtom (SSymbol "if"), exprToSExpr cond,- exprToSExpr action, exprToSExpr altAction]- EList exprs -> - -- This case is not likely to be used,- -- but if it is, the exprs might need to be evaluated,- -- so we have to use list instead of quote- SList (SAtom (SSymbol "list") : (map exprToSExpr exprs))- ECall fsym args -> - SList (exprToSExpr (ESymbol fsym) : map exprToSExpr args) - _ -> errcats ["exprToSExpr: extended expr:", show expr]---- Convert Sifflet function names to corresponding Scheme function names.--- There are a few special cases; otherwise, the names are the same.--- In particular, all of these have the same names in Sifflet--- as in standard (R5RS) Scheme:--- +, -, *, --- All of these are defined in the library sifflet.scm, --- with the prefix "sifflet-" (e.g., sifflet-div):--- div, add1, sub1, /, not-equal?--- Notes: 1+ and 1- are commonly found in Scheme implementations,--- but not standard.--functionNameToSchemeName :: String -> String-functionNameToSchemeName name =- case name of- "mod" -> "modulo"- "add1" -> "sifflet-add1"- "sub1" -> "sifflet-sub1"- "==" -> "equal?"- "/=" -> "sifflet-not-equal?"- "null" -> "null?"- "head" -> "car"- "tail" -> "cdr"- ":" -> "cons"- _ -> name---- Converting Sifflet Values to SExprs--valueToSExpr :: Value -> SExpr-valueToSExpr value =- case value of- VList vs -> - SList [SAtom (SSymbol "quote"), SList (map valueToSExpr vs)]- _ ->- SAtom (case value of- VBool b -> SBool b- VChar c -> SChar c- VNumber (Exact i) -> SInt i- VNumber (Inexact x) -> SFloat x- VString s -> SString s- VFun f -> SFunction f- VList _ -> - error ("valueToSExpr: Impossible! " ++- "We can't have VList here!")- )---- Converting Exprs to Strings of Scheme code--exprToSchemeRepr :: Expr -> String-exprToSchemeRepr = repr . exprToSExpr--exprToSchemePretty :: Expr -> String-exprToSchemePretty = pretty . exprToSExpr--exprToScheme :: Expr -> String-exprToScheme = exprToSchemePretty---- Converting SExprs to Strings of Scheme code--instance Repr SExpr where- repr sexpr =- case sexpr of- SAtom satom ->- case satom of- SFloat x -> show x- SInt i -> show i- SSymbol name -> name -- without ""- SString str -> show str -- with ""- SChar char -> show char- SBool False -> "#f"- SBool True -> "#t"- -- SFunction: is this case really needed?- -- Isn't the conversion to Scheme function names- -- done in exprToSExpr? - SFunction (Function mname _ _ _) ->- case mname of- Nothing -> - error "SExpr/repr: cannot repr unnamed function"- Just name -> - functionNameToSchemeName name- SList exprs ->- "(" ++ unwords (map repr exprs) ++ ")"--instance Pretty SExpr where- pretty = prettyLoop 0--prettyLoop :: Indent -> SExpr -> String-prettyLoop ind sexpr =- case sexpr of- SAtom _ -> repr sexpr- SList xs ->- if flattish xs- then repr sexpr- else- case xs of- [] -> error "prettyLoop: non-flattish xs cannot be []."- [SAtom (SSymbol "if"), _, _, _] ->- displayList2 ind (ind + 4) xs- [SAtom (SSymbol "define"), _, _] ->- displayList2 ind (ind + 4) xs- [SAtom (SSymbol "lambda"), _, _] ->- displayList2 ind (ind + 4) xs- SAtom (SSymbol name) : args ->- -- If it starts as a symbol, it's probably a function call,- -- so put function name and first argument on one line,- -- and indent everything following to first argument.- case args of- [] ->- -- no argument- displayList1 ind (ind + length name + 2) xs- _ ->- -- at least one argument, so at least two elements- -- in the list- displayList2 ind (ind + length name + 2) xs- _ -> displayList1 ind (ind + 1) xs----- | Newline and indent-inl :: Int -> String-inl ind = "\n" ++ replicate ind ' '---- displayList1 "shows" the first list element on the first line--- and then the rest on succeeding lines, so it must have at--- least one element-displayList1 :: Indent -> Indent -> [SExpr] -> String-displayList1 ind ind' xs =- case xs of- [] -> error "displayList1: empty list"- x:xs' -> "(" ++ - prettyLoop ind x ++ - displayTail ind' xs'---- Like displayList1 but "shows" the first *two* list elements--- on the first line.-displayList2 :: Indent -> Indent -> [SExpr] -> String-displayList2 ind ind' xs =- case xs of- x0:x1:xs' -> "(" ++ prettyLoop ind x0 ++- " " ++ prettyLoop ind' x1 ++- displayTail ind' xs'- _ -> error "displayList2: list is too short"---- displayTail "shows" the tail of an SExpr which is a list,--- it assumes the initial "(" and first element have already--- been "shown"-displayTail :: Indent -> [SExpr] -> String-displayTail ind xs =- case xs of- [] -> ")"- -- to prevent final ")" from being on a line by itself- x:[] -> inl ind ++ prettyLoop ind x ++ ")"- x:xs' -> inl ind ++ prettyLoop ind x ++ displayTail ind xs'------ Converting Sifflet definitions to Scheme definitions.---- | Convert Sifflet Functions to Scheme SExprs-functionsToSExprs :: SchemeOptions -> Functions -> [SExpr]-functionsToSExprs options (Functions fs) =- map (defToSExpr options . functionToDef) fs---- | Convert Sifflet Functions to pretty Scheme-functionsToPrettyScheme :: SchemeOptions -> Functions -> String-functionsToPrettyScheme options = - sepLines2 . map pretty . functionsToSExprs options---- | Convert a FunctionDefTuple to a Scheme SExpr.--- Use the form (define (name . args) body)--- except when there are zero arguments, which becomes a--- Scheme constant rather than a function,--- use (define name expr).--defToSExpr :: SchemeOptions -> FunctionDefTuple -> SExpr-defToSExpr options (name, args, _atypes, _rtype, body) =- let asym = SAtom . SSymbol- sdefine = asym "define"- sname = asym name- sbody = exprToSExpr body- in case args of- [] -> SList [sdefine, sname, sbody]- _:_ ->- let argAtoms = map asym args- in if defineWithLambda options- then let slambda = asym "lambda"- sargs = SList argAtoms- slambdaArgsBody = SList [slambda, sargs, sbody]- in SList [sdefine, sname, slambdaArgsBody]- else let snameArgs = SList (sname : argAtoms)- in SList [sdefine, snameArgs, sbody]---- | Export functions to a Scheme file.---- This, too, could use an extra "explicit lambda" argument,--- like defToSExpr.--exportScheme :: SchemeOptions -> Exporter-exportScheme options functions path = do- {- let header = ";;; File: " ++ path ++- "\n;;; Generated by the Sifflet->Scheme exporter."- ; lib <- schemeLibSiffletPath >>= readFile- ; writeFile path - (sepLines2 [header,- functionsToPrettyScheme options functions,- lib])- }---- | The path to the "sifflet.scm" file.-schemeLibSiffletPath :: IO FilePath---- getDataFileName is provided by Cabal-schemeLibSiffletPath = getDataFileName "sifflet.scm"
− Sifflet/Language/Expr.hs
@@ -1,1148 +0,0 @@-module Sifflet.Language.Expr- (- exprToValue, valueToLiteral, valueToLiteral'- , Symbol(..)- , OStr, OBool, OChar- , Expr(..), eSymbol, eSym, eInt, eString, eChar, eFloat- , exprIsAtomic- , exprIsCompound- , eBool, eFalse, eTrue, eIf- , eList, eCall- , exprIsLiteral- , exprSymbols, exprVarNames- , Operator(..)- , Precedence- , OperatorGrouping(..)- , ExprTree, ExprNode(..), ExprNodeLabel(..)- , exprNodeIoletCounter -- needs work ****** get rid of it???- , exprToTree, treeToExpr, exprToReprTree- , EvalResult, EvalRes(EvalOk, EvalError, EvalUntried)- , evalTree, unevalTree- , Value(..), valueFunction- , Functions(..)- , Function(..), functionName, functionNArgs- , functionArgTypes, functionResultType, functionType- , functionArgNames, functionBody, functionImplementation- , FunctionDefTuple, functionToDef, functionFromDef- , FunctionImpl(..)- , VpType(..), typeMatch, typeCheck, vpTypeOf- , TypeEnv, emptyTypeEnv- , Env, emptyEnv, makeEnv, extendEnv, envInsertL, envPop- , envIns, envSet, envGet- , envGetFunction, envLookup, envLookupFunction- , envSymbols, envFunctionSymbols, envFunctions- , eval, apply- , decideTypes, newUndefinedFunction, undefinedTypes- , ePlus, eTimes, eMinus, eDiv, eMod- , eAdd1, eSub1- , eEq, eNe, eGt, eGe, eLt, eLe- , eZerop, ePositivep, eNegativep- , baseEnv)--where---- drop this after debugging:-import System.IO.Unsafe(unsafePerformIO)--import Data.Map as Map hiding (filter, foldl, map, null)-import Data.List as List--import Data.Number.Sifflet-import Sifflet.Data.Tree as T-import Sifflet.Text.Pretty-import Sifflet.Text.Repr ()-import Sifflet.Util---- | Transform a numerical expression into its negation,--- e.g., 5 --> (-5).--- Fails if the expression is not an ENumber.--eNegate :: Expr -> SuccFail Expr-eNegate expr = - case expr of- ENumber n -> Succ $ ENumber (negate n)- _ -> Fail $ "eNegate: cannot handle" ++ show expr---- Symbols have names, and may or may not have values,--- but the value is stored in an environment, not in the symbol itself.--data Symbol = Symbol String -- symbol name- deriving (Eq, Read, Show)--instance Pretty Symbol where- pretty (Symbol s) = s--instance Repr Symbol where repr (Symbol s) = s---- The Haskell representations of V's primitive data types.--- Data.Number.Sifflet.Number represents exact and inexact numbers.-type OStr = String-type OBool = Bool-type OChar = Char---- | A more highly "parsed" type of expression------ I've restricted function calls to the case where the function expression--- is just a symbol, since otherwise it will be hard to visualize.--- But with some thought, it may be possible to generalize--- this to --- ECall [Expr] -- (function:args) --- The constructors EOp and EGroup are not used in Sifflet itself,--- but they are needed for export to Python, Haskell, and similar languages;--- they allow a distinction between operators and functions, and--- wrapping expressions in parentheses.--- EGroup e represents parentheses used for grouping: (e);--- it is not used for other cases of parentheses, e.g.,--- around the argument list in a function call.] --data Expr = EUndefined- | ESymbol Symbol - | EBool Bool- | EChar Char- | ENumber Number- | EString String- | EIf Expr Expr Expr -- ^ if test branch1 branch2- | EList [Expr]- | ECall Symbol [Expr] -- ^ function name, arglist- | EOp Operator Expr Expr -- ^ binary operator application- | EGroup Expr -- ^ grouping parentheses- deriving (Eq, Show)--instance Repr Expr where- repr e =- case e of- EUndefined -> "*undefined*"- ESymbol s -> repr s- EBool b -> repr b- EChar c -> repr c- ENumber n -> repr n- EString s -> show s- EIf t a b -> par "if" (map repr [t, a, b])- EList xs -> if exprIsLiteral e- then reprList "[" ", " "]" xs- else error ("Expr.repr: EList expression is non-literal: " - ++ show e)- -- check *** was: par "EList" (map repr items)- ECall (Symbol fname) args -> par fname (map repr args)- EOp op left right -> unwords [repr left, opName op, repr right]- EGroup e' -> "(" ++ repr e' ++ ")"----- | An Expr is "extended" if it uses the extended constructors--- EOp or EGroup. In pure Sifflet, no extended Exprs are used.--exprIsExtended :: Expr -> Bool-exprIsExtended e =- case e of- EOp _ _ _ -> True- EGroup _ -> True- EIf t a b -> exprIsExtended t ||- exprIsExtended a ||- exprIsExtended b- EList xs -> any exprIsExtended xs- ECall (Symbol _) args -> any exprIsExtended args- _ -> False---- | Is an Expr a literal? A literal is a boolean, character, number, string,--- or list of literals. We (should) only allow user input expressions--- to be literal expressions.--exprIsLiteral :: Expr -> Bool-exprIsLiteral e =- case e of - EBool _ -> True- EChar _ -> True- ENumber _ -> True- EString _ -> True- EList es -> all exprIsLiteral es- -- Shouldn't we say that - -- EGroup e' *not* a literal, even if e' is a literal?- -- But consider carefully the effect on exprIsAtomic and ()'s removal.- EGroup e' -> True -- or False, or exprIsLiteral e' ???- _ -> False---- | Is an expression atomic?--- Atomic expressions do not need parentheses in any reasonable language,--- because there is nothing to be grouped (symbols, literals)--- or in the case of lists, they already have brackets--- which separate them from their neighbors.------ All lists are atomic, even if they are not literals,--- because (for example) we can remove parentheses--- from ([a + b, 7])--exprIsAtomic :: Expr -> Bool-exprIsAtomic e =- case e of- ESymbol _ -> True- EList _ -> True- _ -> exprIsLiteral e---- | Compound = non-atomic-exprIsCompound :: Expr -> Bool-exprIsCompound = not . exprIsAtomic--eSymbol, eSym :: String -> Expr-eSymbol = ESymbol . Symbol-eSym = eSymbol--eInt :: Integer -> Expr-eInt = ENumber . Exact--eString :: OStr -> Expr-eString = EString--eChar :: OChar -> Expr-eChar = EChar--eFloat :: Double -> Expr-eFloat = ENumber . Inexact--eBool :: Bool -> Expr-eBool = EBool--eFalse, eTrue :: Expr-eFalse = eBool False-eTrue = eBool True--eIf :: Expr -> Expr -> Expr -> Expr-eIf = EIf--eList :: [Expr] -> Expr-eList = EList---- | Example:--- ePlus_2_3 = eCall "+" [eInt 2, eInt 3]-eCall :: String -> [Expr] -> Expr-eCall = ECall . Symbol---- | An operator, such as * or +--- An operator is associative, like +, if (a + b) + c == a + (b + c).--- Its grouping is left to right if (a op b op c) means (a op b) op c;--- right to left if (a op b op c) means a op (b op c).--- Most operators group left to right.-data Operator = Operator {opName :: String- , opPrec :: Precedence- , opAssoc :: Bool -- ^ associative?- , opGrouping :: OperatorGrouping- }- deriving (Eq, Show)--instance Pretty Operator where- pretty = opName---- | Operator priority, normally is > 0 or >= 0, --- but does that really matter? I think not.-type Precedence = Int---- | Operator grouping: left to right or right to left,--- or perhaps not at all-data OperatorGrouping = GroupLtoR | GroupRtoL | GroupNone- deriving (Eq, Show)---- | --- EXPRESSION TREES--- For pure Sifflet, so not defined for extended expressions.--type ExprTree = Tree ExprNode-data ExprNode = ENode ExprNodeLabel EvalResult- deriving (Eq, Show)--data ExprNodeLabel = NUndefined | NSymbol Symbol - |- -- formerly NLit Value- NBool Bool | NChar Char | NNumber Number - | NString String- | NList [Expr] -- ???- deriving (Eq, Show)--instance Repr ExprNode where- reprl (ENode label evalRes) =- case label of- NUndefined ->- case evalRes of- EvalUntried -> ["undefined"]- EvalError e -> ["undefined", "error: " ++ e]- EvalOk _ -> - errcats ["reprl of ExprNode: NUndefined with EvalOk",- "should not happen!"]- NSymbol s ->- case evalRes of- EvalOk v -> [repr s, repr v]- EvalError e -> [repr s, "error: " ++ e]- EvalUntried -> reprl s-- -- NLit l -> reprl l- NBool b -> reprl b- NChar c -> reprl c- NNumber n -> reprl n- NString s -> [show s]- NList es -> reprl (EList es) -- check ***---- This was--- exprNodeIoletCounter :: Env -> IoletCounter ExprNode--- but IoletCounter is not available here, so use equivalent type.--- Returns (no. of inlets, no. of outlets)-exprNodeIoletCounter :: Env -> ExprNode -> (Int, Int)-exprNodeIoletCounter env (ENode nodeLabel _nodeResult) =- case nodeLabel of- NUndefined -> (0, 1)- NSymbol (Symbol "if") -> (3, 1) - NSymbol (Symbol s) -> - case envLookup env s of- Nothing -> (0, 1) -- probably a parameter of the function- Just value ->- case value of- VFun function -> (functionNArgs function, 1)- _ -> (0, 1) -- symbol bound to non-function value- _ -> (0, 1)--exprToTree :: Expr -> ExprTree-exprToTree expr =- let leafnode :: ExprNodeLabel -> T.Tree ExprNode- leafnode e = node e []- node :: ExprNodeLabel -> [T.Tree ExprNode] -> T.Tree ExprNode- node e ts = T.Node (ENode e EvalUntried) ts- errext = error ("exprToTree: extended expr: " ++ show expr)- in case expr of- -- EUndefined, ESymbol, and literals map directly - -- to NUndefined, NSymbol, E(literal-type) - EUndefined -> leafnode NUndefined- ESymbol s -> leafnode (NSymbol s)-- -- Literals- EBool b -> leafnode (NBool b)- EChar c -> leafnode (NChar c)- ENumber n -> leafnode (NNumber n)- EString s -> leafnode (NString s)-- -- EIf maps to symbol "if" at the root, 3 subtrees- EIf t a b -> node (NSymbol (Symbol "if")) (map exprToTree [t, a, b])-- -- ECall maps to symbol f (function name) at the root,- -- each argument forms a subtree- ECall f args -> node (NSymbol f) (map exprToTree args)- EList xs -> leafnode (NList xs)- -- Extended Exprs not supported!- EGroup _ -> errext- EOp _ _ _ -> errext---- | Convert an expression tree (back) to an expression--- It will not give back the *same* expression in the case of an EList.-treeToExpr :: ExprTree -> Expr-treeToExpr (T.Node (ENode label _) trees) =- let wrong msg =- errcat ["treeToExpr: ", msg, ": node label = ",- show label, "; trees = ", show trees]- lit e = if null trees then e- else wrong "literal node with non-empty subtrees"- in case label of- NUndefined -> EUndefined- NBool b -> lit (EBool b)- NChar c -> lit (EChar c)- NNumber n -> lit (ENumber n)- NString s -> lit (EString s)- NList xs -> lit (EList xs)- NSymbol s -> - if s == Symbol "if"- then case trees of- [q, a, b] -> - EIf (treeToExpr q) (treeToExpr a) (treeToExpr b)- _ -> wrong "'if' node with /= 3 subtrees"- else - -- VVV Do I really need to distinguish these two cases?- if null trees - then - -- s = terminal symbol- ESymbol s - else -- s = function symbol in function call- ECall s (map treeToExpr trees) ---- Convert an expression to a repr tree (of string elements)--- (Why?)--exprToReprTree :: Expr -> Tree String-exprToReprTree = fmap repr . exprToTree---- Evaluation results (or non-results)--type EvalResult = EvalRes Value--data EvalRes e = EvalOk e | EvalError String | EvalUntried- deriving (Eq, Show)--instance Monad EvalRes where- EvalOk value >>= f = f value- EvalError e >>= _f = EvalError e- EvalUntried >>= _f = EvalUntried- return = EvalOk- fail = EvalError---- Evaluate an expression tree showing the evaluation at each node.--- There's a lot of redundancy in this computation, but does it matter?--evalTree :: ExprTree -> Env -> ExprTree-evalTree atree env = evalTreeWithLimit atree env stackSize--evalTreeWithLimit :: ExprTree -> Env -> Int -> ExprTree-evalTreeWithLimit atree env stacksize =-- let T.Node root subtrees = atree- ss' = pred stacksize- in case root of- ENode (NSymbol (Symbol "if")) _ ->- case subtrees of- [tt, ta, tb] ->- let tt' = evalTreeWithLimit tt env ss'- ENode _ testResult = rootLabel tt'- subEval subtree =- let subtree' = evalTreeWithLimit subtree env ss'- ENode _ subresult = rootLabel subtree'- in (subresult, subtree')- ifNode result = ENode (NSymbol (Symbol "if")) result- in case testResult of- EvalOk (VBool True) -> - let (taValue, ta') = subEval ta in- T.Node (ifNode taValue) [tt', ta', tb]-- EvalOk (VBool False) -> - let (tbValue, tb') = subEval tb in- T.Node (ifNode tbValue) [tt', ta, tb']-- EvalOk weirdValue ->- -- This shouldn't happen with proper type- -- checking!- let msg = "if: non-boolean condition value: " ++- repr weirdValue- in T.Node (ifNode (EvalError msg)) [tt', ta, tb]-- EvalError msg ->- T.Node (ifNode (EvalError msg)) [tt', ta, tb]-- _ -> errcats ["evalTreeWithLimit (if):",- "unexpected test result"]-- _ -> error "evalTreeWithLimit: if: wrong number of subtrees"-- ENode rootOper _ ->- T.Node (ENode rootOper (evalWithLimit (treeToExpr atree) env ss'))- [evalTreeWithLimit s env ss' | s <- subtrees]---- remove the values from the ExprNodes--- "inverse" of evalTree -unevalTree :: ExprTree -> ExprTree-unevalTree atree = - let unevalNode (ENode oper _) = ENode oper EvalUntried- in fmap unevalNode atree---- VALUES AND EVALUATION--data Value = VBool OBool- | VChar OChar- | VNumber Number- | VString OStr- | VFun Function- | VList [Value] - deriving (Eq, Show)- -- no Read for Function--instance Repr Value where- repr (VBool b) = show b- repr (VChar c) = show c- repr (VNumber n) = show n- repr (VString s) = show s- repr (VFun f) = show f- repr (VList vs) = reprList "[" ", " "]" vs--valueFunction :: Value -> Function-valueFunction value =- case value of- VFun function -> function- _ -> error "valueFunction: non-function value"---- | The value of an expression in the base environment.--exprToValue :: Expr -> SuccFail Value-exprToValue expr = - case eval expr baseEnv of - EvalOk value -> Succ value- EvalError msg -> Fail msg- EvalUntried -> error "exprToValue: eval resulted in EvalUntried"--valueToLiteral :: Value -> SuccFail Expr-valueToLiteral v = - case v of- VBool b -> Succ $ EBool b- VChar c -> Succ $ EChar c- VNumber n -> Succ $ ENumber n- VString s -> Succ $ EString s- -- VList [] -> Succ $ EList []- -- VV Should this be fixed? VV- -- VList _ -> Fail "cannot convert non-empty list to literal expression"- VList vs -> mapM valueToLiteral vs >>= Succ . EList- VFun _f -> Fail "cannot convert function to literal expression"--valueToLiteral' :: Value -> Expr-valueToLiteral' v = case valueToLiteral v of- Fail msg -> error ("valueToLiteral: " ++ msg)- Succ e -> e---- | Convert a literal expression to the value it represents.--- It is an error if the expression is non-literal.--- See exprIsLiteral. -literalToValue :: Expr -> Value-literalToValue e =- case e of- EBool b -> VBool b- EChar c -> VChar c- ENumber n -> VNumber n- EString s -> VString s- EList es -> if exprIsLiteral e- then VList (map literalToValue es)- else errcats ["literalToValue: ",- "non-literal list expression: ",- show e]- _ -> errcats ["literalToValue: non-literal or extended expression: " , - show e]- --data VpType = VpTypeBool- | VpTypeChar- | VpTypeNum- | VpTypeString - | VpTypeList VpType -- list with fixed type of elements- | VpTypeFunction [VpType] VpType -- argument, result types- | VpTypeVar String -- named type variable- deriving (Eq, Show)---type TypeEnv = Map String VpType--emptyTypeEnv :: TypeEnv-emptyTypeEnv = Map.empty---- | Try to match a single type and value,--- may result in binding a type variable in a new environment--- or just the old environment-typeMatch :: VpType -> Value -> TypeEnv -> SuccFail TypeEnv-typeMatch vptype value env = - let sorry x etype =- Fail $ repr x ++ ": " ++ etype ++ " expected"- in case (vptype, value) of- -- easy cases- (VpTypeBool, VBool _) -> Succ env- (VpTypeBool, x) -> sorry x "True or False"- (VpTypeChar, VChar _) -> Succ env- (VpTypeChar, x) -> sorry x "character"- (VpTypeNum, VNumber _) -> Succ env- (VpTypeNum, x) -> sorry x "number"- (VpTypeString, VString _) -> Succ env- (VpTypeString, x) -> sorry x "string"- -- VV Harder- -- VV Are the avalues below supposed to be equal to the value above?- (VpTypeVar name, avalue) -> - case Map.lookup name env of- Nothing -> - -- bind type variable- vpTypeOf avalue >>= \ vtype -> Succ $ Map.insert name vtype env- Just concreteType -> typeMatch concreteType avalue env- (VpTypeList etype, VList lvalues) ->- case lvalues of- [] -> Succ env- v:vs -> - typeMatch etype v env >>= - typeMatch (VpTypeList etype) (VList vs)- (VpTypeFunction _atypes _rtype, _) ->- -- this will require matching type variables with type variables!- error "typeMatch: unimplemented case for VpTypeFunction"- _ -> Fail $ "type mismatch: " ++ show (vptype, value)----- | Determine the type of a value.--- May result in a type variable.--vpTypeOf :: Value -> SuccFail VpType-vpTypeOf v =- case v of- VBool _ -> Succ VpTypeBool- VChar _ -> Succ VpTypeChar- VNumber _ -> Succ VpTypeNum- VString _ -> Succ VpTypeString- VFun (Function _ atypes rtype _) -> Succ $ VpTypeFunction atypes rtype-- VList [] -> Succ $ VpTypeList $ VpTypeVar "list_element"- VList (x:xs) -> - do- xtype <- vpTypeOf x- xstypes <- mapM vpTypeOf xs- if filter (/= xtype) xstypes == []- then Succ $ VpTypeList xtype- else Fail "list with diverse element types"---- | Check whether the values agree with the types (which may be abstract)------ This is *probably* too lenient in the case of type variables:--- it can pass a mixed-type list.--typeCheck :: [String] -> [VpType] -> [Value] -> SuccFail [Value]-typeCheck names types values =- let check :: TypeEnv -> [String] -> [VpType] -> [Value] -> SuccFail [Value]- check _ [] [] [] = Succ []- check env (n:ns) (t:ts) (v:vs) = - case typeMatch t v env of- Succ env' -> check env' ns ts vs >>= Succ . (v:)- Fail msg -> Fail $ "For variable " ++ n ++ ":\n" ++ msg- check _ _ _ _ = error "typeCheck: mismatched list lengths"- in check empty names types values- --- | A collection of functions, typically to be saved or exported--- or read from a file--data Functions = Functions [Function]- deriving (Eq, Show)---- | A function may have a name and always has an implementation-data Function = Function (Maybe String) -- function name- [VpType] -- argument types- VpType -- result type- FunctionImpl -- implementation- deriving (Show)--data FunctionImpl = Primitive ([Value] -> EvalResult) -- a Haskell function- | Compound [String] Expr -- arguments, body--instance Show FunctionImpl where- show (Primitive _) = "<primitive function>"- show (Compound args body) = - concat ["Compound function, args = " ++ show args ++ - "; body = " ++ show body]--instance Read FunctionImpl where- readsPrec _ _ = error "readsPrec not implemented for FunctionImpl"--instance Repr Function where- repr (Function mname _ _ _) =- case mname of- Nothing -> "<an unnamed function>"- Just name -> "<function " ++ name ++ ">"--newUndefinedFunction :: String -> [String] -> Function-newUndefinedFunction name argnames =- let (atypes, rtype) = undefinedTypes argnames- impl = Compound argnames EUndefined- in Function (Just name) atypes rtype impl--functionName :: Function -> String-functionName (Function mname _ _ _) = - case mname of- Just name -> name- Nothing -> "anonymous function"--functionNArgs :: Function -> Int-functionNArgs = length . functionArgTypes--functionArgTypes :: Function -> [VpType]-functionArgTypes (Function _ argtypes _ _) = argtypes--functionResultType :: Function -> VpType-functionResultType (Function _ _ rtype _) = rtype---- | Type type of a function, a tuple of (arg types, result type)-functionType :: Function -> ([VpType], VpType) -- (args., result type)-functionType f = (functionArgTypes f, functionResultType f)--functionImplementation :: Function -> FunctionImpl-functionImplementation (Function _ _ _ impl) = impl--functionArgNames :: Function -> [String]-functionArgNames f = case functionImplementation f of- Primitive _ -> - ["dummy" | _t <- functionArgTypes f]- Compound args _body -> args--type FunctionDefTuple = (String, [String], [VpType], VpType, Expr)--functionToDef :: Function -> FunctionDefTuple-functionToDef (Function mname argTypes resType impl) = - case impl of- Primitive _ -> error "functionToDef: primitive function"- Compound argNames body ->- case mname of- Nothing -> error "functionToDef: unnamed function"- Just name -> (name, argNames, argTypes, resType, body)--functionFromDef :: FunctionDefTuple -> Function-functionFromDef (name, argNames, argTypes, resType, body) =- Function (Just name) argTypes resType (Compound argNames body)--functionBody :: Function -> Expr-functionBody f = case functionImplementation f of- Primitive _fp -> - errcats ["functionBody:",- "no body available for primitive function"]- Compound _args body -> body---- | We need to be able to say functions are equal (or not) in order--- to tell if environments are equal or not, in order to know whether--- there are unsaved changes. This is tricky since the primitive--- function implementations do not instantiate Eq, so if it's--- primitive == primitive? we go by the names alone (there's nothing--- else to go by). Otherwise all the parts must be equal.-instance Eq Function where- f1 == f2 =- let Function mname1 atypes1 anames1 impl1 = f1- Function mname2 atypes2 anames2 impl2 = f2- in case (impl1, impl2) of- (Primitive _, Primitive _) -> mname1 == mname2- (Compound args1 body1, Compound args2 body2 ) -> - mname1 == mname2 &&- atypes1 == atypes2 &&- anames1 == anames2 &&- args1 == args2 &&- body1 == body2- _ -> False---- | An Environment contains variable bindings and may be linked to --- a next environment------ Perhaps it may also be used to generate Vp type variables (with int id's)--type EnvFrame = Map String Value-type Env = [EnvFrame] -- should be NON-empty-type Binding = (String, Value)--emptyEnv :: Env-emptyEnv = makeEnv [] []--makeEnv :: [String] -> [Value] -> Env -makeEnv names values = extendEnv names values []--extendEnv :: [String] -> [Value] -> Env -> Env-extendEnv names values env = fromList (zip names values) : env---- | Insert names and values from lists into an environment-envInsertL :: Env -> [String] -> [Value] -> Env-envInsertL env names values =- case env of- [] -> error "envInsertL: empty list"- f : fs ->- let ins :: EnvFrame -> Binding -> EnvFrame- ins frame (name, value) = Map.insert name value frame- in foldl ins f (zip names values) : fs--envIns :: Env -> String -> Value -> Env-envIns env name value =- case env of- [] -> error "envIns: empty list"- f : fs -> Map.insert name value f : fs--envSet :: Env -> String -> Value -> Env-envSet env name value = - -- If name is bound in some map in the environment, update the binding- -- in that map; otherwise insert it into the "front" map- let loop :: Env -> Maybe Env- loop env1 =- case env1 of- [] -> Nothing- f:fs ->- case Map.lookup name f of- Just _ -> Just (envIns env1 name value)- Nothing ->- do -- in the Maybe monad:- fs' <- loop fs- return (f:fs')-- in case loop env of- Just result -> result- Nothing -> envIns env name value---- | Get the value of a variable from an environment-envGet :: Env -> String -> Value-envGet env name = case envLookup env name of- Just value -> value- Nothing -> errcats ["envGet: unbound variable:", name]--envGetFunction :: Env -> String -> Function-envGetFunction env name = func - where VFun func = envGet env name--envLookup :: Env -> String -> Maybe Value-envLookup env name =- case env of- [] -> Nothing- f:fs ->- case Map.lookup name f of- Just value -> Just value- Nothing -> envLookup fs name--envLookupFunction :: Env -> String -> Maybe Function-envLookupFunction env name = - case envLookup env name of- Nothing -> Nothing- Just value ->- case value of- VFun function -> Just function- _ -> Nothing---- | List of all symbols bound in the environment-envSymbols :: Env -> [String]-envSymbols env =- case env of- [] -> []- f : fs -> keys f ++ envSymbols fs---- | List of all symbols bound to functions in the environment-envFunctionSymbols :: Env -> [String]-envFunctionSymbols env =- let isFunction s = case envGet env s of- VFun _ -> True- _ -> False- in [s | s <- envSymbols env, isFunction s]---- | All the functions in the environment-envFunctions :: Env -> Functions-envFunctions env = - Functions (map (envGetFunction env) - (envFunctionSymbols env))---- | Return to the environment prior to an extendEnv-envPop :: Env -> Env-envPop env =- case env of- [] -> error "envPop: empty list"- _f:fs -> fs- -unbound :: String -> Env -> Bool-unbound name env = envLookup env name == Nothing---- EVALUATING EXPRESSIONS---- Limit the stack size for recursion, since we are helping--- novice programmers to learn--stackSize :: Int-stackSize = 1000--eval :: Expr -> Env -> EvalResult-eval expr env = evalWithLimit expr env stackSize--evalWithLimit :: Expr -> Env -> Int -> EvalResult---- Evaluate an expression in an environment with a limited stack--evalWithLimit expr env stacksize =- if stacksize <= 0- then EvalError "stack overflow"- else- let stacksize' = pred stacksize in- case expr of- EUndefined -> EvalError "undefined"- ESymbol (Symbol name) ->- case envLookup env name of- Nothing -> EvalError $ "unbound variable: " ++ name- Just value -> EvalOk value-- EBool b -> EvalOk (VBool b)- EChar c -> EvalOk (VChar c)- ENumber n -> EvalOk (VNumber n)- EString n -> EvalOk (VString n)-- EIf t a b ->- case evalWithLimit t env stacksize' of- EvalOk (VBool True) -> evalWithLimit a env stacksize'- EvalOk (VBool False) -> evalWithLimit b env stacksize'- result -> result-- ECall fsym args ->- -- evaluating a function call- -- I assume that call expressions have *symbols* for the - -- functions.- -- To relax this assumption: change the definition of ECall,- -- but how will you visualize it?-- case evalWithLimit (ESymbol fsym) env stacksize' of- EvalOk f -> - case mapM (\ a -> evalWithLimit a env stacksize') args of- EvalOk argvalues -> apply f argvalues env stacksize'- -- why doesn't this work? err -> err- EvalError e -> EvalError e- EvalUntried -> EvalUntried- err -> err-- EList elist ->- case mapM (\ elt -> evalWithLimit elt env stacksize') elist of- EvalOk values -> EvalOk (VList values)- EvalError e -> EvalError e- EvalUntried -> EvalUntried- _ -> errcats ["evalWithLimit: extended expression not supported",- show expr]- --- | Apply a function fvalue to a list of actual arguments args--- in an environment env and with a limited stack size stacksize-apply :: Value -> [Value] -> Env -> Int -> EvalResult-apply fvalue args env stacksize =- case fvalue of- VFun f ->- case functionImplementation f of- Primitive pf -> pf args- Compound formalArgs body ->- evalWithLimit body (extendEnv formalArgs args env) stacksize- not_a_function ->- EvalError ("apply: first arg is not a function: " ++ - show not_a_function)---- Shortcuts for making expressions that call the primitive functions-ePlus :: Expr -> Expr -> Expr-ePlus e1 e2 = eCall "+" [e1, e2]--eTimes :: Expr -> Expr -> Expr-eTimes e1 e2 = eCall "*" [e1, e2]--eMinus, eDiv, eMod :: Expr -> Expr -> Expr-eMinus e1 e2 = eCall "-" [e1, e2]-eDiv e1 e2 = eCall "div" [e1, e2]-eMod e1 e2 = eCall "mod" [e1, e2]--eAdd1, eSub1 :: Expr -> Expr-eAdd1 e1 = eCall "add1" [e1]-eSub1 e1 = eCall "sub1" [e1]--eEq, eNe, eGt, eGe, eLt, eLe :: Expr -> Expr -> Expr-eEq e1 e2 = eCall "==" [e1, e2]-eNe e1 e2 = eCall "/=" [e1, e2]-eGt e1 e2 = eCall ">" [e1, e2]-eGe e1 e2 = eCall ">=" [e1, e2]-eLt e1 e2 = eCall "<" [e1, e2]-eLe e1 e2 = eCall "<=" [e1, e2]---eZerop, ePositivep, eNegativep :: Expr -> Expr-eZerop e1 = eCall "zero?" [e1]-ePositivep e1 = eCall "positive?" [e1]-eNegativep e1 = eCall "negative?" [e1]---- A good base environment to get started with --primitiveFunctions :: [Function]-primitiveFunctions = [- -- Arithmetic- primN2N "+" (+), -- Number (+)- primN2N "-" (-),- primN2N "*" (*),- primIntDiv,- primIntMod,- primFloatDiv,-- primN1N "add1" succ,- primN1N "sub1" pred,-- -- Comparison- primN2B "==" (==),- primN2B "/=" (/=),- primN2B ">" (>),- primN2B ">=" (>=),- primN2B "<" (<),- primN2B "<=" (<=),-- primN1B "zero?" eqZero,- primN1B "positive?" gtZero,- primN1B "negative?" ltZero,-- -- List operations-- -- null xs tells if xs is an empty list- prim "null" [VpTypeList (VpTypeVar "a")] - VpTypeBool primNull,-- prim "head" [VpTypeList (VpTypeVar "c")] - (VpTypeVar "c")- primHead,- prim "tail" [VpTypeList (VpTypeVar "c")] - (VpTypeList (VpTypeVar "c"))- primTail,- prim ":" [VpTypeVar "d", VpTypeList (VpTypeVar "d")]- (VpTypeList (VpTypeVar "d"))- primCons- ]--type PFun = [Value] -> EvalResult---- Primitive functions of arbitrary type-prim :: String -> [VpType] -> VpType -> PFun -> Function-prim name atypes rtype = Function (Just name) atypes rtype . Primitive---- Primitive arithmetic functions---- | Integer div and mod operations, for exact integers only.--- Using an inexact (floating point) argument is an error,--- even if the argument is "equal" to an integer (e.g., 5.0).--- Division (div or mod) by zero is an error.-primIntDivMod :: String -> (Number -> Number -> Number) -> Function-primIntDivMod name oper =- let func args =- let err msg = EvalError $ concat [name, ": ", msg, - " (", show args, ")"]- in case args of- [VNumber a, VNumber b] ->- if b == 0- then err "zero divisor"- else if isExact a && isExact b- then EvalOk $ VNumber (oper a b)- else err "arguments must be exact numbers"- _ -> error "wrong type or number of arguments"- in prim name [VpTypeNum, VpTypeNum] VpTypeNum func--primIntDiv, primIntMod :: Function-primIntDiv = primIntDivMod "div" div-primIntMod = primIntDivMod "mod" mod---- | Floating point division.--- Integer arguments are coerced to floating point,--- and the result is always floating point.--- operands are ints. --- x / 0 is NaN if x == 0, Infinity if x > 0, -Infinity if x < 0.-primFloatDiv :: Function-primFloatDiv =- let divide args =- case args of- [VNumber x, VNumber y] -> EvalOk $ VNumber (x / y)- _ -> EvalError $ "/: invalid args: " ++ show args- in prim "/" [VpTypeNum, VpTypeNum] VpTypeNum divide---- Primitive functions for lists--primArgCountError :: String -> EvalResult-primArgCountError name = - errcat [name, ": wrong number of arguments in primitive function"]---- Some of the type-checking in these primitive functions--- shouldn't be necessary, if Sifflet knew the types of the--- functions and could do type inference and check input values.--primNull :: PFun-primNull args =- case args of- [VList list] -> EvalOk $ VBool (List.null list)- [_] -> EvalError "null: not a list"- _ -> primArgCountError "primNull"--primHead :: PFun-primHead args =- case args of- [VList (x : _xs)] -> EvalOk x- [VList []] -> EvalError "head: empty list"- [_] -> EvalError "head: not a list"- _ -> primArgCountError "primHead"--primTail :: PFun-primTail args =- case args of- [VList (_x : xs)] -> EvalOk $ VList xs- [VList []] -> EvalError "tail: empty list"- [_] -> EvalError "tail: not a list"- _ -> primArgCountError "primTail"--primCons :: PFun-primCons args =- case args of- [x, VList xs] -> EvalOk $ VList (x:xs)- [_, _] -> EvalError "cons: second argument not a list"- _ -> primArgCountError "primCons"---- Functions for constructing Functions of common types---- | Primitive function with 2 number arguments yield an number value--- fn = Number function to implement for Number operands.-primN2N :: String -> (Number -> Number -> Number) -> Function-primN2N name fn =- let impl args =- case args of- [VNumber x, VNumber y] -> EvalOk $ VNumber (fn x y)- _ -> EvalError $ name ++ ": invalid args: " ++ show args- in prim name [VpTypeNum, VpTypeNum] VpTypeNum impl---- | Primitive unary functions number to number-primN1N :: String -> (Number -> Number) -> Function-primN1N name fn = - let impl args =- case args of- [VNumber x] -> EvalOk $ VNumber (fn x)- _ -> EvalError $ name ++ ": invalid args: " ++ show args- in prim name [VpTypeNum] VpTypeNum impl---- Primitive frunctions with 2 number args and a boolean result-primN2B :: String -> (Number -> Number -> OBool) -> Function-primN2B name fn =- let impl args =- case args of- [VNumber x, VNumber y] -> EvalOk $ VBool (fn x y)- _ -> EvalError $ name ++ ": invalid args: " ++ show args- in prim name [VpTypeNum, VpTypeNum] VpTypeBool impl----- Primitive unary functions number to boolean-primN1B :: String -> (Number -> Bool) -> Function-primN1B name fn = - let impl args =- case args of- [VNumber x] -> EvalOk $ VBool (fn x)- _ -> EvalError $ name ++ ": invalid args: " ++ show args- in prim name [VpTypeNum] VpTypeBool impl--baseEnv :: Env-baseEnv = - makeEnv (map functionName primitiveFunctions)- (map VFun primitiveFunctions)---- | Given an expression, return the list of names of variables--- occurring n the expression-exprSymbols :: Expr -> [Symbol]-exprSymbols expr = - nub $ case expr of- EUndefined -> [] -- is *not* a variable- ESymbol s -> [s]- EIf t a b -> nub $ concat [exprSymbols t,- exprSymbols a,- exprSymbols b]- ECall f args -> - case args of- [] -> [f]- a:as -> nub $ concat [exprSymbols a,- exprSymbols (ECall f as)]- EList items -> nub $ concatMap exprSymbols items- _ -> if exprIsExtended expr- then errcats ["exprSymbols: extended expr not supported:",- show expr]- else [] -- literal types bool, char, number, string---- | exprVarNames expr returns the names of variables in expr--- that are UNBOUND in the base environment. This may not be ideal,--- but it's a start.--exprVarNames :: Expr -> [String]-exprVarNames expr = [name | (Symbol name) <- exprSymbols expr,- unbound name baseEnv]---- | decideTypes tries to find the argument types and return type--- of an expression considered as the body of a function,--- at the same time checking for consistency of inputs and--- outputs between the parts of the expression.--- It returns Right (argtypes, returntype) if successful;--- Left errormessage otherwise.--decideTypes :: Expr -> [String] -> Env -> Either String ([VpType], VpType)-decideTypes expr args _env =- unsafePerformIO $ do - {- print "Fudged the decideTypes"- ; print expr- ; return (if True- then Right (undefinedTypes args)- else Left "decideTypes: not implemented")- }--undefinedTypes :: [String] -> ([VpType], VpType)-undefinedTypes argnames =- let atypes = [VpTypeVar ('_' : name) | name <- argnames]- rtype = VpTypeVar "_result"- in (atypes, rtype)
− Sifflet/Language/Parser.hs
@@ -1,309 +0,0 @@--- | A parser for Sifflet input values.--- This is not a parser for all Sifflet expressions,--- but just those that might be input in textual form--- through the function call dialog that asks for the argument values.--- So, it is limited (deliberately) to "data" types of expressions:--- that is, Exprs using the constructors:--- ELit--- EList--- That means excluding Exprs constructed with EUndefined,--- ESymbol, EIf, and ECall.--module Sifflet.Language.Parser- (parseExpr- , parseValue- , parseLiteral- , parseTest- , parseSuccFail- , parseTypedInput2, parseTypedInputs2- , parseTypedInput3, parseTypedInputs3- , nothingBut- , expr, list- , value, typedValue- , bool, qchar, qstring, integer, double- , number- )--where--import Text.ParserCombinators.Parsec--import Data.Number.Sifflet-import Sifflet.Language.Expr-import Sifflet.Util---- | Parse a Sifflet data literal (number, string, char, bool, or list),--- returning an Expr-parseExpr :: String -> SuccFail Expr-parseExpr = parseSuccFail expr---- | Parse a Sifflet literal expression and return its Value-parseValue :: String -> SuccFail Value-parseValue s =- -- take a shortcut here?- -- case parseExpr s of -- stringToExpr s of- -- Succ expr -> exprToValue expr- -- Fail errmsg -> Fail errmsg- parseLiteral s >>= exprToValue--parseLiteral :: String -> SuccFail Expr-parseLiteral s = - -- parseValue s >>= valueToLiteral- case parseExpr s of- Succ e -> if exprIsLiteral e- then Succ e- else Fail $ - "parseLiteral: expr is non-literal" ++ show e- Fail errmsg -> Fail errmsg--parseSuccFail :: Parser a -> String -> SuccFail a-parseSuccFail p s =- case parse p "user input" s of- Left perr -> Fail (show perr)- Right v -> Succ v---- | Try to parse an input value of a specific type-parseTypedInput2 :: (String, VpType) -> SuccFail Value-parseTypedInput2 (str, vartype) =- parseSuccFail (nothingBut (typedValue vartype)) str---- | Try to parse input values of specific types-parseTypedInputs2 :: [String] -- ^ input strings- -> [VpType] -- ^ expected types- -> SuccFail [Value]-parseTypedInputs2 strs vartypes = - mapM parseTypedInput2 (zip strs vartypes)---- | Try to parse an input value for a named variable of a specific type-parseTypedInput3 :: (String, String, VpType) -> SuccFail Value-parseTypedInput3 (s, varname, vartype) =- case parseSuccFail (nothingBut (typedValue vartype)) s of- Fail msg -> Fail ("For variable " ++ varname ++ ":\n" ++ msg)- Succ v -> Succ v---- | Try to parse input values for named variables of specific types-parseTypedInputs3 :: [String] -- ^ inputs- -> [String] -- ^ variable names- -> [VpType] -- ^ variable types- -> SuccFail [Value]-parseTypedInputs3 strs varnames vartypes =- mapM parseTypedInput3 (zip3 strs varnames vartypes)---- | Like expr, but consumes the entire input,--- so there must not be any extraneous characters after the Expr.-input :: Parser Expr-input = nothingBut expr---- | 'nothingBut p is like 'p', but consumes the entire input,--- so there must be no extraneous characters (except space)--- after whatever 'p' parses.-nothingBut :: Parser a -> Parser a-nothingBut p = (many space >> p) `prog1` (many space >> eof)--prog1 :: (Monad m) => m a -> m b -> m a-prog1 m1 m2 = m1 >>= (\ r -> m2 >> return r)---- | Parse a Sifflet data expression -- actually only a literal--- or a list of literals.-expr :: Parser Expr-expr = -- (try (list expr >>= return . EList)) <|>- (bool >>= return . EBool) <|>- (qchar >>= return . EChar) <|>- (qstring >>= return . EString) <|>- try (double >>= return . ENumber . Inexact) <|>- (integer >>= return . ENumber . Exact) <|>- (list expr >>= return . EList)- -list :: Parser a -> Parser [a]-list element = - let sep = try (skipMany space >> char ',' >> skipMany space)- in (char '[' >> many space >> sepBy element sep)- `prog1`- (many space >> char ']')- -- do I need (...) above?- <?> "list" -- ???---- --- | Parser for a Value of any type (any VpType),--- except that we cannot parse as VpTypeVar or VpTypeFunction.--value :: Parser Value-value = (bool >>= return . VBool) <|>- (qchar >>= return .VChar) <|>- (qstring >>= return . VString) <|>- try (double >>= return . VNumber . Inexact) <|>- (integer >>= return . VNumber . Exact) <|>- (list value >>= return . VList)---- | Parser for a value with a specific VpType expected.--- Again, we cannot do this for VpTypeVar (why not?)--- or VpTypeFunction--typedValue :: VpType -> Parser Value-typedValue t = - (case t of- VpTypeBool -> bool >>= return . VBool- VpTypeChar -> qchar >>= return . VChar- VpTypeString -> qstring >>= return . VString- VpTypeNum -> number >>= return . VNumber- VpTypeList e -> list (typedValue e) >>= return . VList- VpTypeVar _ -> value -- can't check, so just accept anything- VpTypeFunction _ _ -> - error "typedValue: not implemented for VpTypeFunction"- )- <?> typeName t---- | A name for the type, for use in parser error reporting-typeName :: VpType -> String-typeName t =- case t of - VpTypeBool -> "boolean" -- "boolean (True or False)"- VpTypeChar -> "character" -- "character (in single quotes)"- VpTypeNum -> "number"- VpTypeString -> "string" -- "string (in double quotes)"- VpTypeList e -> "list" ++ -- "list (in brackets)" ++- case e of- VpTypeVar _ -> ""- _ -> " of " ++ typeName e-- VpTypeVar _ -> "anything" -- could be more specific!- VpTypeFunction _ _ -> "function" -- ???---bool :: Parser Bool-bool = (try (string "True" >> return True) <|>- (string "False" >> return False))- <?> typeName VpTypeBool----- quoted character 'c'-qchar :: Parser Char-qchar = - let sq = '\'' -- single quote character- in (((char sq <?> "opening single quote") >> - (try escapedChar <|> noneOf [sq])) - `prog1`- (char sq <?> "closing single quote")- )- <?> typeName VpTypeChar- --- quoted string "c..."--qstring :: Parser String-qstring = - let dq = '\"' -- double quote character- in (char dq >> - many (escapedChar <|> noneOf [dq] <?> "")) - `prog1` - (char dq <?> "close of quotation")- -- Do I need (...) above?- <?> typeName VpTypeString---- escapedChar recognizes the following escape sequences:--- \t = tab--- \n = newline--- \r = carriage return--- \\ = backslash--- Anything else that begins with \ is an error.--escapedChar :: Parser Char-escapedChar = - let bs = '\\' -- backslash character- in char bs >> - (oneOf "ntr\\" <?> "n, t, r, or \\ to follow \\") >>=- (\ c ->- return (case c of- 'n' -> '\n'- 't' -> '\t'- 'r' -> '\r'- '\\' -> '\\'- _ -> error "escapedChar: c MUST be n, t, r, or \\"- )- )--data Sign = Minus | Plus---- Integer ::= (+|-)? digit+--integer :: Parser Integer -- sign, digits-integer = do { s <- optSign;- u <- unsignedInteger;- return (applySign s u)- }- <?> "integer"--unsignedInteger :: Parser Integer-unsignedInteger = many1 digit >>= return . read---- An optional + or - defaulting to +--optSign :: Parser Sign -- 1: negative; 0: non-negative-optSign = try ( char '-' >> return Minus ) <|>- try ( char '+' >> return Plus ) <|>- return Plus--applySign :: (Num n) => Sign -> n -> n-applySign s x =- case s of - Minus -> (- x)- Plus -> x---- A double (float) may begin with a sign (+ or -) and must contain--- a decimal point along with at least one digit before and/or after--- the decimal point.--- So there are three cases:--- [sign] digits '.' digits--- [sign] digits '.'--- [sign] '.' digits--double :: Parser Double---- Double FAILS if there is a decimal point.--- It succeeds in the following cases:--double = - let digits1 = many1 digit- point = char '.'- -- wpf: whole-part point fraction-part- wpf = do { dd <- digits1;- dd' <- point >> digits1;- return (dd, dd')- }- -- wp: whole-part point- wp = do { dd <- digits1 `prog1` point;- return (dd, "0")- }- -- pf: point fraction-part- pf = do { dd' <- point >> digits1;- return ("0", dd')- }- -- optional trailing exponent notation e.g. e-4- scale = do { i <- oneOf "eE" >> integer;- return (10 ** fromIntegral i)- }- <|> return 1-- in do { sign <- optSign- ; (whole, frac) <- (try wpf <|>- try wp <|>- try pf)- ; m <- scale;- ; let w = read (whole ++ ".0") -- whole part as number- f = read ("0." ++ frac) -- frac part as number- ; return (m * applySign sign (w + f))- }- <?> "real number"---- A number is a Sifflet Number, which is exact unless it contains--- a decimal point.--- To avoid consuming "123" from "123." and interpreting it as an exact--- number, we MUST try to parse double before integer.-number :: Parser Number-number = (try (double >>= return . Inexact) <|> - (integer >>= return . Exact))- <?> typeName VpTypeNum---
− Sifflet/Language/SiffML.hs
@@ -1,468 +0,0 @@--- | SiffML : Sifflet Markup Language.--- An XML application for storing and retrieving Sifflet programs--- and libraries.--module Sifflet.Language.SiffML- (- ToXml(..)- , produceSiffMLFile- , consumeSiffMLFile- , xmlToFunctions- -- for testing- , testFromXml--- , consumeString- )--where---- import Text.XML.HXT.Arrow-import Text.XML.HXT.Core--import Data.Number.Sifflet-import Sifflet.Language.Expr-import Sifflet.Util--class ToXml a where- toXml :: a -> XMLProducer---- | An XMLProducer produces XML-type XMLProducer = IOSLA (XIOState ()) XmlTree XmlTree---- | An XMLConsumer consumes XML-type XMLConsumer a b = IOSLA (XIOState ()) a b--defaultOptions :: SysConfigList-defaultOptions = [withIndent yes, withValidate no ]--produceSiffMLFile :: (ToXml a) => a -> FilePath -> IO ()-produceSiffMLFile src path = - let arrow :: XMLProducer- arrow = toXml src- options = defaultOptions- in do- {- putStrLn ""- ; [rc] <- runX (root [] [arrow] >>>- writeDocument options path >>>- getErrStatus)- ; putStrLn (case rc of- 0 -> "Okay"- _ -> "Failed")-- }-consumeSiffMLFile :: XMLConsumer XmlTree a -> FilePath -> IO [a]-consumeSiffMLFile fromXml filePath =- let options = defaultOptions- in runX (readDocument options filePath >>> fromXml)---- | Symbols--instance ToXml Symbol where- toXml = symbolToXml--symbolToXml :: Symbol -> XMLProducer-symbolToXml (Symbol name) =- selem "symbol" [txt name]----- | Expr--instance ToXml Expr where- toXml = exprToXml--exprToXml :: Expr -> XMLProducer-exprToXml expr =- let literal label text = - -- future: (omit label arg.): selem label [txt text]- selem "literal" [selem label [txt text]]- in case expr of- EUndefined -> - eelem "undefined"- ESymbol (Symbol name) -> - selem "symbol" [txt name]-- -- "Literals"- -- New way: duplicates parts of valueToXml (bad) ***- EBool b -> - -- future: selem "bool" [eelem (show b)]- selem "literal" [selem "bool" [eelem (show b)]]- EChar c ->- -- future: selem "char" [txt [c]]- literal "char" [c]- ENumber (Exact i) ->- -- future: selem "int" [txt (show i)]- literal "int" (show i)- ENumber (Inexact x) ->- -- future: selem "float" [txt (show x)]- literal "float" (show x)- EString s ->- -- future: selem "string" [txt s]- literal "string" s-- EIf e1 e2 e3 -> - selem "if" [toXml e1, toXml e2, toXml e3]- EList xs -> - -- I predict that this is going to be troublesome! ***- -- No checking for whether the list elements are literals!- selem "literal" [selem "list" - (map (toXml . literalToValue) xs)]- -- future: selem "list" (map toXml xs)- ECall (Symbol name) xs -> - selem "call" - (selem "symbol" [txt name] :- map toXml xs)- _ -> errcats ["exprToXml: extended expr:", show expr]---- | Convert a literal expression to a value.--- It is an error if the expr is not a literal.--- Compare exprToValue in Expr.hs-literalToValue :: Expr -> Value-literalToValue e =- if exprIsLiteral e- then case e of- EBool b -> VBool b- EChar c -> VChar c- ENumber n -> VNumber n- EString s -> VString s- EList es -> VList (map literalToValue es)- EGroup e' -> literalToValue e'- _ -> error "literalToValue: expr is literal, but not literal?"- else error ("literalToValue: expr is not a literal: " ++ show e)--xmlToExpr :: XMLConsumer XmlTree Expr-xmlToExpr = - isElem >>>- (- (hasName "undefined" >>> constA EUndefined) <+>- (hasName "symbol" >>> getChildren >>> isText >>> getText >>>- arr (ESymbol . Symbol)) <+>-- -- future: remove extra level "literal"- (hasName "literal" >>> getChildren >>> xmlToExpr) <+>-- -- boolean values- (hasName "True" >>> constA (EBool True)) <+>- (hasName "False" >>> constA (EBool False)) <+>-- -- chars- (hasName "char" >>> getChildren >>> isText >>> getText >>>- -- VVV head dangerous ???- arr (EChar . head)) <+>-- -- numbers -- why not use parser instead of read???- (hasName "int" >>> getChildren >>> isText >>> getText >>>- arr (ENumber . Exact . read)) <+> -- read dangerous?- (hasName "float" >>> getChildren >>> isText >>> getText >>>- arr (ENumber . Inexact . read)) <+> -- read dangerous?- - -- strings- (hasName "string" >>> getChildren >>> isText >>> getText >>> - arr EString) <+>--- (hasName "if" >>> listA (getChildren >>> xmlToExpr) >>> - -- sometimes I get bogus run-time errors here about- -- this pattern [a, b, c] being non-exhaustive.- -- Of course, it *is* non-exhaustive; but it is- -- never violated in practice- arr (\ [a, b, c] -> EIf a b c)) <+>- -- This is very awkward, but needed for compatibility with the- -- present SiffML doctype:- (hasName "list" >>> - -- future?: listA (getChildren >>> xmlToExpr) >>> - -- Anyway, *why* does this not work???- listA (getChildren >>> xmlToExpr) >>> - -- past?:- -- listA (getChildren >>> xmlToValue >>> arr valueToLiteral') >>> - arr EList) <+>- -- VVV Would be less awkward if ECall :: Symbol -> [Expr] -> Expr- -- were changed to ECall :: Expr -> [Expr] -> Expr- (hasName "call" >>> listA (getChildren >>> xmlToExpr) >>>- arr (\ (ESymbol symf : args) -> ECall symf args))- )---- | Values--- Still used in exprToXml in the EList case :-(--instance ToXml Value where- toXml = valueToXml---- Is this still needed? ***-valueToXml :: Value -> XMLProducer-valueToXml value =- case value of- VBool b ->- -- <True/> or <False/> - -- complicate? selem "bool" [txt (show b)]- eelem (show b)- VChar c ->- selem "char" [txt [c]]- VString s ->- selem "string" [txt s]- VNumber (Exact i) ->- selem "int" [txt (show i)]- VNumber (Inexact x) ->- selem "float" [txt (show x)]- -- Are VFun and VList needed???- VFun f ->- selem "function" [toXml f]- VList vs ->- selem "list" (map toXml vs)---- xmlToValue: still needed? ***-xmlToValue :: XMLConsumer XmlTree Value-xmlToValue = - isElem >>>- ((hasName "True" >>> constA (VBool True)) <+>- (hasName "False" >>> constA (VBool False)) <+>- (hasName "char" >>> getChildren >>> isText >>> getText >>>- arr (VChar . head)) <+>- (hasName "string" >>> getChildren >>> isText >>> getText >>> - arr VString) <+>- (hasName "int" >>> getChildren >>> isText >>> getText >>>- arr (VNumber . Exact . read)) -- dangerous?- <+>- (hasName "float" >>> getChildren >>> isText >>> getText >>>- arr (VNumber . Inexact . read)) -- dangerous?-- <+>- (hasName "function" >>> getChildren >>> xmlToFunction >>> arr VFun) -- <+>- -- listA arr collects the results of arr into a list, so to speak;- -- note that listA (arr1 >>> arr2) - -- does not equal listA arr1 >>> listA arr2- -- and probably will not even have a well-defined type.- -- In particular:- -- getChildren --> [child1]- -- listA getChildren --> [childi for i = 1 to n]- -- listA getChildren >>> xmlToValue- -- --> [child1] if child1 passes xmlToValue (it does not)- -- listA (getChildren >>> xmlToValue)- -- --> [childi for i = 1 to n if childi passes xmlToValue]- (hasName "list" >>> listA (getChildren >>> xmlToValue) >>>- arr VList)- )---- | VpTypes--instance ToXml VpType where- toXml = vpTypeToXml--vpTypeToXml :: VpType -> XMLProducer-vpTypeToXml vtype =- case vtype of- VpTypeString -> eelem "string-type"- VpTypeChar -> eelem "char-type"- VpTypeNum -> eelem "num-type"- VpTypeBool -> eelem "bool-type"- VpTypeList eltType -> selem "list-type" [vpTypeToXml eltType]- VpTypeVar typeVarName -> selem "type-variable" [txt typeVarName]- VpTypeFunction _ _ -> errcats ["vpTypeToXml: VpTypeFunction cannot be",- "converted to XML"]--xmlToVpType :: XMLConsumer XmlTree VpType-xmlToVpType =- isElem >>> - ((hasName "string-type" >>> constA VpTypeString) <+>- (hasName "char-type" >>> constA VpTypeChar) <+>- (hasName "num-type" >>> constA VpTypeNum) <+>- (hasName "bool-type" >>> constA VpTypeBool) <+>- (hasName "list-type" >>> getChildren >>> xmlToVpType >>> - arr VpTypeList) <+>- (hasName "type-variable" >>> getChildren >>>- isText >>> getText >>> arr VpTypeVar)- )---- | Functions--instance ToXml Function where- toXml = functionToXml--functionToXml :: Function -> XMLProducer-functionToXml (Function mName argTypes retType impl) =- case impl of- Primitive _ ->- -- shouldn't happen- errcats ["functionToXml:",- "primitive functions cannot be exported to XML",- show (mName, argTypes, retType)]- Compound argNames body ->- selem "compound-function"- (let name s = selem "name" [txt s]- rest = - [selem "return-type" [vpTypeToXml retType],- selem "arg-types" (map vpTypeToXml argTypes),- selem "arg-names" (map name argNames),- selem "body" [toXml body]]- in case mName of- Nothing -> rest- Just fName -> name fName : rest- )--xmlToFunction :: XMLConsumer XmlTree Function-xmlToFunction = - let getChildElem :: XMLConsumer XmlTree XmlTree- getChildElem = getChildren >>> isElem-- getFuncName :: XMLConsumer XmlTree String- getFuncName = hasName "name" >>> getChildren >>> isText >>> getText-- getReturnType :: XMLConsumer XmlTree VpType- getReturnType = hasName "return-type" >>> getChildren >>> xmlToVpType-- getArgTypes :: XMLConsumer XmlTree [VpType]- getArgTypes = hasName "arg-types" >>> - listA (getChildren >>> xmlToVpType)-- getArgNames :: XMLConsumer XmlTree [String]- getArgNames = hasName "arg-names" >>> - listA (getChildElem >>> getFuncName)-- getBody :: XMLConsumer XmlTree Expr- getBody = hasName "body" >>> getChildren >>> xmlToExpr -- in - isElem >>> hasName "compound-function" >>> - -- NOTE:- -- If arr1 "produces" a, and arr2 "produces" b,- -- then (arr1 &&& arr2) "produces" (a, b).- (- ( -- function name is optional, though it *should* be in the XML file- listA (getChildElem >>> getFuncName)) &&& - (getChildElem >>> getReturnType) &&&- (getChildElem >>> getArgTypes) &&&- (getChildElem >>> getArgNames) &&&- (getChildElem >>> getBody)- )- >>>- (arr (\ (names, (returnType, (argTypes, (argNames, body)))) -> - Function (case names of - [] -> Nothing- (fname : _) -> (Just fname)- )- argTypes- returnType (Compound argNames body)))--functionsToXml :: Functions -> XMLProducer-functionsToXml (Functions fs) =- selem "functions" (map toXml fs)--xmlToFunctions :: XMLConsumer XmlTree Functions-xmlToFunctions =- isElem >>> -- document root- getChildren >>>- hasName "functions" >>>- listA (getChildren >>> xmlToFunction) >>>- arr Functions--instance ToXml Functions where- toXml = functionsToXml---- -- | Examples and tests---- exampleFunction :: Function--- exampleFunction = --- let esym = ESymbol . Symbol--- in Function (Just "cincr") --- [VpTypeBool, VpTypeNum, VpTypeNum]--- VpTypeNum--- (Compound ["incr", "a", "b"]--- (EIf (esym "incr")--- (ECall (Symbol "+") [esym "a", esym "b"])--- (esym "a")))---- -- | This is for testing, when I don't know the type --- -- of the result I'm getting--- xmlToX :: XMLConsumer XmlTree Functions -- [Function] -- XmlTree--- xmlToX = --- isElem -- document root--- >>>--- getChildren--- >>>--- hasName "functions" --- >>>--- listA (getChildren >>> xmlToFunction) --- >>>--- arr Functions---- -- | Tests---- testOut :: IO ()--- testOut = --- produceStdout (Functions [exampleFunctions !! 0, exampleFunctions !! 1])----- testFromFile :: (Show a) => XMLConsumer XmlTree a -> FilePath -> IO ()--- testFromFile fromXml filePath = do--- {--- results <- consumeSiffMLFile fromXml filePath--- ; putStrLn ""--- ; print (length results)--- ; print results--- ; putStrLn ""--- }---- testIn :: (Show a) => XMLConsumer XmlTree a -> IO ()--- testIn fromXml = testFromFile fromXml "-"---- UNUSED:---- | testFromXml :: (ToXml a, Show a) => a -> XMLConsumer XmlTree a -> IO ()--- VVV This type generalization (a, a to a, b) is for debugging, undo it later:-testFromXml :: (ToXml a, Show b) => Int -> a -> XMLConsumer XmlTree b -> IO ()-testFromXml traceLevel src consumer = do- {- produceSiffMLFile src "test.xml"- ; results <- runX (readDocument - (defaultOptions ++ [withTrace traceLevel])- "test.xml" >>>- isElem >>> -- document root- getChildren >>>- consumer)- ; case results of- [] -> putStrLn "Failed"- result : _ -> print result- }---- testToXmlAndBack :: (ToXml a, Show a) => a -> XMLConsumer XmlTree a -> IO ()--- testToXmlAndBack = testFromXml----- xmlToSymbol :: XMLConsumer XmlTree Symbol--- xmlToSymbol = --- isElem >>> hasName "symbol" >>> -- symbol element--- getChildren >>> isText >>> -- text element--- getText >>> -- String--- arr Symbol -- quasi (return . Symbol)---- testXmlToSymbol :: Symbol -> IO ()--- testXmlToSymbol sym = testFromXml sym xmlToSymbol------ produceStdout :: (ToXml a) => a -> IO ()--- produceStdout src = produceSiffMLFile src "-"----- produceXmlTrees :: (ToXml a) => a -> IO [XmlTree]--- produceXmlTrees src = --- let arrow :: XMLProducer--- arrow = toXml src--- options = defaultOptions--- in do--- {--- putStrLn ""--- ; docs <- runX (root [] [arrow] >>> writeDocument options "-")--- ; case docs of--- [] -> putStrLn "Failed"--- doc : _ ->--- print doc--- ; return docs---- }------ consumeStdin :: XMLConsumer XmlTree a -> IO [a]--- consumeStdin fromXml = consumeSiffMLFile fromXml "-"
− Sifflet/Rendering/Draw.hs
@@ -1,223 +0,0 @@-module Sifflet.Rendering.Draw- ( - Draw(..), DrawMode(..)- , drawBox- , drawTextBox- , modeTextCol, modeEdgeCol, modeFillCol- , setColor-- )--where--import Control.Monad-import Graphics.Rendering.Cairo hiding (translate)--import Sifflet.Data.Functoid-import Sifflet.Data.Geometry-import Sifflet.Data.Tree-import Sifflet.Data.TreeLayout--setColor :: VColor -> Render ()--setColor (ColorRGB red green blue) =- setSourceRGB red green blue --setColor (ColorRGBA red green blue alpha) =- setSourceRGBA red green blue alpha---- Drawing things--- THINK: is this class well conceived?--- A lot of things could be done with draw and translate--- if it were not tied to Style and DrawMode--class Draw a where- draw :: Style -> DrawMode -> a -> Render ()---- VV DrawMode is now very awkward, since --- a node needs to know not only if it is selected,--- but which port(s) are selected--data DrawMode = DrawNormal | DrawActive - | DrawSelectedNode - | DrawSelectedInlet Int - | DrawSelectedOutlet Int- deriving (Eq)--instance Draw FunctoidLayout where- draw style mode (FLayoutTree t) = draw style mode t- draw style mode (FLayoutForest f _) = draw style mode f--instance (Draw e) => Draw [e] where- draw style mode = mapM_ (draw style mode)--instance (Draw e) => Draw (Tree e) where- draw style mode t = do- draw style mode (rootLabel t)- draw style mode (subForest t)--instance Draw (LayoutNode e) where- draw style mode (LayoutNode gnode _treeBB) = draw style mode gnode--instance Draw (GNode e) where-- draw style mode (GNode _value textboxes nodeBB inlets outlets) = - do- -- draw node box- let (nodeTextCol, nodeEdgeCol, nodeFillCol) = - case mode of- DrawActive -> - (styleActiveTextColor, - styleActiveEdgeColor,- styleActiveFillColor)- DrawSelectedNode -> - (styleSelectedTextColor, - styleSelectedEdgeColor,- styleSelectedFillColor)- _ -> (styleNormalTextColor, - styleNormalEdgeColor, - styleNormalFillColor)- - -- (overall box for the node)- when (styleShowNodeBoxes style) $- drawBox (Just (nodeFillCol style))- (Just (nodeEdgeCol style)) nodeBB- -- -- assert textboxes has one or two elements- -- draw the first text box- drawTextBox (Just (styleFont style))- (Just (nodeFillCol style)) -- background- Nothing -- frame color- (nodeTextCol style) -- text color- (head textboxes)-- -- draw the second textbox, if any, using "aux" style- case (tail textboxes) of- [tbAux] -> - drawTextBox (Just (styleAuxFont style))- Nothing- Nothing- (styleAuxColor style)- tbAux- _ -> return ()--- -- Draw the iolets- when (styleShowNodePorts style) $ do- drawInlets style mode inlets- drawOutlets style mode outlets- --instance Draw TextBox where- draw style mode =- drawTextBox (Just (styleFont style))- (Just (modeFillCol mode style)) - Nothing- (modeTextCol mode style) --drawTextBox :: Maybe VFont -> Maybe VColor -> Maybe VColor -> VColor ->- TextBox -> Render ()-drawTextBox mfont mbgcolor mframecolor textcolor - (TextBox text textBB boxBB) = do- let BBox textX textY _textW _textH = textBB- drawBox mbgcolor mframecolor boxBB- setColor textcolor- case mfont of - (Just font) -> setFont font- _ -> return ()- moveTo textX textY- showText text--instance Draw BBox where- draw style mode = - drawBox (Just (modeFillCol mode style)) - (Just (modeEdgeCol mode style)) --drawBox :: Maybe VColor -> Maybe VColor -> BBox -> Render ()-drawBox mBgColor mFgColor (BBox x y w h) = - -- draw the BBox, in the specified colors, irrespective of style- let setup color = - do- rectangle x y w h- setColor color- in case (mBgColor, mFgColor) of- (Just bgColor, Just fgColor) ->- do- setup bgColor- fillPreserve- setColor fgColor- stroke- (Just bgColor, Nothing) -> - do- setup bgColor- fill- (Nothing, Just fgColor) -> - do- setup fgColor- stroke- _ -> return ()--instance Draw Position where- draw _style _mode _pos = return () -- bare points are invisible ??? ******--instance Draw Iolet where- draw style mode (Iolet circle) = draw style mode circle--drawIolet :: Iolet -> VColor -> VColor -> Render ()-drawIolet (Iolet circle) = drawCircle circle--drawInlets :: Style -> DrawMode -> [Iolet] -> Render ()-drawInlets style mode inlets =- let selected i = mode == DrawSelectedInlet i- in drawIolets selected style inlets--drawOutlets :: Style -> DrawMode -> [Iolet] -> Render ()-drawOutlets style mode outlets =- let selected o = mode == DrawSelectedOutlet o- in drawIolets selected style outlets--drawIolets :: (Int -> Bool) -> Style -> [Iolet] -> Render ()-drawIolets selected style iolets =- -- (selected n) should be true iff n is the selected iolet- let loop _ [] = return ()- loop n (p:ps) = - uncurry (drawIolet p)- (if selected n - then (styleSelectedFillColor style, - styleSelectedEdgeColor style)- else (styleNormalFillColor style, - styleNormalEdgeColor style)) >>- loop (n + 1) ps- in loop 0 iolets- -instance Draw Circle where- draw style mode circle = - drawCircle circle (modeFillCol mode style) (modeEdgeCol mode style) --drawCircle :: Circle -> VColor -> VColor -> Render ()-drawCircle (Circle (Position x y) r) bgColor fgColor = do- newPath -- otherwise we get a line to the arc- arc x y r 0 (2 * pi)- setColor bgColor- fillPreserve- setColor fgColor- stroke---- Helper functions to find the background and foreground colors for a mode--modeFillCol :: DrawMode -> (Style -> VColor)-modeFillCol DrawNormal = styleNormalFillColor-modeFillCol DrawActive = styleActiveFillColor-modeFillCol _ = styleSelectedFillColor--modeTextCol :: DrawMode -> (Style -> VColor)-modeTextCol DrawNormal = styleNormalTextColor-modeTextCol DrawActive = styleActiveTextColor-modeTextCol _ = styleSelectedTextColor--modeEdgeCol :: DrawMode -> (Style -> VColor)-modeEdgeCol DrawNormal = styleNormalEdgeColor-modeEdgeCol DrawActive = styleActiveEdgeColor-modeEdgeCol _ = styleSelectedEdgeColor-
− Sifflet/Rendering/DrawTreeGraph.hs
@@ -1,182 +0,0 @@--- | Tree graph rendering-module Sifflet.Rendering.DrawTreeGraph- (- graphQuickView- , graphWriteImageFile, graphRender, treeRender- , treeWriteImageFile, gtkShowTree- )--where--import System.IO-import Data.IORef-import System.Cmd-import Graphics.UI.Gtk.Gdk.EventM--import Graphics.Rendering.Cairo hiding (translate)-import Data.Graph.Inductive as G--import Sifflet.UI.LittleGtk--import Sifflet.Data.Geometry-import Sifflet.Data.Tree-import Sifflet.Data.TreeGraph-import Sifflet.Data.TreeLayout-import Sifflet.Rendering.Draw-import Sifflet.Text.Repr ()---- ============================================================--- GRAPH VIEWING AND RENDERING--- ============================================================---- Quick view of a graph using GraphViz--graphQuickView :: (Graph g, Show a, Show b) => g a b -> IO ()-graphQuickView g = - let dot_src = graphviz g "graphQuickView" (6, 4) (1, 1) Portrait- dot_file = "tmp.dot"- png_file = "tmp.png"- in do- h <- openFile dot_file WriteMode- hPutStr h dot_src- hClose h- _ <- system ("dot -Tpng -o" ++ png_file ++ " " ++ dot_file)- _ <- system ("feh " ++ png_file)- return ()---graphWriteImageFile :: (Repr n) => - Style -> Maybe Node -> Maybe Node ->- Double -> Double -> - LayoutGraph n e -> String -> - IO String -graphWriteImageFile style mactive mselected dwidth dheight graph file = do- withImageSurface FormatARGB32 (round dwidth) (round dheight) $ \surf -> do- renderWith surf $ - graphRender style mactive mselected graph- surfaceWriteToPNG surf file- return file--graphRender :: (Repr n) => - Style -> Maybe Node -> Maybe Node -> LayoutGraph n e -> - Render ()-graphRender style mactive mselected graph = do- let renderNode :: Node -> Render ()- renderNode node = do- let Just layoutNode = lab graph node - -- ^^ is this safe? it can't be Nothing?- nodeBB = gnodeNodeBB (nodeGNode layoutNode)- active = (mactive == Just node)- selected = case mselected of- Nothing -> False- Just sel -> sel == node- mode = if active then DrawActive- else if selected then DrawSelectedNode -- !!!- else DrawNormal- xcenter = bbXCenter nodeBB- draw style mode layoutNode- connectParent node xcenter (bbTop nodeBB)- return ()-- connectParent node x y = - let parents = pre graph node in- case parents of- [] -> return ()- [parent] -> do- let Just playoutNode = lab graph parent- parentBB = gnodeNodeBB (nodeGNode playoutNode)- px = bbXCenter parentBB- py = bbBottom parentBB- setColor (styleNormalEdgeColor style)- moveTo px (py + snd (vtinypad style)) -- bottom of parent- lineTo x (y - fst (vtinypad style)) -- top of node- stroke-- _ -> error "Too many parents"- - setAntialias AntialiasDefault-- -- canvas background- setColor (styleNormalFillColor style)- let Just layoutNode = lab graph 1 -- root node, represents whole tree- BBox x y bwidth bheight = nodeTreeBB layoutNode- rectangle x y bwidth bheight- fill-- -- draw the graph/tree- setLineWidth (lineWidth style)- mapM_ renderNode (nodes graph)--treeRender :: (Repr e) => Style -> TreeLayout e -> Render ()-treeRender style = graphRender style Nothing Nothing . orderedTreeToGraph--treeWriteImageFile :: (Repr e) => - Style -> IoletCounter e -> Tree e -> String -> IO String--treeWriteImageFile style counter atree filename = do- let tlo = treeLayout style counter atree- Size surfWidth surfHeight = treeLayoutPaddedSize style tlo- withImageSurface FormatARGB32 (round surfWidth) (round surfHeight) $ - \ surf -> do- renderWith surf $ treeRender style tlo- surfaceWriteToPNG surf filename- return filename---- ============================================================--- Simple tree viewing.--- gtkShowTree displays a single tree very simply--- Works for any kind of (Repr e, Show e) => Tree e.--gtkShowTree :: (Repr e, Show e) => - Style -> IoletCounter e -> Tree e -> IO ()-gtkShowTree style counter atree = do- let tlo = treeLayout style counter atree- Size dwidth dheight = treeLayoutPaddedSize style tlo-- tloRef <- newIORef tlo-- _ <- initGUI-- -- init window- window <- windowNew- set window [windowTitle := "Test Cairo Tree"]- _ <- onDestroy window mainQuit-- -- init vbox- vbox <- vBoxNew False 5 -- width not homogeneous; spacing- set window [containerChild := vbox]-- -- init canvas- canvas <- layoutNew Nothing Nothing- _ <- onSizeRequest canvas - (return (Requisition (round dwidth) (round dheight)))- widgetSetCanFocus canvas True -- to receive key events-- -- event handlers- _ <- on canvas exposeEvent (updateCanvas style canvas tloRef)- _ <- on canvas keyPressEvent (keyPress window)-- -- pack, show, and run- boxPackStartDefaults vbox canvas- widgetShowAll window- mainGUI--updateCanvas :: (Repr e) => Style -> Layout -> IORef (TreeLayout e) - -> EventM EExpose Bool-updateCanvas style canvas tloRef = - tryEvent $ liftIO $ do- { - tlo <- readIORef tloRef- ; win <- layoutGetDrawWindow canvas- ; renderWithDrawable win (treeRender style tlo)- }--keyPress :: Window -> EventM EKey Bool-keyPress window =- tryEvent $ do- {- kname <- eventKeyName- ; case kname of- "q" -> liftIO $ widgetDestroy window -- implies mainQuit- _ -> stopEvent- }
− Sifflet/Text/Pretty.hs
@@ -1,56 +0,0 @@-module Sifflet.Text.Pretty - (Pretty(..)- , indentLine, sepLines, sepLines2- , sepComma, sepCommaSp, sepSpace)--where--import Data.List (intercalate)----- | The class of types that can be pretty-printed.--- (Unfortunately this is not very useful, because--- and Expr can be pretty Haskell or pretty Python or pretty Scheme,--- leading to overlapping instance declarations.)------ pretty x is a pretty String representation of x.--- prettyList prefix infix postfix xs is a pretty String representation--- of the list xs, with prefix, infix, and postfix specifying the--- punctuation. For example, if (pretty x) => "x",--- then prettyList "[" ", " "]" [x, x, x] => "[x, x, x]".------ Minimal complete implementation: define pretty.--class Pretty a where-- pretty :: a -> String-- prettyList :: String -> String -> String -> [a] -> String- prettyList pre tween post xs =- pre ++ intercalate tween (map pretty xs) ++ post---- | Indent a single line n spaces.-indentLine :: Int -> String -> String-indentLine n line = replicate n ' ' ++ line---- | sepLines is like unlines, but omits the \n at the end of the--- last line.-sepLines :: [String] -> String-sepLines = intercalate "\n"---- | sepLines2 is like sepLines, but adds an extra \n between each--- pair of lines so they are "double spaced."-sepLines2 :: [String] -> String-sepLines2 = intercalate "\n\n"---- | Separate strings by commas and nothing else (",")-sepComma :: [String] -> String-sepComma = intercalate ","---- | Separate strings by commas and spaces (", ")-sepCommaSp :: [String] -> String-sepCommaSp = intercalate ", "---- | Separate strings by just spaces (" ")-sepSpace :: [String] -> String-sepSpace = unwords
− Sifflet/Text/Repr.hs
@@ -1,65 +0,0 @@-module Sifflet.Text.Repr- (Repr(..)- , Name(..)- )--where--import Data.Number.Sifflet-import Data.List (intercalate)---- | class Repr: representable by a String or a list of Strings------ repr x is a String representation of x.--- reprl x is a [String] representation of x,--- where the first element should be the same as repr x,--- and the rest provide auxiliary information--- that you want to be shown with x.--- reprs x is a reduction of reprl x to a single String.--- reprList prefix infix postfix xs is the representation of a list of xs------ Minimal complete implementation: define repr, or define reprl.--- The normal way is to define repr. Define reprl instead,--- if for some reason you want to include additional information--- such as the value of an expression in an expression node.------ Examples:--- - (3 :: Int) has repr => "3", reprl => ["3"], reprs => "3"--- - In Sifflet.Language.Expr, (ENode (NSymbol "x") (EvalOk (3 :: Int)--- has reprl => ["x", "3"], reprs => "x 3", and repr => "x".--- - reprList "(" " " ")" [3 :: Int, 4, 5] => "(3 4 5)"--class Repr a where-- repr :: a -> String- repr = head . reprl-- reprl :: a -> [String]- reprl x = [repr x]-- reprs :: a -> String- reprs = unwords . reprl-- reprList :: String -> String -> String -> [a] -> String- reprList pre tween post xs =- pre ++ intercalate tween (map repr xs) ++ post--instance Repr Bool where repr = show-instance Repr Char where repr = show-instance Repr Int where repr = show-instance Repr Integer where repr = show-instance Repr Number where repr = show-instance Repr Float where repr = show-instance Repr Double where repr = show---- instance Repr String won't work because String is a type synonym,--- unless you ask ghc nicely, which I'd prefer not to do.--- Use Name data type in Testing/Tree.hs instead, or Symbol in Expr.hs--- I don't know if I can use Expr.Symbol here, since Expr.hs also--- imports Tree.hs (this file) -- is mutual import allowed?--data Name = Name String- deriving (Eq, Read, Show)--instance Repr Name where- repr (Name s) = s
− Sifflet/UI.hs
@@ -1,30 +0,0 @@-{- Workspace.hs implements the graphical workspace of Sifflet -}--module Sifflet.UI- (- module Sifflet.UI.Frame- , module Sifflet.UI.LittleGtk- , module Sifflet.UI.RPanel- , module Sifflet.UI.Types- , module Sifflet.UI.Callback- , module Sifflet.UI.Canvas- , module Sifflet.UI.Tool- , module Sifflet.UI.Workspace- , module Sifflet.UI.GtkForeign- , module Sifflet.UI.GtkUtil- , module Sifflet.UI.Window- )--where--import Sifflet.UI.Callback-import Sifflet.UI.Canvas-import Sifflet.UI.Frame-import Sifflet.UI.Tool-import Sifflet.UI.Workspace-import Sifflet.UI.GtkForeign-import Sifflet.UI.GtkUtil-import Sifflet.UI.LittleGtk-import Sifflet.UI.RPanel-import Sifflet.UI.Types-import Sifflet.UI.Window
− Sifflet/UI/Callback.hs
@@ -1,140 +0,0 @@-module Sifflet.UI.Callback- (- CBMgr, CBMgrCmd(..), mkCBMgr- , MenuSpec(..), MenuItemSpec(..), MenuItemAction- , createMenuBar, addMenu, createMenu, createMenuItem- , modifyIORefIO- ) --where--import Data.IORef--import Graphics.UI.Gtk--import Sifflet.UI.Types---- | The CBMgr (Callback manager) encapsulates (in an enclosure, no less!)--- an IORef VPUI. It is used *solely* to set up callbacks--- and similar stuff in Gtk, where the callback needs access--- to the IORef. By passing a CBMgr to a function, we can--- avoid passing the IORef directly, and all the harm and--- confusion that could result.------ We only need *one* CBMgr for the application;--- however, two CBMgrs with the same IORef are logically equivalent,--- so there would be no harm in having two as long as they share one IORef.-type CBMgr = CBMgrCmd -> IO ()---- | Commands for the CBMgr-data CBMgrCmd- = -- window events- OnWindowConfigure Window (IORef VPUI -> EventM EConfigure Bool)- | OnWindowDestroy Window (IORef VPUI -> IO ())- | OnWindowKeyPress Window (IORef VPUI -> EventM EKey Bool)- -- layout events- | OnLayoutExpose Layout (IORef VPUI -> EventM EExpose Bool)- | OnLayoutMouseMove Layout (IORef VPUI -> EventM EMotion Bool)- | OnLayoutButtonPress Layout (IORef VPUI -> EventM EButton Bool)- | OnLayoutButtonRelease Layout (IORef VPUI -> EventM EButton Bool)- -- other events- | OnMenuItemActivateLeaf MenuItem (VPUI -> IO VPUI)- | OnEntryActivate Entry (IORef VPUI -> IO ())- | AfterButtonClicked Button (IORef VPUI -> IO ())-- | UMTest---- | Create the CBMgr-mkCBMgr :: IORef VPUI -> CBMgr-mkCBMgr uiref cmd = - case cmd of- -- window events- OnWindowConfigure window action ->- on window configureEvent (action uiref) >> return ()- OnWindowDestroy window action ->- onDestroy window (action uiref) >> return ()- OnWindowKeyPress window action ->- on window keyPressEvent (action uiref) >> return ()- -- layout events- OnLayoutExpose layout action ->- on layout exposeEvent (action uiref) >> return ()- OnLayoutMouseMove layout action ->- on layout motionNotifyEvent (action uiref) >> return ()- OnLayoutButtonPress layout action ->- on layout buttonPressEvent (action uiref) >> return ()- OnLayoutButtonRelease layout action ->- on layout buttonReleaseEvent (action uiref) >> return ()- -- other events- OnMenuItemActivateLeaf menuItem action ->- onActivateLeaf menuItem (modifyIORefIO uiref action) >> return ()- OnEntryActivate entry action ->- onEntryActivate entry (action uiref) >> return ()- AfterButtonClicked button action ->- afterClicked button (action uiref) >> return ()-- UMTest -> - putStrLn "UMTest"----- ============================================================--- MENUS---- Easy creation of menus from lists.--- Originally from ~/src/haskell-etudes/gtk2hs/gMenu.hs--data MenuSpec = MenuSpec String [MenuItemSpec]-data MenuItemSpec = MenuItem String MenuItemAction- | SubMenu MenuSpec--type MenuItemAction = VPUI -> IO VPUI -- was just IO ()--createMenuBar :: [MenuSpec] -> CBMgr -> IO MenuBar-createMenuBar menuSpecs cbmgr = do- bar <- menuBarNew- mapM_ (addMenu bar cbmgr) menuSpecs- return bar--addMenu :: MenuBar -> CBMgr -> MenuSpec -> IO ()-addMenu mbar cbmgr mspec@(MenuSpec name _itemSpecs) = do- menuHead <- menuItemNewWithLabel name -- visible "item" at top of the menu- menuShellAppend mbar menuHead- -- Right-justify help menu.- -- Deprecated (bad for right-to-left languages),- -- but retained for compatibility with menus_hard.py.- menuItemSetRightJustified menuHead (name == "Help") -- ??????-- -- menu = the container for menu items- menu <- createMenu mspec cbmgr- menuItemSetSubmenu menuHead menu--createMenu :: MenuSpec -> CBMgr -> IO Menu-createMenu (MenuSpec _name itemSpecs) cbmgr = do- menu <- menuNew- mapM_ (createMenuItem menu cbmgr) itemSpecs- return menu--createMenuItem :: Menu -> CBMgr -> MenuItemSpec -> IO ()-createMenuItem menu cbmgr mispec = - case mispec of- MenuItem label action ->- do- {- item <- menuItemNewWithLabel label- ; cbmgr (OnMenuItemActivateLeaf item action)- -- may need to read/write IORef here ***- ; menuShellAppend menu item- }- SubMenu subspec@(MenuSpec label _itemSpecs) ->- do- {- item <- menuItemNewWithLabel label- ; submenu <- createMenu subspec cbmgr - ; menuItemSetSubmenu item submenu- ; menuShellAppend menu item- }---- | Read an IORef, update with IO, and write the updated value.--- This is like modifyIORef, but the type of the second argument is (a -> IO a)--- instead of (a -> a).-modifyIORefIO :: IORef a -> (a -> IO a) -> IO ()-modifyIORefIO ref updateIO = readIORef ref >>= updateIO >>= writeIORef ref
− Sifflet/UI/Canvas.hs
@@ -1,982 +0,0 @@--- File: Canvas.hs--- Canvas and CanvFrame data and operations--module Sifflet.UI.Canvas- (- atLeastSize- , cfContext- , connect- , disconnect- , drawCanvas- , editFunction- , frameChanged- , nodeContainerFrame- , pointSelection- , vcAddFrame- , vcClearSelection- , vcClearFrame- , vcCloseFrame- , vcEvalDialog- , vcFrameAddFunctoidNode- , vcFrameAddNode- , vcFrameDeleteNode- , vcFrameDeleteTree- , vcFrameSubframes- , vcGetFrame- , vcInvalidateFrameWithParent- , vcInvalidateBox- , vcUpdateFrameAndGraph- , vcanvasNew- , vcanvasNodeAt- , vcanvasNodeRect- , whichFrame - , callFrames- )--where--import Control.Monad-import Data.List as List--import Data.Graph.Inductive as G--import Graphics.Rendering.Cairo hiding (translate)--import Sifflet.Data.Functoid-import Sifflet.Data.Geometry as Geometry-import Sifflet.Data.Tree as T-import Sifflet.Data.TreeGraph-import Sifflet.Data.TreeLayout-import Sifflet.Data.WGraph-import Sifflet.Language.Expr-import Sifflet.Language.Parser-import Sifflet.Rendering.Draw-import Sifflet.UI.Frame-import Sifflet.UI.GtkUtil-import Sifflet.UI.LittleGtk-import Sifflet.UI.Types-import Sifflet.Util---- Experimental:-enableDoubleBuffering :: Bool-enableDoubleBuffering = True--vcanvasNew :: Style -> Double -> Double -> IO VCanvas-vcanvasNew style width height = do- -- create gtkLayout (the "drawing canvas")- gtkLayout <- layoutNew Nothing Nothing- -- Turn double buffering on or off. - -- Normally, double buffering eliminates flicker,- -- but if the rendering is through the network,- -- it might be better to disable it.- -- See docs for Graphics.UI.Gtk.Gdk.DrawWindow- -- (drawWindow{Begin,End}PaintRegion), and- -- Graphics.UI.Gtk.Abstract.Widget- -- (widgetSetDoubleBuffered).- widgetSetDoubleBuffered gtkLayout enableDoubleBuffering-- -- create the VCanvas- let vCanvas = VCanvas {vcLayout = gtkLayout, vcStyle = style, - vcGraph = wgraphNew,- vcFrames = [],- -- vcSize is the requested size of the- -- canvas (Gtk.Layout)- vcSize = Size width height,- -- but this is the requested size;- -- how can I get the actual, current size?- -- Answer: - -- layoutGetSize :: Layout -> IO (Int, Int)- -- removed: vcLocalEnv = env,- vcMousePos = (0, 0), -- ???- vcTool = Nothing,- vcDragging = Nothing,- vcActive = Nothing,- vcSelected = Nothing-- }- -- Most essential event handlers- _ <- onSizeRequest gtkLayout- (return (Requisition (round width) (round height)))- - return vCanvas--nodeContainerFrame :: VCanvas -> WGraph -> G.Node -> CanvFrame-nodeContainerFrame vcanvas g = vcGetFrame vcanvas g . nodeContainerFrameNode g---- Ask the VCanvas to find the frame whose frame node element is the--- given Node; it is an error if not found or if there is more than one.-vcGetFrame :: VCanvas -> WGraph -> Node -> CanvFrame-vcGetFrame vcanvas graph frameNode = - let frames = [f | f <- vcFrames vcanvas, cfFrameNode f == frameNode]- err phrase = errcats ["vcGetFrame", phrase,- "frameNode:", show frameNode, - "\nframes:", show frames,- "\ngraph:\n", show graph]- in case frames of- [frame] -> frame- [] -> err "no frame found"- (_:_:_) -> err "multiple frames found"---- | Ask the vcanvas to update the frame and install a new graph.--- Frames are identified by their frame nodes, so the new frame --- must have the same frame node as the old.--- It is an unreported error if there is not exactly one match.-vcUpdateFrameAndGraph :: VCanvas -> CanvFrame -> WGraph -> VCanvas-vcUpdateFrameAndGraph vcanvas newFrame newGraph =- let frames = vcFrames vcanvas- frameNode = cfFrameNode newFrame -- should match frameNode of old frame- frames' = - [if cfFrameNode f == frameNode then newFrame else f | f <- frames]- in vcanvas {vcFrames = frames', vcGraph = newGraph}---- | Like vcUpdateFrameAndGraph, but keep the canvas's old graph.-vcUpdateFrame :: VCanvas -> CanvFrame -> VCanvas-vcUpdateFrame vcanvas newFrame =- vcUpdateFrameAndGraph vcanvas newFrame (vcGraph vcanvas)---- Delete a frame from the vcanvas's frames ref--- This does not update the graph -- see vcCloseFrame for that.-vcDeleteFrame :: VCanvas -> CanvFrame -> VCanvas-vcDeleteFrame vcanvas frame =- let frames = vcFrames vcanvas- node = cfFrameNode frame- frames' = [f | f <- frames, cfFrameNode f /= node]- in vcanvas {vcFrames = frames'}---- RENDERING--- perhaps ought to be its own module---- Perhaps this ought to be called graphRenderFunctoid! ***--graphRenderFunctoidParts :: - Style -> Maybe Node -> Maybe Selection -> WGraph -> CanvFrame -> Render ()-graphRenderFunctoidParts style mact msel graph frame = - case cfFunctoid frame of- FunctoidFunc _ -> error "graphRenderFunctoidParts: not an edit frame"- FunctoidParts {} -> - graphRenderForest style mact msel graph - (nodeProperSimpleDescendants graph - (cfFrameNode frame))--graphRenderForest :: - Style -> Maybe Node -> Maybe Selection -> WGraph -> [G.Node] -> Render ()-graphRenderForest style mact msel graph roots = - let renderNode node = graphRenderTree style mact msel graph node False- in mapM_ renderNode roots--graphRenderTree :: Style -> Maybe Node -> Maybe Selection -> WGraph ->- G.Node -> Bool -> Render ()-graphRenderTree style mact msel graph rootNode fillBackground =- let loop :: Maybe Iolet -> G.Node -> Render ()- loop mInlet currentNode = do- -- Render the root- (inlets, outs) <-- graphRenderNode style mact msel graph currentNode mInlet- -- Render the subtrees- loopWithInlets 0 inlets (sortBy adjCompareEdge outs)- - -- loopWithInlets n inletPositions outs:- -- n = the current inlet number, starting from 0- -- inletPositions = the points to connect to on the parent- -- outs = a list of (child, edge) pairs (adjs)- -- going to *simple* children (i.e., not frame nodes)- -- There must be at least as many inlets as there are outs.- -- If the edge of the first outs equals n, we use the- -- first inletPosition. Otherwise we skip the inlet- -- but not out.- loopWithInlets :: Int -> [Iolet] -> [(G.Node, WEdge)] -> Render ()- loopWithInlets _n _is [] = return ()- loopWithInlets n (i:is) (a:as) =- -- n: number of child, i: inlet, a: adjacency (node, edge)- let (node, edge) = a in- if edge == WEdge n- then do- loop (Just i) node -- draw node with current inlet- loopWithInlets (n + 1) is as -- and draw the rest- else -- skip current inlet- loopWithInlets (n + 1) is (a:as) - - loopWithInlets _n [] (a:as) = - errcats ["loopWithInlets: insufficient inlets, with these",- "outs remaining:", show (a:as)]-- in do- graphStartRender style graph rootNode fillBackground- loop Nothing rootNode--graphStartRender :: Style -> WGraph -> G.Node -> Bool -> Render ()-graphStartRender style graph rootNode fillBackground = do- -- global actions: can be done once for the whole drawing- -- instead of once per subtree- -- Choose: Antialias{Default,None,Gray,Subpixel}- setAntialias AntialiasDefault-- -- draw the canvas background- setColor (styleNormalFillColor style)- let rootCtx = context graph rootNode- WSimple lroot = lab' rootCtx- BBox x y w' h' = nodeTreeBB lroot- when fillBackground $ do { rectangle x y w' h'; fill}-- -- now set up for the rest- -- setColor (styleNormalTextColor style)- setLineWidth (lineWidth style)---graphRenderNode :: - Style -> Maybe Node -> Maybe Selection -> WGraph ->- G.Node -> Maybe Iolet -> Render ([Iolet], [(G.Node, WEdge)])--- Returns a list of inlets and a list of "outs":--- a list of (child, edge) pairs (adjs) --- going to *simple* children only (not to frames formed--- by expanding a node).--- This can then be used if we wish to render the children of--- the node, as when rendering a tree.-graphRenderNode style mact msel graph node mInlet = - -- status of this node- let nodeActive = mact == Just node- mode = if nodeActive- then DrawActive- else case msel of- Nothing -> DrawNormal- Just sel ->- if selNode sel /= node then DrawNormal- else case sel of- SelectionNode _ -> DrawSelectedNode- SelectionInlet {selInEdge = WEdge i} ->- DrawSelectedInlet i- SelectionOutlet {selOutEdge = WEdge o} ->- DrawSelectedOutlet o- - connectInlet :: Iolet -> Double -> Double -> Render ()- connectInlet inlet tx ty = do- -- draw the line from the parent inlet to this node's outlet- let Position px py = ioletCenter inlet- setColor (styleNormalEdgeColor style)- moveTo px (py + snd (vtinypad style)) -- bottom of parent- lineTo tx (ty - fst (vtinypad style)) -- top of this node- stroke -- ctx = context graph node- lnode :: LayoutNode ExprNode- WSimple lnode = lab' ctx-- -- where to connect to this node- nodeBB = gnodeNodeBB (nodeGNode lnode)- xcenter = bbXCenter nodeBB- defaultInlet = Iolet (Geometry.Circle - (Position xcenter (bbBottom nodeBB)) 0)- inlets =- case gnodeInlets (nodeGNode lnode) of- [] -> replicate (length outs) defaultInlet- is -> is- - -- children (nodes and edges)- outs = lsuc' ctx :: [(G.Node, WEdge)]- -- omit links to frames opened from a node- outs' = [(child, edge) |- (child, edge) <- outs, nodeIsSimple graph child]-- in if length inlets < length outs'- then errcats ["graphRenderTree: insufficient inlets:", - show (length inlets, length outs'),- show (inlets, outs')]- else do-- -- Render the node- draw style mode lnode-- -- Connect to its parent (if any)- case mInlet of- Nothing -> return ()- Just inlet -> connectInlet inlet xcenter (bbTop nodeBB) -- return (inlets, outs')---- END OF RENDERING--- ------------------------------------------------------------------------- | Make nothing be selected--vcClearSelection :: VCanvas -> IO VCanvas-vcClearSelection canvas =- case vcSelected canvas of- Nothing -> return canvas- Just sel ->- let node = selectionNode sel- in do- vcInvalidateSimpleNode canvas node- return (canvas {vcSelected = Nothing})---- | The Graph Node of a Selection-selectionNode :: Selection -> G.Node-selectionNode sel =- case sel of- SelectionNode n -> n- SelectionInlet n _ -> n- SelectionOutlet n _ -> n---- | What is selected (if anything) at a point--pointSelection :: WGraph -> CanvFrame -> Position -> Maybe Selection-pointSelection graph frame point =- -- Try to find something to select at the point,- -- i.e., a node or an iolet on the node- case cfFunctoid frame of- FunctoidFunc _ -> error "graphFindFunctionPart: not an edit frame"- FunctoidParts {fpNodes = grNodes} ->- let layoutNodes = map (grExtractLayoutNode graph) grNodes- tuples = zip grNodes layoutNodes-- loop :: [(G.Node, LayoutNode ExprNode)] -> Maybe Selection- loop [] = Nothing- loop (t:ts) = -- (n:ns) =- let (gn, ln) = t -- graph node, tlo node- gnode = nodeGNode ln- inlets = gnodeInlets gnode- outlets = gnodeOutlets gnode- in- -- look at the ports first,- case pointIolet point 0 inlets of- Just i ->- Just (SelectionInlet gn (WEdge i))- Nothing ->- case pointIolet point 0 outlets of- Just o ->- Just (SelectionOutlet gn (WEdge o))- Nothing ->- -- try in the node proper- if pointInGNode point gnode- then- Just (SelectionNode gn)- else- -- try the remaining tuples- loop ts -- in loop tuples---- | Connect nodes--connect :: VCanvas -> G.Node -> WEdge -> G.Node -> WEdge -> IO VCanvas-connect canvas parent inlet child outlet = do- -- if parent and child are different,- -- connect the ith inlet of node parent- -- to the oth outlet of node child- -- provided that doing so would not create a cycle- -- parent -> child -> ... -> parent-- let graph = vcGraph canvas- if elem parent (reachable child graph)- then - do- showErrorMessage "Sorry, this connection would create a cycle."- return canvas- else - -- now we need to store a labeled edges (inlet -> outlet)- -- and to clear any previous connections of the two.- let graph' = grConnect graph parent inlet child outlet- in return $ canvas {vcGraph = graph'}---- | Disconnect nodes---- disconnect wouldn't need to be in the IO monad,--- except that it needs the same type signature as connect-disconnect :: VCanvas -> G.Node -> WEdge -> G.Node -> WEdge - -> IO VCanvas-disconnect canvas parent inlet child outlet = do- -- Opposite of connect, except we don't have to check for cycles- -- of any kind. We also reconnect the child to the frame node- -- as its "parent."- let graph = vcGraph canvas- graph' = grDisconnect graph parent inlet child outlet True- return $ canvas {vcGraph = graph'}--vcFrameAddFunctoidNode :: - VCanvas -> CanvFrame -> Functoid -> Double -> Double -> IO VCanvas-vcFrameAddFunctoidNode canvas frame nodeFunc x y = - let exprNode = ENode (NSymbol (Symbol (functoidName nodeFunc))) EvalUntried- args = functoidArgNames nodeFunc- in vcFrameAddNode canvas frame exprNode args x y--vcFrameAddNode :: VCanvas -> CanvFrame -> ExprNode -> [String] - -> Double -> Double -> IO VCanvas-vcFrameAddNode canvas frame exprNode inletLabels x y =- case cfFunctoid frame of- FunctoidFunc _function -> error "vcFrameAddNode: frame is not an edit frame"- fp@FunctoidParts {fpNodes = ns} -> - do- let -- Converting to a tree to lay it out seems overkill--- exprTree = T.Node exprNode []- style = styleIncreasePadding (vcStyle canvas) 10- counter = argIoletCounter inletLabels- layoutTree = treeLayout style counter exprTree-- let graph = vcGraph canvas- layoutTree' = layoutTreeMoveCenterTo x y layoutTree- layoutRoot = rootLabel layoutTree'- newNode = WSimple layoutRoot- -- insert into graph- (graph', gNodeId) = grInsertNode graph newNode- frameNode = cfFrameNode frame- edge = (frameNode, gNodeId, WEdge (outdeg graph frameNode + 1))- graph'' = insEdge edge graph'- -- insert into the fpNodes- ns' = (gNodeId:ns)- fp' = fp {fpNodes = ns'}-- {-- -- DON'T!- -- Adjust header and footer for new body size-- -- BUT: IS THERE ANYTHING HERE THAT I SHOULD KEEP- -- WHEN I MAKE FRAMES KEEP A MINIMUM SIZE TO FIT- -- THE LAYOUT OF THEIR BODIES?-- layoutNodes = map (grExtractLayoutNode graph') ns'- bodyBB = -- layoutTreeBB layoutTree'- bbMergeList (map nodeTreeBB layoutNodes)- header' = alignHeader (cfHeader frame) bodyBB- footer' = alignFooter (cfFooter frame) bodyBB- -- grow box to fit new node- box' = bbMergeList [tbBoxBB header', tbBoxBB footer', bodyBB]- -- insert into frame- frame' = frame {cfHeader = header',- cfFooter = footer',- cfBox = box', - cfFunctoid = fp'}- -}- frame' = frame {cfFunctoid = fp'}-- -- store new frame and graph into canvas- canvas' = vcUpdateFrameAndGraph canvas frame' graph''-- -- Ready to redraw- frameChanged canvas graph frame graph'' frame' -- return canvas'--vcFrameDeleteNode :: VCanvas -> CanvFrame -> G.Node -> IO VCanvas-vcFrameDeleteNode canvas frame node = - let -- Remove the graph node from the frame and canvas,- -- giving its orphaned children the frame as their new parent- graph = vcGraph canvas- frameNode = cfFrameNode frame- children = nodeAllChildren graph node- -- Remove node from graph- graph' = grRemoveNode graph node- -- Frame adopts orphans- graph'' = foldl (\ g child -> connectToFrame child frameNode g) - graph' - children- -- Remove node from funcparts- fp@FunctoidParts {fpNodes = ns} = cfFunctoid frame- fp' = fp {fpNodes = List.delete node ns}- frame' = frame {cfFunctoid = fp'}- - -- Update references- canvas' = vcUpdateFrameAndGraph canvas frame' graph''- in do- -- Ask to be redrawn- frameChanged canvas graph frame graph'' frame'- return canvas'---- | Remove the (sub)tree rooted at the given node.--- Removes it from the graph of the canvas--- and from the FunctoidParts of the frame.-vcFrameDeleteTree :: VCanvas -> CanvFrame -> G.Node -> IO VCanvas-vcFrameDeleteTree canvas frame rootNode = -- let removeTree :: (WGraph, [G.Node]) -> G.Node -> (WGraph, [G.Node])- removeTree (g, ns) root =- let g' = grRemoveNode g root- ns' = List.delete root ns- in foldl removeTree (g', ns') (nodeAllChildren g root)- - graph = vcGraph canvas- fp@FunctoidParts {fpNodes = fnodes} = cfFunctoid frame- (graph', fnodes') = removeTree (graph, fnodes) rootNode- frame' = frame {cfFunctoid = fp {fpNodes = fnodes'}}- - -- Update references- canvas' = vcUpdateFrameAndGraph canvas frame' graph'- in do- -- Ask to be redrawn- frameChanged canvas graph frame graph' frame'- return canvas'----- | Add a frame representing a functoid to the canvas.------ Use mvalues = Nothing if you do not want the frame to be evaluated--- as a function call, otherwise mvalues = Just values.--- --- prevEnv is *supposed* to be the previous environment, --- i.e., that of the--- "parent" frame or the canvas, not of the new frame,--- because vcAddFrame itself will extend the environment--- with the new (vars, values).--- But this is odd, because openNode calls vcAddFrame --- apparently with the *new* environment as prevEnv,--- and yet it works correctly.------ Caution: I think it is necessary for the canvas to have been realized--- before calling this function!--vcAddFrame :: VCanvas -> Functoid -> Maybe [Value] -> FrameType- -> Env -> Double -> Double -> Double -> Maybe G.Node - -> IO VCanvas-vcAddFrame canvas functoid mvalues mode prevEnv x y z mparent = do- let graph = vcGraph canvas- (_, hi) = nodeRange graph- frameNode = hi + 1 -- implicit: root = hi + 2- style = vcStyle canvas- (newFrame, tlo) = frameNewWithLayout style (Position x y) z - functoid mvalues - CallFrame -- mode may change below- frameNode prevEnv mparent- inAdj = case mparent of- Nothing -> []- Just parent -> - -- Adjacency (priority, parent)- [(WEdge (outdeg graph parent), parent)]- -- add the new frame node, possibly linked to its parent,- -- and the tree of the new frame- graph' = grAddGraph - ((inAdj, frameNode, WFrame frameNode, []) & graph)- (flayoutToGraph tlo)- -- connect the frame to the tree-layout roots- layoutRoots = map (+ frameNode) (flayoutToGraphRoots tlo)- outEdges = [(frameNode, root, WEdge priority) | - (priority, root) <- zip [0..] layoutRoots]- graph'' = insEdges outEdges graph'- -- update the frames and the graph in the canvas- frames = vcFrames canvas-- canvas' = canvas {vcFrames = (newFrame:frames)- , vcGraph = graph''}-- -- Make sure canvas is big enough to contain newFrame- -- This is also done when the window is resized- -- (../Callbacks.hs: configuredCallback).-- frameBB = cfBox newFrame- canvas'' = - atLeastSize (Size (bbRight frameBB) (bbBottom frameBB)) canvas'-- -- Request redraw of the region, including tether to parent, if any- -- error occurs here if the widget is not yet realized, I think- vcInvalidateFrameWithParent canvas graph'' newFrame- case mode of- CallFrame -> return canvas''- EditFrame -> editFunction canvas'' newFrame ---- | Return a canvas of at least the specified size--- and otherwise like the given canvas.-atLeastSize :: Size -> VCanvas -> VCanvas-atLeastSize minSize@(Size minW minH) canvas =- let Size w h = vcSize canvas- frames = vcFrames canvas- frames' = if canvasEditing canvas- then -- only one frame, expand it to fill desired size- [atLeastSizeFrame minSize (head frames)]- else frames- in canvas {vcSize = Size (max w minW) (max h minH), vcFrames = frames'}--vcInvalidateFrameWithParent :: VCanvas -> WGraph -> CanvFrame -> IO ()-vcInvalidateFrameWithParent vcanvas graph frame =- -- "Invalidate" the frame itself, and if it has a parent,- -- the region between it and its parent, so that the frame- -- and its tether will be redrawn- let box1 = cfBox frame- box2 = - case cfParent frame of- Just parent -> bbMerge (nodeBBox graph parent) box1- Nothing -> box1- in vcInvalidateBox vcanvas box2--vcInvalidateSimpleNode :: VCanvas -> G.Node -> IO ()-vcInvalidateSimpleNode vcanvas node = - -- For WSimple nodes --- -- Similarly, invalidate the node itself - -- (but not yet its parent (if any))- case wlab (vcGraph vcanvas) node of- WFrame _ -> error "vcInvalidateSimpleNodeWithParent: node is not simple"- WSimple layoutNode ->- vcInvalidateBox vcanvas (gnodeNodeBB (nodeGNode layoutNode))--vcInvalidateBox :: VCanvas -> BBox -> IO ()-vcInvalidateBox vcanvas (BBox x y width height) = - -- take into account the line width of the box as drawn- -- and the radius of Iolets. We'll do this even- -- though frames do not have Iolets on their edges!- -- This is also a little broader than necessary even for- -- WSimple nodes, since the Iolets are on top and bottom,- -- not on the side.- let style = vcStyle vcanvas- t = lineWidth style + styleIoletRadius style- rect = bbToRect (BBox (x - t) (y - t) - (width + 2 * t) (height + 2 * t)) - in do- win <- layoutGetDrawWindow (vcLayout vcanvas)- drawWindowInvalidateRect win rect False--frameChanged :: - VCanvas -> WGraph -> CanvFrame -> WGraph -> CanvFrame -> IO ()-frameChanged vcanvas g f g' f' = - -- (graph g, frame f) has been replaced by (g', f')- do- vcInvalidateFrameWithParent vcanvas g f -- old- vcInvalidateFrameWithParent vcanvas g' f' -- new---- MORE RENDERING--- -----------------------------------------------------------------------drawCanvas :: VCanvas -> Rectangle -> IO ()-drawCanvas canvas clipbox = do- {- -- alternatively match {eventRegion = region}, from which you can get a- -- list of rectangles-- ; let graph = vcGraph canvas- mactive = vcActive canvas- mselected = vcSelected canvas- frames = vcFrames canvas- Size w h = vcSize canvas- - style = vcStyle canvas-- setClip (Rectangle ix iy iwidth iheight) = do- {- rectangle (fromIntegral ix) (fromIntegral iy)- (fromIntegral iwidth) (fromIntegral iheight)- ; clip- }-- drawBackground = do- {- setColor (ColorRGB 0.4 0.4 0.4)- ; rectangle 0 0 w h- ; fill- }-- renderFrame frame = - case frameType frame of- EditFrame -> renderEditFrame frame- CallFrame -> renderCallFrame frame-- renderEditFrame frame = do- {- renderFrameHeader frame- -- ; renderFrameFooter frame- -- the body:- ; setAntialias AntialiasDefault- ; setColor (styleNormalFillColor style)- ; drawBox (Just (styleNormalFillColor style)) Nothing - (frameBodyBox frame)- -- now draw the nodes, if any:- ; graphRenderFunctoidParts style mactive mselected graph frame- ; renderFrameBorder frame- }-- renderCallFrame frame = do- {- let frameRoot = cfRoot frame- -- Just (WSimple _) = lab graph frameRoot -- can't be WFrame!- -- BBox x y width height = nodeTreeBB layoutNode-- -- draw tether from parent (if any)- -- drawTether (nodeParent graph (cfFrameNode frame)) frame- ; fancyTether (nodeParent graph (cfFrameNode frame)) frame-- -- render the graph -- requires the graph, which is *not*- -- in the frame! That's why this is not a "frame method".- ; graphRenderTree style mactive mselected graph frameRoot True-- -- render the header and footer- -- MAYBE do this before the tether?- -- Footer needs to be drawn after body- -- in case the frame is resized too small- ; renderFrameHeader frame- ; renderFrameFooter frame - ; renderFrameBorder frame- }-- renderFrameHeader frame = drawtb cream black black (cfHeader frame)-- renderFrameFooter frame =- drawtb cream black - (if cfEvalReady frame - then lightBlue - else styleAuxColor style)- (cfFooter frame)-- renderFrameBorder frame = drawBox Nothing (Just black) (cfBox frame)- drawtb bgcolor framecolor textcolor tbox = - drawTextBox (Just (styleFont style)) - (Just bgcolor) - (Just framecolor) - textcolor tbox- --- plainTether :: Maybe G.Node -> CanvFrame -> Render ()--- plainTether Nothing _ = return ()--- plainTether (Just parent) frame = --- let pb = nodeBBox graph parent--- fb = cfBox frame--- line f1 f2 = do--- {--- moveTo (f1 pb) (f2 pb) --- ; lineTo (f1 fb) (f2 fb)--- }--- in do--- {--- -- outline the frame's parent--- drawBox Nothing (Just (styleTetherColor style)) pb--- -- draw tether lines--- ; setColor (styleTetherColor style)--- ; line bbLeft bbTop--- ; line bbLeft bbBottom--- ; line bbRight bbTop--- ; line bbRight bbBottom--- ; stroke--- }-- fancyTether :: Maybe G.Node -> CanvFrame -> Render ()- fancyTether Nothing _ = return ()- fancyTether (Just parent) frame = - let pb = nodeBBox graph parent- fb = cfBox frame- side f1 f2 f3 f4 =- do- newPath- moveTo (f1 pb) (f2 pb)- lineTo (f1 fb) (f2 fb)- lineTo (f3 fb) (f4 fb)- lineTo (f3 pb) (f4 pb)- closePath- fill- in do- {- -- outline the frame's parent- drawBox Nothing (Just (styleTetherColor style)) pb- -- draw tether lines- ; setColor (styleTetherColor style)- ; side bbLeft bbTop bbRight bbTop- ; side bbRight bbTop bbRight bbBottom- ; side bbRight bbBottom bbLeft bbBottom- ; side bbLeft bbBottom bbLeft bbTop- }-- ; drawWin <- layoutGetDrawWindow (vcLayout canvas)- ; renderWithDrawable drawWin $ do- {- setClip clipbox- ; drawBackground- -- sorting is a possible bottleneck? - ; (mapM_ renderFrame (sortBy levelOrder frames))- }-- }---- | Find the node, if any, at a given position on the canvas.-vcanvasNodeAt :: VCanvas -> Position -> Maybe G.Node-vcanvasNodeAt vcanvas point =- - -- searchFrames could be done with the Maybe monad, I guess- let searchFrames :: [CanvFrame] -> Maybe G.Node- searchFrames [] = Nothing- searchFrames (f:fs) =- case frameNodeAt f (vcGraph vcanvas) point of- Nothing -> searchFrames fs- Just node -> Just node- - in searchFrames (vcFrames vcanvas)- --vcanvasNodeRect :: VCanvas -> G.Node -> Rectangle-vcanvasNodeRect vcanvas node =- let Just (WSimple layoutNode) = lab (vcGraph vcanvas) node- in bbToRect (gnodeNodeBB (nodeGNode layoutNode))--whichFrame :: VCanvas -> Double -> Double -> Maybe CanvFrame-whichFrame vcanvas x y = - -- Find the frame, if any, in which (x, y) occurs.- -- If there's more than one match, we should return the- -- one "on top" or at the highest level -- this is not yet implemented.- let frames = vcFrames vcanvas- inFrame position = pointInBB position . cfBox- matches = filter (inFrame (Position x y)) frames- in case matches of- [] -> Nothing- [m1] -> Just m1- (m1:_:_) -> - -- multiple frames match, so here needs some additional work- Just m1---- | editFunction: reverse of defineFunction: replace the call frame by--- an edit frame; does not change the VPUI (global env.), just the canvas..-editFunction :: VCanvas -> CanvFrame -> IO VCanvas-editFunction canvas frame = - case frameType frame of- EditFrame -> - return canvas- - CallFrame ->- let FunctoidFunc function = cfFunctoid frame- parts = functionToParts function (vcGraph canvas) - (cfFrameNode frame)- frame' = frame {cfFunctoid = parts, frameType = EditFrame}- -- Make the frame fill the canvas.- frame'' = atLeastSizeFrame (vcSize canvas) frame'- in return $ vcUpdateFrame canvas frame''---- | Find a frame's subframes, i.e., those that were expanded--- to trace the execution of a function call.--- Cannot be in an edit frame.-vcFrameSubframes :: VCanvas -> CanvFrame -> [CanvFrame]-vcFrameSubframes canvas frame =- let graph = vcGraph canvas- subframeNodes = - case frameType frame of- EditFrame -> []- CallFrame -> grTreeSubframeNodes graph (cfRoot frame)- in map (vcGetFrame canvas graph) subframeNodes----- | Given a graph with a rooted tree, collect list of "subframes,"--- i.e., frames that are children of nodes in the tree-grTreeSubframeNodes :: WGraph -> G.Node -> [G.Node]-grTreeSubframeNodes g root =- nodeFrameChildren g root ++ - concatMap (grTreeSubframeNodes g) (nodeSimpleChildren g root)--vcEvalDialog :: VCanvas -> CanvFrame -> IO VCanvas-vcEvalDialog canvas frame =- let FunctoidFunc function = cfFunctoid frame -- FunctoidParts shouldn't happen- varnames = cfVarNames frame- in if null varnames- then evalFrame canvas frame [] -- skip dialog, no inputs- else let argDefault env arg = - case envLookup env arg of- Nothing -> ""- Just v -> repr v- defaults = map (argDefault (cfEnv frame)) varnames-- reader :: Reader [String] [Value]- reader inputs = parseTypedInputs3 inputs varnames - (functionArgTypes function)- in do- dialog <- - createEntryDialog "Input Values" varnames defaults reader (-1)- result <- runEntryDialog dialog- case result of- Nothing -> return canvas- Just values -> evalFrame canvas frame values---- | Evaluate the frame, having gotten a list of values from the dialog--evalFrame :: VCanvas -> CanvFrame -> [Value] -> IO VCanvas-evalFrame canvas frame values = do- -- Close subframes (those that were made by- -- expanding a node of this frame)- canvas' <- vcCloseSubframes canvas frame-- -- Re-evaluate expression tree and update display- let graph = vcGraph canvas'-- -- Pop the current frame's values, if any, before- -- extending with the new values.- -- A frame with no values has a dummy extension,- -- so this is still okay.- frameNode = cfFrameNode frame- -- It's a call frame, so it has a root- root = cfRoot frame- style = vcStyle canvas'- headerTB = cfHeader frame-- -- The tlo may *change*, since showing values may require- -- extra space.-- (frame', tlo') = frameNewWithLayout style - (bbPosition (tbBoxBB headerTB))- (cfLevel frame)- (cfFunctoid frame) (Just values) CallFrame- frameNode - (envPop (cfEnv frame))- Nothing-- -- Since the frame is a call frame, we should have a tree tlo.-- case tlo' of- FLayoutTree _t ->- do- -- update the tree in the graph- let graph' = grUpdateFLayout graph [root] tlo'- canvas'' = vcUpdateFrameAndGraph canvas' frame' graph'- - -- request redrawing of old and new areas- frameChanged canvas' graph frame graph' frame'- - return canvas''-- FLayoutForest _f _b ->- error "vcEvalDialog: finishDialog: tlo is not a tree"---- WORK HERE ***--- This will be a lot like vcEvalDialog, except we are *un*-evaluating.--- :-(---- | vcClearFrame - clear a frame in a canvas; not yet implemented--- What does this mean?--vcClearFrame :: VCanvas -> CanvFrame -> IO VCanvas-vcClearFrame canvas _frame = - showInfoMessage "Sorry" "Stub: vcClear is not yet implemented" >>- return canvas---- | Close a frame and any subframes of it--vcCloseFrame :: VCanvas -> CanvFrame -> IO VCanvas-vcCloseFrame canvas frame = do- -- close any subframes of this frame- canvas' <- vcCloseSubframes canvas frame -- was vpui' = ...-- -- remove it from the frames list- let canvas'' = vcDeleteFrame canvas' frame -- was vpui'' = ...-- -- remove it and its edges from the graph- let -- was vcanvas = vpuiCanvas vpui''- graph = vcGraph canvas''- (_mcontext, graph') = match (cfFrameNode frame) graph- canvas''' = canvas'' {vcGraph = graph'}-- -- graphically invalidate the region of the frame- -- and its tether (i.e., to its parent)- -- (yes, using the *old* vcanvas, graph, and frame)-- vcInvalidateFrameWithParent canvas (vcGraph canvas) frame- return canvas'''---- | Close any subframes of the frame, but not the frame itself-vcCloseSubframes :: VCanvas -> CanvFrame -> IO VCanvas-vcCloseSubframes canvas frame =- foldM vcCloseFrame canvas (vcFrameSubframes canvas frame)---cfContext :: CanvFrame -> ToolContext-cfContext frame =- case frameType frame of- EditFrame -> TCEditFrame frame- CallFrame -> TCCallFrame frame---- | Is our canvas editing a function?-canvasEditing :: VCanvas -> Bool-canvasEditing canvas =- case vcFrames canvas of- [oneFrame] -> frameType oneFrame == EditFrame- _ -> False---- | Find the frames that are calling the named function-callFrames :: VCanvas -> String -> [CanvFrame]-callFrames canvas funcName =- let isCaller frame = functoidName (cfFunctoid frame) == funcName- in filter isCaller (vcFrames canvas)
− Sifflet/UI/Frame.hs
@@ -1,306 +0,0 @@-module Sifflet.UI.Frame- (- CanvFrame(..), FrameType(..)- , argIoletCounter- , atLeastSizeFrame- , cfEvalReady- , cfPointInHeader- , cfPointInFooter- , cfRoot- , frameNewWithLayout- , frameBodyBox- , frameNodeAt- , frameOffset- , levelOrder- , nodeCompoundFunction- , pointIolet- , resizeFrame- , translateFrame- , grTranslateFrameNodes- )--where--import Data.Function-import Data.List--import Data.Graph.Inductive as G--import Sifflet.Data.Functoid-import Sifflet.Data.Geometry-import Sifflet.Data.Tree-import Sifflet.Data.TreeLayout-import Sifflet.Data.WGraph-import Sifflet.Language.Expr-import Sifflet.Util---- ------------------------------------------------------------------------ CanvFrame operations--- -------------------------------------------------------------------------- | A CanvFrame represents (indirectly, through cfRoot, and we--- access to the graph which is provided by the VCanvas)--- a "subgraph" such as the expression tree--- of a function which is being edited or called.--data CanvFrame = CanvFrame {- cfHeader :: TextBox -- ^ top area of the frame- , cfFooter :: TextBox -- ^ bottom area- , cfVarNames :: [String] -- ^ variable (parameter) names- , cfParent :: Maybe G.Node -- ^ the node opened to make this frame- , cfFrameNode :: G.Node -- ^ this frame as a node in the graph;- -- also serves as the ID of the frame.- , cfEnv :: Env -- ^ environment for evaluation- , cfBox :: BBox -- ^ box of the whole frame (header, tree, and footer)- , cfLevel :: Double -- ^ 0 = bottom level, 1 = next higher, etc.- , cfFunctoid :: Functoid -- ^ includes tlo for an edit frame- , frameType :: FrameType -- ^ edit or call frame- }- deriving (Show)- ----- | CanvFrame needs to be Eq in order to be Ord,--- but maybe the Eq and Ord definitions should be more--- in the same spirit?-instance Eq CanvFrame where (==) = (==) `on` cfFrameNode--data FrameType = EditFrame | CallFrame deriving (Eq, Read, Show)---- | Use levelOrder for sorting frames before drawing them-levelOrder :: CanvFrame -> CanvFrame -> Ordering-levelOrder f1 f2 = compare (cfLevel f1) (cfLevel f2) ---- | The root of the tree displayed in the frame-cfRoot :: CanvFrame -> G.Node-cfRoot frame = - case frameType frame of- EditFrame -> error "cfRoot: an edit frame has no root"- CallFrame -> succ (cfFrameNode frame)---- | A frame is "eval ready" -- that is, okay to run the Eval Frame dialog ----- if it is a call frame with no parent-cfEvalReady :: CanvFrame -> Bool-cfEvalReady frame = - (frameType frame == CallFrame) && (cfParent frame == Nothing)--cfPointInHeader :: CanvFrame -> Double -> Double -> Bool-cfPointInHeader frame x y = - pointInBB (Position x y) (tbBoxBB (cfHeader frame))--cfPointInFooter :: CanvFrame -> Double -> Double -> Bool-cfPointInFooter frame x y = - pointInBB (Position x y) (tbBoxBB (cfFooter frame))--frameBodyBox :: CanvFrame -> BBox-frameBodyBox frame =- -- the space between the header and footer- let hbb = tbBoxBB (cfHeader frame)- fbb = tbBoxBB (cfFooter frame)- top = bbBottom hbb- bottom = bbTop fbb- -- assuming both header and footer are left aligned and same width- in BBox (bbLeft hbb) top (bbWidth hbb) (bottom - top)--editFrameNodes :: CanvFrame -> [G.Node]-editFrameNodes frame =- case frameType frame of- EditFrame -> fpNodes (cfFunctoid frame)- CallFrame -> error "editFrameNodes: not an EditFrame"--frameNodeAt :: CanvFrame -> WGraph -> Position -> Maybe G.Node-frameNodeAt frame graph point = - case frameType frame of- CallFrame -> callFrameNodeAt frame graph point- EditFrame -> editFrameNodeAt frame graph point--editFrameNodeAt :: CanvFrame -> WGraph -> Position -> Maybe G.Node-editFrameNodeAt _frame _graph _point = Nothing -- STUB ***- -- this could probably make use of the function that finds ***- -- a selection from a point ***--callFrameNodeAt :: CanvFrame -> WGraph -> Position -> Maybe G.Node-callFrameNodeAt frame graph point = - let search :: [G.Node] -> Maybe G.Node- search [] = Nothing- search (r:rs) =- case lab graph r of- Just (WFrame _) -> search rs- Just (WSimple layoutNode) ->- let LayoutNode rootGNode treeBB = layoutNode- in- if pointInBB point treeBB - -- it's in the tree rooted at r- then if pointInBB point (gnodeNodeBB rootGNode) - -- it's in the root node- then Just r- else search (suc graph r) -- maybe in the subtrees- else search rs -- maybe in the siblings- Nothing -> - errcats ["editFrameNodeAt: search: no label for node",- show r]- -- since this is a call frame, it had better have a root- in search [cfRoot frame]--atLeastSizeFrame :: Size -> CanvFrame -> CanvFrame-atLeastSizeFrame (Size minW minH) frame =- let BBox _ _ width height = cfBox frame- dwidth = if minW > width then minW - width else 0- dheight = if minH > height then minH - height else 0- in resizeFrame frame dwidth dheight--resizeFrame :: CanvFrame -> Double -> Double -> CanvFrame-resizeFrame frame dw dh = - let BBox x y bwidth height = frameBodyBox frame- -- do not shrink body to negative size- bwidth' = max 0 (bwidth + dw)- height' = max 0 (height + dh)- bodyBB' = BBox x y bwidth' height'- header' = alignHeader (cfHeader frame) bodyBB'- footer' = alignFooter (cfFooter frame) bodyBB'- frameBox' = bbMergeList [tbBoxBB header', tbBoxBB footer', bodyBB']- in frame {cfHeader = header', cfFooter = footer', cfBox = frameBox'}---translateFrame :: CanvFrame -> Double -> Double -> CanvFrame-translateFrame frame dx dy =- frame {cfHeader = translate dx dy (cfHeader frame),- cfFooter = translate dx dy (cfFooter frame),- cfBox = translate dx dy (cfBox frame)}----- | Where to position a new frame that is grown out of an old frame?--- This is a very rough draft of frameOffset-frameOffset :: Style -> CanvFrame -> Position-frameOffset style oldFrame = - let bb = cfBox oldFrame- in Position (bbRight bb + styleFramePad style) (bbTop bb - 40)----nodeCompoundFunction :: WGraph -> CanvFrame -> Node -> Maybe Function-nodeCompoundFunction graph frame node = - case lab graph node of- Nothing -> error "nodeCompoundFunction: no label for node"- Just (WFrame _) -> error "nodeCompoundFunction: node has a WFrame label"- Just (WSimple layoutNode) ->- case gnodeValue (nodeGNode layoutNode) of- ENode (NSymbol (Symbol "if")) _mvalue -> Nothing -- not a function- ENode (NSymbol (Symbol symbolName)) _mvalue ->- case envLookup (cfEnv frame) symbolName of- Nothing -> Nothing -- unbound symbol okay, at least sometimes- Just (VFun func@(Function _ _ _ (Compound _ _))) -> Just func- Just _ -> Nothing -- not a compound function- _ -> Nothing -- not a symbol--grTranslateFrameNodes :: WGraph -> CanvFrame -> Double -> Double -> WGraph-grTranslateFrameNodes wgraph frame dx dy =- case frameType frame of- CallFrame -> translateTree dx dy wgraph (cfRoot frame)- EditFrame -> translateNodes dx dy wgraph (editFrameNodes frame)- -pointIolet :: Position -> Int -> [Iolet] -> Maybe Int-pointIolet point n iolets =- -- find the number of the iolet, if any, containing point- case iolets of - [] -> Nothing- (p:ps) ->- if pointInIolet point p - then Just n- else pointIolet point (n + 1) ps----- | argIoletCounter returns (no. of inlets, no. of outlets)--- derived from the argument list of a function still being defined-argIoletCounter :: [String] -> ExprNode -> (Int, Int)-argIoletCounter labels _exprNode = (length labels, 1)---- | Aligning a CanvFrame's header and footer with the body of the frame.--- Aligns the header above, and the footer below, the body of the frame,--- also matching the width if the body widened--alignHeader :: TextBox -> BBox -> TextBox-alignHeader header bodybox =- let headerBB = tbBoxBB header- y0 = bbBottom headerBB- y1 = bbTop bodybox- x0 = bbLeft headerBB- x1 = bbLeft bodybox- in translate (x1 - x0) (y1 - y0) (tbSetWidth header (bbWidth bodybox))--alignFooter :: TextBox -> BBox -> TextBox-alignFooter footer bodybox =- let footerBB = tbBoxBB footer- y0 = bbTop footerBB- y1 = bbBottom bodybox- x0 = bbLeft footerBB- x1 = bbLeft bodybox- in translate (x1 - x0) (y1 - y0) (tbSetWidth footer (bbWidth bodybox))----- | Figure out the frame layout for a function. Returns the layout and frame.--- Currently, the frame is marked as a "call frame"; if you want to edit it,--- call (editFrame? editFunction?)--frameNewWithLayout :: Style -> Position -> Double- -> Functoid -> Maybe [Value] -> FrameType -- added arg- -> Node -> Env -> Maybe G.Node - -> (CanvFrame, FunctoidLayout) -- reversed tuple-frameNewWithLayout style (Position x y) z - functoid mvalues mode frameNode prevEnv mparent = - -- Figure out the positions for a function call with the- -- given function and (possibly) values as arguments- let headerText = functoidHeader functoid- vars = functoidArgNames functoid- footerText = buildFooterText vars mvalues- env = case mvalues of- Nothing ->- -- dummy extension to be popped off- extendEnv [] [] prevEnv - Just values ->- extendEnv vars values prevEnv- -- body tlo- layout0 = flayout style functoid env mvalues- -- header and footer layouts- headerTB0 = makeTextBox style headerText -- at 0 0- footerTB0 = makeTextBox style footerText -- at 0 0-- -- make all three the same width- Size lw lh = flayoutSize layout0- fwidth = maximum [tbWidth headerTB0, lw, tbWidth footerTB0]- -- The __widen functions ensure that each part - -- (header, footer, tree tlo) have the desired width.- headerTB1 = translate x y (widen headerTB0 fwidth)- (dx, dy) = (x - hpad style, tbBottom headerTB1 - vpad style)- layout1 = translate dx dy (flayoutWiden layout0 fwidth)- footerTB1 = translate x (flayoutBottom layout1) - (widen footerTB0 fwidth)- frameBox = BBox x y fwidth - (tbHeight headerTB0 + lh + tbHeight footerTB0)- frame = CanvFrame {cfHeader = headerTB1,- cfFooter = footerTB1,- cfVarNames = vars,- cfParent = mparent,- cfFrameNode = frameNode,- cfEnv = env, - cfBox = frameBox,- cfLevel = z,- cfFunctoid = functoid,- frameType = mode}- in (frame, layout1)----buildFooterText :: [String] -> Maybe [Value] -> String-buildFooterText vars mvalues = - let items =- case mvalues of- Nothing -> vars- Just [] -> vars- Just values ->- if length vars /= length values- then error "buildFooterText: mismatched lists"- else [var ++ " = " ++ repr value |- (var, value) <- zip vars values]- in concat (intersperse ", " items)
− Sifflet/UI/GtkForeign.hs
@@ -1,123 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface, CPP #-}---- | Provides a function to set the cursor to a specified shape.--- Generalized from an example in email--- Re: [Gtk2hs-users] Changing the mouse cursor shape--- From: Chris Mears <chris@cm...> - 2007-05-22 07:48--- found on--- https://sourceforge.net/mailarchive/forum.php?thread_name=87ps50ie9o.fsf%40loki.cmears.id.au&forum_name=gtk2hs-users--- and modified by removing the definition of data Cursor--- since that is now part of gtk2hs/gtk--- although the enum values are not :-(---- The CursorType type itself is now exported from--- Graphics.UI.Gtk.Gdk.Cursor, as also cursorNew--- and cursorNewFromPixmap.--module Sifflet.UI.GtkForeign (setCursor)-where--import System.Glib-import System.Glib.FFI-import Foreign.ForeignPtr ()--import Graphics.UI.Gtk--foreign import ccall "gdk_window_set_cursor" gdkWindowSetCursor :: - Ptr DrawWindow -> Ptr Cursor -> IO ()--foreign import ccall "gdk_cursor_new" gdkCursorNew :: Int -> IO (Ptr Cursor)---- | Set the X Window cursor for a window to a specified type-setCursor :: Window -> CursorType -> IO ()-setCursor window cursorType = do- c <- gdkCursorNew (cursorTypeNumber cursorType)- d <- widgetGetDrawWindow window- let GObject fp = toGObject d- withForeignPtr (castForeignPtr fp) $ \ptr -> gdkWindowSetCursor ptr c---- These numbers are from /usr/include/gtk-2.0/gdk/gdkcursor.h--cursorTypeNumber :: CursorType -> Int-cursorTypeNumber cursorType =- case cursorType of- XCursor -> 0- Arrow -> 2- BasedArrowDown -> 4- BasedArrowUp -> 6- Boat -> 8- Bogosity -> 10- BottomLeftCorner -> 12- BottomRightCorner -> 14- BottomSide -> 16- BottomTee -> 18- BoxSpiral -> 20- CenterPtr -> 22- Circle -> 24- Clock -> 26- CoffeeMug -> 28- Cross -> 30- CrossReverse -> 32- Crosshair -> 34- DiamondCross -> 36- Dot -> 38- Dotbox -> 40- DoubleArrow -> 42- DraftLarge -> 44- DraftSmall -> 46- DrapedBox -> 48- Exchange -> 50- Fleur -> 52- Gobbler -> 54- Gumby -> 56- Hand1 -> 58- Hand2 -> 60- Heart -> 62- Icon -> 64- IronCross -> 66- LeftPtr -> 68- LeftSide -> 70- LeftTee -> 72- Leftbutton -> 74- LlAngle -> 76- LrAngle -> 78- Man -> 80- Middlebutton -> 82- Mouse -> 84- Pencil -> 86- Pirate -> 88- Plus -> 90- QuestionArrow -> 92- RightPtr -> 94- RightSide -> 96- RightTee -> 98- Rightbutton -> 100- RtlLogo -> 102- Sailboat -> 104- SbDownArrow -> 106- SbHDoubleArrow -> 108- SbLeftArrow -> 110- SbRightArrow -> 112- SbUpArrow -> 114- SbVDoubleArrow -> 116- Shuttle -> 118- Sizing -> 120- Spider -> 122- Spraycan -> 124- Star -> 126- Target -> 128- Tcross -> 130- TopLeftArrow -> 132- TopLeftCorner -> 134- TopRightCorner -> 136- TopSide -> 138- TopTee -> 140- Trek -> 142- UlAngle -> 144- Umbrella -> 146- UrAngle -> 148- Watch -> 150- Xterm -> 152- LastCursor -> 153- BlankCursor -> (-2)- CursorIsPixmap -> (-1)
− Sifflet/UI/GtkUtil.hs
@@ -1,224 +0,0 @@-{-# LANGUAGE CPP #-}--module Sifflet.UI.GtkUtil - (suppressScimBridge- , showChoicesDialog, defaultDialogPosition- , runDialogM, runDialogS- -- , runDialogHelper- , showInputErrorMessage, showErrorMessage- , showInfoMessage- , showMessage- , EntryDialog, Reader- , createEntryDialog, runEntryDialog- , addEntryWithLabel- )--where--import Control.Monad--#if !defined(mingw32_HOST_OS) && !defined(__MINGW32__) -import System.Posix.Env-#endif--import Sifflet.UI.LittleGtk-import Sifflet.Util---- SCIM Bridge causes problems, so shush it--suppressScimBridge :: IO ()-suppressScimBridge = -#if defined(mingw32_HOST_OS) || defined(__MINGW32__) - return ()-#else- putEnv "GTK_IM_MODULE=gtk-im-context-simple"-#endif---- ============================================================--- CUSTOMIZABLE DIALOGS---- | Show a message and a set of choices;--- run the action corresponding to the selected choice;--- you might want to include a "Cancel" option with the action return ().--- There *should* be as many actions as there are options.-showChoicesDialog :: String -> String -> [String] -> [IO a] -> IO a-showChoicesDialog title message options actions = do- {- -- Create basic dialog- dialog <- dialogNew- ; windowSetTitle dialog title- ; widgetSetName dialog ("Sifflet-" ++ title)-- -- Add message- ; vbox <- dialogGetUpper dialog- ; label <- labelNew (Just message)- ; boxPackStartDefaults vbox label- ; widgetShowAll vbox-- -- Add buttons- -- Work around bug in gtk2hs v. 0.10.0 and 0.10.1,- -- which is fixed in darcs gtk2hs.- -- fromResponse (ResponseUser i) requires i > 0.- -- so zip [1..] instead of zip [0..]- ; let indexOptions = zip [1..] options- addButton (i, option) = - dialogAddButton dialog option (ResponseUser i)- ; forM_ indexOptions addButton- ; dialogGetActionArea dialog >>= widgetShowAll-- -- Run dialog- ; response <- dialogRun dialog- ; widgetDestroy dialog -- here? or after handling response?- ; case response of- ResponseUser i -> - let j = i - 1 -- work around bug described above- in if j >= 0 && j < length actions- then actions !! j- else errcats ["showChoicesDialog: response index",- show j, "is out of range for actions"]- _ -> errcats ["showChoicesDialog: expected a ResponseUser _ but got",- show response]- }- -createDialog :: String -> (VBox -> IO a) -> IO (Dialog, a)-createDialog title addContent = do- -- Create basic dialog- dialog <- dialogNew- windowSetTitle dialog title- widgetSetName dialog ("Sifflet-" ++ title)-- -- Add custom content- vbox <- dialogGetUpper dialog- content <- addContent vbox-- -- Add standard buttons- _ <- dialogAddButton dialog "OK" ResponseOk- _ <- dialogAddButton dialog "Cancel" ResponseCancel- dialogSetDefaultResponse dialog ResponseOk -- has no effect?-- return (dialog, content)---- | Where to put a dialog window.--- Possible values are--- WinPosNone WinPosCenter WinPosMouse WinPosCenterAlways --- WinPosCenterOnParent--defaultDialogPosition :: WindowPosition-defaultDialogPosition = WinPosMouse---- | Customizable framework for running a dialog--runDialogS :: Dialog -> a -> (a -> IO (SuccFail b)) -> IO (Maybe b)-runDialogS dialog inputs processInputs = - runDialogHelper dialog inputs processInputs True--runDialogM :: Dialog -> a -> (a -> IO (Maybe b)) -> IO (Maybe b)-runDialogM dialog inputs processInputs =- let process' inputs' = do- result <- processInputs inputs'- case result of- Nothing -> return $ Fail "_Nothing_"- Just value -> return $ Succ value- in runDialogHelper dialog inputs process' False--runDialogHelper :: Dialog -> a -> (a -> IO (SuccFail b)) -> Bool -> IO (Maybe b)-runDialogHelper dialog inputs processInputs retryOnError = do- -- Position and show the dialog- windowSetPosition dialog defaultDialogPosition- widgetShowAll dialog- windowPresent dialog-- let run = do- respId <- dialogRun dialog- case respId of- ResponseOk ->- do- result <- processInputs inputs- case result of- Fail msg ->- if retryOnError- then do- showErrorMessage msg- run -- try again- else finish Nothing- Succ value -> finish (Just value)- _ -> finish Nothing-- finish result = do- widgetDestroy dialog- return result-- run--showInputErrorMessage :: String -> IO ()-showInputErrorMessage message =- showErrorMessage ("Input Error:\n" ++ message)--showErrorMessage :: String -> IO ()-showErrorMessage = showMessage (Just "Error") MessageError ButtonsClose--showInfoMessage :: String -> String -> IO ()-showInfoMessage title = showMessage (Just title) MessageInfo ButtonsClose--showMessage :: Maybe String -> MessageType -> ButtonsType -> String -> IO ()-showMessage mtitle messagetype buttonstype message = do- {- msgDialog <- - messageDialogNew- Nothing -- ? or (Just somewindow) -- what does this do?- [] -- flags- messagetype- buttonstype- message- ; case mtitle of- Nothing -> widgetSetName msgDialog "Sifflet-dialog"- Just title -> windowSetTitle msgDialog title >>- widgetSetName msgDialog ("Sifflet-" ++ title)- ; windowSetPosition msgDialog defaultDialogPosition- ; windowPresent msgDialog- ; _ <- dialogRun msgDialog- ; widgetDestroy msgDialog- }---- ============================================================--- INPUT DIALOGS--type Reader a b = (a -> SuccFail b)--data EntryDialog a = EntryDialog Dialog [Entry] (Reader [String] a)--createEntryDialog :: - String -> [String] -> [String] -> (Reader [String] a) -> Int -> - IO (EntryDialog a)-createEntryDialog title labels defaults reader width = do- -- Interpret width = -1 as don't care-- (dialog, entries) <- - createDialog title (addEntries labels defaults)- windowSetDefaultSize dialog width (-1)- return $ EntryDialog dialog entries reader--runEntryDialog :: (Show a) => EntryDialog a -> IO (Maybe a)-runEntryDialog (EntryDialog dialog entries reader) = - let -- processInputs :: [Entry] -> IO (SuccFail a)- -- weird error like in runComboBoxDialog ^- processInputs entries' = do- inputs <- mapM entryGetText entries'- return (reader inputs)-- in runDialogS dialog entries processInputs--addEntries :: [String] -> [String] -> VBox -> IO [Entry]-addEntries labels defaults vbox = do- entries <- mapM (const entryNew) labels- mapM_ (addEntryWithLabel vbox) (zip3 labels entries defaults)- return entries---- | Add a labeled text entry to the vbox.--addEntryWithLabel :: VBox -> (String, Entry, String) -> IO ()-addEntryWithLabel vbox (name, entry, defaultValue) = do- label <- labelNew (Just name)- entrySetText entry defaultValue- boxPackStartDefaults vbox label- boxPackStartDefaults vbox entry
− Sifflet/UI/LittleGtk.hs
@@ -1,179 +0,0 @@--- | The purpose of this module is simply to re-export the--- Gtk functions and types that I use, and avoid those--- that I don't, particularly those that conflict with--- names in Sifflet, like Layout.--- Just hiding these is not enough, because Graphics.UI.Gtk--- keeps changing what it exports.--- For example, in 0.10.5 it no longer exports Function, fill,--- function.--module Sifflet.UI.LittleGtk- (- -- These are just directly re-exported from Gtk:-- -- Attributes- AttrOp(..) -- for (:=)- , get- , set-- , adjustmentNew-- , boxPackEnd- , boxPackStart- , boxPackStartDefaults-- , Button- , buttonNewWithLabel- , buttonPressEvent-- , ButtonsType(..)-- , ContainerClass- , castToContainer- , containerAdd- , containerChild- , containerForeach-- , CursorType(..)- , customStoreSetColumn-- -- Dialog- , Dialog- , dialogNew- , dialogAddButton- , dialogGetActionArea- , dialogGetUpper - , dialogRun- , dialogSetDefaultResponse- , toDialog-- , DrawWindow- , drawWindowInvalidateRect-- , entryCompletionInsertPrefix- , entryCompletionModel- , entryCompletionNew- , entryCompletionSetTextColumn-- , Entry- , entryGetText- , entryGetCompletion- , entryNew- , entrySetCompletion- , entrySetText-- , EventMask(..)-- , eventBoxNew-- , Expander- , expanderNew- , expanderSetExpanded-- , exposeEvent-- , fileChooserDialogNew- , fileChooserGetFilename-- , FileChooserAction(..)-- , frameNew-- , grabAdd- , grabRemove-- , HBox- , hBoxNew-- , keyPressEvent-- , Label- , labelNew- , labelSetText-- , Layout- , layoutGetDrawWindow- , layoutNew- , layoutPut- , layoutSetSize-- , listStoreNew-- , makeColumnIdString-- , menuPopup-- , MessageType(..)- , messageDialogNew-- , on- , onDestroy- , onSizeRequest-- , Packing(..)-- , PolicyType(PolicyAutomatic)-- , Rectangle(..)-- , renderWithDrawable-- , Requisition(..)-- , ResponseId(..)-- , ScrolledWindow- , scrolledWindowNew- , scrolledWindowSetPolicy-- , Statusbar- , statusbarGetContextId- , statusbarNew- , statusbarPop- , statusbarPush-- , VBox- , vBoxNew-- , WidgetClass-- , widgetAddEvents- , widgetClassPath- , widgetDestroy- , widgetGrabFocus- , widgetSetCanFocus- , widgetSetDoubleBuffered- , widgetSetName- , widgetSetSizeRequest- , widgetShow- , widgetShowAll- , widgetSizeRequest- , widgetVisible-- , Window- , windowDeletable- , windowMove- , windowNew- , windowPresent- , windowSetDefaultSize- , windowSetPosition- , windowSetTitle- , windowTitle-- , WindowPosition(..)- -- General GTK stuff- , initGUI- , mainGUI- , mainQuit-- -- These are renamed:- , GtkFrame- , GtkLayout- )--where--import Graphics.UI.Gtk--type GtkFrame = Frame-type GtkLayout = Layout
− Sifflet/UI/RPanel.hs
@@ -1,154 +0,0 @@--- | "Rowed Panel." --- Expandable framed panel with a two-dimensional layout--- (rows of widgets, but not with aligned columns like in a table).--module Sifflet.UI.RPanel- (- RPanel, newRPanel, rpanelId, rpanelRoot, rpanelContent- , rpanelAddWidget, rpanelAddWidgets, rpanelNewRow- , rpanelAddRows- )--where--import Control.Monad--import Sifflet.UI.LittleGtk-import Sifflet.Util--debugTracing :: Bool-debugTracing = False--data RPanel - = RPanel {- -- Public- rpId :: String -- ^ text for the expander button- , rpRoot :: GtkFrame -- ^ use the root to add rpanel to a container- , rpContent :: [[String]] -- ^ ids of widgets added, in rows-- -- Widgets that make up the RPanel- , rpFrame :: GtkFrame -- ^ frame, same as root- , rpExpander :: Expander -- ^ expander- , rpVBox :: VBox -- ^ vbox to contain the rows- , rpCurrentRow :: HBox -- ^ next element goes here if it fits-- -- Geometry book-keeping- , rpCurrentRowFreeWidth :: Int -- ^ free width in current row- , rpMaxWidth :: Int -- ^ maximum row width- , rpHPad :: Int -- ^ horizontal padding- }--rpanelId :: RPanel -> String-rpanelId = rpId--rpanelRoot :: RPanel -> GtkFrame-rpanelRoot = rpRoot--rpanelContent :: RPanel -> [[String]]-rpanelContent = rpContent--newRPanel :: String -> Int -> Int -> Int -> IO RPanel-newRPanel cid hpad vpad maxWidth = do- {- frame <- frameNew -- adds a border (not labeled, since the expander is)- - ; expander <- expanderNew cid- ; expanderSetExpanded expander True- ; set frame [containerChild := expander]-- ; vbox <- vBoxNew False vpad -- non-homogeneous heights- ; widgetSetSizeRequest vbox maxWidth (-1) -- height = don't care- ; set expander [containerChild := vbox]-- ; hbox <- hBoxNew False hpad -- non-homogeoneous widths- ; boxPackStart vbox hbox PackNatural 0-- ; return $ RPanel {rpId = cid- , rpRoot = frame- , rpFrame = frame- , rpExpander = expander- , rpVBox = vbox- , rpCurrentRow = hbox- , rpContent = [[]]- , rpCurrentRowFreeWidth = maxWidth - hpad- , rpMaxWidth = maxWidth - hpad- , rpHPad = hpad- }- }---- | Given a list of (name, widget) pairs, add each of the widgets--- and its name to the rpanel-rpanelAddWidgets :: (WidgetClass widget) =>- RPanel -> [(String, widget)] -> IO RPanel-rpanelAddWidgets rp pairs = - let addPair rp' (widgetId, widget) = rpanelAddWidget rp' widgetId widget- in foldM addPair rp pairs---- | Add a single named widget to the RPanel-rpanelAddWidget :: (WidgetClass widget) =>- RPanel -> String -> widget -> IO RPanel-rpanelAddWidget rp widgetId widget = do- {- Requisition widgetWidth _ <- widgetSizeRequest widget- ; let freeWidth = rpCurrentRowFreeWidth rp- freeWidth' = freeWidth - widgetWidth - rpHPad rp- ; if freeWidth' >= 0 || freeWidth == rpMaxWidth rp- -- Either there is room enough, OR we're at the start of a row- -- so starting another won't help -- in fact it would lead to- -- infinite recursion- then do- {- let content' = insertLastLast (rpContent rp) widgetId- packMode = -- PackNatural -- to left justify- PackGrow -- to fill- -- PackRepel -- to center-- ; boxPackStart (rpCurrentRow rp) widget packMode 0- -- ; widgetShow widget -- do this here???- ; when debugTracing $- putStr (unlines ["Adding " ++ widgetId ++ - " width " ++ show widgetWidth- , "Free width = " ++ show freeWidth ++- " -> " ++ show freeWidth'- , "Content -> " ++ show content'])-- ; return $ rp {rpContent = content'- , rpCurrentRowFreeWidth = freeWidth'}- }- else - -- We're out of room, but not at the start of the current row,- -- so start a new row- do- {- rp' <- rpanelNewRow rp- ; rpanelAddWidget rp' widgetId widget- }- }---- | Force the RPanel to begin a new row--rpanelNewRow :: RPanel -> IO RPanel-rpanelNewRow rp = do- {- hbox <- hBoxNew False (rpHPad rp)- ; boxPackStart (rpVBox rp) hbox PackNatural 0- ; return $ rp {rpCurrentRow = hbox- , rpContent = insertLast (rpContent rp) []- , rpCurrentRowFreeWidth = rpMaxWidth rp}- }---- | Given a list of lists, each sublist representing a row of widgets,--- add the widgets to the RPanel, preserving the row structure--- as much as possible.--- (Row structure will be broken if any intended row is too wide.)-rpanelAddRows :: (WidgetClass widget) =>- RPanel -> [[(String, widget)]] -> IO RPanel-rpanelAddRows rp rows = foldM rpanelAddRow rp rows---- | Add a row of widgets to an RPanel.--- This does not start a new row before the first widget,--- but after the last, so at the end, the current row will be empty.-rpanelAddRow :: (WidgetClass widget) =>- RPanel -> [(String, widget)] -> IO RPanel-rpanelAddRow rp row = - rpanelAddWidgets rp row >>= rpanelNewRow
− Sifflet/UI/Tool.hs
@@ -1,561 +0,0 @@-module Sifflet.UI.Tool- (- ToolId(..)- , checkMods- , functionTool- , functionToolsFromLists- , makeConnectTool- , makeCopyTool- , makeDeleteTool- , makeDisconnectTool- , makeIfTool- , makeMoveTool- , showFunctionEntry- , showLiteralEntry- , vpuiSetTool- , vpuiWindowSetTool-- , vwAddFrame- , vpuiAddFrame-- -- Statusbar- , wsPopStatusbar, wsPushStatusbar-- -- frame context menu commands (do these belong elsewhere?)- , dumpFrame- , dumpGraph- , dumpWorkWin- , clearFrame- , closeFrame- )--where--import Control.Monad-import Control.Monad.Trans (liftIO)-import Data.IORef-import Data.List--import Data.Graph.Inductive as G--import Graphics.UI.Gtk.Gdk.EventM--import Sifflet.Data.Functoid-import Sifflet.Data.Geometry-import Sifflet.Data.Tree (putTree, repr)-import Sifflet.Data.TreeGraph (graphToOrderedTreeFrom)-import Sifflet.Data.WGraph-import Sifflet.Language.Expr-import Sifflet.Language.Parser-import Sifflet.UI.Callback-import Sifflet.UI.Canvas-import Sifflet.UI.Frame-import Sifflet.UI.LittleGtk -import Sifflet.UI.Types-import Sifflet.Util--data ToolId - = ToolConnect- | ToolDisconnect- | ToolIf- | ToolMove- | ToolDelete- | ToolFunction String -- ^ function name- | ToolLiteral Expr- | ToolArg String -- ^ argument name- deriving (Eq, Show)---- Tools--vpuiSetTool :: ToolId -> WinId -> VPUI -> IO VPUI-vpuiSetTool toolId winId =- vpuiUpdateWindowIO winId (vpuiWindowSetTool (toolIdToTool toolId))--vpuiWindowSetTool :: Tool -> VPUIWindow -> IO VPUIWindow-vpuiWindowSetTool tool vw =- case vw of- VPUIWorkWin ws _ ->- do- {- ; wsPopStatusbar ws- ; wsPushStatusbar ws ("Tool: " ++ toolName tool)- ; let canvas' = (wsCanvas ws) {vcTool = Just tool}- ; canvas'' <- toolActivated tool canvas'- ; return $ vpuiWindowSetCanvas vw canvas''- }- _ -> return vw--toolIdToTool :: ToolId -> Tool-toolIdToTool toolId =- case toolId of- ToolConnect -> makeConnectTool- ToolDisconnect -> makeDisconnectTool- ToolIf -> makeIfTool- ToolMove -> makeMoveTool- ToolDelete -> makeDeleteTool- ToolFunction funcname -> functionTool funcname- ToolLiteral e -> makeBoundLiteralTool e- ToolArg argname -> makeBoundArgTool argname---defaultContextDescription :: String-defaultContextDescription = "default context"--wsPushStatusbar :: Workspace -> String -> IO ()-wsPushStatusbar ws msg = do - {- let sbar = wsStatusbar ws- ; contextId <- statusbarGetContextId sbar defaultContextDescription- ; _ <- statusbarPush sbar contextId msg- ; return ()- }--wsPopStatusbar :: Workspace -> IO ()-wsPopStatusbar ws = do- {- let sbar = wsStatusbar ws- ; contextId <- statusbarGetContextId sbar defaultContextDescription- ; statusbarPop sbar contextId- }--makeMoveTool :: Tool-makeMoveTool = - let move canv toolContext _mods x y = - case toolContext of- TCEditFrame frame ->- let graph = vcGraph canv- in case pointSelection graph frame (Position x y) of- sel@(Just (SelectionNode node)) ->- let dragging = - Dragging {draggingNode = node,- draggingPosition = Position x y}- in do- {- vcInvalidateFrameWithParent canv graph frame- ; return $ canv {vcSelected = sel,- vcDragging = Just dragging}- }- _ -> -- no node selected, do nothing- return canv- _ ->- return canv -- not an edit frame, do nothing- in Tool "MOVE" return (toToolOpVW move)--makeDeleteTool :: Tool-makeDeleteTool = - let del :: CanvasToolOp- del canv toolContext mods x y =- case toolContext of- TCEditFrame frame -> - let graph = vcGraph canv- in case pointSelection graph frame (Position x y) of- Just (SelectionNode node) ->- if checkMods [Shift] mods- then vcFrameDeleteTree canv frame node- else vcFrameDeleteNode canv frame node- _ ->- return canv -- no node selected, do nothing-- _ -> - return canv -- not an edit frame, do nothing- - in Tool "DELETE" vcClearSelection (toToolOpVW del)---- | Check that all required modifiers are in found-checkMods :: [Modifier] -> [Modifier] -> Bool-checkMods required found =- all (\ r -> elem r found) required--makeConnectTool :: Tool-makeConnectTool = - Tool "CONNECT" vcClearSelection (toToolOpVW (conn connect))- -makeDisconnectTool :: Tool-makeDisconnectTool = - Tool "DISCONNECT" vcClearSelection (toToolOpVW (conn disconnect))--conn :: (VCanvas -> G.Node -> WEdge -> G.Node -> WEdge -> IO VCanvas)- -> CanvasToolOp-conn action canvas toolContext _mods x y =- case toolContext of- TCEditFrame frame ->- let oldSel = vcSelected canvas- graph = vcGraph canvas- requestRedraw =- vcInvalidateFrameWithParent canvas graph frame- in case pointSelection graph frame (Position x y) of- - -- Overall algorithm: If we're on a port and there's- -- an opposite port selected, apply the action them- -- and clear the selection. Otherwise, if we're on- -- a port, select it. Otherwise do nothing.-- Just sel@(SelectionInlet parent inlet) ->- -- Case 1: we're on an inlet port, - -- opposite port = outlet- do - {- requestRedraw- ; case oldSel of- Just (SelectionOutlet child outlet) ->- do- {- canvas' <- action canvas parent - inlet child outlet- ; return $ canvas' {vcSelected = Nothing}- }- _ -> return $ canvas {vcSelected = Just sel}- }- Just sel@(SelectionOutlet child outlet) ->- -- Case 2: we're on an outlet port, - -- opposite port = inlet- do- {- requestRedraw- ; case oldSel of- Just (SelectionInlet parent inlet) ->- do- {- canvas' <- action canvas parent - inlet child outlet- ; return $ canvas' {vcSelected = Nothing}- }- _ -> return $ canvas {vcSelected = Just sel}- }-- _ -> - -- Case 3: we're not on an iolet, do nothing- return canvas- _ -> - -- not in an edit frame, do nothing- return canvas--makeBoundLiteralTool :: Expr -> Tool-makeBoundLiteralTool e =- let enode node = ENode node EvalUntried- addLitNode node vw toolContext _mods x y =- case toolContext of- TCEditFrame frame ->- vcFrameAddNode vw frame (enode node) [] x y- _ ->- return vw -- Nothing- mktool node =- Tool ("Literal: " ++ repr e) - return - (toToolOpVW (addLitNode node))- in case e of- EBool b -> mktool (NBool b)- EChar c -> mktool (NChar c)- ENumber n -> mktool (NNumber n)- EString s -> mktool (NString s)- EList es -> if exprIsLiteral e- then mktool (NList es)- else errcats ["makeBoundLiteralTool: ",- "non-literal list expression",- show e]- _ ->- errcats ["makeBoundLiteralTool: non-literal or",- "extended expression", show e]--makeBoundArgTool :: String -> Tool-makeBoundArgTool label = - let node = ENode (NSymbol (Symbol label)) EvalUntried- addArgNode vw toolContext _mods x y =- case toolContext of- TCEditFrame frame ->- vcFrameAddNode vw frame node [] x y- _ ->- return vw -- no effect- in Tool ("Argument: " ++ label) return (toToolOpVW addArgNode)--makeIfTool :: Tool-makeIfTool = - let if_ vpui toolContext _mods x y =- case toolContext of- TCEditFrame frame ->- let node = ENode (NSymbol (Symbol "if")) EvalUntried- labels = ["test", "left", "right"]- in vcFrameAddNode vpui frame node labels x y- _ ->- return vpui -- do nothing- in Tool "if" return (toToolOpVW if_)--makeCopyTool :: Tool-makeCopyTool = dummyTool "COPY"--dummyTool :: String -> Tool-dummyTool name = - let op vpui _winId _context _mods x y =- info ("dummyTool", name, x, y) >>- return vpui- in Tool ("*" ++ name ++ "*") return op---- functionTool needs the VPUI in its op,--- because it needs to get an environment.--functionTool :: String -> Tool-functionTool name =- let op :: ToolOp- op vpui winId toolContext _mods x y = - -- Some of these are not used in some cases,- -- but with lazy evaluation it doesn't hurt to- -- declare them all up here:- let env = vpuiGlobalEnv vpui- func = envGetFunction env name- in case toolContext of- TCCallFrame _ -> - return vpui -- do nothing- TCEditFrame frame ->- let modify canvas =- vcFrameAddFunctoidNode canvas frame- (FunctoidFunc func)- x y- in vpuiModCanvasIO vpui winId modify- TCExprNode ->- return vpui -- do nothing- TCWorkspace ->- case functionImplementation func of- Primitive _ -> - return vpui -- do nothing- Compound _ _ -> - -- Add a call frame to the workspace- vpuiAddFrame vpui winId (FunctoidFunc func) - Nothing CallFrame - env x y 0 Nothing- in Tool name return op----- | Add a frame representing a functoid to the canvas --- of a particular window, specified by its window id (title).--vpuiAddFrame :: VPUI -> WinId -> Functoid -> Maybe [Value] -> FrameType- -> Env -> Double -> Double -> Double -> Maybe G.Node - -> IO VPUI-vpuiAddFrame vpui winId functoid mvalues mode prevEnv x y z mparent = - let update vw = - vwAddFrame vw functoid mvalues mode prevEnv x y z mparent- in vpuiUpdateWindowIO winId update vpui---- | Add a frame representing a functoid to the canvas --- of a VPUIWindow (which ought to have a canvas, of course).--- Otherwise like vcAddFrame.--vwAddFrame :: VPUIWindow -> Functoid -> Maybe [Value] -> FrameType - -> Env -> Double -> Double -> Double -> Maybe G.Node - -> IO VPUIWindow-vwAddFrame vw functoid mvalues mode prevEnv x y z mparent = - let modify canvas = vcAddFrame canvas functoid mvalues mode prevEnv- x y z mparent- in vpuiWindowModCanvasIO vw modify--functionToolsFromLists :: [[String]] -> [[Tool]]-functionToolsFromLists = map2 functionTool- --- | Open an entry for user input of function name to select a function tool.--- Returns unaltered VPUI, for convenience in menus and key callbacks.--showFunctionEntry :: WinId -> CBMgr -> VPUI -> IO VPUI-showFunctionEntry winId uimgr vpui = - let env = vpuiGlobalEnv vpui- fsymbols = (envFunctionSymbols env)- -- Check whether the name is bound to a function- checkFunctionName :: String -> SuccFail String- checkFunctionName name =- case envLookup env name of- Nothing -> Fail $ name ++ ": unbound variable"- Just (VFun _) -> Succ name- _ -> Fail $ name ++ ": bound to non-function value"- in showToolEntry winId "Function name" - (Just fsymbols) -- completions- checkFunctionName -- parser- -- activateTool -- action- ToolFunction -- tool type specifier- uimgr -- state- vpui---- | Show an entry for input of a literal value.--- Returns unaltered VPUI, for convenience in menus and key callbacks.--showLiteralEntry :: WinId -> CBMgr -> VPUI -> IO VPUI-showLiteralEntry winId = - -- Needs uimgr for action when entry is activated- showToolEntry winId "Literal value" - Nothing -- completions- parseLiteral -- parser- -- activateTool -- action- ToolLiteral -- tool type specifier---- | New, light replacement for most dialogs---- | Prompt for input in a text entry.--- When the user presses Return, attempt to parse the input text.--- If parse succeeds, apply the toolType to the resulting value--- to produce a ToolId and set the corresponding tool.--- If user presses Escape, the input is closed with no action.--- If mcompletions is (Just comps), comps is a list of possible--- completions for the entry.--- --- Returns the vpui, with NO UPDATE, for convenience in callbacks and menus---- Note: this has been specialized; the action only sets a tool--- on the window. It can be generalized again--- to any sort of action if needed.-showToolEntry :: WinId -> String -> Maybe [String] - -> (String -> SuccFail a) -> (a -> ToolId)- -> CBMgr -> VPUI- -> IO VPUI-showToolEntry winId prompt mcompletions parser toolType uimgr vpui =- let vw = vpuiGetWindow vpui winId- in case vpuiWindowLookupCanvas vw of- Nothing -> error "showToolEntry: no canvas!"- Just canvas ->- let layout = vcLayout canvas- (xx, yy) = vcMousePos canvas- in do- {- ; vbox <- vBoxNew False 5- ; evtBox <- eventBoxNew -- needed for Label visibility- ; label <- labelNew (Just prompt)- ; entry <- entryNew- ; case mcompletions of- Nothing -> return ()- Just comps -> addEntryCompletions entry comps-- -- Organize as (vbox (eventbox (label), entry))- ; containerAdd evtBox label- ; boxPackStartDefaults vbox evtBox- ; boxPackStartDefaults vbox entry- ; layoutPut layout vbox (round xx) (round yy)- ; widgetShowAll vbox-- -- grab and handle events- ; grabAdd entry -- all keyboard and mouse events of the app- ; widgetGrabFocus entry -- grabs keyboard events, still necessary- -- Set actions for TAB (entry completion) and ESC (cancel)- ; _ <- on entry keyPressEvent (entryKeyPress vbox entry)- ; uimgr (OnEntryActivate entry- (entryActivated vbox winId entry parser toolType))- ; return vpui- }---- | When activated, send a message to set the current tool -entryActivated :: (ContainerClass c) => - c -> WinId -> Entry - -> (String -> SuccFail a) -- parser- -> (a -> ToolId) -- tool type- -> IORef VPUI -- state- -> IO ()-entryActivated container winId entry parser toolType uiref = do--- This could be rewritten in the form of :: ... -> VPUI -> IO VPUI--- and transformed in the CBMgr, "lifting" the readIORef/writeIORef,--- in fact streamlining it to "mutateIORef"- {- text <- entryGetText entry- ; case parser text of- Fail msg -> info msg- Succ v -> - grabRemove entry >>- widgetDestroy container >>- readIORef uiref >>= - vpuiSetTool (toolType v) winId >>=- writeIORef uiref- }--addEntryCompletions :: Entry -> [String] -> IO ()-addEntryCompletions entry comps = do- {- -- prepare the model- model <- listStoreNew comps-- -- make EntryCompletion and set model and column- ; ec <- entryCompletionNew- ; set ec [entryCompletionModel := Just model]- ; customStoreSetColumn model (makeColumnIdString 0) id- ; entryCompletionSetTextColumn ec (makeColumnIdString 0)-- -- attach EntryCompletion to Entry- ; entrySetCompletion entry ec- ; return ()- }---- | Set actions for Tab and Escape-entryKeyPress :: (ContainerClass c) => c -> Entry -> EventM EKey Bool-entryKeyPress container entry =- tryEvent $ do- {- kname <- eventKeyName- ; case kname of- "Escape" ->- liftIO $ - grabRemove entry >>- widgetDestroy container- "Tab" ->- -- complete the entry as far as possible- liftIO $- entryGetCompletion entry >>=- entryCompletionInsertPrefix- _ -> stopEvent- } ---- | For debugging (frame context menu command)-dumpFrame :: VPUI -> WinId -> CanvFrame -> IO ()-dumpFrame vpui winId frame = - let vw = vpuiGetWindow vpui winId- canv = vpuiWindowGetCanvas vw- graph = vcGraph canv- frameNode = cfFrameNode frame- frame' = vcGetFrame canv graph frameNode -- ?? possibly different- tree = graphToOrderedTreeFrom graph frameNode- in - info ("frame functoid:", cfFunctoid frame) >>- info ("frame' functoid:", cfFunctoid frame') >>- info ("frame' all descendants:", - nodeAllSimpleDescendants graph frameNode) >>- info "Tree rooted at frame:" >>- putTree tree---- | For debugging (frame context menu command)-dumpGraph :: VPUI -> WinId -> IO ()-dumpGraph vpui = print . vcGraph . vpuiWindowGetCanvas . vpuiGetWindow vpui---- | For debugging the window's widget children-dumpWorkWin :: VPUI -> WinId -> IO ()-dumpWorkWin vpui winId =- case vpuiTryGetWindow vpui winId of- Nothing -> putStrLn ("dumpWorkWin: no window found with id " ++ winId)- Just vpuiWindow ->- let window = vpuiWindowWindow vpuiWindow- in dumpWidget window >>- containerForeach window dumpWidget--dumpWidget :: (WidgetClass w) => w -> IO ()-dumpWidget w = do- {- (_, path, _) <- widgetClassPath w- ; cname <- widgetClassName w- ; vis <- get w widgetVisible- ; print (path, cname, vis)- ; when (elem cname ["GtkVBox", "GtkHBox", "GtkLayout"]) $- containerForeach (castToContainer w) dumpWidget- }--widgetClassName :: (WidgetClass w) => w -> IO String-widgetClassName w = do- {- (_len, _path, rpath) <- widgetClassPath w- ; return (case elemIndex '.' rpath of- Nothing -> "Nil"- Just n -> reverse (take n rpath))- }---- | Clear frame indicated by mouse location-clearFrame :: WinId -> CanvFrame -> VPUI -> IO VPUI-clearFrame winId frame vpui =- let clear canvas = vcClearFrame canvas frame- in vpuiModCanvasIO vpui winId clear---- | Close frame (context menu command)-closeFrame :: VPUI -> WinId -> CanvFrame -> IO VPUI-closeFrame vpui winId frame =- let close canvas = vcCloseFrame canvas frame- in vpuiModCanvasIO vpui winId close
− Sifflet/UI/Types.hs
@@ -1,308 +0,0 @@-module Sifflet.UI.Types - (VPUI(..)- , WinId, VPUIWindow(..)- , vpuiFileChanged- , vpuiUserEnvAList-- -- | Operations on a VPUI involving its window- , vpuiInsertWindow- , vpuiTryGetWindow- , vpuiGetWindow- , vpuiUpdateWindow- , vpuiReplaceWindow- , vpuiUpdateWindowIO- , vpuiRemoveVPUIWindow-- -- | Operations on a window involving its canvas- , vpuiWindowLookupCanvas, vpuiWindowGetCanvas- , vpuiWindowSetCanvas, vpuiWindowModCanvas - , vpuiWindowModCanvasIO-- -- | Operation on a VPUI involving the canvas of its window- , vpuiModCanvas, vpuiModCanvasIO- - -- | Other operations on a window- , vpuiWindowWindow-- , VPToolkit(..)-- , Toolbox(..)- , Tool(..)- , ToolContext(..)-- , CanvasToolOp- , ToolOp- , toToolOpVW -- , Workspace(..)- , FunctionEditor(..)- , fedFunctionName- , VCanvas(..)- , Selection(..)- , Dragging(..)- )--where--import Data.Map as Map -import Data.Graph.Inductive as G--import Graphics.UI.Gtk.Gdk.EventM (Modifier(..))--import Sifflet.Data.Geometry-import Sifflet.Data.TreeLayout-import Sifflet.Data.WGraph-import Sifflet.Language.Expr--import Sifflet.UI.Frame-import Sifflet.UI.LittleGtk-import Sifflet.UI.RPanel-import Sifflet.Util----- | VPUI: Sifflet (formerly VisiProg) User Interface--- The initialEnv is apt to contain "builtin" functions;--- it's preserved here so that when writing to a file,--- we can skip the functions that were in the initial env.-data VPUI = VPUI {- vpuiWindows :: Map WinId VPUIWindow, -- ^ all the windows of the program- vpuiToolkits :: [(String, VPToolkit)], -- ^ ordered association list,- -- collections of tools- vpuiFilePath :: Maybe FilePath, -- ^ the file opened or to save- vpuiStyle :: Style, -- ^ for windows, canvases, editors- vpuiInitialEnv :: Env, -- ^ initial value of global environment- vpuiGlobalEnv :: Env, -- ^ the global environment- vpuiFileEnv :: Env -- ^ global env as of last file open or save,- -- used to detect unsaved changes- }---- | Tell whether the global environmkent has changed since the--- last file open or save-vpuiFileChanged :: VPUI -> Bool-vpuiFileChanged vpui = vpuiGlobalEnv vpui /= vpuiFileEnv vpui---- | Extract from the environment the part defined by the user--- But you probably want to use Sifflet.UI.Window.UserFunctions--- instead of this.-vpuiUserEnvAList :: VPUI -> [(String, Value)]-vpuiUserEnvAList vpui =- let env' = vpuiGlobalEnv vpui -- I hope- env = vpuiInitialEnv vpui- in if length env == 1 && length env' == 1- then assocs (Map.difference (head env') (head env))- else errcats ["vpuiUserEnv: env lengths are not one",- "|env'|:", show (length env'),- "|env|:", show (length env)]---- | Insert a window in the window map-vpuiInsertWindow :: VPUI -> WinId -> VPUIWindow -> VPUI-vpuiInsertWindow vpui winId vw =- vpui {vpuiWindows = Map.insert winId vw (vpuiWindows vpui)}---- | Try to get the VPUIWindow with the given window ID,--- return Just result or Nothing-vpuiTryGetWindow :: VPUI -> WinId -> Maybe VPUIWindow-vpuiTryGetWindow vpui winId = Map.lookup winId (vpuiWindows vpui)---- | Get the VPUIWindow with the given window ID;--- it is an error if this fails.-vpuiGetWindow :: VPUI -> WinId -> VPUIWindow-vpuiGetWindow vpui winId = vpuiWindows vpui ! winId---- | Replace a VPUIWindow with given window ID;--- it is an error if this fails.-vpuiReplaceWindow :: VPUI -> WinId -> VPUIWindow -> VPUI-vpuiReplaceWindow vpui winId vpuiWin =- let winMap = vpuiWindows vpui- winMap' = insert winId vpuiWin winMap- in vpui {vpuiWindows = winMap'}---- | Apply an update function to a VPUIWindow with given window ID;--- it is an error if this fails.-vpuiUpdateWindow :: VPUI -> WinId -> (VPUIWindow -> VPUIWindow) -> VPUI-vpuiUpdateWindow vpui winId updater =- let winMap = vpuiWindows vpui- winMap' = adjust updater winId winMap- in vpui {vpuiWindows = winMap'}---- | Apply an update IO action to a VPUIWindow with given window ID;--- it is an error if this fails.-vpuiUpdateWindowIO :: WinId -> (VPUIWindow -> IO VPUIWindow) -> VPUI -> IO VPUI-vpuiUpdateWindowIO winId updater vpui = do- {- let winMap = vpuiWindows vpui- vw = winMap ! winId- ; vw' <- updater vw- ; let winMap' = insert winId vw' winMap- ; return $ vpui {vpuiWindows = winMap'}- }---- | Remove a window from the windows map; it has already been destroyed--- in the GUI-vpuiRemoveVPUIWindow :: WinId -> VPUI -> VPUI-vpuiRemoveVPUIWindow winId vpui =- let winMap = vpuiWindows vpui- winMap' = delete winId winMap- in vpui {vpuiWindows = winMap'}--data VPUIWindow = -- VPUIJustWindow Window - VPUIWorkWin Workspace Window- | FunctionPadWindow Window [(String, RPanel)]---vpuiWindowWindow :: VPUIWindow -> Window-vpuiWindowWindow vw =- case vw of- VPUIWorkWin _ w -> w- FunctionPadWindow w _ -> w---- | Try to find canvas; fail gracefully-vpuiWindowLookupCanvas :: VPUIWindow -> Maybe VCanvas-vpuiWindowLookupCanvas vw =- case vw of- VPUIWorkWin ws _ -> Just (wsCanvas ws)- _ -> Nothing---- | Find canvas or fail dramatically-vpuiWindowGetCanvas :: VPUIWindow -> VCanvas-vpuiWindowGetCanvas vw =- case vpuiWindowLookupCanvas vw of- Nothing -> error "vpuiWindowGetCanvas: no canvas found"- Just canvas -> canvas--vpuiWindowSetCanvas :: VPUIWindow -> VCanvas -> VPUIWindow-vpuiWindowSetCanvas vw canvas =- case vw of- VPUIWorkWin ws w -> VPUIWorkWin (ws {wsCanvas = canvas}) w- _ -> error "vpuiWindowSetCanvas: not a workspace window"--vpuiWindowModCanvas :: VPUIWindow -> (VCanvas -> VCanvas) -> VPUIWindow-vpuiWindowModCanvas vw f =- case vpuiWindowLookupCanvas vw of- Nothing -> error "vpuiWindowModCanvas: plain VPUIWindow"- Just canvas -> vpuiWindowSetCanvas vw (f canvas)--vpuiWindowModCanvasIO :: VPUIWindow -> (VCanvas -> IO VCanvas) -> IO VPUIWindow-vpuiWindowModCanvasIO vw f =- case vpuiWindowLookupCanvas vw of- Nothing -> error "vpuiWindowModCanvas: plain VPUIWindow"- Just canvas -> - do- { - canvas' <- f canvas- ; return $ vpuiWindowSetCanvas vw canvas'- }---- | Update the canvas of the specified window, without IO-vpuiModCanvas :: VPUI -> WinId -> (VCanvas -> VCanvas) -> VPUI-vpuiModCanvas vpui winId modCanvas = - let modWindow vw = vpuiWindowModCanvas vw modCanvas- in vpuiUpdateWindow vpui winId modWindow---- | Update the canvas of the specified window, with IO-vpuiModCanvasIO :: VPUI -> WinId -> (VCanvas -> IO VCanvas) -> IO VPUI-vpuiModCanvasIO vpui winId modCanvas = - let modWindow vw = vpuiWindowModCanvasIO vw modCanvas- in vpuiUpdateWindowIO winId modWindow vpui--type WinId = String--data Workspace = Workspace {wsBox :: VBox, -- ^ container of the rest- wsCanvas :: VCanvas, -- ^ the canvas- wsButtonBar :: HBox,- wsStatusbar :: Statusbar}--data FunctionEditor = FunctionEditor {fedWindow :: Window,- fedWinTitle :: String,- fedFunction :: Function,- fedCanvas :: VCanvas- -- , fedUIRef :: IORef VPUI- }--fedFunctionName :: FunctionEditor -> String-fedFunctionName = functionName . fedFunction---- | Toolkit functions are organized in groups (rows) for presentation--- in a toolbox-data VPToolkit = VPToolkit {toolkitName :: String,- toolkitWidth :: Int, -- (-1) = don't care- toolkitRows :: [[Tool]]}---- | A Toolbox is a framed VBox with a set of Toolbars attached-data Toolbox = Toolbox {toolboxFrame :: GtkFrame- , toolboxVBox :: VBox}---- | ToolOp a is intended for a = VPUIWindow or VCanvas--- type ToolOp a --- = VPUI -> a -> ToolContext -> [Modifier] -> Double -> Double -> IO a--type ToolOp - = VPUI -> WinId -> ToolContext -> [Modifier] -> Double -> Double -> IO VPUI--type CanvasToolOp- = VCanvas -> ToolContext -> [Modifier] -> Double -> Double -> IO VCanvas--data Tool = Tool {toolName :: String, -- the tool's name-- -- what to do when the tool is selected from the toolbox- toolActivated :: VCanvas -> IO VCanvas,-- -- what to do to apply the tool to a point on the canvas- toolOp :: ToolOp- }---- | A helper for making toolOps from actions on VCanvas--toToolOpVW :: CanvasToolOp -> ToolOp-toToolOpVW vcOp vpui winId toolContext mods x y = do- {- let vw = vpuiGetWindow vpui winId- canv = vpuiWindowGetCanvas vw- ; canv' <- vcOp canv toolContext mods x y- ; let vw' = vpuiWindowSetCanvas vw canv'- ; return $ vpuiReplaceWindow vpui winId vw'- }- --- | ToolContext: The way a tool should be applied depends on --- where it is being used --data ToolContext = TCWorkspace - | TCCallFrame CanvFrame - | TCEditFrame CanvFrame- | TCExprNode -- ???------ | A canvas that can display multiple boxes representing --- expressions or function definitions or calls--data VCanvas = VCanvas {- vcLayout :: GtkLayout,- vcStyle :: Style,- vcGraph :: WGraph,- vcFrames :: [CanvFrame],- vcSize :: Size,- -- vcLocalEnv :: Env, -- only good for function editor, I think? - vcMousePos :: (Double, Double),- vcTool :: Maybe Tool, -- current tool on this canvas- vcActive :: Maybe Node, -- active node, if any- vcSelected :: Maybe Selection, -- selected node(s), if any- vcDragging :: Maybe Dragging -- what we're dragging, if anything- }---data Selection = SelectionNode {selNode :: G.Node}- | SelectionInlet {selNode :: G.Node,- selInEdge :: WEdge} -- numbered from 0- | SelectionOutlet {selNode :: G.Node,- selOutEdge :: WEdge} -- normally just 0- deriving (Eq, Read, Show)---- | A Dragging keeps track of the object (node) being dragged--- and the current mouse position.--data Dragging = Dragging { draggingNode :: G.Node,- draggingPosition :: Position- }- deriving (Eq, Read, Show)-
− Sifflet/UI/Window.hs
@@ -1,1172 +0,0 @@-module Sifflet.UI.Window- (- -- Window utilities- showWindow- , newWindowTitled-- , showWorkWin- , showWorkspaceWindow-- , showFedWin- , fedWindowTitle-- , showFunctionPadWindow- , newFunctionDialog-- , openFilePath- , setWSCanvasCallbacks- , keyBindingsHelpText- )--where--import Control.Monad-import Control.Monad.Trans (liftIO) -- for use in EventM-import Data.IORef-import Data.List as List-import Data.Map as Map (fromList, lookup)-import Data.Map (Map)--import Data.Graph.Inductive as G--import Graphics.UI.Gtk.Gdk.EventM--import Sifflet.Data.Functoid-import Sifflet.Data.Geometry-import Sifflet.Data.WGraph--import Sifflet.Foreign.Exporter-import Sifflet.Foreign.ToHaskell (defaultHaskellOptions, exportHaskell)-import Sifflet.Foreign.ToPython (defaultPythonOptions, exportPython)-import Sifflet.Foreign.ToScheme (SchemeOptions(..), exportScheme)--import Sifflet.Language.Expr-import Sifflet.Language.SiffML--import Sifflet.UI.Frame-import Sifflet.UI.Canvas-import Sifflet.UI.Types-import Sifflet.UI.Callback-import Sifflet.UI.Tool-import Sifflet.UI.Workspace-import Sifflet.UI.GtkForeign-import Sifflet.UI.GtkUtil-import Sifflet.UI.LittleGtk-import Sifflet.UI.RPanel--import Sifflet.Util---import Data.Version-import Paths_sifflet_lib as Paths---- ------------------------------------------------------------------------ | Finding, creating, and initializing windows (VPUIWindow)---- | Find and show a window, if it exists.--- If not, create the window, put it in the vpui's window map,--- and initialize it and any auxiliary objects using the initWin function.--- The 3rd argument of initWin will be the window's title.--- Always presents the window (shows and raises).--- Returns a 3-tuple: the VPUIWindow contains the Window,--- and the Bool value is True if the Window is new--- (and therefore might need some further initialization).--- The third tuple element is an IORef to the VPUIWindow;--- it may be useful for setting up signal and event handling.--showWindow :: WinId -> CBMgr- -> (VPUI -> Window -> IO VPUIWindow) -- initializes Gtk Window- -> (VPUI -> WinId -> CBMgr -> IO ()) -- initializes callbacks- -> VPUI -> IO (VPUI, VPUIWindow, Bool)-showWindow winId uimgr initWin initCB vpui = do- {- (vpui', vw, isNew) <- - case vpuiTryGetWindow vpui winId of- Nothing ->- do- {- window <- newWindowTitled winId- ; widgetSetName window ("Sifflet-" ++ winId)- ; vwin <- initWin vpui window- ; let vpui' = vpuiInsertWindow vpui winId vwin- ; uimgr (OnWindowDestroy window (onWindowDestroy winId))- ; return (vpui', vwin, True)- }- Just vw ->- return (vpui, vw, False)- ; when isNew (initCB vpui' winId uimgr) -- add callbacks on new window- ; windowPresent (vpuiWindowWindow vw)- ; return (vpui', vw, isNew)- }--onWindowDestroy :: WinId -> IORef VPUI -> IO ()-onWindowDestroy winId uiref =- if (winId == workspaceId) - then - readIORef uiref >>=- checkForChanges "quit (by closing the workspace window)" True False- (\ vpui -> do { mainQuit; return vpui }) >>- return ()- else modifyIORef uiref (vpuiRemoveVPUIWindow winId)---- | Default "do-nothing" add-callbacks function-initCBDefault :: VPUI -> WinId -> CBMgr -> IO ()-initCBDefault _vpui _winId _uimgr = return ()--newWindowTitled :: String -> IO Window-newWindowTitled winId = do- window <- windowNew- set window [windowTitle := winId]- widgetSetName window ("Sifflet-" ++ winId)- return window---- | Show a workspace window, with a given title, _not_ editing a function--showWorkWin :: VPUI -> WinId -> CBMgr -> IO VPUI-showWorkWin vpui winId uimgr = do- {- (vpui', _, _) <- showWorkspaceWindow winId uimgr Nothing vpui- ; return vpui'- }---- | Show a workspace window with a given title and maybe function to edit--showWorkspaceWindow :: WinId -> CBMgr -> Maybe Function -> VPUI- -> IO (VPUI, VPUIWindow, Bool)-showWorkspaceWindow winId cbmgr mfunc =- showWindow winId cbmgr (workspaceWindowInit cbmgr winId mfunc) - setWSCanvasCallbacks----- | Initialize a Workspace window.--- Called in sifflet.hs:Main from showWindow called from showWorkspaceWindow.--workspaceWindowInit :: CBMgr -> WinId -> Maybe Function -> VPUI -> Window- -> IO VPUIWindow-workspaceWindowInit cbmgr winId mfunc vpui window = do- {- let style = vpuiStyle vpui- env = vpuiGlobalEnv vpui- ; ws <- case mfunc of - Nothing -> workspaceNewDefault style (buildMainMenu cbmgr)- Just func -> workspaceNewEditing style env func- ; set window [windowTitle := winId, containerChild := wsBox ws]- -- this should suppress the window close button,- -- but doesn't, at least in Fluxbox- -- windowDeletable := False] -- no close button- ; widgetShowAll window- ; windowPresent window-- ; return $ VPUIWorkWin ws window- }---- Menu specs here need to coordinate accelerators (shortcuts)--- with keyBindingsList in WindowManagement.hs--buildMainMenu :: CBMgr -> VBox -> IO ()-buildMainMenu cbmgr vbox = do- {- -- menu bar - let mspecs = - [MenuSpec "File"- [ -- "new" isn't implemented yet- -- MenuItem "New ..." menuFileNew- -- , - -- Temporarily disabling file I/O operations- MenuItem "Open ... (C-o)" (menuFileOpen cbmgr)- , MenuItem "Save (C-s)" menuFileSave- , MenuItem "Save as ..." menuFileSaveAs- , MenuItem "Export to Haskell ..." - menuFileExportHaskell- , MenuItem "Export to Python3 ..." menuFileExportPython- , MenuItem "Export to Scheme ..." menuFileExportScheme- , MenuItem "Quit (C-q)" menuFileQuit]- , MenuSpec "Functions"- [MenuItem "New ... (n)"- (newFunctionDialog "ignore" cbmgr)- , MenuItem "Function Pad"- (showFunctionPadWindow cbmgr)]- , MenuSpec "Help"- [MenuItem "Help ..." showHelpDialog- , MenuItem "Complaints and praise ..." showBugs- , MenuItem "About ..." showAboutDialog]- ]- ; menubar <- createMenuBar mspecs cbmgr- ; boxPackStart vbox menubar PackNatural 0-}---- | Show a function editor window = a workspace window --- editing a given function.--- Use argNames for a new function; ignore them if funcName is bound.--showFedWin :: CBMgr -> String -> [String] -> VPUI -> IO VPUI-showFedWin cbmgr funcName argNames vpui = do- {- ; let initEnv = vpuiGlobalEnv vpui- function = case envLookupFunction initEnv funcName of- Nothing -> newUndefinedFunction funcName argNames- Just func -> func- winId = fedWindowTitle funcName-- ; (vpui', vw, isNew) <- showWorkspaceWindow winId cbmgr (Just function) vpui-- ; if isNew- then do- {- let canvas = vpuiWindowGetCanvas vw- -- Can this use vpuiAddFrame? ***- ; canvas' <- vcAddFrame canvas (FunctoidFunc function) - Nothing EditFrame- initEnv 0 0 0 Nothing- ; canvas'' <- - case vcFrames canvas' of- [] -> info "showFedWin: ERROR: no frame on canvas" >> - return canvas'- _:_:_ -> - info "showFedWin: ERROR: too many frames on canvas" >> - return canvas'- [frame] -> editFunction canvas' frame - ; addArgToolButtons cbmgr winId (functionArgNames function) vpui'- ; addApplyCloseButtons cbmgr winId vpui'- ; return (vpuiReplaceWindow vpui' winId - (vpuiWindowSetCanvas vw canvas''))- }- else return vpui'- }--fedWindowTitle :: String -> WinId-fedWindowTitle funcName = "Edit " ++ funcName--updateFunctionPadIO :: String -> (RPanel -> IO RPanel) -> VPUI -> IO VPUI-updateFunctionPadIO padName update =- let updateWindow vw =- case vw of- FunctionPadWindow window rpAList ->- do- {- rpAList' <- adjustAListM padName update rpAList- ; return (FunctionPadWindow window rpAList')- }- _ -> return vw- in vpuiUpdateWindowIO "Function Pad" updateWindow--showFunctionPadWindow :: CBMgr -> VPUI -> IO VPUI-showFunctionPadWindow cbmgr vpui = - let initWindow _vpui window = do- {- -- widgetSetName window "SiffletFunctionPadWindow"- ; vbox <- vBoxNew False 0 -- non-homogenous, 0 padding- ; set window [containerChild := vbox]-- ; let rpnames = ["Base", "Examples", "My Functions"]- ; rps <- mapM (makeFunctionPadPanel cbmgr vpui) rpnames- ; mapM_ (\ rp -> boxPackStart vbox (rpanelRoot rp) PackNatural 0)- rps-- ; windowMove window 5 5- ; widgetShowAll window -- redundant?- ; windowPresent window -- redundant?-- ; return $ FunctionPadWindow window (zip rpnames rps) - -- maybe need reference only the "My Functions" panel though **- }- in do- {- (vpui', _, windowIsNew) <- - showWindow functionPadWinId cbmgr initWindow initCBDefault vpui- -- "My Functions" default is empty; add any user-defined- -- functions in the environment to it- ; if windowIsNew- then addUserFunctions cbmgr vpui'- else return vpui'- }--functionPadWinId :: String-functionPadWinId = "Function Pad"--addUserFunctions :: CBMgr -> VPUI -> IO VPUI-addUserFunctions cbmgr vpui =- let names = map fst (vpuiUserEnvAList vpui)- update rp = do- {- buttons <- mapM (makeToolButton cbmgr . functionTool) names- ; rp' <- rpanelAddWidgets rp (zip names buttons)- ; widgetShowAll (rpanelRoot rp')- ; return rp'- } - in updateFunctionPadIO "My Functions" update vpui--makeFunctionPadPanel :: CBMgr -> VPUI -> String -> IO RPanel-makeFunctionPadPanel cbmgr vpui name =- let VPToolkit _ width toolrows = - case List.lookup name (vpuiToolkits vpui) of- Nothing ->- errcats ["makeFunctionPadPanel:",- "can't find toolkit definition:", name]- Just atoolkit -> atoolkit- in do- {- buttonRows <- makeToolButtonRows cbmgr toolrows- :: IO [[(String, Button)]]- ; rp <- newRPanel name 3 3 width- ; rpanelAddRows rp buttonRows- }--makeToolButtonRows :: CBMgr -> [[Tool]] -> IO [[(String, Button)]]-makeToolButtonRows cbmgr toolRows = - mapM2 (makeNamedToolButton cbmgr) toolRows--makeNamedToolButton :: CBMgr -> Tool -> IO (String, Button)-makeNamedToolButton cbmgr tool = do- {- button <- makeToolButton cbmgr tool- ; return (toolName tool, button)- }--makeToolButton :: CBMgr -> Tool -> IO Button-makeToolButton cbmgr tool = do- {- button <- buttonNewWithLabel (toolName tool)- ; cbmgr (AfterButtonClicked button- ((flip modifyIORefIO) - (forallWindowsIO (vpuiWindowSetTool tool))))- ; return button- }---- | Add a tool button to the function pad window in a specified panel-addFunctionPadToolButton :: CBMgr -> String -> Tool -> VPUIWindow - -> IO VPUIWindow-addFunctionPadToolButton cbmgr panelId tool vw = - case vw of- FunctionPadWindow window panelAList ->- let adjustPanel :: RPanel -> IO RPanel- adjustPanel rp = do- {- -- make the tool button from the tool- button <- makeToolButton cbmgr tool- -- add it to the panel- ; rp' <- rpanelAddWidget rp (toolName tool) button- ; widgetShowAll (rpanelRoot rp')- ; return rp'- }- in do- {- panelAList' <- adjustAListM panelId adjustPanel panelAList- ; return $ FunctionPadWindow window panelAList'- }- _ -> return vw----- | Ask user for new function name and arguments,--- then begin editing the function.--newFunctionDialog :: WinId -> CBMgr -> VPUI -> IO VPUI-newFunctionDialog _winId cbmgr vpui =- -- _winId is ignored, but needed for use in KeyBindingsList- let reader :: Reader [String] (String, [String])- reader inputLines =- case inputLines of- [fname, fargs] ->- return (fname, words fargs)- _ -> fail "wrong number of lines"- in do- {- inputDialog <- - createEntryDialog "New Function"- ["Function name", "Argument names (space between)"]- ["", ""]- reader- (-1)- ; values <- runEntryDialog inputDialog- ; case values of- Nothing -> return vpui- Just (name, args) -> editNewFunction cbmgr name args vpui- }---- --------------------------------------------------------------- Implementation of menu commands---- -- | Create a new file, but what does this mean?---- menuFileNew :: VPUI -> IO VPUI--- menuFileNew vpui = putStrLn "Not implemented: \"New\"" >> return vpui--- -- Notes for future implementation:--- -- checkChangesAndContinue vpui ...----- | Quit from Sifflet--menuFileQuit :: VPUI -> IO VPUI-menuFileQuit = checkForChanges "quit" False True vpuiQuit---- | Open a file (load its function definitions)--menuFileOpen :: CBMgr -> VPUI -> IO VPUI-menuFileOpen cbmgr =- checkForChanges "open file" True True (continueFileOpen cbmgr)---- | Offer to save changes, if any, and continue with the continuation.--- The continuation gets the current vpui if there are no changes--- or if the offer to save is rejected; otherwise, it gets a--- vpui which knows it has saved its last changes.--- If offerCancel is true, there is an option to cancel the operation;--- this won't work if the user is closing the main (workspace) window.--- The message, if any, is a confirmation that the file was--- saved and that we are going on to the next operation ----- useful for open file, but not for quit.--checkForChanges :: String -> Bool -> Bool -> (VPUI -> IO VPUI) - -> VPUI -> IO VPUI-checkForChanges beforeOperation acknowledge offerCancel continue vpui =- let mAckIfSaved vpui' = - when (not (vpuiFileChanged vpui') && acknowledge)- (- showInfoMessage "Changes saved" - ("Your changes are now saved; " ++- "proceeding to " ++- beforeOperation ++ ".")- ) - >>- return vpui'- choices = [("Save them", - menuFileSave vpui >>= mAckIfSaved >>= continue),- ("Throw them away", - return vpui >>= continue)] ++ - if offerCancel- then [("Cancel " ++ beforeOperation, return vpui)]- else []- labels = map fst choices- actions = map snd choices- -- labels = ["Save them", - -- "Throw them away", - -- ]- -- actions = [menuFileSave vpui >>= mAckIfSaved >>= continue, -- save- -- return vpui >>= continue, -- throw away- -- return vpui] -- cancel- offerSaveAndContinue = showChoicesDialog "Save changes?"- ("There are unsaved changes. " ++- "Before you " ++ beforeOperation ++- ", would you ...")- labels- actions- - in if vpuiFileChanged vpui- then offerSaveAndContinue- else continue vpui---- | Continue with opening the file, after having possibly saved changes-continueFileOpen :: CBMgr -> VPUI -> IO VPUI-continueFileOpen cbmgr vpui = do- mpath <- showDialogFileOpen vpui- case mpath of- Nothing -> return vpui- Just filePath -> openFilePath cbmgr filePath vpui---- | Now that we have a file path, go ahead and open it,--- loading the function definitions into Sifflet--openFilePath :: CBMgr -> FilePath -> VPUI -> IO VPUI-openFilePath cbmgr filePath vpui = do- {- loadResult <- loadFile vpui filePath- ; case loadResult of- Fail msg ->- showErrorMessage msg >> return vpui- Succ (vpui', functions) -> - let title = "My Functions"- updatePad rp =- -- Figure out which functions are new,- -- i.e., not already on the pad- let oldNames = concat (rpanelContent rp)- loadedNames = map functionName functions- -- use set difference to avoid duplicates- newNames = loadedNames \\ oldNames- newTools = map functionTool newNames- in do - {- ; newPairs <- - mapM (makeNamedToolButton cbmgr) newTools- ; rp' <- rpanelAddWidgets rp newPairs- ; widgetShowAll (rpanelRoot rp)- ; return rp'- }- in do- {- vpui'' <- - showFunctionPadWindow cbmgr vpui' >>=- updateFunctionPadIO title updatePad - ; return $ vpui'' {vpuiFilePath = Just filePath, - vpuiFileEnv = vpuiGlobalEnv vpui'- }- }- }--showDialogFileOpen :: VPUI -> IO (Maybe FilePath)-showDialogFileOpen _vpui = do- chooser <- fileChooserDialogNew- (Just "Open file ...") -- default title- Nothing -- transient parent of the dialog- FileChooserActionOpen- [("Open", ResponseOk), ("Cancel", ResponseCancel)] -- buttons- result <- runDialogM (toDialog chooser) chooser fileChooserGetFilename- return result--loadFile :: VPUI -> FilePath -> IO (SuccFail (VPUI, [Function]))-loadFile vpui filePath = do- {- functions <- consumeSiffMLFile xmlToFunctions filePath- ; case functions of- [Functions fs] ->- let vpui' = foldl bindFunction vpui fs- in return (Succ (vpui', fs))- _ ->- return (Fail "file format error")-- }--bindFunction :: VPUI -> Function -> VPUI-bindFunction vpui function =- let env = vpuiGlobalEnv vpui- Function (Just name) _argTypes _resType _impl = function- env' = envIns env name (VFun function)- in vpui {vpuiGlobalEnv = env'}---- | Implements File menu "Save" command.--menuFileSave :: VPUI -> IO VPUI-menuFileSave vpui = - case vpuiFilePath vpui of- Nothing -> menuFileSaveAs vpui- Just filePath -> saveFile vpui filePath---- | Implements File menu "Save as" command.-menuFileSaveAs :: VPUI -> IO VPUI-menuFileSaveAs vpui = do- {- mFilePath <- chooseOutputFile "Save" vpui- ; case mFilePath of- Nothing -> return vpui- Just filePath -> saveFile vpui filePath- }---- | Unconditionally save user functions in SiffML file.--- Called from menuFileSave and menuFileSaveAs.--- Updates vpuiFilePath and vpuiFileEnv.-saveFile :: VPUI -> FilePath -> IO VPUI-saveFile vpui filePath =- produceSiffMLFile (userFunctions vpui) filePath >>- return vpui {vpuiFilePath = Just filePath, - vpuiFileEnv = vpuiGlobalEnv vpui}---- | The user-defined functions of the environment-userFunctions :: VPUI -> Functions-userFunctions vpui = - Functions (map (valueFunction . snd) - (vpuiUserEnvAList vpui))---- | Export user functions to a file,--- given an exporter and a path,--- returning the vpui unchanged.-maybeExportUserFunctions :: VPUI -> (opts -> Exporter) - -> Maybe (FilePath, opts) -> IO VPUI-maybeExportUserFunctions vpui export mpathOptions =- case mpathOptions of- Nothing -> return vpui - Just (path, options) -> - export options (userFunctions vpui) path >> return vpui---- | Export user functions to Haskell file-menuFileExportHaskell :: VPUI -> IO VPUI-menuFileExportHaskell vpui =- chooseOutputFile "Export Haskell" vpui >>=- maybeDefaultOptions defaultHaskellOptions >>=- maybeExportUserFunctions vpui exportHaskell---- | Export user functions to Python file-menuFileExportPython :: VPUI -> IO VPUI-menuFileExportPython vpui =- chooseOutputFile "Export Python" vpui >>=- maybeDefaultOptions defaultPythonOptions >>=- maybeExportUserFunctions vpui exportPython---- | Export user functions to Scheme file-menuFileExportScheme :: VPUI -> IO VPUI-menuFileExportScheme vpui =- chooseOutputFile "Export Scheme" vpui >>= - maybeRunSchemeOptionsDialog >>=- -- maybeExportUserFunctions vpui (exportScheme defaultSchemeOptions) - maybeExportUserFunctions vpui exportScheme---- | Choose an output file, for file save, save as, and export commands-chooseOutputFile :: String -> VPUI -> IO (Maybe FilePath)-chooseOutputFile verb _vpui = do- chooser <- fileChooserDialogNew- (Just (verb ++ " to file ...")) -- title- Nothing -- transient parent of the dialog- FileChooserActionSave- [(verb, ResponseOk), ("Cancel", ResponseCancel)] -- buttons- result <- runDialogM (toDialog chooser) chooser fileChooserGetFilename- return result--maybeDefaultOptions :: a -> Maybe FilePath -> IO (Maybe (FilePath, a))-maybeDefaultOptions defaultOptions mpath =- case mpath of- Nothing -> return Nothing- Just path -> return $ Just (path, defaultOptions)--maybeRunSchemeOptionsDialog :: Maybe FilePath - -> IO (Maybe (FilePath, SchemeOptions))-maybeRunSchemeOptionsDialog mpath =- case mpath of- Nothing -> return Nothing- Just path ->- let result :: Bool -> IO (Maybe (FilePath, SchemeOptions))- result useLambda =- return (Just (path, - SchemeOptions {defineWithLambda = useLambda}))- in showChoicesDialog "Scheme Export Options"- "Use lambda in function definitions?"- ["Yes", "No"]- [result True, result False]---- | Text shown by the help dialog-helpText :: String-helpText =- unlines ["Functions menu:",- " \"New\" enters a dialog to create a new function.",- " \"Function pad\" raises the function pad window.",- "Keystroke shortcuts for the menu commands are shown " ++- "using \"C-\" for Control. For example, Quit " ++- "is C-q, meaning Control+Q.",- "",- "In a function editor, right-click for the context menu.",- "",- "For more help, please visit the Sifflet web site,",- "http://mypage.iu.edu/~gdweber/software/sifflet/",- "especially the Sifflet Tutorial:",- "http://mypage.iu.edu/~gdweber/software/sifflet/doc/tutorial.html"- ]---- | Show the help dialog-showHelpDialog :: MenuItemAction-showHelpDialog vpui = showInfoMessage "Sifflet Help" helpText >> return vpui---- | How to report bugs-bugsText :: String-bugsText =- unlines ["To report bugs, please send mail to " ++ bugReportAddress,- "and mention \"Sifflet\" in the Subject header.",- "To send praise, follow the same procedure.",- "Seriously, whether you like Sifflet or dislike it,",- "I'd like to hear from you."- ]--bugReportAddress :: String-bugReportAddress = concat ["gdweber", at, "iue", punctum, "edu"]- where at = "@"- punctum = "."--showBugs :: MenuItemAction-showBugs vpui = showInfoMessage "Reporting bugs" bugsText >> return vpui---- | Text for the About dialog-aboutText :: String-aboutText =- unlines ["Sifflet version " ++ showVersion Paths.version,- "Copyright (C) 2010-2012 Gregory D. Weber",- "",- "BSD3 License",- "",- "Sifflet home page:",- "http://mypage.iu.edu/~gdweber/software/sifflet/"- ]--showAboutDialog :: MenuItemAction-showAboutDialog vpui = showInfoMessage "About Sifflet" aboutText >> return vpui---- -------------------------------------------------------------------------- Moved here from Callbacks.hs:--setWSCanvasCallbacks :: VPUI -> WinId -> CBMgr -> IO ()-setWSCanvasCallbacks vpui winId cbmgr = do- {- let vw = vpuiGetWindow vpui winId- window = vpuiWindowWindow vw- ; case vpuiWindowLookupCanvas vw of- Nothing ->- errcats ["setWSCanvasCallbacks: VPUIWindow is not a VPUIWorkWin",- "and has no canvas"]- Just canvas ->- do- {- -- Notice when the window size is changed- ; cbmgr (OnWindowConfigure window (configuredCallback winId))-- -- Keypress events -- send to canvas window because the Gtk.Layout- -- cannot receive them (why ever not?)- ; cbmgr (OnWindowKeyPress window (keyPressCallback winId cbmgr))-- -- Send remaining events to the Gtk.Layout (why?)- ; let layout = vcLayout canvas- ; widgetSetCanFocus layout True- ; cbmgr (OnLayoutExpose layout (exposedCallback winId))-- -- Mouse events - ; widgetAddEvents layout [PointerMotionMask]- ; cbmgr (OnLayoutMouseMove layout (mouseMoveCallback winId))- ; cbmgr (OnLayoutButtonPress layout - (buttonPressCallback winId cbmgr))- ; cbmgr (OnLayoutButtonRelease layout (buttonReleaseCallback winId))- }- }---- | Context menu command to edit the function displayed in --- a CallFrame--editFrameFunction :: CBMgr -> CanvFrame -> VPUI -> IO VPUI-editFrameFunction cbmgr frame vpui =- let func = cfFunctoid frame- in showFedWin cbmgr (functoidName func) (functoidArgNames func) vpui---- | Create a new function, add it to the global environment --- with body undefined, and start editing it in a new window. --- Also update and show "My Functions" toolbox and--- update its toolkit.--editNewFunction :: CBMgr -> String -> [String] -> VPUI -> IO VPUI-editNewFunction cbmgr name args vpui = - let updateEnv :: VPUI -> IO VPUI- updateEnv vpui' =- let env = vpuiGlobalEnv vpui'- env' = envIns env name (VFun (newUndefinedFunction name args))- in return $ vpui' {vpuiGlobalEnv = env'} - in - -- Show window first, with the *old* functions- showFunctionPadWindow cbmgr vpui >>=- updateEnv >>=- vpuiUpdateWindowIO functionPadWinId- (addFunctionPadToolButton cbmgr "My Functions" - (functionTool name)) >>=- showFedWin cbmgr name args--configuredCallback :: WinId -> IORef VPUI -> EventM EConfigure Bool-configuredCallback winId uiref =- tryEvent $ do- {- (w, h) <- eventSize- ; liftIO $ modifyIORef uiref (handleConfigured winId w h)- -- We *must* "stop the event", forcing the event handler - -- to return False, or else the canvas remains "squeezed in"- -- -- Weird!!- ; stopEvent- }---- | Handle the Configured event.-handleConfigured :: WinId -> Int -> Int -> VPUI -> VPUI-handleConfigured winId width height vpui = - let vw = vpuiGetWindow vpui winId- vw' = vpuiWindowModCanvas vw - (atLeastSize (Size (fromIntegral width) (fromIntegral height)))- in vpuiReplaceWindow vpui winId vw'--exposedCallback :: WinId -> IORef VPUI -> EventM EExpose Bool-exposedCallback winId uiref =- tryEvent $ do- {- clipbox <- eventArea- ; liftIO (readIORef uiref >>= handleExposed winId clipbox)- }---- | Handle the Exposed event, should be called only for a window--- with a canvas-handleExposed :: WinId -> Rectangle -> VPUI -> IO ()-handleExposed winId clipbox vpui = - let vw = vpuiGetWindow vpui winId -- error if not found- in case vpuiWindowLookupCanvas vw of- Nothing -> info "handleExposed: no canvas found!"- Just canvas -> drawCanvas canvas clipbox --data KeyBinding = KeyBinding {kbGtkKeyName :: String,- kbAltKeyName :: Maybe String, -- for humans- kbRequiredModifiers :: [Modifier],- kbDescription :: String,- kbAction :: KeyAction}--data KeyAction - = KeyActionST (WinId -> VPUI -> IO VPUI) -- ^ set a tool- | KeyActionDG (WinId -> CBMgr -> VPUI -> IO VPUI) -- ^ start a dialog- | KeyActionModIO (CBMgr -> VPUI -> IO VPUI) -- ^ modify VPUI with IO- | KeyActionHQ (VPUI -> IO ()) -- ^ help or quit---- | Key bindings map. This is derived from keyBindingsList.-keyBindingsMap :: Map String KeyBinding-keyBindingsMap = Map.fromList [(kbGtkKeyName kb, kb) | kb <- keyBindingsList]---- | KeyBinding list for workspace and function editor windows.--keyBindingsList :: [KeyBinding]-keyBindingsList = - [- -- Bindings to set tools- KeyBinding "c" Nothing [] "connect" - (KeyActionST (vpuiSetTool ToolConnect))- , KeyBinding "d" Nothing [] "disconnect" - (KeyActionST (vpuiSetTool ToolDisconnect))- , KeyBinding "i" Nothing [] "if" (KeyActionST (vpuiSetTool ToolIf))- , KeyBinding "m" Nothing [] "move" (KeyActionST (vpuiSetTool ToolMove))- , KeyBinding "KP_Delete" (Just "Keypad-Del") [] "delete" - (KeyActionST (vpuiSetTool ToolDelete))-- -- Bindings to start dialogs- , KeyBinding "n" Nothing [] "new function" (KeyActionDG newFunctionDialog)- , KeyBinding "f" Nothing [] "function" (KeyActionDG showFunctionEntry)- , KeyBinding "l" Nothing [] "literal" (KeyActionDG showLiteralEntry)-- -- Help and quit-- , KeyBinding "question" (Just "?") [] "help" (KeyActionHQ vpuiKeyHelp)-- -- Shortcuts for menu commands (GTK "accelerators", but not done- -- in the standard GTK way).- -- These need to be coordinated with buildMainMenu,- -- in WindowManagement.hs---- Oops! Binding Ctrl+F here interferes with binding just plain f above.--- , KeyBinding "f" (Just "Control-f") [Control] "function-pad"--- (KeyActionModIO showFunctionPadWindow)-- , KeyBinding "o" (Just "Control-o") [Control] "open"- (KeyActionModIO menuFileOpen)- , KeyBinding "s" (Just "Control-s") [Control] "save"- (KeyActionModIO (\ _cbmgr -> menuFileSave))- , KeyBinding "q" (Just "Control-q") [Control] "quit" - (KeyActionHQ (\ vpui -> menuFileQuit vpui >> return ()))- ]---- | Unused argument needed for key bindings-vpuiKeyHelp :: VPUI -> IO ()-vpuiKeyHelp _vpui = putStrLn keyBindingsHelpText---- | Help text built from key bindings-keyBindingsHelpText :: String-keyBindingsHelpText = - let add :: String -> KeyBinding -> String- add result (kb@KeyBinding {kbAltKeyName = mkey}) =- concat [result, " ", - case mkey of - Nothing -> kbGtkKeyName kb- Just akey -> akey, - " = ", kbDescription kb, "\n"]- in foldl add "" keyBindingsList---- | What to do when a key is pressed-keyPressCallback :: WinId -> CBMgr -> IORef VPUI -> EventM EKey Bool-keyPressCallback winId cbmgr uiref =- tryEvent $ do- {- kname <- eventKeyName- ; mods <- eventModifier- -- ; liftIO $ print mods-- ; let giveUp = - -- liftIO (info ("Unrecognized key: " ++ kname)) >> - stopEvent-- ; case Map.lookup kname keyBindingsMap of- Nothing -> - giveUp- Just keyBinding -> - if checkMods (kbRequiredModifiers keyBinding) mods- then liftIO $ - case kbAction keyBinding of- KeyActionModIO f0 ->- -- update with IO- modifyIORefIO uiref (f0 cbmgr)- KeyActionST f1 ->- -- update with IO and window ID- modifyIORefIO uiref (f1 winId)- KeyActionDG f2 ->- -- update with IO and cbmgr to set further callbacks- modifyIORefIO uiref (f2 winId cbmgr)- KeyActionHQ f3 ->- -- no update, no cbmgr, no further callbacks- readIORef uiref >>= f3- else giveUp- }--buttonPressCallback :: WinId -> CBMgr -> IORef VPUI -> EventM EButton Bool-buttonPressCallback winId cbmgr uiref =- tryEvent $ do- {- ; (x, y) <- eventCoordinates- ; mouseButton <- eventButton- ; mods <- eventModifier- ; timestamp <- eventTime- ; liftIO - (modifyIORefIO uiref - (handleButtonPress winId cbmgr mouseButton x y mods timestamp))- }--mouseMoveCallback :: WinId -> IORef VPUI -> EventM EMotion Bool-mouseMoveCallback winId uiref =- tryEvent $ do- {- (x, y) <- eventCoordinates- ; mods <- eventModifier- ; liftIO (modifyIORefIO uiref (handleMouseMove winId x y mods))- }--buttonReleaseCallback :: WinId -> IORef VPUI -> EventM EButton Bool-buttonReleaseCallback winId uiref =- tryEvent $ do- {- mouseButton <- eventButton- ; liftIO (modifyIORefIO uiref (handleButtonRelease winId mouseButton))- }---- | Handle the ButtonPress event. Should be called only for a window--- with a canvas.-handleButtonPress :: WinId -> CBMgr -> MouseButton - -> Double -> Double -- x, y- -> [Modifier] -> TimeStamp -- timestamp not needed?- -> VPUI ->IO VPUI-handleButtonPress winId cbmgr mouseButton x y mods timestamp vpui =- let vw = vpuiGetWindow vpui winId- in case vpuiWindowLookupCanvas vw of- Nothing -> info "handleButtonPress: no canvas found!" >>- return vpui- Just canvas ->- case whichFrame canvas x y of- Nothing ->- case vcTool canvas of- Nothing -> return vpui- Just tool -> toolOp tool vpui winId TCWorkspace mods x y - Just frame -> - frameButtonPressed winId cbmgr vw- frame mods (x, y)- mouseButton timestamp vpui---- | Handles button pressed in a frame-frameButtonPressed :: WinId -> CBMgr -> VPUIWindow -> CanvFrame - -> [Modifier] -> (Double, Double) -> MouseButton - -> TimeStamp - -> VPUI- -> IO VPUI-frameButtonPressed winId cbmgr vw frame mods (x, y) mouseButton timestamp vpui =- let retWrap :: VPUIWindow -> IO VPUI- retWrap = return . vpuiReplaceWindow vpui winId- in case mouseButton of- LeftButton ->- if cfPointInHeader frame x y - then beginFrameDrag vw frame x y >>= retWrap- else if cfPointInFooter frame x y- then leftButtonPressedInFrameFooter vw frame >>= retWrap- else frameBodyButtonPressed vpui winId frame - mouseButton mods x y- MiddleButton -> return vpui- RightButton -> - offerContextMenu winId cbmgr frame RightButton timestamp >> - return vpui- OtherButton _ -> return vpui---- | Handles button pressed in the body of a frame--- frameBodyButtonPressed needs VPUIWindow because it calls a toolOp.--- mb (mouse button) is unused, but might be used later.-frameBodyButtonPressed :: VPUI -> WinId -> CanvFrame - -> MouseButton -> [Modifier] -> Double -> Double - -> IO VPUI-frameBodyButtonPressed vpui winId frame _mb mods x y = do- {- let vw = vpuiGetWindow vpui winId- canvas = vpuiWindowGetCanvas vw- mnode = vcanvasNodeAt canvas (Position x y)- ; case mnode of- Nothing -> - case vcTool canvas of- Nothing -> return vpui- Just tool -> toolOp tool vpui winId (cfContext frame) mods x y- Just node -> - do- {- vw' <- openNode vw node- ; return $ vpuiReplaceWindow vpui winId vw'- }- }---- | Handles left button pressed in the footer of a frame-leftButtonPressedInFrameFooter ::- VPUIWindow -> CanvFrame -> IO VPUIWindow-leftButtonPressedInFrameFooter vw frame = - let canvas = vpuiWindowGetCanvas vw- in case frameType frame of- CallFrame -> - -- request argument values and evaluate call- if cfEvalReady frame- then do- canvas' <- vcEvalDialog canvas frame- return $ vpuiWindowSetCanvas vw canvas'- else return vw- EditFrame ->- -- ignore- return vw---- | Handles beginning of mouse-drag-beginFrameDrag :: VPUIWindow -> CanvFrame -> Double -> Double - -> IO VPUIWindow-beginFrameDrag vw frame x y = - let canvas = vpuiWindowGetCanvas vw- window = vpuiWindowWindow vw- dragging = Dragging {draggingNode = cfFrameNode frame,- draggingPosition = Position x y}- canvas' = canvas {vcDragging = Just dragging}- in setCursor window Fleur >> - (return $ vpuiWindowSetCanvas vw canvas')---- | Handle mouse move event-handleMouseMove :: WinId -> Double -> Double -> [Modifier] -> VPUI -> IO VPUI-handleMouseMove winId x y mods vpui =--- Needs to be in IO because of drawWindowInvalidateRect- let vw = vpuiGetWindow vpui winId- in case vpuiWindowLookupCanvas vw of- Nothing -> - info "SQUAWK! No canvas! Shouldn't happen!" >>- return vpui -- shouldn't happen- Just canvas -> - do- {- -- Highlight the active node, if any- let active = vcActive canvas- active' = vcanvasNodeAt canvas (Position x y)-- invalidate :: DrawWindow -> Maybe G.Node -> IO ()- invalidate win mnode =- case mnode of- Nothing -> return ()- Just node -> - drawWindowInvalidateRect win - (vcanvasNodeRect canvas node) False-- ; when (active /= active') $- do- {- win <- layoutGetDrawWindow (vcLayout canvas)- ; invalidate win active- ; invalidate win active'- }- -- if dragging, continue drag- ; canvas' <- continueDrag (canvas {vcActive = active', - vcMousePos = (x, y)}) - mods x y- ; let vw' = vpuiWindowSetCanvas vw canvas'- ; return $ vpuiReplaceWindow vpui winId vw'- }--continueDrag :: VCanvas -> [Modifier] -> Double -> Double -> IO VCanvas-continueDrag canvas mods x y =- case vcDragging canvas of- Nothing -> return canvas- Just dragging -> - let graph = vcGraph canvas- dnode = draggingNode dragging- wnode = wlab graph dnode- Position oldX oldY = draggingPosition dragging- (dx, dy) = (x - oldX, y - oldY)- in- case wnode of- WSimple _ -> - continueDragSimple canvas dragging dnode mods x y dx dy - WFrame frameNode -> - continueDragFrame canvas dragging frameNode x y dx dy --continueDragSimple :: VCanvas -> Dragging -> G.Node -> [Modifier] - -> Double -> Double -> Double -> Double -> IO VCanvas-continueDragSimple canvas dragging simpleNode mods x y dx dy =- let graph = vcGraph canvas- frame = nodeContainerFrame canvas graph simpleNode- dragging' = dragging {draggingPosition = Position x y}- translateSelection = if checkMods [Shift] mods- then translateTree- else translateNode- graph' = translateSelection dx dy graph simpleNode- canvas' = canvas {vcGraph = graph'}- in vcInvalidateFrameWithParent canvas graph frame >>- return (canvas' {vcDragging = Just dragging'})--continueDragFrame :: - VCanvas -> Dragging -> G.Node -> - Double -> Double -> Double -> Double -> IO VCanvas-continueDragFrame canvas dragging frameNode x y dx dy =- let graph = vcGraph canvas- frame = vcGetFrame canvas graph frameNode- frame' = translateFrame frame dx dy- graph' = grTranslateFrameNodes graph frame dx dy- canvas' = vcUpdateFrameAndGraph canvas frame' graph'- dragging' = Just dragging {draggingPosition = Position x y}- in - -- Tell the GUI about the changes so they will be redrawn- -- Mark the frame changed so it will be redrawn- frameChanged canvas graph frame graph' frame' >>- -- Also, any frames opened from nodes of this frame- mapM_ (\f -> frameChanged canvas graph f graph' f)- (vcFrameSubframes canvas frame) >>- -- Return the modified canvas- return (canvas' {vcDragging = dragging'})--handleButtonRelease :: WinId -> MouseButton -> VPUI -> IO VPUI-handleButtonRelease winId mouseButton vpui =- case mouseButton of- LeftButton -> - -- End drag- let vw = vpuiGetWindow vpui winId- canvas = vpuiWindowGetCanvas vw- window = vpuiWindowWindow vw- vw' = vpuiWindowSetCanvas vw (canvas {vcDragging = Nothing})- vpui' = vpuiReplaceWindow vpui winId vw'- in setCursor window LeftPtr >>- return vpui'- _ -> return vpui---- | Show a context menu for mouse click in a frame.-offerContextMenu :: WinId -> CBMgr -> CanvFrame - -> MouseButton -> TimeStamp -> IO ()-offerContextMenu winId cbmgr frame button timestamp = do- -- Needs CBMgr to specify menu actions.- {- let menuSpec = - MenuSpec "Context Menu" (contextMenuOptions winId cbmgr frame)- ; menu <- createMenu menuSpec cbmgr- ; widgetShowAll menu- ; menuPopup menu (Just (button, timestamp))- }---- | Options for context menu that depend on the frame type.--contextMenuOptions :: WinId -> CBMgr -> CanvFrame -> [MenuItemSpec]-contextMenuOptions winId cbmgr frame =- let typeDependentOptions :: [MenuItemSpec]- typeDependentOptions =- case frameType frame of- CallFrame -> - [MenuItem "Edit" (editFrameFunction cbmgr frame)- , MenuItem "Close" (\ vpui -> closeFrame vpui winId frame)]- EditFrame -> - [- -- The next items duplicate parts of keyBindingsList- MenuItem "CONNECT (c)" (vpuiSetTool ToolConnect winId)- , MenuItem "DISCONNECT (d)" (vpuiSetTool ToolDisconnect winId)- , MenuItem "IF (i)" (vpuiSetTool ToolIf winId)- , MenuItem "FUNCTION (f)" (showFunctionEntry winId cbmgr)- , MenuItem "LITERAL (l)" (showLiteralEntry winId cbmgr)- -- , ("CLEAR (not implemented)", clearFrame winId frame)- , MenuItem "MOVE (m)" (vpuiSetTool ToolMove winId)- , MenuItem "DELETE (KP-Del)" (vpuiSetTool ToolDelete winId)- ]- in typeDependentOptions ++- [--- ("Dump frame (debug)", --- \ vpui -> dumpFrame vpui winId frame >> return vpui)--- , ("Dump graph (debug)", \ vpui -> --- dumpGraph vpui winId >> return vpui)--- , ("--QUIT--", \ vpui -> vpuiQuit vpui >> return vpui)- ]
− Sifflet/UI/Workspace.hs
@@ -1,405 +0,0 @@-{- Currently, this module contains functions for VPUI, VPUIWindow,-Workspace, VCanvas, and more. A reorganization seems called for,-but for now I will just keep adding functions here. -}--module Sifflet.UI.Workspace- (- -- VPUI and Workspace- vpuiNew- , workspaceNewDefault- , workspaceNewEditing- , addArgToolButtons- , addApplyCloseButtons- , defineFunction- , workspaceId- , openNode-- -- Quitting:- , removeWindow- , vpuiQuit-- -- Windows:- , forallWindowsIO-- , baseFunctionsRows- )--where--import Control.Monad--import Data.Map ((!), keys)-import qualified Data.Map as Map (empty)--import Data.Graph.Inductive as G--import Sifflet.Data.Functoid-import Sifflet.Data.Geometry-import Sifflet.Data.TreeLayout-import Sifflet.Language.Expr-import Sifflet.UI.GtkUtil-import Sifflet.UI.LittleGtk-import Sifflet.Util--import Sifflet.UI.Callback-import Sifflet.Data.TreeGraph-import Sifflet.UI.Types-import Sifflet.UI.Canvas-import Sifflet.UI.Frame-import Sifflet.UI.Tool-import Sifflet.Data.WGraph---- | Create a new VPUI.--- This used to set up the basic "q to quit" and "on exposed" callbacks,--- but now does not even do that. --- The 'init' function argument--- may perform additional initialization;--- if there is none, simply use 'return'.---- The following comment is out of date,--- but may explain some bizarre features historically:---- Note that if you want to set up callbacks,--- there is some trickiness: the vpui contains the workspace,--- and the layout (which is on the workspace) needs to have callbacks--- which know the uiref. So, create the workspace, vpui, and uiref,--- in that order, and then set up the callbacks.---vpuiNew :: Style -> Env -> IO VPUI-vpuiNew style env =- return VPUI {vpuiWindows = Map.empty,- vpuiToolkits = [],- vpuiFilePath = Nothing,- vpuiStyle = style,- vpuiInitialEnv = env,- vpuiGlobalEnv = env,- vpuiFileEnv = env- }------ | Create a new "main" workspace window, with a given style.--- The second argument should set up a menu bar and place it on the vbox, --- or do nothing if no menu is wanted.-workspaceNewDefault :: Style -> (VBox -> IO ()) -> IO Workspace-workspaceNewDefault style = - workspaceNew style (Size 3600.0 2400.0) (Just (Size 900.0 600.0)) --workspaceNewEditing :: Style -> Env -> Function -> IO Workspace-workspaceNewEditing style initEnv func = do- {- ; let funcFrame = fedFuncFrame style func initEnv -- throw-away- Size fwidth fheight = bbSize (cfBox funcFrame)- canvSize = Size (max fwidth 300) (max fheight 300)- mViewSize = Nothing- addNoMenu _ = return ()- ; workspaceNew style canvSize mViewSize addNoMenu- }--addArgToolButtons :: CBMgr -> WinId -> [String] -> VPUI -> IO ()-addArgToolButtons cbmgr winId labels vpui =- case vpuiGetWindow vpui winId of- VPUIWorkWin ws _ -> - let bbar = wsButtonBar ws- in mapM_ (addArgToolButton cbmgr winId bbar) labels- _ -> return ()- -addArgToolButton :: CBMgr -> WinId -> HBox -> String -> IO ()-addArgToolButton cbmgr winId buttonBox label = do- {- button <- buttonNewWithLabel label- ; boxPackStart buttonBox button PackNatural 3 -- spacing between buttons- ; widgetShow button- ; cbmgr (AfterButtonClicked button - (\ uiref ->- modifyIORefIO uiref (vpuiSetTool (ToolArg label) winId)))- ; return ()- }---- | Add "Apply" and "Close" buttons to a function-editor window-addApplyCloseButtons :: CBMgr -> WinId -> VPUI -> IO ()-addApplyCloseButtons cbmgr winId vpui = - case vpuiGetWindow vpui winId of- VPUIWorkWin ws window ->- addApplyCloseButtons2 cbmgr winId ws window- _ -> return ()--addApplyCloseButtons2 :: CBMgr -> WinId -> Workspace -> Window -> IO ()-addApplyCloseButtons2 cbmgr winId ws window =- let bbar = wsButtonBar ws- applyFrame :: VPUI -> IO VPUI- applyFrame vpui = - case vcFrames (vpuiWindowGetCanvas (vpuiGetWindow vpui winId)) of- [frame] -> defineFunction winId frame vpui- _ -> info "ApplyFrame: no unique frame found" >> return vpui- -- addButton :: String -> (IORef VPUI -> IO ())- addButton label action = do- {- button <- buttonNewWithLabel label- ; boxPackEnd bbar button PackNatural 3- ; widgetShow button- ; cbmgr (AfterButtonClicked button action)- }- in addButton "Close" (\ _uiref -> widgetDestroy window) >>- addButton "Apply" (\ uiref -> modifyIORefIO uiref applyFrame)---- | fedFuncFrame generates a throw-away frame for the sole purplse--- of obtaining its measurements before initializing the canvas--fedFuncFrame :: Style -> Function -> Env -> CanvFrame-fedFuncFrame style func prevEnv = - let (frame, _) =- frameNewWithLayout style (Position 0 0) 0 - (FunctoidFunc func) Nothing- CallFrame -- mode may change below- 0 prevEnv Nothing- in frame----- | If mViewSize is Nothing, no scrollbars are put on the canvas,--- and its display size request is its natural size.--- If mViewSize = Just viewSize, then scrollbars are wrapped around--- the canvas, and its displayed size request is viewSize.--- addMenuBar is an action which, if desired, adds a menu bar;--- if you don't want one, just pass (\ _ -> return ()).--workspaceNew :: Style -> Size -> Maybe Size -> (VBox -> IO ()) -> IO Workspace-workspaceNew style canvSize mViewSize addMenuBar = do- {- ; let Size dcWidth dcHeight = canvSize -- Double, Double- (icWidth, icHeight) = (round dcWidth, round dcHeight)-- scrolled :: GtkLayout -> Size -> IO ScrolledWindow- scrolled layout viewSize = do- {- let Size dvWidth dvHeight = viewSize -- Double, Double- (iViewWidth, iViewHeight) = (round dvWidth, round dvHeight)- -- Wrap layout directly in a ScrolledWindow .- -- Adjustments: value lower upper stepIncr pageIncr pageSize- ; xAdj <- adjustmentNew 0.0 0.0 dcWidth 10.0 dvWidth dvWidth- ; yAdj <- adjustmentNew 0.0 0.0 dcHeight 10.0 dvHeight dvHeight- ; scrollWin <- scrolledWindowNew (Just xAdj) (Just yAdj)- -- show scrollbars? (never, always, or if needed)- ; scrolledWindowSetPolicy scrollWin PolicyAutomatic PolicyAutomatic- -- request view size for _layout_- ; widgetSetSizeRequest layout iViewWidth iViewHeight- ; set scrollWin [containerChild := layout]- ; return scrollWin- }-- bare :: GtkLayout -> IO GtkLayout- bare layout = do- {- -- request canvas size for _layout_- ; widgetSetSizeRequest layout icWidth icHeight -- new- ; return layout- }-- -- The canvas itself- ; vcanvas <- vcanvasNew style dcWidth dcHeight- ; let layout = vcLayout vcanvas- -- Set the actual size of the canvas layout, which may be more- -- than is displayed if scrollbars are used- ; layoutSetSize layout icWidth icHeight-- -- An empty HBox for buttons (or it may remain empty)- ; buttonBar <- hBoxNew False 3-- -- The Statusbar- ; statusBar <- statusbarNew- - -- All together in a VBox- ; vbox <- vBoxNew False 0 -- not equal space allotments, 0 spacing-- ; addMenuBar vbox-- ; let packGrow :: WidgetClass w => w -> IO ()- packGrow w = boxPackStart vbox w PackGrow 0- ; case mViewSize of- Nothing -> bare layout >>= packGrow- Just viewSize -> scrolled layout viewSize >>= packGrow-- ; boxPackStart vbox buttonBar PackNatural 0- ; boxPackStart vbox statusBar PackNatural 0- ; return $ Workspace vbox vcanvas buttonBar statusBar-}---vpuiQuit :: VPUI -> IO VPUI-vpuiQuit vpui = do- {- -- This should also check for unsaved changes? ***- vpui' <- foldM (\ vp winId -> removeWindow vp True winId)- vpui- (vpuiAllWindowKeys vpui)- ; mainQuit- ; return vpui'- }---- | List of all window ids of the vpui, --vpuiAllWindowKeys :: VPUI -> [WinId]-vpuiAllWindowKeys = keys . vpuiWindows---- | Perform action on all windows--- (actually (WinId, VPUIWindow) pairs.--- Returns updated VPUI (in case any windows are changed).--forallWindowsIO :: (VPUIWindow -> IO VPUIWindow) -> VPUI -> IO VPUI-forallWindowsIO action vpui = - let loop ks vpui' =- case ks of- [] -> return vpui'- k : ks' -> - let w = vpuiGetWindow vpui' k- in do - {- w' <- action w- ; loop ks' (vpuiReplaceWindow vpui' k w')- }- in loop (vpuiAllWindowKeys vpui) vpui----- | This function is called either when a window *has been* destroyed,--- with destroy = False,--- or when you *want to* destroy a window, with destroy = True.---- WOULD BE BETTER to have two functions, windowRemoved and removeWindow???---- | removeWindow actually *closes* the window if destroy = True,--- as well as removing it from the vpui's windows map.-removeWindow :: VPUI -> Bool -> WinId -> IO VPUI-removeWindow vpui destroy winId = do- {- -- Remove the window from vpui;- -- if destroy is true, also destroy it.- -- It is an error if the window id does not exist.- let vwMap = vpuiWindows vpui- ; when destroy $ widgetDestroy (vpuiWindowWindow (vwMap ! winId))- ; return $ vpuiRemoveVPUIWindow winId vpui- }---- | Context menu command to apply the function definition--- of an EditFrame.---- | "Execute" the definition currently represented in the frame,--- i.e., bind the function name in the global environment--- to the function definition found in the frame.--defineFunction :: WinId -> CanvFrame -> VPUI -> IO VPUI-defineFunction winId frame vpui = - case frameType frame of- CallFrame ->- showErrorMessage "Software error\nNot in an edit frame!"- >> return vpui- EditFrame ->- case cfFunctoid frame of- FunctoidFunc _function ->- return vpui- fparts@FunctoidParts {} ->- let env = vpuiGlobalEnv vpui- vw = vpuiGetWindow vpui winId- canv = vpuiWindowGetCanvas vw- graph = vcGraph canv- frameNode = cfFrameNode frame- in case functoidToFunction fparts graph frameNode env of- Fail errmsg -> - showErrorMessage errmsg >>- return vpui- Succ function ->- let BBox x y _ _ = cfBox frame- z = cfLevel frame- fname = functionName function- env' = envSet env fname (VFun function)- vpui' = vpui {vpuiGlobalEnv = env'}- in do - {- ; canv' <- vcCloseFrame canv frame- -- can this use vpuiAddFrame? ***- ; canv'' <- - vcAddFrame canv' (FunctoidFunc function) - Nothing - EditFrame- env' x y z Nothing- ; let vw' = vpuiWindowSetCanvas vw canv''- vpui'' = vpuiReplaceWindow vpui' winId vw'- ; vpuiUpdateCallFrames vpui'' fname- }--workspaceId :: String-workspaceId = "Sifflet Workspace"---- | In the workspace window, update each frame calling the named function --- to reflect the current function definition-vpuiUpdateCallFrames :: VPUI -> String -> IO VPUI-vpuiUpdateCallFrames vpui fname = - case vpuiTryGetWindow vpui workspaceId of- Nothing -> return vpui- Just w -> do- {- ; let canvas = vpuiWindowGetCanvas w- env = vpuiGlobalEnv vpui- frames = callFrames canvas fname- update canv frame = canvasUpdateCallFrame canv frame fname env- ; canvas' <- foldM update canvas frames - ; let w' = vpuiWindowSetCanvas w canvas'- ; return $ vpuiReplaceWindow vpui workspaceId w'- }---- | In the canvas, update a call frame with the current function--- definition from the environment, returning a new canvas.--- Root call frames are torn down and rebuilt with the new function definition.--- Call frames that are called by other call frames are simply torn down.-canvasUpdateCallFrame :: VCanvas -> CanvFrame -> String -> Env -> IO VCanvas-canvasUpdateCallFrame canvas frame fname env = do- {- -- Tear down old frame- canvas' <- vcCloseFrame canvas frame- ; case cfParent frame of- Nothing -> - -- root frame; build up new frame- let Position x y = bbPosition (cfBox frame)- z = cfLevel frame- functoid = FunctoidFunc {fpFunc = envGetFunction env fname}- in vcAddFrame canvas' functoid Nothing CallFrame env x y z Nothing- Just _ -> - -- frame with a parent; finished- return canvas'- }--openNode :: VPUIWindow -> G.Node -> IO VPUIWindow-openNode vw node = do- let canvas = vpuiWindowGetCanvas vw- graph = vcGraph canvas- if not (nodeIsSimple graph node)- then return vw -- WFrame node -- is this possible?- else if nodeIsOpen graph node- then info "Already open" >> return vw- else let frame = nodeContainerFrame canvas graph node- in case nodeCompoundFunction graph frame node of- Nothing -> - info "Cannot be opened" >> return vw- Just function ->- case nodeInputValues graph node of- EvalOk (VList values) ->- let env = extendEnv (functionArgNames function)- values (cfEnv frame)- Position x y = - frameOffset (vcStyle canvas) frame- z = succ (cfLevel frame)- in vwAddFrame vw - (FunctoidFunc function) - (Just values) CallFrame- env x y z (Just node)- EvalOk x ->- errcats ["openNode: non-VList result:", show x]- _ -> - info "Cannot be opened: lacking input values" >>- return vw--baseFunctionsRows :: [[String]]-baseFunctionsRows = [["+", "-", "*", "div", "mod", "add1", "sub1", "/"],- ["==", "/=", "<", ">", "<=", ">="],- ["zero?", "positive?", "negative?"],- ["null", "head", "tail", ":"]]
− Sifflet/Util.hs
@@ -1,135 +0,0 @@-module Sifflet.Util (- -- | Parser Utilities- SuccFail(Succ, Fail)- , parsef, parseInt, parseDouble, parseVerbatim- -- | String Utilities- , par- -- Output Utilities- , putCatsLn- , putCatLn- , info- , fake- , stub-- -- | Error Reporting- , errcat- , errcats-- -- | List Utilities- , map2, mapM2- , adjustAList, adjustAListM- , insertLastLast, insertLast- )--where--import Control.Monad()---- SuccFail: the result of an attempt, succeeds or fails-data SuccFail a = Succ a -- value- | Fail String -- error message- deriving (Eq, Read, Show)--instance Monad SuccFail where- Succ val >>= f = f val- Fail err >>= _f = Fail err- return = Succ- fail = Fail---- PARSER UTILITIES--parsef :: (Read a) => String -> String -> String -> SuccFail a-parsef typeName inputLabel input =- case reads input of- [(value, "")] -> Succ value- [(_, more)] -> - Fail $ inputLabel ++ ": extra characters after " ++ - typeName ++ ": " ++ more- _ -> Fail $ inputLabel ++ ": cannot parse as " ++ - typeName ++ ": " ++ input--parseInt :: String -> String -> SuccFail Int-parseInt = parsef "integer"--parseDouble :: String -> String -> SuccFail Double-parseDouble = parsef "real number"--parseVerbatim :: String -> String -> SuccFail String-parseVerbatim _label = Succ---- | Enclose in parentheses, like a Lisp function call.--- Example: par "foo" ["x", "y"] = "(foo x y)"--par :: String -> [String] -> String-par f xs = "(" ++ unwords (f:xs) ++ ")"---- | Write a list of words, separated by spaces-putCatsLn :: [String] -> IO ()-putCatsLn = putStrLn . unwords---- | Write a list of words, not separated by spaces-putCatLn :: [String] -> IO ()-putCatLn = putStrLn . concat--info :: (Show t) => t -> IO ()-info = print--fake :: String -> IO ()-fake what = putStrLn $ "Faking " ++ what ++ "..."--stub :: String -> IO ()-stub name = putStrLn $ "Stub for " ++ name---- ERROR REPORTING---- | Signal an error using a list of strings to be concatenated-errcat :: [String] -> a-errcat = error . concat---- | Signal an error using a list of strings to be concatenated--- with spaces between (unwords).-errcats :: [String] -> a-errcats = error . unwords---- LIST UTILITIES---- | Generalization of map to lists of lists-map2 :: (a -> b) -> [[a]] -> [[b]]-map2 f rows = -- map (\ row -> map f row) rows- map (map f) rows---- | Generalization of mapM to lists of lists-mapM2 :: (Monad m) => (a -> m b) -> [[a]] -> m [[b]]-mapM2 f rows = -- mapM (\ row -> mapM f row) rows- mapM (mapM f) rows---- | Insert an item into a list of lists of items,--- making it the last element in the last sublist-insertLastLast :: [[a]] -> a -> [[a]]-insertLastLast xss x = init xss ++ [insertLast (last xss) x]---- | Insert an item in a list of items, making it the last element-insertLast :: [a] -> a -> [a]-insertLast xs x = xs ++ [x]---- | Update a value at a given key by applying a function.--- Similar to Data.Map.adjust.---- This implementation, using map, could be inefficient--- if the key to be updated is near the front of a long list.-adjustAList :: (Eq k) => k -> (v -> v) -> [(k, v)] -> [(k, v)]-adjustAList key f alist =- map (\ (k, v) -> if k == key then (k, f v) else (k, v)) - alist---- | Monadic generalization of adjustAList ---- Same caution re. inefficiency-adjustAListM :: (Eq k, Monad m) => - k -> (v -> m v) -> [(k, v)] -> m [(k, v)]-adjustAListM key f alist =- mapM (\ (k, v) -> - if k == key - then do { v' <- f v; return (k, v') }- else return (k, v))- alist
+ Text/Sifflet/Pretty.hs view
@@ -0,0 +1,56 @@+module Text.Sifflet.Pretty + (Pretty(..)+ , indentLine, sepLines, sepLines2+ , sepComma, sepCommaSp, sepSpace)++where++import Data.List (intercalate)+++-- | The class of types that can be pretty-printed.+-- (Unfortunately this is not very useful, because+-- and Expr can be pretty Haskell or pretty Python or pretty Scheme,+-- leading to overlapping instance declarations.)+--+-- pretty x is a pretty String representation of x.+-- prettyList prefix infix postfix xs is a pretty String representation+-- of the list xs, with prefix, infix, and postfix specifying the+-- punctuation. For example, if (pretty x) => "x",+-- then prettyList "[" ", " "]" [x, x, x] => "[x, x, x]".+--+-- Minimal complete implementation: define pretty.++class Pretty a where++ pretty :: a -> String++ prettyList :: String -> String -> String -> [a] -> String+ prettyList pre tween post xs =+ pre ++ intercalate tween (map pretty xs) ++ post++-- | Indent a single line n spaces.+indentLine :: Int -> String -> String+indentLine n line = replicate n ' ' ++ line++-- | sepLines is like unlines, but omits the \n at the end of the+-- last line.+sepLines :: [String] -> String+sepLines = intercalate "\n"++-- | sepLines2 is like sepLines, but adds an extra \n between each+-- pair of lines so they are "double spaced."+sepLines2 :: [String] -> String+sepLines2 = intercalate "\n\n"++-- | Separate strings by commas and nothing else (",")+sepComma :: [String] -> String+sepComma = intercalate ","++-- | Separate strings by commas and spaces (", ")+sepCommaSp :: [String] -> String+sepCommaSp = intercalate ", "++-- | Separate strings by just spaces (" ")+sepSpace :: [String] -> String+sepSpace = unwords
+ Text/Sifflet/Repr.hs view
@@ -0,0 +1,65 @@+module Text.Sifflet.Repr+ (Repr(..)+ , Name(..)+ )++where++import Data.Number.Sifflet+import Data.List (intercalate)++-- | class Repr: representable by a String or a list of Strings+--+-- repr x is a String representation of x.+-- reprl x is a [String] representation of x,+-- where the first element should be the same as repr x,+-- and the rest provide auxiliary information+-- that you want to be shown with x.+-- reprs x is a reduction of reprl x to a single String.+-- reprList prefix infix postfix xs is the representation of a list of xs+--+-- Minimal complete implementation: define repr, or define reprl.+-- The normal way is to define repr. Define reprl instead,+-- if for some reason you want to include additional information+-- such as the value of an expression in an expression node.+--+-- Examples:+-- - (3 :: Int) has repr => "3", reprl => ["3"], reprs => "3"+-- - In Language.Sifflet.Expr, (ENode (NSymbol "x") (EvalOk (3 :: Int)+-- has reprl => ["x", "3"], reprs => "x 3", and repr => "x".+-- - reprList "(" " " ")" [3 :: Int, 4, 5] => "(3 4 5)"++class Repr a where++ repr :: a -> String+ repr = head . reprl++ reprl :: a -> [String]+ reprl x = [repr x]++ reprs :: a -> String+ reprs = unwords . reprl++ reprList :: String -> String -> String -> [a] -> String+ reprList pre tween post xs =+ pre ++ intercalate tween (map repr xs) ++ post++instance Repr Bool where repr = show+instance Repr Char where repr = show+instance Repr Int where repr = show+instance Repr Integer where repr = show+instance Repr Number where repr = show+instance Repr Float where repr = show+instance Repr Double where repr = show++-- instance Repr String won't work because String is a type synonym,+-- unless you ask ghc nicely, which I'd prefer not to do.+-- Use Name data type in Testing/Tree.hs instead, or Symbol in Expr.hs+-- I don't know if I can use Expr.Symbol here, since Expr.hs also+-- imports Tree.hs (this file) -- is mutual import allowed?++newtype Name = Name String+ deriving (Eq, Read, Show)++instance Repr Name where+ repr (Name s) = s
+ datafiles/siffml-1.0.rnc view
@@ -0,0 +1,60 @@+# -*- mode: conf -*-+# schema for siffml-1.0.+# RELAX NG compact syntax, using a grammar++start = Functions++# Functions and children++Functions = element functions { CompoundFunction* }+CompoundFunction = + element compound-function { Name, ReturnType, ArgTypes, ArgNames, Body }+Name = element name { text }+ReturnType = element return-type { SiffletType }+ArgTypes = element arg-types { SiffletType* }+ArgNames = element arg-names { Name* }+Body = element body { SiffletExpr }++# Types (variants of VpType)++SiffletType = + StringType | CharType | NumType | BoolType | ListType | TypeVariable++StringType = element string-type { empty }+CharType = element char-type { empty }+NumType = element num-type { empty }+BoolType = element bool-type { empty }+ListType = element list-type { SiffletType }+TypeVariable = element type-variable { text }++# Expressions (variants of Expr)+# Expr also has a EList constructor, but this is not used in SiffML++SiffletExpr = Undefined | Symbol | Literal | If | Call++Undefined = element undefined { empty }+Symbol = element symbol { text }+Literal = element literal { SiffletValue }+If = element if { SiffletExpr, SiffletExpr, SiffletExpr }+Call = element call { Symbol, SiffletExpr* }++# A "list" element as an expr is probably a mistake.+# List = element list { SiffletExpr }++# Values (variants of Value)++SiffletValue = TRUE | FALSE | Char | String | Int | Float | List | Function++TRUE = element True { empty }+FALSE = element False { empty }+Char = element char { text }+String = element string { text }+Int = element int { text }+Float = element float { text }++# A "list" element represents a list value, not a list expr.+List = element list { SiffletValue* }++# Function values shouldn't happen, but just in case:+Function = element function { CompoundFunction }+
+ datafiles/siffml-2.0.rnc view
@@ -0,0 +1,71 @@+# -*- mode: conf -*-+# schema for siffml-2.0.+# RELAX NG compact syntax, using a grammar+#+# All valid siffml-1.0 documents are also valid as siffml-2.0,+# as the only change is the addition of FunctionType as+# another alternative in SiffletType++start = Functions++# Functions and children++Functions = element functions { CompoundFunction* }+CompoundFunction = + element compound-function { Name, ReturnType, ArgTypes, ArgNames, Body }+Name = element name { text }+ReturnType = element return-type { SiffletType }+ArgTypes = element arg-types { SiffletType* }+ArgNames = element arg-names { Name* }+Body = element body { SiffletExpr }++# Types (variants of VpType)++SiffletType = + StringType | CharType | NumType | BoolType | ListType | + FunctionType | TypeVariable++StringType = element string-type { empty }+CharType = element char-type { empty }+NumType = element num-type { empty }+BoolType = element bool-type { empty }+ListType = element list-type { SiffletType }++FunctionType = element function-type { + SiffletType, # argument type+ SiffletType # return type (which may be a function type)+ }++TypeVariable = element type-variable { text }++# Expressions (variants of Expr)+# Expr also has a EList constructor, but this is not used in SiffML++SiffletExpr = Undefined | Symbol | Literal | If | Call++Undefined = element undefined { empty }+Symbol = element symbol { text }+Literal = element literal { SiffletValue }+If = element if { SiffletExpr, SiffletExpr, SiffletExpr }+Call = element call { Symbol, SiffletExpr* }++# A "list" element as an expr is probably a mistake.+# List = element list { SiffletExpr }++# Values (variants of Value)++SiffletValue = TRUE | FALSE | Char | String | Int | Float | List | Function++TRUE = element True { empty }+FALSE = element False { empty }+Char = element char { text }+String = element string { text }+Int = element int { text }+Float = element float { text }++# A "list" element represents a list value, not a list expr.+List = element list { SiffletValue* }++# Function values shouldn't happen, but just in case:+Function = element function { CompoundFunction }+
sifflet-lib.cabal view
@@ -1,5 +1,5 @@ name: sifflet-lib-version: 1.2.5.1+version: 2.0.0.0 cabal-version: >= 1.8 build-type: Simple license: BSD3@@ -9,25 +9,22 @@ maintainer: "gdweber" ++ drop 3 "abc@" ++ "iue.edu" bug-reports: mailto:"gdweber" ++ drop 3 "abc@" ++ "iue.edu" homepage: http://mypage.iu.edu/~gdweber/software/sifflet/-stability: Experimental; expect API, especially the location of- modules in Haskell's hierarchical module system, to change radically- and without notice.+stability: Experimental. synopsis: Library of modules shared by sifflet and its tests and its exporters. description: Supporting modules for the Sifflet visual, functional programming language (Hackage 'sifflet' package).- Version 1.2.5: Compatibility with GHC 7.4.1.- Version 1.2.4: Dependencies revised for compatibility with HXT 9.1.- Version 1.2.3: Dependencies revised for compatibility with GHC 7.0- and Haskell Platform 2011.2.+ Version 2.0.0.0 adds partial support for higher-order functions, like map + and filter. category: Language , Visual Programming-tested-with: GHC == 7.4.1-data-files: sifflet.scm sifflet.py siffml-1.0.dtd+tested-with: GHC == 7.4.2+data-files: sifflet.scm sifflet.py siffml-1.0.dtd siffml-1.0.rnc + siffml-2.0.rnc data-dir: datafiles extra-tmp-files:-extra-source-files: README+extra-source-files: README ISSUES RELEASE-NOTES -- Library section @@ -58,39 +55,50 @@ extra-libraries: gdk-x11-2.0 gtk-x11-2.0 exposed-modules: Data.Number.Sifflet- , Sifflet.Data.Geometry- , Sifflet.Data.Functoid- , Sifflet.Data.Tree- , Sifflet.Data.TreeGraph- , Sifflet.Data.TreeLayout- , Sifflet.Data.WGraph- , Sifflet.Examples- , Sifflet.Foreign.Exporter- , Sifflet.Foreign.Haskell- , Sifflet.Foreign.Python- , Sifflet.Foreign.ToHaskell- , Sifflet.Foreign.ToPython- , Sifflet.Foreign.ToScheme- , Sifflet.Language.Expr- , Sifflet.Language.Parser- , Sifflet.Language.SiffML- , Sifflet.Rendering.Draw- , Sifflet.Rendering.DrawTreeGraph- , Sifflet.Text.Pretty- , Sifflet.Text.Repr- , Sifflet.UI- , Sifflet.UI.Callback- , Sifflet.UI.Canvas- , Sifflet.UI.Frame- , Sifflet.UI.GtkForeign- , Sifflet.UI.GtkUtil- , Sifflet.UI.LittleGtk- , Sifflet.UI.Tool- , Sifflet.UI.RPanel- , Sifflet.UI.Types- , Sifflet.UI.Window- , Sifflet.UI.Workspace- , Sifflet.Util++ , Data.Sifflet.Functoid+ , Data.Sifflet.Geometry+ , Data.Sifflet.Tree+ , Data.Sifflet.TreeGraph+ , Data.Sifflet.TreeLayout+ , Data.Sifflet.WGraph++ , Graphics.Rendering.Sifflet.Draw+ , Graphics.Rendering.Sifflet.DrawTreeGraph++ , Graphics.UI.Sifflet+ , Graphics.UI.Sifflet.Callback+ , Graphics.UI.Sifflet.Canvas+ , Graphics.UI.Sifflet.EditArgsPanel+ , Graphics.UI.Sifflet.Frame+ , Graphics.UI.Sifflet.GtkForeign+ , Graphics.UI.Sifflet.GtkUtil+ , Graphics.UI.Sifflet.LittleGtk+ , Graphics.UI.Sifflet.Tool+ , Graphics.UI.Sifflet.RPanel+ , Graphics.UI.Sifflet.Types+ , Graphics.UI.Sifflet.Window+ , Graphics.UI.Sifflet.Workspace++ , Language.Sifflet.Examples++ , Language.Sifflet.Export.Exporter+ , Language.Sifflet.Export.Haskell+ , Language.Sifflet.Export.Python+ , Language.Sifflet.Export.ToHaskell+ , Language.Sifflet.Export.ToPython+ , Language.Sifflet.Export.ToScheme++ , Language.Sifflet.Expr+ , Language.Sifflet.ExprTree+ , Language.Sifflet.Parser + , Language.Sifflet.TypeCheck+ , Language.Sifflet.SiffML+ , Language.Sifflet.Util++ , Text.Sifflet.Pretty+ , Text.Sifflet.Repr+ -- Paths to data files, generated by Cabal other-modules: Paths_sifflet_lib hs-source-dirs: