]> git.cameronkatri.com Git - mandoc.git/commitdiff
Added more validation (parents/msecs).
authorKristaps Dzonsons <kristaps@bsd.lv>
Fri, 16 Jan 2009 14:04:26 +0000 (14:04 +0000)
committerKristaps Dzonsons <kristaps@bsd.lv>
Fri, 16 Jan 2009 14:04:26 +0000 (14:04 +0000)
Initial function documentation for mdoc.3.

macro.c
mdoc.3 [new file with mode: 0644]
mdoc.c
mdocml.1
mdocml.3 [deleted file]
validate.c

diff --git a/macro.c b/macro.c
index d87e2dd7049b6abd48b136f760231be8a080ac2e..ae7dfec0cdbc75fb2fc301fd2e47f4462a17caa0 100644 (file)
--- a/macro.c
+++ b/macro.c
@@ -1,4 +1,4 @@
-/* $Id: macro.c,v 1.38 2009/01/16 11:50:54 kristaps Exp $ */
+/* $Id: macro.c,v 1.39 2009/01/16 14:04:26 kristaps Exp $ */
 /*
  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
  *
@@ -950,6 +950,8 @@ macro_constant_delimited(MACRO_PROT_ARGS)
                /* FALLTHROUGH */
        case (MDOC_Ns):
                /* FALLTHROUGH */
+       case (MDOC_Pf):
+               /* FALLTHROUGH */
        case (MDOC_Ux):
                /* FALLTHROUGH */
        case (MDOC_St):
@@ -1044,8 +1046,6 @@ macro_constant(MACRO_PROT_ARGS)
        struct mdoc_arg  argv[MDOC_LINEARG_MAX];
        char            *p;
 
-       /* FIXME: parsing macros! */
-
        fl = 0;
        if (MDOC_QUOTABLE & mdoc_macros[tok].flags)
                fl = ARGS_QUOTED;
@@ -1084,6 +1084,15 @@ macro_constant(MACRO_PROT_ARGS)
                if (ARGS_EOLN == c)
                        break;
 
+               if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
+                       return(0);
+               else if (MDOC_MAX != c) {
+                       if ( ! rewind_elem(mdoc, tok))
+                               return(0);
+                       return(mdoc_macro(mdoc, c, line, 
+                                               lastarg, pos, buf));
+               }
+
                if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
                        return(0);
                mdoc->next = MDOC_NEXT_SIBLING;
