glance/app/Main.hs
2016-05-09 23:45:37 -07:00

550 lines
14 KiB
Haskell

{-# LANGUAGE NoMonomorphismRestriction, FlexibleContexts, TypeFamilies #-}
module Main where
-- Note: (#) and (&) are hidden in all Glance source files, since they would require
-- - an special case when translating when Glance is run on its own source code.
import Diagrams.Prelude hiding ((#), (&))
import Diagrams.Backend.SVG.CmdLine
import qualified Language.Haskell.Exts as Exts
import Icons(flatLambda, textBox, colorScheme, ColorStyle(..), nestedApplyDia)
import Rendering(renderDrawing)
import Util(toNames, portToPort, iconToPort, iconToIcon,
iconToIconEnds, iconTailToPort)
import Types(Icon(..), Drawing(..), EdgeEnd(..))
import Translate(translateString, drawingsFromModule)
-- TODO Now --
-- Fix icon nesting if a non-nestable icon (eg. flatLambdaIcon) is part of the expression.
-- - eg. y = f $ g (\x -> x)
-- Fix rotation missing edges to nested diagrams.
-- Add a maximum nesting depth.
-- Clean up Rendering and Icons.
-- Refactor Translate
-- Add documentation.
-- Have the file be a command line argument to main.
-- Move tests out of main.
-- TODO Later --
-- Why is totalLengthOfLines not nesting?
-- Visual todos:
-- Don't rotate text and nested icons, give them rectangualar bounding boxes in GraphViz. (Perhaps use a typeclass for isRotateAble)
-- Give lines a black border to make line crossings easier to see.
-- Give lines that cross the border of a lambda function a special color.
-- Line intersections should have a small circle. This could probably be done with
-- a line ending.
-- Let each bool, value pair in Guard icon be flipped to reduce line crossings. Do the same for case.
-- Let lines connect to ports in multiple locations (eg. case value, or guard result)
-- Rotate icons based on the outgoing line's difference from ideal angle, not line distance.
-- Improve line routing. Draw curved lines with outgoing lines at fixed angles.
-- - connectPerim might be useful for this.
-- For nested apply, cycle through different colors and line styles (eg. dashed, solid, wavy)
-- - for each nesting level. This will help distinguish what is an argument to which funciton.
-- Translate todos:
-- Make nested version of FlatLambdaIcon
-- Fix test case x of {0 -> 1; y -> y}.
-- Add proper RecConstr, and RecUpdate support.
-- Eliminate BranchIcon in Alts.
-- Eliminate BranchIcon for the identity funciton "y x = x"
-- otherwise Guard special case
(d0A, d0B, d0Res, d0Foo, d0Bar) = ("A", "B", "res", "foo", "bar")
d0Icons = toNames
[(d0A, ApplyAIcon 1),
(d0B, ApplyAIcon 1),
(d0Res, ResultIcon),
(d0Foo, TextBoxIcon d0Foo),
(d0Bar, TextBoxIcon d0Bar)
]
d0Edges =
[
portToPort d0A 0 d0B 1,
iconToPort d0Foo d0B 0,
iconToPort d0Res d0A 1,
iconToPort d0Foo d0B 0,
iconToPort d0Bar d0B 2,
iconToPort d0Bar d0A 2
]
drawing0 = Drawing d0Icons d0Edges []
d0Name = toName "d0"
(s1Lam, s1y, s1z, s1q) = ("lam0", "y", "z", "q")
superIcons = toNames [
(s1Lam, LambdaRegionIcon 3 d0Name),
(s1y, TextBoxIcon s1y),
(s1z, TextBoxIcon s1z),
(s1q, TextBoxIcon s1q)
]
superEdges =
[
portToPort (s1Lam .> d0A) 1 s1Lam 0,
iconToIcon s1y s1Lam,
iconToIcon s1z s1Lam,
iconToIcon s1q s1Lam,
iconToIcon d0A s1z,
iconToPort (s1Lam .> d0Foo .> d0Foo) s1Lam 0
]
--superDrawing = Drawing [((toName "lam0"), LambdaRegionIcon 3 (toName"d0"))] superEdges [((toName "d0"), drawing0)]
superDrawing = Drawing superIcons superEdges [(d0Name, drawing0)]
super2Icons = toNames [
(s1Lam, LambdaRegionIcon 1 d0Name),
--("y", TextBoxIcon "y"),
("lam1", LambdaRegionIcon 2 d0Name)
]
super2Edges =
[
iconToIcon s1Lam "lam1"
--iconToIcon "y" "lam0"
]
super2Drawing = Drawing super2Icons super2Edges [(d0Name, drawing0)]
super2Name = toName "s2"
super3Icons = toNames [
(s1Lam, LambdaRegionIcon 3 super2Name),
--("y", TextBoxIcon "y"),
("lam1", LambdaRegionIcon 4 super2Name)
]
super3Edges =
[
-- iconToIcon "lam0" "lam1",
iconToIcon s1Lam "A"
]
super3Drawing = Drawing super3Icons super2Edges [(super2Name, super2Drawing)]
(fG0, fOne, fEq0, fMinus1, fEq0Ap, fMinus1Ap, fTimes, fRecurAp, fTimesAp, fArg, fRes) =
("g0", "one", "eq0", "-1", "eq0Ap", "-1Ap", "*", "recurAp", "*Ap", "arg", "res")
fact0Icons = toNames
[
(fG0, GuardIcon 2),
(fOne, TextBoxIcon "1"),
(fEq0, TextBoxIcon "== 0"),
(fMinus1, TextBoxIcon fMinus1),
(fEq0Ap, ApplyAIcon 1),
(fMinus1Ap, ApplyAIcon 1),
(fTimes, TextBoxIcon fTimes),
(fRecurAp, ApplyAIcon 1),
(fTimesAp, ApplyAIcon 2),
(fArg, BranchIcon),
(fRes, ResultIcon)
]
fact0Edges = [
iconToPort fEq0 fEq0Ap 0,
portToPort fEq0Ap 1 fG0 3,
iconToPort fMinus1 fMinus1Ap 0,
iconToPort fTimes fTimesAp 0,
iconToPort fOne fG0 2,
portToPort fTimesAp 2 fG0 4,
portToPort fRecurAp 1 fTimesAp 3,
iconToPort fArg fEq0Ap 2,
iconToPort fArg fMinus1Ap 2,
iconToPort fArg fTimesAp 1,
portToPort fMinus1Ap 1 fRecurAp 2,
iconToPort fRes fG0 0
]
fact0Drawing = Drawing fact0Icons fact0Edges []
fact0Name = toName "fac0"
factLam0Icons = toNames [
("lam0", LambdaRegionIcon 1 fact0Name),
("fac", TextBoxIcon "factorial")
]
factLam0Edges = [
iconToPort ("lam0" .> fArg) "lam0" 0,
iconToPort "lam0" ("lam0" .> fRecurAp) 0,
iconToIcon "lam0" "fac"
]
factLam0Drawing = Drawing factLam0Icons factLam0Edges [(fact0Name, fact0Drawing)]
fact1Icons = toNames
[
(fG0, GuardIcon 2),
(fOne, TextBoxIcon "1"),
(fEq0, TextBoxIcon "== 0"),
(fMinus1, TextBoxIcon fMinus1),
(fTimes, TextBoxIcon fTimes),
(fRecurAp, ApplyAIcon 1),
(fTimesAp, ApplyAIcon 2),
(fArg, BranchIcon),
(fRes, ResultIcon)
]
fact1Edges = [
iconToIconEnds fArg EndNone fEq0 EndAp1Arg,
iconTailToPort fEq0 EndAp1Result fG0 3,
iconToIconEnds fArg EndNone fMinus1 EndAp1Arg,
iconTailToPort fMinus1 EndAp1Result fRecurAp 2,
iconToPort fTimes fTimesAp 0,
iconToPort fOne fG0 2,
portToPort fTimesAp 1 fG0 4,
portToPort fRecurAp 1 fTimesAp 3,
iconToPort fArg fTimesAp 2,
iconToPort fRes fG0 0
]
fact1Drawing = Drawing fact1Icons fact1Edges []
factLam1Drawing = Drawing factLam0Icons factLam0Edges [(fact0Name, fact1Drawing)]
-- fact2 is like fact1, but uses fTimesAp port 2 to distrubute the argument,
-- not fArg
fact2Icons = toNames
[
(fG0, GuardIcon 2),
(fOne, TextBoxIcon "1"),
(fEq0, TextBoxIcon "== 0"),
(fMinus1, TextBoxIcon fMinus1),
(fTimes, TextBoxIcon fTimes),
(fRecurAp, ApplyAIcon 1),
(fTimesAp, ApplyAIcon 2),
--(fArg, BranchIcon),
(fRes, ResultIcon)
]
fact2Edges = [
--iconToIconEnds fArg EndNone fEq0 EndAp1Arg,
iconTailToPort fEq0 EndAp1Arg fTimesAp 2,
iconTailToPort fEq0 EndAp1Result fG0 3,
--iconToIconEnds fArg EndNone fMinus1 EndAp1Arg,
iconTailToPort fMinus1 EndAp1Arg fTimesAp 2,
iconTailToPort fMinus1 EndAp1Result fRecurAp 2,
iconToPort fTimes fTimesAp 0,
iconToPort fOne fG0 2,
portToPort fTimesAp 1 fG0 4,
portToPort fRecurAp 1 fTimesAp 3,
--iconToPort fArg fTimesAp 2,
iconToPort fRes fG0 0
]
fact2Drawing = Drawing fact2Icons fact2Edges []
factLam2Edges = [
iconToPort ("lam0" .> fTimesAp .> (2 :: Int)) "lam0" 0,
iconToPort "lam0" ("lam0" .> fRecurAp) 0,
iconToIcon "lam0" "fac"
]
factLam2Drawing = Drawing factLam0Icons factLam2Edges [(fact0Name, fact2Drawing)]
(arr1, arr2, arr3, arr4) = ("arr1", "arr2", "arr3", "arr4")
arrowTestIcons = toNames [
(arr1, TextBoxIcon "1"),
(arr2, TextBoxIcon "2"),
(arr3, TextBoxIcon "3"),
(arr4, TextBoxIcon "4")
]
arrowTestEdges = [
iconToIconEnds arr1 EndAp1Arg arr2 EndAp1Result,
iconToIconEnds arr1 EndAp1Result arr3 EndAp1Arg,
iconToIconEnds arr2 EndAp1Result arr3 EndAp1Result,
iconToIconEnds arr1 EndAp1Arg arr4 EndAp1Arg
]
arrowTestDrawing = Drawing arrowTestIcons arrowTestEdges []
nestedTestIcons = toNames [
("n1", NestedApply "N1" args),
("t1", TextBoxIcon "T1"),
("t2", TextBoxIcon "t2")
]
where
innerArgs = [Just (toName "t", TextBoxIcon "t"), Nothing, Just (toName "n2", NestedApply "n2" [Nothing])]
args = [
Nothing, Just (toName "foo", TextBoxIcon "3"),
Just (toName "in", NestedApply "inner" innerArgs)
]
nestedTestEdges = [
iconToPort "t1" "n1" 2,
--iconToPort "t1" "in" 1,
--iconToPort "t2" ("n1" .> "in") 3,
iconToPort "t2" ("n1" .> "in" .> "n2") 2
]
nestedTextDrawing = Drawing nestedTestIcons nestedTestEdges []
main1 :: IO ()
main1 = do
placedNodes <- renderDrawing nestedTextDrawing
mainWith (bgFrame 1 (backgroundC colorScheme) placedNodes :: Diagram B)
main2 = mainWith ((bgFrame 0.1 black $ dia False 0) :: Diagram B)
where
args = [Nothing, Just (toName "foo", TextBoxIcon "3"), Just (toName "in", NestedApply "inner" [Just (toName "t", TextBoxIcon "t")])]
dia = nestedApplyDia "Hello world" args
main3 :: IO ()
main3 = do
renderedDiagrams <- traverse renderDrawing allDrawings
let vCattedDrawings = vsep 0.5 renderedDiagrams
mainWith (bgFrame 1 (backgroundC colorScheme) vCattedDrawings :: Diagram B)
where
allDrawings = [
drawing0,
superDrawing,
super2Drawing,
super3Drawing,
fact0Drawing,
factLam0Drawing,
fact1Drawing,
factLam1Drawing,
fact2Drawing,
factLam2Drawing,
arrowTestDrawing
]
nestedTests = [
"y = f x",
"y = f (g x)",
"y = let x = 1 in f x",
"y = let x = 1 in f (g x)",
"y = f []",
"y = f [1]",
"y = f [1,2]",
"y = f [g 3, h 5]"
]
dollarTests = [
"y = f $ g 3",
" y = f 1 $ g 2 "
]
specialTests = [
"lookupTail EndAp1Arg = (arrowTail .~ dart')",
"y = x .~ y",
"initialIdState = IDState 0",
"y = f x",
"yyy = fff xxx",
"yyyyy = fffff xxxxx"
]
negateTests = [
"y = -1",
"y = -1/2",
"y = -x"
]
doTests = [
"y = do {x1}",
"y = do {x1; x2}",
"y = do {x1; x2; x3}",
"y = do {x1 <- m1; x2}",
"y = do {(x1, x2) <- m1; x1 + x2}",
"y = do {x1 <- m1; x2 <- f x1; g x2}",
"y = do {let {x = 1}; x2 <- x; f x2}"
]
enumTests = [
"y = [1..]",
"y = [1,2..]",
"y = [0..10]",
"y = [0,1..10]"
]
tupleTests = [
"y = ()",
"(x, y) = (1,2)",
"(x, y, z) = (1,2,3)"
]
listTests = [
"y = []",
"y = [1]",
"y = [1,2]",
"y = [1,2,3]",
"[x] = 1",
"[x, y] = 2",
"[x, y, z] = 3"
-- TODO: Add this test "(x:y) = 3"
]
caseTests = [
"y = case x of {0 -> 1; 2 -> 3}",
"y = case f x of {0 -> 1; 2 -> 3}",
"y = case x of {Foo a -> a}",
"y = case x of {Foo a -> f a; Bar a -> f a}",
"y = case x of {F x -> x; G x -> x}",
"y = case x of {F -> 0; G -> 1}",
"z = case x of {0 -> 1; y -> y}"
]
guardTests = [
"y x\n\
\ | x == 0 = 1",
"y x\n\
\ | x == 0 = 1\n\
\ | otherwise = 2"
]
patternTests = [
"Foo _ x = 3",
"y (F x) = x",
"y = (\\(F x) -> x)",
"y = let {g = 3; F x y = h g} in x y",
"y = let {F x y = 3} in x y",
"y = let {g = 3; F x y = g} in x y",
"y = let F x y = g in x y",
"F x = g x",
"Foo (Bar x) (Baz y) = f 1 2 x y",
"Foo x y = f 1 y x",
"t@(x,y) = (x,y)",
"y = let {t@(_,_) = (3,4)} in t + 3",
"y = let {(x, y) = (1,2)} in x + y",
-- TODO: Fix so that lines between patterns are Pattern Color.
"y = let {(x, y) = (1,2); (z, w) = x; (m, g) = y} in foo x y z w m g",
"(x:y) = 2"
]
lambdaTests = [
"y = (\\x -> (\\x -> (\\x -> x) x) x)",
"y = (\\x -> (\\x -> (\\x -> x)))",
"y = (\\y -> y)",
"y = (\\x1 -> (\\x2 -> (\\x3 -> x1 x2 x3)))",
"y x = (\\z -> x)",
"y = (\\x -> (\\z -> x))",
"y x = x",
"y x = y x",
"y x = g y y",
"y f x = f x",
"y x = x y",
"y x1 x2 = f x1 x3 x2",
"y x1 x2 = f x1 x2",
"y x = f x1 x2",
"{y 0 = 1; y 1= 0}",
"y (-1) = 2",
"y 1 = 0",
"{y (F x) = x; y (G x) = x}",
"{y (F x) z = x z; y (G x) z = z x}",
"y x = z 3 where z = f x y",
"y x = z where z = f x y"
]
letTests = [
"y = let {z = (\\x -> y x)} in z",
"y = let {z x = y x} in z ",
"y = x where x = f 3 y",
"y x1 = let {x2 = x1; x3 = x2; x4 = f x3} in x4",
"y x1 = let x2 = f x1 in x2 x1",
"y x = let x = 3 in x",
"y = let {a= 1; x = let {a = 27; x = f a 2} in x} in x",
"y = let {a = b; b = a; d = f a} in d",
"y = let {a = b; b = a} in a",
"y = let x = x in x",
"y = let {fibs = cons 0 (cons 1 (zipWith (+) fibs (tail fibs)))} in fibs",
"fibs = cons 0 (cons 1 (zipWith (+) fibs (tail fibs)))",
"y = let x = f x in x",
"y = f y",
"y = let {a = f b; b = g a} in b",
"y = let {a = 48; b = a + 3} in b",
"y = let {b = a; a = 84} in f b",
"y = let {x = 1} in f x",
"y = let z = 2 in z",
"y = let {z = 3; z2 = z} in z2",
"y x = let z = x in z"
]
operatorTests = [
"y = 1 + 2",
"y = map (1 ++) 3",
"y = map (++ 1) 3"
]
otherTests = [
"y = f 1 'c' 2.3 \"foobar\"",
"fact x = if (x == 0) then 1 else (fact x (x - 1))",
"fact x = if ((==) 0 x) then 1 else (fact x ((-) x 1))",
"y x = if x then (if z then q else x) else w",
"y x1 x2 x3 = if f x1 then g x2 else h x3",
"y x1 x2 x3 = if x1 then x2 else x3",
"y = if b then x else n",
"y2 = f x1 x2 x3 x4",
"y = x",
"y = f x",
"y = f (g x)",
"y = f (g x1 x2) x3",
"y = (f x1 x2) (g x1 x2)",
"y = Foo.bar"
]
testDecls = mconcat [
--dollarTests
nestedTests
-- ,negateTests
-- ,doTests
-- ,enumTests
-- ,caseTests
-- ,lambdaTests
-- ,guardTests
-- ,patternTests
-- ,specialTests
-- ,tupleTests
-- ,listTests
-- ,letTests
-- ,operatorTests
-- ,otherTests
]
translateStringToDrawing :: String -> IO (Diagram B)
translateStringToDrawing s = do
let
(drawing, decl) = translateString s
print decl
putStr "\n"
print drawing
putStr "\n\n"
renderDrawing drawing
main4 :: IO ()
main4 = do
drawings <- traverse translateStringToDrawing testDecls
let
textDrawings = fmap (\t -> alignL $ textBox t False 0) testDecls
vCattedDrawings = vsep 1 $ zipWith (===) (fmap alignL drawings) textDrawings
mainWith (bgFrame 1 (backgroundC colorScheme) vCattedDrawings :: Diagram B)
testFiles = [
"./app/Main.hs",
"./test/test_translate.hs"
]
main5 :: IO ()
main5 = do
parseResult <- Exts.parseFileWithExts [Exts.EnableExtension Exts.MultiParamTypeClasses, Exts.EnableExtension Exts.FlexibleContexts]
--"./app/Icons.hs"
"./test/test_nesting.hs"
let
parsedModule = Exts.fromParseResult parseResult
drawings = drawingsFromModule parsedModule
print parsedModule
print "\n\n"
--print drawings
diagrams <- traverse renderDrawing drawings
let
vCattedDrawings = vsep 1 $ fmap alignL diagrams
mainWith (bgFrame 1 (backgroundC colorScheme) vCattedDrawings :: Diagram B)
main :: IO ()
main = main5