]>
git.cameronkatri.com Git - mandoc.git/blob - macro.c
1 /* $Id: macro.c,v 1.43 2009/01/19 17:51:32 kristaps Exp $ */
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
30 /* FIXME: maxlineargs should be per LINE, no per TOKEN. */
32 static int rewind_alt(int);
33 static int rewind_dohalt(int, enum mdoc_type
,
34 const struct mdoc_node
*);
35 #define REWIND_REWIND (1 << 0)
36 #define REWIND_NOHALT (1 << 1)
37 #define REWIND_HALT (1 << 2)
38 static int rewind_dobreak(int, const struct mdoc_node
*);
41 static int rewind_elem(struct mdoc
*, int);
42 static int rewind_impblock(struct mdoc
*, int, int, int);
43 static int rewind_expblock(struct mdoc
*, int, int, int);
44 static int rewind_subblock(enum mdoc_type
,
45 struct mdoc
*, int, int, int);
46 static int rewind_last(struct mdoc
*, struct mdoc_node
*);
47 static int append_delims(struct mdoc
*, int, int *, char *);
48 static int lookup(struct mdoc
*, int, int, int, const char *);
52 lookup(struct mdoc
*mdoc
, int line
, int pos
, int from
, const char *p
)
56 res
= mdoc_find(mdoc
, p
);
57 if (MDOC_PARSED
& mdoc_macros
[from
].flags
)
61 if ( ! mdoc_pwarn(mdoc
, line
, pos
, WARN_SYNTAX
, "macro-like parameter"))
68 rewind_last(struct mdoc
*mdoc
, struct mdoc_node
*to
)
72 mdoc
->next
= MDOC_NEXT_SIBLING
;
74 while (mdoc
->last
!= to
) {
75 if ( ! mdoc_valid_post(mdoc
))
77 if ( ! mdoc_action_post(mdoc
))
79 mdoc
->last
= mdoc
->last
->parent
;
83 if ( ! mdoc_valid_post(mdoc
))
85 return(mdoc_action_post(mdoc
));
132 rewind_dohalt(int tok
, enum mdoc_type type
, const struct mdoc_node
*p
)
135 if (MDOC_ROOT
== p
->type
)
137 if (MDOC_VALID
& p
->flags
)
138 return(REWIND_NOHALT
);
141 /* One-liner implicit-scope. */
161 assert(MDOC_BODY
!= type
);
162 assert(MDOC_TAIL
!= type
);
163 if (type
== p
->type
&& tok
== p
->tok
)
164 return(REWIND_REWIND
);
167 /* Multi-line implicit-scope. */
169 assert(MDOC_TAIL
!= type
);
170 if (type
== p
->type
&& tok
== p
->tok
)
171 return(REWIND_REWIND
);
172 if (MDOC_BODY
== p
->type
&& MDOC_Bl
== p
->tok
)
176 if (type
== p
->type
&& tok
== p
->tok
)
177 return(REWIND_REWIND
);
180 assert(MDOC_TAIL
!= type
);
181 if (type
== p
->type
&& tok
== p
->tok
)
182 return(REWIND_REWIND
);
183 if (MDOC_BODY
== p
->type
&& MDOC_Sh
== p
->tok
)
187 /* Multi-line explicit scope start. */
217 if (type
== p
->type
&& tok
== p
->tok
)
218 return(REWIND_REWIND
);
221 /* Multi-line explicit scope close. */
251 if (type
== p
->type
&& rewind_alt(tok
) == p
->tok
)
252 return(REWIND_REWIND
);
259 return(REWIND_NOHALT
);
264 rewind_dobreak(int tok
, const struct mdoc_node
*p
)
267 assert(MDOC_ROOT
!= p
->type
);
268 if (MDOC_ELEM
== p
->type
)
270 if (MDOC_TEXT
== p
->type
)
272 if (MDOC_VALID
& p
->flags
)
276 /* Implicit rules. */
278 return(MDOC_It
== p
->tok
);
280 return(MDOC_Ss
== p
->tok
);
282 if (MDOC_Ss
== p
->tok
)
284 return(MDOC_Sh
== p
->tok
);
286 /* Extra scope rules. */
288 if (MDOC_It
== p
->tok
)
295 if (MDOC_EXPLICIT
& mdoc_macros
[tok
].flags
)
296 return(p
->tok
== rewind_alt(tok
));
297 else if (MDOC_BLOCK
== p
->type
)
300 return(tok
== p
->tok
);
305 rewind_elem(struct mdoc
*mdoc
, int tok
)
310 if (MDOC_ELEM
!= n
->type
)
312 assert(MDOC_ELEM
== n
->type
);
313 assert(tok
== n
->tok
);
315 return(rewind_last(mdoc
, n
));
320 rewind_subblock(enum mdoc_type type
, struct mdoc
*mdoc
,
321 int tok
, int line
, int ppos
)
327 for (n
= mdoc
->last
; n
; n
= n
->parent
) {
328 c
= rewind_dohalt(tok
, type
, n
);
329 if (REWIND_HALT
== c
)
331 if (REWIND_REWIND
== c
)
333 else if (rewind_dobreak(tok
, n
))
335 return(mdoc_perr(mdoc
, line
, ppos
, "scope breaks prior %s", mdoc_node2a(n
)));
339 return(rewind_last(mdoc
, n
));
344 rewind_expblock(struct mdoc
*mdoc
, int tok
, int line
, int ppos
)
350 for (n
= mdoc
->last
; n
; n
= n
->parent
) {
351 c
= rewind_dohalt(tok
, MDOC_BLOCK
, n
);
352 if (REWIND_HALT
== c
)
353 return(mdoc_perr(mdoc
, line
, ppos
, "closing macro has no context"));
354 if (REWIND_REWIND
== c
)
356 else if (rewind_dobreak(tok
, n
))
358 return(mdoc_perr(mdoc
, line
, ppos
, "scope breaks prior %s", mdoc_node2a(n
)));
362 return(rewind_last(mdoc
, n
));
367 rewind_impblock(struct mdoc
*mdoc
, int tok
, int line
, int ppos
)
373 for (n
= mdoc
->last
; n
; n
= n
->parent
) {
374 c
= rewind_dohalt(tok
, MDOC_BLOCK
, n
);
375 if (REWIND_HALT
== c
)
377 else if (REWIND_REWIND
== c
)
379 else if (rewind_dobreak(tok
, n
))
381 return(mdoc_perr(mdoc
, line
, ppos
, "scope breaks prior %s", mdoc_node2a(n
)));
385 return(rewind_last(mdoc
, n
));
390 append_delims(struct mdoc
*mdoc
, int line
, int *pos
, char *buf
)
400 c
= mdoc_args(mdoc
, line
, pos
, buf
, 0, &p
);
403 else if (ARGS_EOLN
== c
)
405 assert(mdoc_isdelim(p
));
406 if ( ! mdoc_word_alloc(mdoc
, line
, lastarg
, p
))
408 mdoc
->next
= MDOC_NEXT_SIBLING
;
416 macro_scoped_close(MACRO_PROT_ARGS
)
418 int tt
, j
, c
, lastarg
, maxargs
, flushed
;
430 tt
= rewind_alt(tok
);
432 mdoc_msg(mdoc
, "parse: %s closing %s",
433 mdoc_macronames
[tok
], mdoc_macronames
[tt
]);
435 if ( ! (MDOC_CALLABLE
& mdoc_macros
[tok
].flags
)) {
436 if (0 == buf
[*pos
]) {
437 if ( ! rewind_subblock(MDOC_BODY
, mdoc
, tok
, line
, ppos
))
439 return(rewind_expblock(mdoc
, tok
, line
, ppos
));
441 return(mdoc_perr(mdoc
, line
, ppos
, "macro expects no parameters"));
444 if ( ! rewind_subblock(MDOC_BODY
, mdoc
, tok
, line
, ppos
))
451 if ( ! mdoc_tail_alloc(mdoc
, line
, ppos
, tt
))
453 mdoc
->next
= MDOC_NEXT_CHILD
;
456 for (j
= 0; /* No sentinel. */; j
++) {
459 if (j
== maxargs
&& ! flushed
) {
460 if ( ! rewind_expblock(mdoc
, tok
, line
, ppos
))
465 c
= mdoc_args(mdoc
, line
, pos
, buf
, ARGS_DELIM
, &p
);
473 if (-1 == (c
= lookup(mdoc
, line
, lastarg
, tok
, p
)))
475 else if (MDOC_MAX
!= c
) {
477 if ( ! rewind_expblock(mdoc
, tok
, line
, ppos
))
481 if ( ! mdoc_macro(mdoc
, c
, line
, lastarg
, pos
, buf
))
486 if ( ! mdoc_word_alloc(mdoc
, line
, lastarg
, p
))
488 mdoc
->next
= MDOC_NEXT_SIBLING
;
491 if ( ! flushed
&& ! rewind_expblock(mdoc
, tok
, line
, ppos
))
496 return(append_delims(mdoc
, line
, pos
, buf
));
501 macro_text(MACRO_PROT_ARGS
)
503 int la
, lastpunct
, c
, fl
, argc
;
504 struct mdoc_arg argv
[MDOC_LINEARG_MAX
];
510 for (argc
= 0; argc
< MDOC_LINEARG_MAX
; argc
++) {
512 c
= mdoc_argv(mdoc
, line
, tok
, &argv
[argc
], pos
, buf
);
515 if (ARGV_WORD
== c
) {
518 } else if (ARGV_ARG
== c
)
521 mdoc_argv_free(argc
, argv
);
525 if (MDOC_LINEARG_MAX
== argc
) {
526 mdoc_argv_free(argc
- 1, argv
);
527 return(mdoc_perr(mdoc
, line
, ppos
, "parameter hard-limit exceeded"));
530 c
= mdoc_elem_alloc(mdoc
, line
, ppos
, tok
, argc
, argv
);
533 mdoc_argv_free(argc
, argv
);
537 mdoc
->next
= MDOC_NEXT_CHILD
;
540 if (MDOC_QUOTABLE
& mdoc_macros
[tok
].flags
)
546 c
= mdoc_args(mdoc
, line
, pos
, buf
, fl
, &p
);
547 if (ARGS_ERROR
== c
) {
548 mdoc_argv_free(argc
, argv
);
557 if (-1 == (c
= lookup(mdoc
, line
, la
, tok
, p
)))
559 else if (MDOC_MAX
!= c
) {
560 if (0 == lastpunct
&& ! rewind_elem(mdoc
, tok
)) {
561 mdoc_argv_free(argc
, argv
);
564 mdoc_argv_free(argc
, argv
);
566 c
= mdoc_macro(mdoc
, c
, line
, la
, pos
, buf
);
571 return(append_delims(mdoc
, line
, pos
, buf
));
574 if (mdoc_isdelim(p
)) {
575 if (0 == lastpunct
&& ! rewind_elem(mdoc
, tok
)) {
576 mdoc_argv_free(argc
, argv
);
580 } else if (lastpunct
) {
581 c
= mdoc_elem_alloc(mdoc
, line
,
582 ppos
, tok
, argc
, argv
);
584 mdoc_argv_free(argc
, argv
);
587 mdoc
->next
= MDOC_NEXT_CHILD
;
591 if ( ! mdoc_word_alloc(mdoc
, line
, la
, p
))
593 mdoc
->next
= MDOC_NEXT_SIBLING
;
596 mdoc_argv_free(argc
, argv
);
598 if (0 == lastpunct
&& ! rewind_elem(mdoc
, tok
))
602 return(append_delims(mdoc
, line
, pos
, buf
));
607 macro_scoped(MACRO_PROT_ARGS
)
609 int c
, lastarg
, argc
, fl
;
610 struct mdoc_arg argv
[MDOC_LINEARG_MAX
];
613 assert ( ! (MDOC_CALLABLE
& mdoc_macros
[tok
].flags
));
615 if ( ! (MDOC_EXPLICIT
& mdoc_macros
[tok
].flags
)) {
616 if ( ! rewind_subblock(MDOC_BODY
, mdoc
, tok
, line
, ppos
))
618 if ( ! rewind_impblock(mdoc
, tok
, line
, ppos
))
622 for (argc
= 0; argc
< MDOC_LINEARG_MAX
; argc
++) {
624 c
= mdoc_argv(mdoc
, line
, tok
, &argv
[argc
], pos
, buf
);
627 if (ARGV_WORD
== c
) {
630 } else if (ARGV_ARG
== c
)
632 mdoc_argv_free(argc
, argv
);
636 if (MDOC_LINEARG_MAX
== argc
) {
637 mdoc_argv_free(argc
- 1, argv
);
638 return(mdoc_perr(mdoc
, line
, ppos
, "parameter hard-limit exceeded"));
641 c
= mdoc_block_alloc(mdoc
, line
, ppos
,
642 tok
, (size_t)argc
, argv
);
643 mdoc_argv_free(argc
, argv
);
648 mdoc
->next
= MDOC_NEXT_CHILD
;
650 if (0 == buf
[*pos
]) {
651 if ( ! mdoc_head_alloc(mdoc
, line
, ppos
, tok
))
653 if ( ! rewind_subblock(MDOC_HEAD
, mdoc
, tok
, line
, ppos
))
655 if ( ! mdoc_body_alloc(mdoc
, line
, ppos
, tok
))
657 mdoc
->next
= MDOC_NEXT_CHILD
;
661 if ( ! mdoc_head_alloc(mdoc
, line
, ppos
, tok
))
663 mdoc
->next
= MDOC_NEXT_CHILD
;
666 if (MDOC_TABSEP
& mdoc_macros
[tok
].flags
)
671 c
= mdoc_args(mdoc
, line
, pos
, buf
, fl
, &p
);
680 if (-1 == (c
= lookup(mdoc
, line
, lastarg
, tok
, p
)))
682 else if (MDOC_MAX
== c
) {
683 if ( ! mdoc_word_alloc(mdoc
, line
, lastarg
, p
))
685 mdoc
->next
= MDOC_NEXT_SIBLING
;
689 if ( ! mdoc_macro(mdoc
, c
, line
, lastarg
, pos
, buf
))
694 if ( ! rewind_subblock(MDOC_HEAD
, mdoc
, tok
, line
, ppos
))
696 if (1 == ppos
&& ! append_delims(mdoc
, line
, pos
, buf
))
699 if ( ! mdoc_body_alloc(mdoc
, line
, ppos
, tok
))
701 mdoc
->next
= MDOC_NEXT_CHILD
;
708 macro_scoped_line(MACRO_PROT_ARGS
)
713 if ( ! mdoc_block_alloc(mdoc
, line
, ppos
, tok
, 0, NULL
))
715 mdoc
->next
= MDOC_NEXT_CHILD
;
717 if ( ! mdoc_head_alloc(mdoc
, line
, ppos
, tok
))
719 mdoc
->next
= MDOC_NEXT_CHILD
;
721 /* XXX - no known argument macros. */
726 c
= mdoc_args(mdoc
, line
, pos
, buf
, ARGS_DELIM
, &p
);
735 if (-1 == (c
= lookup(mdoc
, line
, lastarg
, tok
, p
)))
737 else if (MDOC_MAX
== c
) {
738 if ( ! mdoc_word_alloc(mdoc
, line
, lastarg
, p
))
740 mdoc
->next
= MDOC_NEXT_SIBLING
;
744 if ( ! mdoc_macro(mdoc
, c
, line
, lastarg
, pos
, buf
))
750 if ( ! rewind_subblock(MDOC_HEAD
, mdoc
, tok
, line
, ppos
))
752 if ( ! append_delims(mdoc
, line
, pos
, buf
))
754 } else if ( ! rewind_subblock(MDOC_HEAD
, mdoc
, tok
, line
, ppos
))
756 return(rewind_impblock(mdoc
, tok
, line
, ppos
));
761 macro_constant_scoped(MACRO_PROT_ARGS
)
763 int lastarg
, flushed
, j
, c
, maxargs
;
778 if ( ! mdoc_block_alloc(mdoc
, line
, ppos
, tok
, 0, NULL
))
780 mdoc
->next
= MDOC_NEXT_CHILD
;
783 if ( ! mdoc_head_alloc(mdoc
, line
, ppos
, tok
))
785 if ( ! rewind_subblock(MDOC_HEAD
, mdoc
, tok
, line
, ppos
))
787 if ( ! mdoc_body_alloc(mdoc
, line
, ppos
, tok
))
790 } else if ( ! mdoc_head_alloc(mdoc
, line
, ppos
, tok
))
793 mdoc
->next
= MDOC_NEXT_CHILD
;
795 for (j
= 0; /* No sentinel. */; j
++) {
798 if (j
== maxargs
&& ! flushed
) {
799 if ( ! rewind_subblock(MDOC_HEAD
, mdoc
, tok
, line
, ppos
))
802 if ( ! mdoc_body_alloc(mdoc
, line
, ppos
, tok
))
804 mdoc
->next
= MDOC_NEXT_CHILD
;
807 c
= mdoc_args(mdoc
, line
, pos
, buf
, ARGS_DELIM
, &p
);
815 if (-1 == (c
= lookup(mdoc
, line
, lastarg
, tok
, p
)))
817 else if (MDOC_MAX
!= c
) {
819 if ( ! rewind_subblock(MDOC_HEAD
, mdoc
, tok
, line
, ppos
))
822 if ( ! mdoc_body_alloc(mdoc
, line
, ppos
, tok
))
824 mdoc
->next
= MDOC_NEXT_CHILD
;
826 if ( ! mdoc_macro(mdoc
, c
, line
, lastarg
, pos
, buf
))
831 if ( ! flushed
&& mdoc_isdelim(p
)) {
832 if ( ! rewind_subblock(MDOC_HEAD
, mdoc
, tok
, line
, ppos
))
835 if ( ! mdoc_body_alloc(mdoc
, line
, ppos
, tok
))
837 mdoc
->next
= MDOC_NEXT_CHILD
;
840 if ( ! mdoc_word_alloc(mdoc
, line
, lastarg
, p
))
842 mdoc
->next
= MDOC_NEXT_SIBLING
;
846 if ( ! rewind_subblock(MDOC_HEAD
, mdoc
, tok
, line
, ppos
))
848 if ( ! mdoc_body_alloc(mdoc
, line
, ppos
, tok
))
850 mdoc
->next
= MDOC_NEXT_CHILD
;
855 return(append_delims(mdoc
, line
, pos
, buf
));
860 macro_constant_delimited(MACRO_PROT_ARGS
)
862 int lastarg
, flushed
, j
, c
, maxargs
, argc
;
863 struct mdoc_arg argv
[MDOC_LINEARG_MAX
];
886 for (argc
= 0; argc
< MDOC_LINEARG_MAX
; argc
++) {
888 c
= mdoc_argv(mdoc
, line
, tok
, &argv
[argc
], pos
, buf
);
891 if (ARGV_WORD
== c
) {
894 } else if (ARGV_ARG
== c
)
896 mdoc_argv_free(argc
, argv
);
900 if (MDOC_LINEARG_MAX
== argc
) {
901 mdoc_argv_free(argc
- 1, argv
);
902 return(mdoc_perr(mdoc
, line
, ppos
, "parameter hard-limit exceeded"));
905 c
= mdoc_elem_alloc(mdoc
, line
, ppos
, tok
, argc
, argv
);
906 mdoc_argv_free(argc
, argv
);
911 mdoc
->next
= MDOC_NEXT_CHILD
;
913 for (j
= 0; /* No sentinel. */; j
++) {
916 if (j
== maxargs
&& ! flushed
) {
917 if ( ! rewind_elem(mdoc
, tok
))
922 c
= mdoc_args(mdoc
, line
, pos
, buf
, ARGS_DELIM
, &p
);
930 if (-1 == (c
= lookup(mdoc
, line
, lastarg
, tok
, p
)))
932 else if (MDOC_MAX
!= c
) {
933 if ( ! flushed
&& ! rewind_elem(mdoc
, tok
))
936 if ( ! mdoc_macro(mdoc
, c
, line
, lastarg
, pos
, buf
))
941 if ( ! flushed
&& mdoc_isdelim(p
)) {
942 if ( ! rewind_elem(mdoc
, tok
))
947 if ( ! mdoc_word_alloc(mdoc
, line
, lastarg
, p
))
949 mdoc
->next
= MDOC_NEXT_SIBLING
;
952 if ( ! flushed
&& ! rewind_elem(mdoc
, tok
))
957 return(append_delims(mdoc
, line
, pos
, buf
));
962 macro_constant(MACRO_PROT_ARGS
)
964 int c
, lastarg
, argc
, fl
;
965 struct mdoc_arg argv
[MDOC_LINEARG_MAX
];
970 if (MDOC_QUOTABLE
& mdoc_macros
[tok
].flags
)
973 for (argc
= 0; argc
< MDOC_LINEARG_MAX
; argc
++) {
975 c
= mdoc_argv(mdoc
, line
, tok
, &argv
[argc
], pos
, buf
);
978 if (ARGV_WORD
== c
) {
981 } else if (ARGV_ARG
== c
)
984 mdoc_argv_free(argc
, argv
);
988 if (MDOC_LINEARG_MAX
== argc
) {
989 mdoc_argv_free(argc
- 1, argv
);
990 return(mdoc_perr(mdoc
, line
, ppos
, "parameter hard-limit exceeded"));
993 c
= mdoc_elem_alloc(mdoc
, line
, ppos
, tok
, argc
, argv
);
994 mdoc_argv_free(argc
, argv
);
999 mdoc
->next
= MDOC_NEXT_CHILD
;
1003 c
= mdoc_args(mdoc
, line
, pos
, buf
, fl
, &p
);
1004 if (ARGS_ERROR
== c
)
1009 if (-1 == (c
= lookup(mdoc
, line
, lastarg
, tok
, p
)))
1011 else if (MDOC_MAX
!= c
) {
1012 if ( ! rewind_elem(mdoc
, tok
))
1014 return(mdoc_macro(mdoc
, c
, line
,
1015 lastarg
, pos
, buf
));
1018 if ( ! mdoc_word_alloc(mdoc
, line
, lastarg
, p
))
1020 mdoc
->next
= MDOC_NEXT_SIBLING
;
1023 if ( ! rewind_elem(mdoc
, tok
))
1025 if ( ! (MDOC_NOKEEP
& mdoc_macros
[tok
].flags
))
1028 assert(mdoc
->last
->tok
== tok
);
1029 if (mdoc
->last
->parent
->child
== mdoc
->last
)
1030 mdoc
->last
->parent
->child
= mdoc
->last
->prev
;
1031 if (mdoc
->last
->prev
)
1032 mdoc
->last
->prev
->next
= NULL
;
1035 assert(NULL
== mdoc
->last
->next
);
1037 if (mdoc
->last
->prev
) {
1038 mdoc
->last
= mdoc
->last
->prev
;
1039 mdoc
->next
= MDOC_NEXT_SIBLING
;
1041 mdoc
->last
= mdoc
->last
->parent
;
1042 mdoc
->next
= MDOC_NEXT_CHILD
;
1045 mdoc_node_freelist(n
);
1053 macro_obsolete(MACRO_PROT_ARGS
)
1056 return(mdoc_pwarn(mdoc
, line
, ppos
, WARN_SYNTAX
, "macro is obsolete"));
1061 macro_end(struct mdoc
*mdoc
)
1063 struct mdoc_node
*n
;
1065 assert(mdoc
->first
);
1068 /* Scan for open explicit scopes. */
1070 n
= MDOC_VALID
& mdoc
->last
->flags
?
1071 mdoc
->last
->parent
: mdoc
->last
;
1073 for ( ; n
; n
= n
->parent
) {
1074 if (MDOC_BLOCK
!= n
->type
)
1076 if ( ! (MDOC_EXPLICIT
& mdoc_macros
[n
->tok
].flags
))
1078 mdoc_nerr(mdoc
, n
, "macro scope still open on exit");
1082 return(rewind_last(mdoc
, mdoc
->first
));