1
0
forked from GitHub/gf-core

the C runtime now supports the same customizations for GraphViz as the Haskell runtime

This commit is contained in:
Krasimir Angelov
2017-08-31 10:58:49 +02:00
parent 675ef4573c
commit eaf9f0c3ac
8 changed files with 189 additions and 64 deletions

View File

@@ -509,21 +509,21 @@ pgfCommands = Map.fromList [
exec = needPGF $ \opts arg env@(pgf, concs) ->
do let es = toExprs arg
let concs = optConcs env opts
{-
let gvOptions=H.GraphvizOptions {H.noLeaves = isOpt "noleaves" opts && not (isOpt "showleaves" opts),
H.noFun = isOpt "nofun" opts || not (isOpt "showfun" opts),
H.noCat = isOpt "nocat" opts && not (isOpt "showcat" opts),
H.nodeFont = valStrOpts "nodefont" "" opts,
H.leafFont = valStrOpts "leaffont" "" opts,
H.nodeColor = valStrOpts "nodecolor" "" opts,
H.leafColor = valStrOpts "leafcolor" "" opts,
H.nodeEdgeStyle = valStrOpts "nodeedgestyle" "solid" opts,
H.leafEdgeStyle = valStrOpts "leafedgestyle" "dashed" opts
}
-}
let gvOptions=graphvizDefaults{noLeaves = isOpt "noleaves" opts && not (isOpt "showleaves" opts),
noFun = isOpt "nofun" opts || not (isOpt "showfun" opts),
noCat = isOpt "nocat" opts && not (isOpt "showcat" opts),
nodeFont = valStrOpts "nodefont" "" opts,
leafFont = valStrOpts "leaffont" "" opts,
nodeColor = valStrOpts "nodecolor" "" opts,
leafColor = valStrOpts "leafcolor" "" opts,
nodeEdgeStyle = valStrOpts "nodeedgestyle" "solid" opts,
leafEdgeStyle = valStrOpts "leafedgestyle" "dashed" opts
}
let grph= if null es || null concs
then []
else graphvizParseTree (snd (head concs)) (cExpr (head es))
else graphvizParseTree (snd (head concs)) gvOptions (cExpr (head es))
if isFlag "view" opts || isFlag "format" opts then do
let file s = "_grph." ++ s
let view = optViewGraph opts
@@ -534,31 +534,27 @@ pgfCommands = Map.fromList [
return void
else return $ fromString grph,
examples = [
mkEx "p -lang=Eng \"John walks\" | vp -- generate a tree and show parse tree as .dot script"--,
-- mkEx "gr | vp -view=\"open\" -- generate a tree and display parse tree on a Mac"
mkEx "p -lang=Eng \"John walks\" | vp -- generate a tree and show parse tree as .dot script",
mkEx "gr | vp -view=\"open\" -- generate a tree and display parse tree on a Mac"
],
options = [
{-
("showcat","show categories in the tree nodes (default)"),
("nocat","don't show categories"),
("showfun","show function names in the tree nodes"),
("nofun","don't show function names (default)"),
("showleaves","show the leaves of the tree (default)"),
("noleaves","don't show the leaves of the tree (i.e., only the abstract tree)")
-}
],
flags = [
("lang","the language to visualize"),
("format","format of the visualization file (default \"png\")"),
("view","program to open the resulting file (default \"open\")")--,
{-
("view","program to open the resulting file (default \"open\")"),
("nodefont","font for tree nodes (default: Times -- graphviz standard font)"),
("leaffont","font for tree leaves (default: nodefont)"),
("nodecolor","color for tree nodes (default: black -- graphviz standard color)"),
("leafcolor","color for tree leaves (default: nodecolor)"),
("nodeedgestyle","edge style between tree nodes (solid/dashed/dotted/bold, default: solid)"),
("leafedgestyle","edge style for links to leaves (solid/dashed/dotted/bold, default: dashed)")
-}
]
}),
@@ -584,9 +580,13 @@ pgfCommands = Map.fromList [
mapM_ putStrLn ss
return void
else do
-- let funs = not (isOpt "nofun" opts)
-- let cats = not (isOpt "nocat" opts)
let grph = unlines (map (graphvizAbstractTree pgf . cExpr) es)
let gvOptions=graphvizDefaults{noFun = isOpt "nofun" opts,
noCat = isOpt "nocat" opts,
nodeFont = valStrOpts "nodefont" "" opts,
nodeColor = valStrOpts "nodecolor" "" opts,
nodeEdgeStyle = valStrOpts "nodeedgestyle" "solid" opts
}
let grph = unlines (map (graphvizAbstractTree pgf gvOptions . cExpr) es)
if isFlag "view" opts || isFlag "format" opts then do
let file s = "_grph." ++ s
let view = optViewGraph opts
@@ -601,14 +601,17 @@ pgfCommands = Map.fromList [
mkEx "p \"hello\" | vt -view=\"open\" -- parse a string and display trees on a Mac"
],
options = [
("api", "show the tree with function names converted to 'mkC' with value cats C")--,
("api", "show the tree with function names converted to 'mkC' with value cats C"),
-- ("mk", "similar to -api, deprecated"),
-- ("nofun","don't show functions but only categories"),
-- ("nocat","don't show categories but only functions")
("nofun","don't show functions but only categories"),
("nocat","don't show categories but only functions")
],
flags = [
("format","format of the visualization file (default \"png\")"),
("view","program to open the resulting file (default \"open\")")
("view","program to open the resulting file (default \"open\")"),
("nodefont","font for tree nodes (default: Times -- graphviz standard font)"),
("nodecolor","color for tree nodes (default: black -- graphviz standard color)"),
("nodeedgestyle","edge style between tree nodes (solid/dashed/dotted/bold, default: solid)")
]
}),