diff --git a/mdoc.3 b/mdoc.3
new file mode 100644 (file)
index 0000000..0df0188
--- /dev/null
+++ b/mdoc.3
@@ -0,0 +1,56 @@
+.\" 
+.Dd $Mdocdate: January 16 2009 $
+.Dt mdoc 3
+.Os
+.\"
+.Sh NAME
+.Nm mdoc_alloc ,
+.Nm mdoc_parseln ,
+.Nm mdoc_endparse ,
+.Nm mdoc_result ,
+.Nm mdoc_free
+.Nd mdoc macro compiler
+.\"
+.Sh SYNOPSIS
+.In mdoc.h
+.Ft "struct mdoc *"
+.Fn mdoc_alloc "void *data" "const struct mdoc_cb *cb"
+.Ft void
+.Fn mdoc_free "struct mdoc *"
+.Ft int
+.Fn mdoc_parseln "struct mdoc *" "int" "char *buf"
+.Ft "const struct mdoc_node *"
+.Fn mdoc_result "struct mdoc *"
+.Ft int
+.Fn mdoc_endparse "struct mdoc *"
+.\"
+.Sh DESCRIPTION
+The
+.Nm mdoc
+library parses lines of mdoc-macro text into an abstract syntax tree.
+In general, applications initiate a parsing sequence with
+.Fn mdoc_alloc ,
+parse each line in a document with 
+.Fn mdoc_parseln ,
+close the parsing session with
+.Fn mdoc_endparse ,
+operate over the syntax tree returned by
+.Fn mdoc_result ,
+then free all allocated memory with
+.Fn mdoc_free .
+See the
+.Sx EXAMPLES
+section for a full example.
+.\" The following requests should be uncommented and used where appropriate.
+.\" This next request is for sections 2, 3, and 9 function return values only.
+.\" .Sh RETURN VALUES
+.\" .Sh EXAMPLES
+.\" The next request is for sections 2, 3, and 9 error and signal handling only.
+.\" .Sh ERRORS
+.\" .Sh SEE ALSO
+.\" .Xr foobar 1
+.\" .Sh STANDARDS
+.\" .Sh HISTORY
+.\" .Sh AUTHORS
+.\" .Sh CAVEATS
+.\" .Sh BUGS
diff --git a/mdoc.c b/mdoc.c
index 6ac73d0eb0cb833620d559a8c37635c6e3cc57bd..88260b79a9416e7079f2102a83e4d8654342b0e6 100644 (file)
--- a/mdoc.c
+++ b/mdoc.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc.c,v 1.31 2009/01/15 17:38:57 kristaps Exp $ */
+/* $Id: mdoc.c,v 1.32 2009/01/16 14:04:26 kristaps Exp $ */
 /*
  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
  *
@@ -99,7 +99,7 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
        { macro_scoped_close, MDOC_EXPLICIT }, /* El */
        { macro_scoped, MDOC_PARSED | MDOC_TABSEP}, /* It */
        { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */ 
-       { macro_constant, MDOC_PARSED }, /* An */
+       { macro_text, MDOC_PARSED }, /* An */
        { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */
        { macro_constant, MDOC_QUOTABLE }, /* Cd */
        { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */
@@ -111,7 +111,7 @@ const       struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
        { macro_constant, 0 }, /* Fd */ 
        { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */
        { macro_text, MDOC_CALLABLE | MDOC_QUOTABLE | MDOC_PARSED }, /* Fn */ 
-       { macro_text, MDOC_PARSED }, /* Ft */ 
+       { macro_text, MDOC_PARSED | MDOC_QUOTABLE }, /* Ft */ 
        { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */ 
        { macro_constant, 0 }, /* In */ 
        { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Li */
@@ -162,7 +162,7 @@ const       struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
        { macro_constant_delimited, MDOC_PARSED }, /* Nx */
        { macro_constant_delimited, MDOC_PARSED }, /* Ox */
        { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Pc */
-       { macro_constant, MDOC_PARSED }, /* Pf */
+       { macro_constant_delimited, MDOC_PARSED }, /* Pf */
        { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Po */
        { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pq */
        { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Qc */
index 6493e411582b239d1ae038cb5f901af8ecf8e573..a43b19b04cc46f287aa82f0f427fec312871def2 100644 (file)
--- a/mdocml.1
+++ b/mdocml.1
@@ -1,54 +1,41 @@
 .\"
-.Dd $Mdocdate: December 10 2008 $
+.Dd $Mdocdate: January 16 2009 $
 .Dt mdocml 1
 .Os
 .\"
 .Sh NAME
 .Nm mdocml
-.Nd compile manpage source into mark-up language
+.Nd mdoc macro compiler
 .\"
 .Sh SYNOPSIS
 .Nm mdocml
 .Op Fl v
 .Op Fl W Ns Ar err...
-.Op Fl f Ar filter
-.Op Fl o Ar outfile
 .Op Ar infile
 .\"
 .Sh DESCRIPTION
 The
 .Nm
-utility parses mdoc formatted manual source and passes results into an
-output filter.  The current output filters are
-.Fl f Ar html
-and
-.Fl f Ar xml .
-By default, 
-.Nm
-only validates its input. This may be explicitly directed with
-.Fl f Ar noop .  
-Arguments common to all filters follow:
-.Bl -tag -width "\-o outfile"
-.It Fl f Ar filter
-The output filter name.  
-.It Fl o Ar outfile
-Write output to 
-.Ar outfile ,
-which may be
-.Dq \-
-for stdout.  This is meaningless for
-.Fl f Ar noop .
+utility interfaces the
+.Xr mdoc 3
+library to validate and parse mdoc-macro documents.  Arguments follow:
+.Bl -tag -width "\-Werr... "
 .It Fl W Ns Ar err...
-Print warning messages.  If set to 
-.Fl W Ns Ar all ,
-all warnings are printed; if
-.Fl W Ns Ar error ,
-warnings are considered errors and cause utility termination.  Multiple 
+Print warning messages.  May be set to 
+.Fl W Ns Ar all
+for all warnings, 
+.Ar compat
+for groff/troff-compatibility warnings, or
+.Ar syntax
+for syntax warnings.  If
+.Fl W Ns Ar error 
+is specified, warnings are considered errors and cause utility
+termination.  Multiple 
 .Fl W
 arguments may be comma-separated, such as
 .Fl W Ns Ar error,all .
 .It Fl v
-Make warning and error messages verbose.
+Print verbose parsing output.
 .It Ar infile
 Read input from
 .Ar infile ,
@@ -57,90 +44,29 @@ which may be
 for stdin.
 .El
 .Pp
+Parsing and validation rules are drawn entirely from the 
+.Xr mdoc 7
+and
+.Xr mdoc.samples 7 
+manuals.
+.Pp
 By default,
 .Nm
-reads from stdin and writes to stdout.
+reads from stdin, writes messages to stdout, and writes errors and
+warnings to stderr.
 .Pp
 .Ex -std mdocml
-.\"
-.Ss Noop Filter
-The default noop 
-.Dq validating
-filter simply validates its input; it produces no output beyond error
-and warning messages.
-.\"
-.Ss XML Filter
-The XML filter, specified by
-.Fl f Ar xml ,
-produces correctly-formatted XML output.  This filter has no additional
-arguments.
-.Pp
-The XML filter creates an XML document where element names are their respective
-roff macro names.  Each element name has an associated
-namespace, which is one of 
-.Dq block ,
-.Dq head ,
-.Dq body ,
-or
-.Dq inline ,
-corresponding to the display mode of a node.  The document root is
-always the
-.Dq mdoc
-element, in the default namespace; the 
-.Dq head
-namespace is for block headers (such as 
-.Sq .Ss
-and
-.Sq .Sh ) ;
-the
-.Dq body
-namespace is for block bodies; and the
-.Dq inline
-namespace is for in-line elements (such as
-.Sq .Em ) .
-.\"
-.Ss HTML Filter
-The HTML filter, specified by
-.Fl f Ar html ,
-accepts the following filter-specific arguments:
-.Bl -tag -width "\-c css"
-.It Fl c Ar css
-The CSS file location, which defaults to 
-.Ar mdocml.css .
-.It Fl e
-Whether to embed the CSS file into the HTML prologue.
-.El
-.Pp
-By default, the HTML filter produces HTML-4.01 strict mark-up.  The
-default CSS document styles the page as it would appear in a terminal
-window.
 .\" 
 .Sh EXAMPLES
-To produce an HTML4-strict document 
-.Pa mdocml.html
-for
-.Pa mdocml.1 
-with the default, embedded style-sheet:
-.Pp
-.D1 % mdocml -fhtml -e -o mdocml.html mdocml.1 
-.Pp
-To create an XML document on standard output from
-.Pa mdocml.1
-with the default namespace identifiers
-.Li head ,
-.Li body ,
-.Li block 
-and
-.Li inline :
-.Pp
-.D1 % mdocml -Wall,error -fxml mdocml.1 
+To validate this manual page:
 .Pp
-The previous example will also halt on input document warnings.
+.D1 % mdocml \-Wall,error mdocml.1 
 .\"
 .Sh SEE ALSO
 .Xr groff 1 ,
 .Xr mdoc.samples 7 ,
-.Xr mdoc 7
+.Xr mdoc 7 ,
+.Xr mdoc 3
 .\" .Sh STANDARDS
 .\" .Sh HISTORY
 .Sh AUTHORS
@@ -150,34 +76,16 @@ utility was written by
 .An Kristaps Dzonsons Aq kristaps@kth.se .
 .\"
 .Sh CAVEATS
-Most caveats of
+The most glaring short-coming of 
 .Nm
-stem from ambiguities in 
-.Xr mdoc 7
-or the necessary limitations of converting an ad hoc language into
-structured ones:
-.Bl -enum -compact -offset indent
-.It 
-The engine doesn't understand the
-.Sq \&No ,
-.Sq \&Db ,
-.Sq \&Xc ,
+is that it doesn't yet support the 
+.Sq \&Xc
 and
 .Sq \&Xo
-mdoc macros.
-.It 
-All macro arguments may be quoted, instead of only some.
-.It 
-Blank lines raise errors.
-.It 
-If terminating punctuation is found, then 
-.Em all
-remaining tokens are flushed after line scope is closed, not just the
-last one.
-.El
-.Pp
-The roff engine in 
-.Nm
-produces text in-line; thus, output may already be partially written by
-the time an error is encountered.
+macros when used to extend the line arguments to
+.Sq \&It ;
+in effect, trampling the body section.  We note that this is explicitly
+discouraged in
+.Xr mdoc.samples 7 ,
+but in practice used quite often.
 .\" .Sh BUGS
diff --git a/mdocml.3 b/mdocml.3
deleted file mode 100644 (file)
index 2a34ea6..0000000
--- a/mdocml.3
+++ /dev/null
@@ -1 +0,0 @@
-TODO.
index 6a22f63442e85ad23b3f149a4c0fb6e4f3c5ff3f..6e777731ddf9d3064fda67cacec18795b1434723 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: validate.c,v 1.32 2009/01/16 12:23:25 kristaps Exp $ */
+/* $Id: validate.c,v 1.33 2009/01/16 14:04:26 kristaps Exp $ */
 /*
  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
  *
@@ -31,11 +31,20 @@ struct      valids {
        v_post  *post;
 };
 
+static int     pre_check_parent(struct mdoc *, struct mdoc_node *, 
+                       int, enum mdoc_type);
+static int     pre_check_msecs(struct mdoc *, struct mdoc_node *, 
+                       int, enum mdoc_msec *);
 
 static int     pre_display(struct mdoc *, struct mdoc_node *);
+static int     pre_sh(struct mdoc *, struct mdoc_node *);
+static int     pre_ss(struct mdoc *, struct mdoc_node *);
 static int     pre_bd(struct mdoc *, struct mdoc_node *);
 static int     pre_bl(struct mdoc *, struct mdoc_node *);
 static int     pre_it(struct mdoc *, struct mdoc_node *);
+static int     pre_cd(struct mdoc *, struct mdoc_node *);
+static int     pre_er(struct mdoc *, struct mdoc_node *);
+static int     pre_ex(struct mdoc *, struct mdoc_node *);
 static int     pre_prologue(struct mdoc *, struct mdoc_node *);
 static int     pre_prologue(struct mdoc *, struct mdoc_node *);
 static int     pre_prologue(struct mdoc *, struct mdoc_node *);
@@ -58,6 +67,11 @@ static       v_pre   pres_d1[] = { pre_display, NULL };
 static v_pre   pres_bd[] = { pre_display, pre_bd, NULL };
 static v_pre   pres_bl[] = { pre_bl, NULL };
 static v_pre   pres_it[] = { pre_it, NULL };
+static v_pre   pres_ss[] = { pre_ss, NULL };
+static v_pre   pres_sh[] = { pre_sh, NULL };
+static v_pre   pres_cd[] = { pre_cd, NULL };
+static v_pre   pres_er[] = { pre_er, NULL };
+static v_pre   pres_ex[] = { pre_ex, NULL };
 
 static v_post  posts_bd[] = { headchild_err_eq0, bodychild_warn_ge1, NULL };
 static v_post  posts_text[] = { elemchild_err_ge1, NULL };
@@ -79,11 +93,9 @@ const        struct valids mdoc_valids[MDOC_MAX] = {
        { pres_prologue, NULL }, /* Os */
        /* FIXME: preceding Pp. */ 
        /* FIXME: NAME section internal ordering. */
-       /* FIXME: can only be a child of root. */
-       { NULL, posts_sh }, /* Sh */ 
+       { pres_sh, posts_sh }, /* Sh */ 
        /* FIXME: preceding Pp. */
-       /* FIXME: can only be a child of Sh. */
-       { NULL, posts_ss }, /* Ss */ 
+       { pres_ss, posts_ss }, /* Ss */ 
        /* FIXME: proceeding... */
        { NULL, posts_pp }, /* Pp */ 
        { pres_d1, posts_d1 }, /* D1 */
@@ -96,16 +108,15 @@ const      struct valids mdoc_valids[MDOC_MAX] = {
        { NULL, NULL }, /* El */
        { pres_it, posts_it }, /* It */
        { NULL, posts_text }, /* Ad */ 
-       /* FIXME */
+       /* FIXME: argument OR parameters. */
        { NULL, NULL }, /* An */ 
        { NULL, NULL }, /* Ar */
-
-       { NULL, posts_text }, /* Cd */ /* FIXME: section 4 only. */
+       { pres_cd, posts_text }, /* Cd */ 
        { NULL, NULL }, /* Cm */
        { NULL, posts_text }, /* Dv */ 
-       { NULL, posts_text }, /* Er */ /* FIXME: section 2 only. */
+       { pres_er, posts_text }, /* Er */ 
        { NULL, posts_text }, /* Ev */ 
-       { NULL, posts_notext }, /* Ex */ /* FIXME: sections 1,6,8 only. */ /* -std required */
+       { pres_ex, posts_notext }, /* Ex */ /* FIXME: -std required */
        { NULL, posts_text }, /* Fa */ 
        { NULL, NULL }, /* Fd */ /* FIXME: SYNOPSIS section. */
        { NULL, NULL }, /* Fl */
@@ -160,7 +171,7 @@ const       struct valids mdoc_valids[MDOC_MAX] = {
        { NULL, NULL }, /* Nx */
        { NULL, NULL }, /* Ox */
        { NULL, NULL }, /* Pc */
-       { NULL, NULL }, /* Pf */ /* FIXME: 2 or more arguments */
+       { NULL, NULL }, /* Pf */ /* FIXME: 2 or more arguments */ /* First should be text. */
        { NULL, NULL }, /* Po */
        { NULL, posts_wline }, /* Pq */ /* FIXME: ignore following Sh/Ss */
        { NULL, NULL }, /* Qc */
@@ -192,6 +203,37 @@ const      struct valids mdoc_valids[MDOC_MAX] = {
 };
 
 
+static int
+pre_check_msecs(struct mdoc *mdoc, struct mdoc_node *node, 
+               int sz, enum mdoc_msec *msecs)
+{
+       int              i;
+
+       for (i = 0; i < sz; i++)
+               if (msecs[i] == mdoc->meta.msec)
+                       return(1);
+       return(mdoc_nwarn(mdoc, node, WARN_COMPAT,
+                               "macro is not appropriate for this manual section"));
+}
+
+
+static int
+pre_check_parent(struct mdoc *mdoc, struct mdoc_node *node, 
+               int tok, enum mdoc_type type)
+{
+
+       if (type != mdoc->last->parent->type) 
+               return(mdoc_nerr(mdoc, node, "invalid macro parent class %s, expected %s", 
+                                       mdoc_type2a(mdoc->last->parent->type),
+                                       mdoc_type2a(type)));
+       if (MDOC_ROOT != type && tok == mdoc->last->parent->tok)
+               return(mdoc_nerr(mdoc, node, "invalid macro parent `%s', expected `%s'", 
+                                       mdoc_macronames[mdoc->last->parent->tok],
+                                       mdoc_macronames[tok]));
+       return(1);
+}
+
+
 static int
 bodychild_err_eq0(struct mdoc *mdoc)
 {
@@ -420,19 +462,67 @@ pre_bd(struct mdoc *mdoc, struct mdoc_node *node)
 
 
 static int
-pre_it(struct mdoc *mdoc, struct mdoc_node *node)
+pre_ss(struct mdoc *mdoc, struct mdoc_node *node)
 {
 
        if (MDOC_BLOCK != mdoc->last->type)
                return(1);
-       assert(MDOC_It == mdoc->last->tok);
+       assert(MDOC_Sh == mdoc->last->tok);
+       return(pre_check_parent(mdoc, node, MDOC_Sh, MDOC_BODY));
+}
 
-       if (MDOC_BODY != mdoc->last->parent->type) 
-               return(mdoc_nerr(mdoc, node, "invalid macro parent `%s'", mdoc_macronames[mdoc->last->parent->tok]));
-       if (MDOC_Bl != mdoc->last->parent->tok)
-               return(mdoc_nerr(mdoc, node, "invalid macro parent `%s'", mdoc_macronames[mdoc->last->parent->tok]));
 
-       return(1);
+static int
+pre_sh(struct mdoc *mdoc, struct mdoc_node *node)
+{
+
+       if (MDOC_BLOCK != mdoc->last->type)
+               return(1);
+       assert(MDOC_Sh == mdoc->last->tok);
+       return(pre_check_parent(mdoc, node, -1, MDOC_ROOT));
+}
+
+
+static int
+pre_ex(struct mdoc *mdoc, struct mdoc_node *node)
+{
+       enum mdoc_msec   msecs[3];
+
+       msecs[0] = MSEC_1;
+       msecs[1] = MSEC_6;
+       msecs[2] = MSEC_8;
+       return(pre_check_msecs(mdoc, node, 3, msecs));
+}
+
+
+static int
+pre_er(struct mdoc *mdoc, struct mdoc_node *node)
+{
+       enum mdoc_msec   msecs[1];
+
+       msecs[0] = MSEC_2;
+       return(pre_check_msecs(mdoc, node, 1, msecs));
+}
+
+
+static int
+pre_cd(struct mdoc *mdoc, struct mdoc_node *node)
+{
+       enum mdoc_msec   msecs[1];
+
+       msecs[0] = MSEC_4;
+       return(pre_check_msecs(mdoc, node, 1, msecs));
+}
+
+
+static int
+pre_it(struct mdoc *mdoc, struct mdoc_node *node)
+{
+
+       if (MDOC_BLOCK != mdoc->last->type)
+               return(1);
+       assert(MDOC_It == mdoc->last->tok);
+       return(pre_check_parent(mdoc, node, MDOC_Bl, MDOC_BODY));
 }
 
 
@@ -552,31 +642,33 @@ post_it(struct mdoc *mdoc)
        assert(TYPE_NONE != type);
 
        if (TYPE_HEAD == type) {
-               if (NULL == (n = mdoc->last->data.block.head)) {
-                       if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
-                               return(0);
-               } else if (NULL == n->child)
+               n = mdoc->last->data.block.head;
+               assert(n);
+               if (NULL == n->child)
                        if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
                                return(0);
 
-               if (NULL == (n = mdoc->last->data.block.body)) {
-                       if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests body children"))
-                               return(0);
-               } else if (NULL == n->child)
+               n = mdoc->last->data.block.body;
+               assert(n);
+               if (NULL == n->child)
                        if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests body children"))
                                return(0);
 
                return(1);
        }
 
-       if (NULL == (n = mdoc->last->data.block.head)) {
-               if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
-                       return(0);
-       } else if (NULL == n->child)
-               if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
+       assert(TYPE_BODY == type);
+       assert(mdoc->last->data.block.head);
+
+       n = mdoc->last->data.block.head;
+       assert(n);
+       if (n->child)
+               if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests no line parameters"))
                        return(0);
 
-       if ((n = mdoc->last->data.block.body) && n->child)
+       n = mdoc->last->data.block.body;
+       assert(n);
+       if (NULL == n->child)
                if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests body children"))
                        return(0);
 
@@ -686,8 +778,10 @@ mdoc_valid_post(struct mdoc *mdoc)
 
        if (MDOC_TEXT == mdoc->last->type)
                return(1);
-       if (MDOC_ROOT == mdoc->last->type)
+       if (MDOC_ROOT == mdoc->last->type) {
+               /* TODO: make sure prologue is complete. */
                return(1);
+       }
 
        if (NULL == mdoc_valids[mdoc->last->tok].post)
                return(1);