Astview - Documentation

Astview is a little desktop program to be used by people that want to investigate syntax trees, e.g. students and lecturers in compiler construction courses. Given a parse function p :: String -> a, where a is a member of haskell's Data typeclass, astview can show syntax trees in a standard tree widget.

The program evolved as a case study in a) generic programming and b) building graphical user interfaces in haskell.

User Guide

Working with source files

We tried to make the user interface as common as possible by following the GNOME human interface guidelines closely. You can open a file by giving the filename at the CLI:

astview .../path/to/mysource.hs
or simply open it via the file menu. The file's extension will determine the parser automaticall. When there are multiple parsers for one extension, the first one will be taken. Launching astview without any files will enable the "lines and words"-parser. Saving works as expected: Ctrl-S saves, Save-As has to be done via the menu. When the file was changed, the usual star appears in the title bar, next to the filename.

Cut-and-Paste functionality works as usual (Ctrl-C/P/X), allowing to copy-paste source code to or from other programs.

Astview uses the same syntax-higlighting sourceview widget as GNOME's standard editor gedit, so any language recognized there will be highlighted by astview. For syntax-highlighting, the language is determined by the name of the parser.

Choosing Parsers

As noted above, the parser is chosen automatically when opening a file. When editing source code, one can change the parser using the parser menu issuing an immediate reparse. Ctrl-P reparses the source at any time.

Adding Custom Parsers

Astview loads the available parsers at runtime using the GHC-API wrapper hint. In this section we show how to add custom parsers.

A parser is described by a 3-tuple

data Parser 
  = Parser { name :: String
           , exts :: [String]
           , tree :: String -> Tree String}

The name> of the parser is shown in the parser menu and is used to determine syntax highlighting. The list of extensions exts is used to determine the parser when opening a file. Finally - the magic bit of the whole tool - the buildTree function constructs a tree of Strings (Data.Tree String) from a haskell value. Each node of this tree denotes a constructor. This tree can be constructed using the data2tree function from the SYB approach to generic programming (TODO: ref), which is delivered with astview. Here is an example:

haskell :: Parser
haskell =  Parser "Haskell" [".hs"] buildTreeHaskell

buildTreeHaskell :: String -> Tree String
buildTreeHaskell s = case parseHaskell s of
     Right ast -> flat $ data2tree (ast::HsModule)
     Left ParseError -> Node "ParseError" []

parseHaskell :: (Data a) => String -> Either ParseError a
parseHaskell s = case parseModule s of
  ParseOk p -> unsafeCoerce $ Right p
  _         -> Left ParseError

You can simply put such a parser into the file

~/.cabal/share/astview-0.2/data/Parsers.hs

which exports a list of all parsers:

parsers :: [Parser]
parsers = haskell:stdParserData

Here, the predefined list of parsers stdParserData is extended with the new haskell-parser.

If your Parser needs additional modules, these modules have either be exposed to GHC's package-management, or have to exist as source-file under the data-Directory of astview. Remember that these modules are linked in at runtime!

To test your parser consider the following ghci-session:

user@host:path$ cd ~/.cabal/share/astview-0.2/data
user@host:~/.cabal/share/astview-0.2/data$ ghci Parsers.hs

-- ghci package-messages stripped

*Parsers> :info Parser
data Parser
  = Parser {name :: !String,
            exts :: [String],
            tree :: String -> Tree String}

-- show all registered parsers
*Parsers> map name parsers                                            
["Haskell","CSV","Expr","Java","IsoPascal","C","Glade","List"]

-- get haskell parser
*Parsers> let haskell = head parsers                                 

-- build a sample tree of strings
*Parsers> let sample = tree haskell "main = putStrLn \"Hello World\""

-- draw the tree
*Parsers> putStrLn $ Data.Tree.drawTree sample

-- lengthy output follows

If drawString works for your sourcecode, astview will too, since ghci uses the parsers in interpreted mode just as astview does.

describe background, especially unsafeCast, describe hin

Developer Notes

Notes for Developers, Short Module descriptions, references to haddock (include haddock !?), code conventions, history, GTK things