View File

@@ -2,8 +2,12 @@
#include "graphviz.h"
#include "linearizer.h"
PgfGraphvizOptions pgf_default_graphviz_options[1] =
{ {0, 0, 0, 1, NULL, NULL, NULL, NULL, NULL, NULL} } ;
static int
pgf_graphviz_abstract_tree_(PgfExpr expr, int *pid,
pgf_graphviz_abstract_tree_(PgfPGF* pgf, PgfExpr expr, int *pid,
PgfGraphvizOptions* opts,
GuOut* out, GuExn* err)
{
int id = -1;
@@ -15,9 +19,16 @@ pgf_graphviz_abstract_tree_(PgfExpr expr, int *pid,
break;
case PGF_EXPR_APP: {
PgfExprApp* app = ei.data;
id = pgf_graphviz_abstract_tree_(app->fun, pid, out, err);
int arg_id = pgf_graphviz_abstract_tree_(app->arg, pid, out, err);
gu_printf(out, err, "n%d -- n%d [style = \"solid\"]\n", id, arg_id);
id = pgf_graphviz_abstract_tree_(pgf, app->fun, pid, opts, out, err);
int arg_id = pgf_graphviz_abstract_tree_(pgf, app->arg, pid, opts, out, err);
gu_printf(out, err, "n%d -- n%d", id, arg_id);
if (opts->nodeEdgeStyle != NULL && *opts->nodeEdgeStyle && opts->nodeColor != NULL && *opts->nodeColor)
gu_printf(out, err, " [style = \"%s\", color = \"%s\"]", opts->nodeEdgeStyle, opts->nodeColor);
else if (opts->nodeEdgeStyle != NULL && *opts->nodeEdgeStyle)
gu_printf(out, err, " [style = \"%s\"]", opts->nodeEdgeStyle);
else if (opts->nodeColor != NULL && *opts->nodeColor)
gu_printf(out, err, " [color = \"%s\"]", opts->nodeColor);
gu_printf(out, err, "\n", id, arg_id);
break;
}
case PGF_EXPR_LIT: {
@@ -58,9 +69,24 @@ pgf_graphviz_abstract_tree_(PgfExpr expr, int *pid,
case PGF_EXPR_FUN: {
PgfExprFun* fun = ei.data;
id = (*pid)++;
gu_printf(out, err, "n%d[label = \"", id);
gu_string_write(fun->fun, out, err);
gu_puts("\", style = \"solid\", shape = \"plaintext\"]\n", out, err);
if (opts->noFun && opts->noCat) {
gu_printf(out, err, "n%d[shape = \"point\"]\n", id);
} else {
gu_printf(out, err, "n%d[label = \"", id);
PgfType* ty = (opts->noCat) ? NULL : pgf_function_type(pgf, fun->fun);
if (!opts->noFun)
gu_string_write(fun->fun, out, err);
if (!opts->noFun && ty != NULL)
gu_puts(" : ", out,err);
if (ty != NULL)
gu_string_write(ty->cid, out, err);
gu_puts("\", shape = \"plaintext\", style = \"solid\"", out, err);
if (opts->nodeColor != NULL && *opts->nodeColor)
gu_printf(out, err, ", fontcolor = \"%s\"", opts->nodeColor);
if (opts->nodeFont != NULL && *opts->nodeFont)
gu_printf(out, err, ", fontname = \"%s\"", opts->nodeFont);
gu_puts("]\n", out, err);
}
break;
}
case PGF_EXPR_VAR:
@@ -68,12 +94,12 @@ pgf_graphviz_abstract_tree_(PgfExpr expr, int *pid,
break;
case PGF_EXPR_TYPED: {
PgfExprTyped* typed = ei.data;
id = pgf_graphviz_abstract_tree_(typed->expr, pid, out, err);
id = pgf_graphviz_abstract_tree_(pgf, typed->expr, pid, opts, out, err);
break;
}
case PGF_EXPR_IMPL_ARG: {
PgfExprImplArg* implarg = ei.data;
id = pgf_graphviz_abstract_tree_(implarg->expr, pid, out, err);
id = pgf_graphviz_abstract_tree_(pgf, implarg->expr, pid, opts, out, err);
break;
}
default:
@@ -84,12 +110,12 @@ pgf_graphviz_abstract_tree_(PgfExpr expr, int *pid,
}
PGF_API void
pgf_graphviz_abstract_tree(PgfPGF* pgf, PgfExpr expr, GuOut* out, GuExn* err)
pgf_graphviz_abstract_tree(PgfPGF* pgf, PgfExpr expr, PgfGraphvizOptions* opts, GuOut* out, GuExn* err)
{
int id = 0;
gu_puts("graph {\n", out, err);
pgf_graphviz_abstract_tree_(expr, &id, out, err);
pgf_graphviz_abstract_tree_(pgf, expr, &id, opts, out, err);
gu_puts("}", out, err);
}
@@ -98,6 +124,7 @@ typedef struct PgfParseNode PgfParseNode;
struct PgfParseNode {
int id;
PgfParseNode* parent;
GuString fun;
GuString label;
};
@@ -107,7 +134,7 @@ typedef struct {
GuPool* pool;
GuOut* out;
GuExn* err;
PgfParseNode* parent;
size_t level;
GuBuf* internals;
@@ -122,6 +149,7 @@ pgf_bracket_lzn_symbol_token(PgfLinFuncs** funcs, PgfToken tok)
PgfParseNode* node = gu_new(PgfParseNode, state->pool);
node->id = 100000 + gu_buf_length(state->leaves);
node->parent = state->parent;
node->fun = NULL;
node->label = tok;
gu_buf_push(state->leaves, PgfParseNode*, node);
}
@@ -156,6 +184,7 @@ pgf_bracket_lzn_begin_phrase(PgfLinFuncs** funcs, PgfCId cat, int fid, int linde
PgfParseNode* node = gu_new(PgfParseNode, state->pool);
node->id = fid;
node->parent = state->parent;
node->fun = fun;
node->label = cat;
gu_buf_push(level, PgfParseNode*, node);
@@ -182,6 +211,7 @@ pgf_bracket_lzn_symbol_meta(PgfLinFuncs** funcs, PgfMetaId meta_id)
PgfParseNode* node = gu_new(PgfParseNode, state->pool);
node->id = 100000 + gu_buf_length(state->leaves);
node->parent = state->parent;
node->fun = NULL;
node->label = "?";
gu_buf_push(state->leaves, PgfParseNode*, node);
}
@@ -197,7 +227,7 @@ static PgfLinFuncs pgf_bracket_lin_funcs = {
};
static void
pgf_graphviz_parse_level(GuBuf* level, GuOut* out, GuExn* err)
pgf_graphviz_parse_level(GuBuf* level, PgfGraphvizOptions* opts, GuOut* out, GuExn* err)
{
gu_puts("\n subgraph {rank=same;\n", out, err);
@@ -208,9 +238,32 @@ pgf_graphviz_parse_level(GuBuf* level, GuOut* out, GuExn* err)
for (size_t i = 0; i < len; i++) {
PgfParseNode* node = gu_buf_get(level, PgfParseNode*, i);
gu_printf(out, err, " n%d[label=\"", node->id);
gu_string_write(node->label, out, err);
gu_puts("\"]\n", out, err);
if (node->fun != NULL) {
gu_printf(out, err, " n%d[label=\"", node->id);
if (!opts->noFun)
gu_string_write(node->fun, out, err);
if (!opts->noFun && !opts->noCat)
gu_puts(" : ", out, err);
if (!opts->noCat)
gu_string_write(node->label, out, err);
gu_puts("\"", out, err);
if (opts->nodeColor != NULL && *opts->nodeColor)
gu_printf(out, err, ", fontcolor = \"%s\"", opts->nodeColor);
if (opts->nodeFont != NULL && *opts->nodeFont)
gu_printf(out, err, ", fontname = \"%s\"", opts->nodeFont);
gu_puts("]\n", out, err);
} else {
if (opts->noLeaves)
gu_printf(out, err, " n%d[label=\"\"]\n", node->id);
else {
gu_printf(out, err, " n%d[label=\"%s\"", node->id, node->label);
if (opts->leafColor != NULL && *opts->leafColor)
gu_printf(out, err, ", fontcolor = \"%s\"", opts->leafColor);
if (opts->leafFont != NULL && *opts->leafFont)
gu_printf(out, err, ", fontname = \"%s\"", opts->leafFont);
gu_puts("]\n", out, err);
}
}
}
if (len > 1) {
@@ -227,13 +280,32 @@ pgf_graphviz_parse_level(GuBuf* level, GuOut* out, GuExn* err)
for (size_t i = 0; i < len; i++) {
PgfParseNode* node = gu_buf_get(level, PgfParseNode*, i);
if (node->parent != NULL)
gu_printf(out, err, " n%d -- n%d\n", node->parent->id, node->id);
if (node->parent != NULL) {
gu_printf(out, err, " n%d -- n%d", node->parent->id, node->id);
GuString edgeStyle, color;
if (node->fun == NULL) {
edgeStyle = opts->leafEdgeStyle;
color = opts->leafColor;
} else {
edgeStyle = opts->nodeEdgeStyle;
color = opts->nodeColor;
}
if (edgeStyle != NULL && *edgeStyle && color != NULL && *color)
gu_printf(out, err, " [style = \"%s\", color = \"%s\"]", edgeStyle, color);
else if (edgeStyle != NULL && *edgeStyle)
gu_printf(out, err, " [style = \"%s\"]", edgeStyle);
else if (color != NULL && *color)
gu_printf(out, err, " [color = \"%s\"]", color);
gu_putc('\n', out, err);
}
}
}
PGF_API void
pgf_graphviz_parse_tree(PgfConcr* concr, PgfExpr expr, GuOut* out, GuExn* err)
pgf_graphviz_parse_tree(PgfConcr* concr, PgfExpr expr, PgfGraphvizOptions* opts, GuOut* out, GuExn* err)
{
GuPool* tmp_pool = gu_local_pool();
@@ -266,9 +338,9 @@ pgf_graphviz_parse_tree(PgfConcr* concr, PgfExpr expr, GuOut* out, GuExn* err)
size_t len = gu_buf_length(state.internals);
for (size_t i = 0; i < len; i++) {
GuBuf* level = gu_buf_get(state.internals, GuBuf*, i);
pgf_graphviz_parse_level(level, out, err);
pgf_graphviz_parse_level(level, opts, out, err);
}
pgf_graphviz_parse_level(state.leaves, out, err);
pgf_graphviz_parse_level(state.leaves, opts, out, err);
gu_puts("}", out, err);

View File

@@ -1,10 +1,25 @@
#ifndef PGF_GRAPHVIZ_H_
#define PGF_GRAPHVIZ_H_
PGF_API_DECL void
pgf_graphviz_abstract_tree(PgfPGF* pgf, PgfExpr expr, GuOut* out, GuExn* err);
typedef struct {
int noLeaves;
int noFun;
int noCat;
int noDep;
GuString nodeFont;
GuString leafFont;
GuString nodeColor;
GuString leafColor;
GuString nodeEdgeStyle;
GuString leafEdgeStyle;
} PgfGraphvizOptions;
extern PgfGraphvizOptions pgf_default_graphviz_options[1];
PGF_API_DECL void
pgf_graphviz_parse_tree(PgfConcr* concr, PgfExpr expr, GuOut* out, GuExn* err);
pgf_graphviz_abstract_tree(PgfPGF* pgf, PgfExpr expr, PgfGraphvizOptions* opts, GuOut* out, GuExn* err);
PGF_API_DECL void
pgf_graphviz_parse_tree(PgfConcr* concr, PgfExpr expr, PgfGraphvizOptions* opts, GuOut* out, GuExn* err);
#endif

View File

@@ -66,7 +66,8 @@ module PGF2 (-- * PGF
-- ** Morphological Analysis
MorphoAnalysis, lookupMorpho, fullFormLexicon,
-- ** Visualizations
graphvizAbstractTree,graphvizParseTree,
GraphvizOptions(..), graphvizDefaults,
graphvizAbstractTree, graphvizParseTree,
-- * Exceptions
PGFError(..),
@@ -316,30 +317,61 @@ compute (PGF p _) (Expr c_expr touch1) =
-----------------------------------------------------------------------------
-- Graphviz
data GraphvizOptions = GraphvizOptions {noLeaves :: Bool,
noFun :: Bool,
noCat :: Bool,
noDep :: Bool,
nodeFont :: String,
leafFont :: String,
nodeColor :: String,
leafColor :: String,
nodeEdgeStyle :: String,
leafEdgeStyle :: String
}
graphvizDefaults = GraphvizOptions False False False True "" "" "" "" "" ""
-- | Renders an abstract syntax tree in a Graphviz format.
graphvizAbstractTree :: PGF -> Expr -> String
graphvizAbstractTree p e =
graphvizAbstractTree :: PGF -> GraphvizOptions -> Expr -> String
graphvizAbstractTree p opts e =
unsafePerformIO $
withGuPool $ \tmpPl ->
do (sb,out) <- newOut tmpPl
exn <- gu_new_exn tmpPl
pgf_graphviz_abstract_tree (pgf p) (expr e) out exn
c_opts <- newGraphvizOptions tmpPl opts
pgf_graphviz_abstract_tree (pgf p) (expr e) c_opts out exn
touchExpr e
s <- gu_string_buf_freeze sb tmpPl
peekUtf8CString s
graphvizParseTree :: Concr -> Expr -> String
graphvizParseTree c e =
graphvizParseTree :: Concr -> GraphvizOptions -> Expr -> String
graphvizParseTree c opts e =
unsafePerformIO $
withGuPool $ \tmpPl ->
do (sb,out) <- newOut tmpPl
exn <- gu_new_exn tmpPl
pgf_graphviz_parse_tree (concr c) (expr e) out exn
c_opts <- newGraphvizOptions tmpPl opts
pgf_graphviz_parse_tree (concr c) (expr e) c_opts out exn
touchExpr e
s <- gu_string_buf_freeze sb tmpPl
peekUtf8CString s
newGraphvizOptions :: Ptr GuPool -> GraphvizOptions -> IO (Ptr PgfGraphvizOptions)
newGraphvizOptions pool opts = do
c_opts <- gu_malloc pool (#size PgfGraphvizOptions)
(#poke PgfGraphvizOptions, noLeaves) c_opts (if noLeaves opts then 1 else 0 :: CInt)
(#poke PgfGraphvizOptions, noFun) c_opts (if noFun opts then 1 else 0 :: CInt)
(#poke PgfGraphvizOptions, noCat) c_opts (if noCat opts then 1 else 0 :: CInt)
(#poke PgfGraphvizOptions, noDep) c_opts (if noDep opts then 1 else 0 :: CInt)
newUtf8CString (nodeFont opts) pool >>= (#poke PgfGraphvizOptions, nodeFont) c_opts
newUtf8CString (leafFont opts) pool >>= (#poke PgfGraphvizOptions, leafFont) c_opts
newUtf8CString (nodeColor opts) pool >>= (#poke PgfGraphvizOptions, nodeColor) c_opts
newUtf8CString (leafColor opts) pool >>= (#poke PgfGraphvizOptions, leafColor) c_opts
newUtf8CString (nodeEdgeStyle opts) pool >>= (#poke PgfGraphvizOptions, nodeEdgeStyle) c_opts
newUtf8CString (leafEdgeStyle opts) pool >>= (#poke PgfGraphvizOptions, leafEdgeStyle) c_opts
return c_opts
-----------------------------------------------------------------------------
-- Functions using Concr
-- Morpho analyses, parsing & linearization

View File

@@ -148,6 +148,7 @@ data PgfCallbacksMap
data PgfOracleCallback
data PgfCncTree
data PgfLinFuncs
data PgfGraphvizOptions
foreign import ccall "pgf/pgf.h pgf_read"
pgf_read :: CString -> Ptr GuPool -> Ptr GuExn -> IO (Ptr PgfPGF)
@@ -364,7 +365,7 @@ foreign import ccall "pgf/expr.h pgf_read_type"
pgf_read_type :: Ptr GuIn -> Ptr GuPool -> Ptr GuExn -> IO PgfType
foreign import ccall "pgf/graphviz.h pgf_graphviz_abstract_tree"
pgf_graphviz_abstract_tree :: Ptr PgfPGF -> PgfExpr -> Ptr GuOut -> Ptr GuExn -> IO ()
pgf_graphviz_abstract_tree :: Ptr PgfPGF -> PgfExpr -> Ptr PgfGraphvizOptions -> Ptr GuOut -> Ptr GuExn -> IO ()
foreign import ccall "pgf/graphviz.h pgf_graphviz_parse_tree"
pgf_graphviz_parse_tree :: Ptr PgfConcr -> PgfExpr -> Ptr GuOut -> Ptr GuExn -> IO ()
pgf_graphviz_parse_tree :: Ptr PgfConcr -> PgfExpr -> Ptr PgfGraphvizOptions -> Ptr GuOut -> Ptr GuExn -> IO ()

View File

@@ -382,6 +382,7 @@ Java_org_grammaticalframework_pgf_PGF_graphvizAbstractTree(JNIEnv* env, jobject
pgf_graphviz_abstract_tree(get_ref(env,self),
gu_variant_from_ptr(l2p(get_ref(env,jexpr))),
pgf_default_graphviz_options,
out, err);
jstring jstr = gu2j_string_buf(env, sbuf);
@@ -1228,6 +1229,7 @@ Java_org_grammaticalframework_pgf_Concr_graphvizParseTree(JNIEnv* env, jobject s
pgf_graphviz_parse_tree(get_ref(env,self),
gu_variant_from_ptr(l2p(get_ref(env,jexpr))),
pgf_default_graphviz_options,
out, err);
jstring jstr = gu2j_string_buf(env, sbuf);

View File

@@ -2281,7 +2281,7 @@ Concr_graphvizParseTree(ConcrObject* self, PyObject *args) {
GuStringBuf* sbuf = gu_new_string_buf(tmp_pool);
GuOut* out = gu_string_buf_out(sbuf);
pgf_graphviz_parse_tree(self->concr, pyexpr->expr, out, err);
pgf_graphviz_parse_tree(self->concr, pyexpr->expr, pgf_default_graphviz_options, out, err);
if (!gu_ok(err)) {
if (gu_exn_caught(err, PgfExn)) {
GuString msg = (GuString) gu_exn_caught_data(err);
@@ -3075,7 +3075,7 @@ PGF_graphvizAbstractTree(PGFObject* self, PyObject *args) {
GuStringBuf* sbuf = gu_new_string_buf(tmp_pool);
GuOut* out = gu_string_buf_out(sbuf);
pgf_graphviz_abstract_tree(self->pgf, pyexpr->expr, out, err);
pgf_graphviz_abstract_tree(self->pgf, pyexpr->expr, pgf_default_graphviz_options, out, err);
if (!gu_ok(err)) {
PyErr_SetString(PGFError, "The abstract tree cannot be visualized");
return NULL;

View File

@@ -159,8 +159,8 @@ cpgfMain qsem command (t,(pgf,pc)) =
"c-lookupmorpho"-> out t=<< morpho # from1 % textInput
"c-flush" -> out t=<< flush
"c-grammar" -> out t grammar
"c-abstrtree" -> outputGraphviz=<< C.graphvizAbstractTree pgf # tree
"c-parsetree" -> outputGraphviz=<< C.graphvizParseTree . snd # from1 %tree
"c-abstrtree" -> outputGraphviz=<< C.graphvizAbstractTree pgf C.graphvizDefaults # tree
"c-parsetree" -> outputGraphviz=<< (\cnc -> C.graphvizParseTree cnc C.graphvizDefaults) . snd # from1 %tree
"c-wordforword" -> out t =<< wordforword # input % to
_ -> badRequest "Unknown command" command
where