mirror of
https://github.com/rgleichman/glance.git
synced 2024-09-11 15:05:41 +03:00
Add a tutorial/introduction file. Add support for multiline comments.
This commit is contained in:
parent
91cacefb3d
commit
6afb02c037
@ -29,7 +29,8 @@ data ColorStyle a = ColorStyle {
|
||||
colorOnBlackScheme :: (Floating a, Ord a) => ColorStyle a
|
||||
colorOnBlackScheme = ColorStyle {
|
||||
backgroundC = black,
|
||||
lineC = white,
|
||||
--lineC = white,
|
||||
lineC = lightgray,
|
||||
textBoxTextC = white,
|
||||
textBoxC = white,
|
||||
apply0C = red,
|
||||
|
24
app/Icons.hs
24
app/Icons.hs
@ -1,4 +1,4 @@
|
||||
{-# LANGUAGE NoMonomorphismRestriction, FlexibleContexts, TypeFamilies, RankNTypes, PartialTypeSignatures #-}
|
||||
{-# LANGUAGE NoMonomorphismRestriction, FlexibleContexts, TypeFamilies, RankNTypes, PartialTypeSignatures, ScopedTypeVariables #-}
|
||||
module Icons
|
||||
(
|
||||
Icon(..),
|
||||
@ -7,6 +7,7 @@ module Icons
|
||||
iconToDiagram,
|
||||
nameDiagram,
|
||||
textBox,
|
||||
multilineText,
|
||||
enclosure,
|
||||
lambdaRegion,
|
||||
resultIcon,
|
||||
@ -15,7 +16,6 @@ module Icons
|
||||
defaultLineWidth,
|
||||
ColorStyle(..),
|
||||
colorScheme,
|
||||
|
||||
nestedApplyDia
|
||||
) where
|
||||
|
||||
@ -183,6 +183,16 @@ bindTextBox :: SpecialBackend b =>
|
||||
String -> SpecialQDiagram b
|
||||
bindTextBox = coloredTextBox (bindTextBoxTextC colorScheme) $ opaque (bindTextBoxC colorScheme)
|
||||
|
||||
|
||||
multilineText :: SpecialBackend b =>
|
||||
Colour Double
|
||||
-> AlphaColour Double -> String -> SpecialQDiagram b
|
||||
multilineText textColor boxColor t = lwG (0.6 * defaultLineWidth) $ textDia
|
||||
where
|
||||
textLines = lines t
|
||||
textAreas = map (singleLineTextArea textColor) textLines
|
||||
textDia = vcat textAreas
|
||||
|
||||
-- Since the normal SVG text has no size, some hackery is needed to determine
|
||||
-- the size of the text's bounding box.
|
||||
coloredTextBox :: SpecialBackend b =>
|
||||
@ -196,6 +206,16 @@ coloredTextBox textColor boxColor t =
|
||||
* fromIntegral (length t)
|
||||
+ (textBoxFontSize * 0.2)
|
||||
|
||||
singleLineTextArea :: SpecialBackend b =>
|
||||
Colour Double -> String -> SpecialQDiagram b
|
||||
singleLineTextArea textColor t =
|
||||
alignL $ fontSize (local textBoxFontSize) (font "freemono" $ fc textColor $ text t)
|
||||
<> rect rectangleWidth (textBoxFontSize * textBoxHeightFactor)
|
||||
where
|
||||
rectangleWidth = textBoxFontSize * monoLetterWidthToHeightFraction
|
||||
* fromIntegral (length t)
|
||||
+ (textBoxFontSize * 0.2)
|
||||
|
||||
-- ENCLOSING REGION --
|
||||
enclosure :: SpecialBackend b =>
|
||||
SpecialQDiagram b -> SpecialQDiagram b
|
||||
|
23
app/Main.hs
23
app/Main.hs
@ -8,12 +8,13 @@ import Diagrams.Prelude hiding ((#), (&))
|
||||
import Diagrams.Backend.SVG.CmdLine
|
||||
import qualified Language.Haskell.Exts as Exts
|
||||
|
||||
import Icons(ColorStyle(..), colorScheme)
|
||||
import Icons(ColorStyle(..), colorScheme, multilineText)
|
||||
import Rendering(renderDrawing)
|
||||
import Translate(drawingsFromModule)
|
||||
|
||||
|
||||
-- TODO Now --
|
||||
-- Ues nesting apply icon even when the function is a line.
|
||||
-- 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.
|
||||
@ -51,12 +52,16 @@ import Translate(drawingsFromModule)
|
||||
-- Eliminate BranchIcon for the identity funciton "y x = x"
|
||||
-- otherwise Guard special case
|
||||
|
||||
renderFile :: String -> IO (Diagram B)
|
||||
renderFile inputFilename= do
|
||||
parseResult <- Exts.parseFileWithExts [Exts.EnableExtension Exts.MultiParamTypeClasses, Exts.EnableExtension Exts.FlexibleContexts]
|
||||
renderFile :: String -> String -> IO (Diagram B)
|
||||
renderFile inputFilename includeComments = do
|
||||
parseResult <- Exts.parseFileWithComments
|
||||
(Exts.defaultParseMode
|
||||
{Exts.extensions = [Exts.EnableExtension Exts.MultiParamTypeClasses, Exts.EnableExtension Exts.FlexibleContexts],
|
||||
Exts.parseFilename = inputFilename
|
||||
})
|
||||
inputFilename
|
||||
let
|
||||
parsedModule = Exts.fromParseResult parseResult
|
||||
(parsedModule, comments) = Exts.fromParseResult parseResult
|
||||
drawings = drawingsFromModule parsedModule
|
||||
print parsedModule
|
||||
print "\n\n"
|
||||
@ -64,8 +69,12 @@ renderFile inputFilename= do
|
||||
|
||||
diagrams <- traverse renderDrawing drawings
|
||||
let
|
||||
vCattedDrawings = vsep 1 $ fmap alignL diagrams
|
||||
pure (bgFrame 1 (backgroundC colorScheme) vCattedDrawings :: Diagram B)
|
||||
commentsInBoxes = fmap (\(Exts.Comment _ _ c) -> alignL $ multilineText white (opaque white) c) comments
|
||||
diagramsAndComments = vsep 2 $ zipWith (\x y -> x === strutY 0.4 === y) commentsInBoxes (fmap alignL diagrams)
|
||||
justDiagrams = vsep 1 $ fmap alignL diagrams
|
||||
diagramsAndMaybeComments = if includeComments == "c" then diagramsAndComments else justDiagrams
|
||||
print comments
|
||||
pure (bgFrame 1 (backgroundC colorScheme) diagramsAndMaybeComments :: Diagram B)
|
||||
|
||||
|
||||
main :: IO ()
|
||||
|
160
examples/tutorial.hs
Normal file
160
examples/tutorial.hs
Normal file
@ -0,0 +1,160 @@
|
||||
{-Welcome to Glance! My goal for this tutorial is teach you how to read
|
||||
Glance drawings, but also to get you thinking about visual programming languages.
|
||||
Why is Glance designed the way it is? What are other ways it could work?
|
||||
How could it be extended? I feel that we are just at the very beginning of
|
||||
visual programming languages, and that there is a huge universe of visual
|
||||
programming language designs waiting to be discovered.
|
||||
|
||||
Let's start with y = f x.
|
||||
The function f is applied to the argument x, and the result is bound to y.
|
||||
-}
|
||||
y = f x
|
||||
|
||||
{-More explicitly-}
|
||||
result = function argument
|
||||
|
||||
{-For example, multiply 3 and 5 and bind the result to y.
|
||||
y = (*) 3 5
|
||||
-}
|
||||
y = (*) 3 5
|
||||
|
||||
{-Infix function application is displayed the same.
|
||||
y = 3 * 5
|
||||
-}
|
||||
y = 3 * 5
|
||||
|
||||
{-You might be thinking that so far it looks like Glance has just decorated
|
||||
the program text with some boxes, lines, triangles and circles. Don't worry,
|
||||
it will start looking much stranger from here on. Keep in mind that the design
|
||||
of the icons by themselves does not have much meaning. In fact, as long as the
|
||||
icons (the non-text parts) are distinguishable from each other, how they are drawn,
|
||||
even if they are rotated, reflected, squashed or squished does not matter.
|
||||
The meaning of Glance is entirely in how the icons are connected to each other.
|
||||
Lines in Glance are largely equivalent to names (variable names, function names,
|
||||
etc.) in textual Haskell. Thus if two things are connected with a line in Glance,
|
||||
it means they have the same value.
|
||||
|
||||
Let's make things a bit more interesing with a nested function application.
|
||||
y = (*) ((+) 8 7) 2
|
||||
-}
|
||||
y = (*) ((+) 8 7) 2
|
||||
|
||||
{-That was probably not too surprising. How about this?-}
|
||||
f x = 3 * x
|
||||
|
||||
{- Let's try to figure it out. First look at the f. It's green and in an orange
|
||||
box which indicates that it is bound to the result. Now look at the
|
||||
function application. It looks like 3 is being multipled by something, but what?
|
||||
There is a line connecting the second argument of * to the dot in the green icon,
|
||||
so the second line probably originates from the dot in the green icon.
|
||||
The result of the function application is also connected to the blue square
|
||||
in the green icon. So the green icon represents a relationship between the
|
||||
overall result f, the second argument to *, and the result of the multiplication.
|
||||
Let's think of some possibilities.
|
||||
Might it be
|
||||
f = 3 * f?
|
||||
Let's see what that looks like:
|
||||
-}
|
||||
f = 3 * f
|
||||
|
||||
{-Nope, no green icon. The recursion of f is just indicated by a line from f to
|
||||
the argument. What construct in Haskell has three ingredients: a name, a variable,
|
||||
and a result that derives from the variable? Here's a hint.
|
||||
y has a value of 14.
|
||||
-}
|
||||
y = (\x -> 3 * x) 7
|
||||
|
||||
{-Now for the answer. As you might have suspected. The green icon defines a
|
||||
function. The dot inside the icon is the formal parameter. The blue square
|
||||
represents the value returned by function (ie. what's on the right size
|
||||
of the -> in a lambda expression). The green circle represents the function
|
||||
that has been defined.
|
||||
|
||||
In this case, the formal parameter x is the dot inside the green lambda icon,
|
||||
and the return value 3 * x is the red circle in the function application icon,
|
||||
which is connected to the blue square. The function itself is bound to the name f.
|
||||
|
||||
f = (\x -> 3 * x)
|
||||
-}
|
||||
f = (\x -> 3 * x)
|
||||
|
||||
{-
|
||||
Here, the new function is applied to the argument 7, and the result of the
|
||||
function application is bound to y.
|
||||
y = (\x -> 3 * x) 7-
|
||||
-}
|
||||
y = (\x -> 3 * x) 7
|
||||
|
||||
{-A more complex example:
|
||||
f x y = max (2 * y) (f (x + y - 40) x*2 )
|
||||
If you have some drawing implements handy, you might want to try drawing this
|
||||
yourself before scrolling down. One place to start is to figure out how functions
|
||||
with "multiple parameters" are defined (which is of course syntactic sugar since
|
||||
Haskell functions only have one parameter).
|
||||
Scroll down for the drawing.
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
Drawing below:
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
f x y = max (2 * y) (f (x + y - 40) x*2 )
|
||||
-}
|
||||
f x y = max (2 * y) (f (x + y - 40) x*2 )
|
||||
|
||||
{-Something different about Glance is that Glance does not have any code regions
|
||||
where, for example, the icons composing the body of a function would be restricted
|
||||
to a rectangle. In Glance, all icons are on the same level. Theoretically,
|
||||
Glance's flat layout should allow more compact drawings since space is not wasted
|
||||
by extra boxes.
|
||||
|
||||
f1 = (\x1 -> (\x2 -> (\x3 -> sum [x1, x2, x3])))
|
||||
-}
|
||||
f1 = (\x1 -> (\x2 -> (\x3 -> sum [x1, x2, x3])))
|
||||
|
||||
{-In most other visual languages, the above code would require three nested
|
||||
regions.
|
||||
|
||||
Regions however can still be useful to tell at what level, or in other words, in
|
||||
which function is a parameter used. In the code below for example, it would
|
||||
probably take some time to figure out that x is only being used in an inner
|
||||
function. To address this, I hope to have Glance draw a perimiter around all
|
||||
icons inside a function (including the function's lambda icon). This would occur
|
||||
after layout so it would not make the drawing any bigger.
|
||||
|
||||
f1 x y = (\z -> x + z) y
|
||||
-}
|
||||
f1 x y = (\z -> x + z) y
|
Loading…
Reference in New Issue
Block a user