Revised prettyprinter

slava 2006-10-28 06:41:21 +00:00
parent 9a56489bf9
commit e520478032
20 changed files with 770 additions and 706 deletions

View File

@ -1,9 +1,4 @@
- live search: timer delay would be nice
- fix this:
[ 1 2 3 4 5 6 7 8 9 10 10 10 10 10 10 10 10 10 10 11 11 11 113 ] .
[ 1 2 3 4 5 6 7 8 9 10 10 10 10 10 10 10 10 10 10 11 11 11 113
]
- prettier printing of hashtable literals, alists, cond, ...
- menu should stay up if mouse button released
- roundoff is still not quite right with tracks
- fix compiled gc check
@ -11,6 +6,7 @@
- float boxing and overflow checks need a gc check too
- [ [ dup call ] dup call ] infer hangs
- growable data heap
- erg's frange crash
+ ui:
@ -36,6 +32,7 @@
+ module system:
- modules should support definition protocol
- track a list of assets loaded from each module's file
- C types should be words
- TYPEDEF: float { ... } { ... } ; ==> \ float T{ c-type ... } "c-type" swp
@ -60,6 +57,7 @@
- infer which variables are read, written in a quotation
- compiled continuations
- compiled call traces
- amd64 structs-by-value bug
+ misc:

View File

@ -1,4 +1,4 @@
USING: help io prettyprint ;
USING: help io prettyprint prettyprint-internals ;
ARTICLE: "prettyprint" "The prettyprinter"
"One of Factor's key features is the ability to print almost any object as a valid source literal expression. This greatly aids debugging and provides the building blocks for light-weight object serialization facilities."
@ -56,8 +56,10 @@ $terpri
"Two types of leaf sections:"
{ $subsection text }
{ $subsection newline }
"Nesting and denesting is done using three words. There are two words to denest a block; they vary in indentation policy:"
{ $subsection <block }
"Nesting and denesting is done using a set words:"
{ $subsection <inset }
{ $subsection <flow }
{ $subsection <narrow }
{ $subsection <defblock }
{ $subsection block> }
{ $subsection block; }
"Recall that since " { $link text } " sections take style hashtables as input, any type of formatted text can be output, including presentations. See " { $link "styles" } " to explore the possibility." ;

View File

@ -9,6 +9,4 @@ GENERIC: where* ( defspec -- loc )
GENERIC: subdefs ( defspec -- seq )
: see-subdefs ( word -- ) subdefs [ see ] each ;
GENERIC: forget ( defspec -- )

View File

@ -2,7 +2,8 @@
! See http://factorcode.org/license.txt for BSD license.
IN: help
USING: arrays definitions errors generic graphs hashtables
io kernel namespaces prettyprint sequences words ;
io kernel namespaces prettyprint prettyprint-internals
sequences words ;
! Help articles
SYMBOL: articles

View File

@ -71,8 +71,11 @@ PROVIDE: library
"compiler/alien/aliens.factor"
"syntax/prettyprint.factor"
"tools/summary.factor"
"prettyprint/core.factor"
"prettyprint/sections.factor"
"prettyprint/backend.factor"
"prettyprint/frontend.factor"
"syntax/parser.factor"
"syntax/parse-stream.factor"
@ -175,15 +178,17 @@ PROVIDE: library
"math/ratio.facts"
"math/trig-hyp.facts"
"math/vectors.facts"
"prettyprint/core.facts"
"prettyprint/sections.facts"
"prettyprint/backend.facts"
"prettyprint/frontend.facts"
"syntax/early-parser.facts"
"syntax/parse-stream.facts"
"syntax/parser.facts"
"syntax/parse-syntax.facts"
"syntax/prettyprint.facts"
"tools/definitions.facts"
"tools/word-tools.facts"
"tools/debugger.facts"
"tools/summary.facts"
"tools/describe.facts"
"tools/inspector.facts"
"tools/listener.facts"

View File

@ -0,0 +1,141 @@
! Copyright (C) 2003, 2006 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
IN: prettyprint-internals
USING: alien arrays generic hashtables io kernel math
namespaces parser sequences strings styles vectors words
prettyprint ;
GENERIC: pprint* ( obj -- )
! Atoms
M: byte-array pprint* drop "( byte array )" text ;
: word-style ( word -- style )
[
dup presented set
parsing? [ bold font-style set ] when
] make-hash ;
: pprint-word ( word -- )
dup word-name swap word-style styled-text ;
M: word pprint*
dup parsing? [ \ POSTPONE: pprint-word ] when pprint-word ;
M: real pprint* number>string text ;
M: f pprint* drop \ f pprint-word ;
M: alien pprint*
dup expired? [
drop "( alien expired )"
] [
\ ALIEN: pprint-word alien-address number>string
] if text ;
! Strings
: ch>ascii-escape ( ch -- str )
H{
{ CHAR: \e "\\e" }
{ CHAR: \n "\\n" }
{ CHAR: \r "\\r" }
{ CHAR: \t "\\t" }
{ CHAR: \0 "\\0" }
{ CHAR: \\ "\\\\" }
{ CHAR: \" "\\\"" }
} hash ;
: ch>unicode-escape ( ch -- str )
>hex 4 CHAR: 0 pad-left "\\u" swap append ;
: unparse-ch ( ch -- )
dup quotable? [
,
] [
dup ch>ascii-escape [ ] [ ch>unicode-escape ] ?if %
] if ;
: do-string-limit ( str -- trimmed )
string-limit get [
dup length margin get > [
margin get 3 - head "..." append
] when
] when ;
: pprint-string ( str prefix -- )
[ % [ unparse-ch ] each CHAR: " , ] "" make
do-string-limit text ;
M: string pprint* "\"" pprint-string ;
M: sbuf pprint* "SBUF\" " pprint-string ;
M: dll pprint* dll-path "DLL\" " pprint-string ;
! Sequences
: nesting-limit? ( -- ? )
nesting-limit get dup [ pprinter-stack get length < ] when ;
: check-recursion ( obj quot -- )
nesting-limit? [
2drop "#" text
] [
over recursion-check get memq? [
2drop "&" text
] [
over recursion-check get push
call
recursion-check get pop*
] if
] if ; inline
: length-limit? ( seq -- trimmed ? )
length-limit get dup
[ over length over > [ head t ] [ drop f ] if ]
[ drop f ] if ;
: hilite-style ( -- hash )
H{
{ background { 0.9 0.9 0.9 1 } }
{ highlight t }
} ;
: pprint-hilite ( object n -- )
hilite-index get = [
hilite-style <flow pprint* block>
] [
pprint*
] if ;
: pprint-elements ( seq -- )
length-limit? >r dup hilite-quotation get eq? [
dup length [ pprint-hilite ] 2each
] [
[ pprint* ] each
] if r> [ "..." text ] when ;
GENERIC: >pprint-sequence ( obj -- seq start end narrow? )
M: complex >pprint-sequence >rect 2array \ C{ \ } f ;
M: quotation >pprint-sequence \ [ \ ] f ;
M: array >pprint-sequence \ { \ } t ;
M: vector >pprint-sequence \ V{ \ } t ;
M: hashtable >pprint-sequence hash>alist \ H{ \ } t ;
M: tuple >pprint-sequence tuple>array \ T{ \ } t ;
M: wrapper >pprint-sequence
wrapped dup 1array swap word? [ \ \ f ] [ \ W{ \ } ] if f ;
M: object pprint*
[
>pprint-sequence H{ } <flow
rot [ pprint-word ] when*
[ H{ } <narrow ] [ H{ } <inset ] if
swap pprint-elements
block> [ pprint-word ] when* block>
] check-recursion ;

View File

@ -0,0 +1,53 @@
USING: help io kernel prettyprint prettyprint-internals words ;
HELP: pprint*
{ $values { "obj" "an object" } }
{ $contract "Adds sections to the current block corresponding to the prettyprinted representation of the object." }
$prettyprinting-note
{ $see-also text newline <inset block> } ;
HELP: pprint-word
{ $values { "word" "a word" } }
{ $description "Adds a text section for the word. Unlike the " { $link word } " method of " { $link pprint* } ", this does not add a " { $link POSTPONE: POSTPONE: } " prefix to parsing words." }
$prettyprinting-note ;
HELP: ch>ascii-escape
{ $values { "ch" "a character" } { "str" "a string" } }
{ $description "Converts a character to an escape code." } ;
HELP: ch>unicode-escape
{ $values { "ch" "a character" } { "str" "a string" } }
{ $description "Converts a character to a Unicode escape code (" { $snippet "\\u1234"} ")." } ;
HELP: unparse-ch
{ $values { "ch" "a character" } }
{ $description "Adds the character to the sequence being constructed (see " { $link "namespaces-make" } "). If the character can appear in a string literal, it is added directly, otherwise an escape code is added." } ;
HELP: do-string-limit
{ $values { "str" "a string" } { "trimmed" "a possibly trimmed string" } }
{ $description "If " { $link string-limit } " is on, trims the string such that it does not exceed the margin, appending \"...\" if trimming took place." } ;
HELP: pprint-string
{ $values { "str" "a string" } { "prefix" "a prefix string" } }
{ $description "Outputs a text section consisting of the prefix, the string, and a final quote (\")." }
$prettyprinting-note ;
HELP: nesting-limit?
{ $values { "?" "a boolean" } }
{ $description "Tests if the " { $link nesting-limit } " has been reached." }
$prettyprinting-note ;
HELP: check-recursion
{ $values { "obj" "an object" } { "quot" "a quotation with stack effect " { $snippet "( obj -- )" } } }
{ $description "If the object is already being printed, that is, if the prettyprinter has encountered a cycle in the object graph, or if the maximum nesting depth has been reached, outputs a dummy string. Otherwise applies the quotation to the object." }
$prettyprinting-note ;
HELP: length-limit?
{ $values { "seq" "a sequence" } { "trimmed" "a trimmed sequence" } { "?" "a boolean indicating if trimming took place" } }
{ $description "If the " { $link length-limit } " is set, trims the sequence if necessary, and outputs a boolean indicating if \"...\" should be output." }
$prettyprinting-note ;
HELP: pprint-elements
{ $values { "seq" "a sequence" } }
{ $description "Prettyprints the elements of a sequence, trimming the sequence to " { $link length-limit } " if necessary." }
$prettyprinting-note ;

View File

@ -0,0 +1,58 @@
! Copyright (C) 2003, 2006 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
IN: prettyprint
USING: alien arrays generic hashtables io kernel math
namespaces parser sequences strings styles vectors words ;
! Configuration
SYMBOL: tab-size
SYMBOL: margin
SYMBOL: nesting-limit
SYMBOL: length-limit
SYMBOL: line-limit
SYMBOL: string-limit
! Special trick to highlight a word in a quotation
SYMBOL: hilite-quotation
SYMBOL: hilite-index
SYMBOL: hilite-next?
IN: prettyprint-internals
! State
SYMBOL: position
SYMBOL: last-newline
SYMBOL: recursion-check
SYMBOL: line-count
SYMBOL: end-printing
SYMBOL: indent
SYMBOL: pprinter-stack
! Utility words
: line-limit? ( -- ? )
line-limit get dup [ line-count get <= ] when ;
: do-indent ( -- ) indent get CHAR: \s <string> write ;
: fresh-line ( n -- )
dup last-newline get = [
drop
] [
last-newline set
line-limit? [ "..." write end-printing get continue ] when
line-count inc
terpri do-indent
] if ;
: text-fits? ( len -- ? )
indent get + margin get <= ;
global [
4 tab-size set
64 margin set
0 position set
0 indent set
0 last-newline set
1 line-count set
string-limit off
] bind

View File

@ -0,0 +1,40 @@
USING: help io kernel prettyprint prettyprint-internals words ;
HELP: position
{ $var-description "The prettyprinter's current character position." } ;
HELP: last-newline
{ $var-description "The character position of the last newline output by the prettyprinter." } ;
HELP: recursion-check
{ $var-description "The current nesting of collections being output by the prettyprinter, used to detect circularity and prevent infinite recursion." } ;
HELP: line-count
{ $var-description "The number of lines output by the prettyprinter so far, used for line limiting (see " { $link line-limit } ")." } ;
HELP: end-printing
{ $var-description "A continuation captured by " { $link do-pprint } " that breaks out of the printer." } ;
HELP: indent
{ $var-description "The prettyprinter's current indent level." } ;
HELP: pprinter-stack
{ $var-description "A stack of " { $link block } " objects currently being constructed by the prettyprinter." } ;
HELP: tab-size
{ $var-description "Prettyprinter tab size. Indent nesting is always a multiple of the tab size. The default is 4." } ;
HELP: margin
{ $var-description "The maximum line length, in characters. Lines longer than the margin are wrapped. The default is 64." } ;
HELP: nesting-limit
{ $var-description "The maximum nesting level. Structures that nest further than this will simply print as a pound sign (#). The default is " { $link f } ", denoting unlimited nesting depth." } ;
HELP: length-limit
{ $var-description "The maximum printed sequence length. Sequences longer than this are truncated, and \"...\" is output in place of remaining elements. The default is " { $link f } ", denoting unlimited sequence length." } ;
HELP: line-limit
{ $var-description "The maximum number of lines output by the prettyprinter before output is truncated with \"...\". The default is " { $link f } ", denoting unlimited line count." } ;
HELP: string-limit
{ $var-description "Toggles whenever printed strings are truncated to the margin." } ;

View File

@ -0,0 +1,57 @@
! Copyright (C) 2003, 2006 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
IN: prettyprint
USING: alien arrays generic hashtables io kernel math
namespaces parser sequences strings styles vectors words
prettyprint-internals ;
: with-pprint ( quot -- )
[
V{ } clone recursion-check set
H{ } <flow> f ?push pprinter-stack set
call end-blocks do-pprint
] with-scope ; inline
: pprint ( obj -- ) [ pprint* ] with-pprint ;
: . ( obj -- )
H{
{ length-limit 1000 }
{ nesting-limit 10 }
} clone [ pprint ] bind terpri ;
: unparse ( obj -- str ) [ pprint ] string-out ;
: pprint-short ( obj -- )
H{
{ line-limit 1 }
{ length-limit 15 }
{ nesting-limit 2 }
{ string-limit t }
} clone [ pprint ] bind ;
: short. ( obj -- ) pprint-short terpri ;
: unparse-short ( obj -- str ) [ pprint-short ] string-out ;
: .b ( n -- ) >bin print ;
: .o ( n -- ) >oct print ;
: .h ( n -- ) >hex print ;
GENERIC: summary ( object -- string )
M: object summary
"an instance of the " swap class word-name " class" append3 ;
M: input summary
"Input: " swap input-string dup string?
[ "\n" split1 "..." "" ? append ] [ unparse-short ] if
append ;
M: vocab-link summary
[
vocab-link-name dup %
" vocabulary (" %
words length #
" words)" %
] "" make ;

View File

@ -0,0 +1,46 @@
IN: prettyprint
USING: help io kernel prettyprint prettyprint-internals words ;
HELP: with-pprint
{ $values { "quot" "a quotation" } }
{ $description "Sets up the prettyprinter and calls the quotation in a new scope. The quotation should add sections to the top-level block. When the quotation returns, the top-level block is printed to the default stream." } ;
HELP: pprint
{ $values { "obj" "an object" } }
{ $description "Prettyprints an object to the default stream. Output is influenced by many variables; see " { $link "prettyprint-variables" } "." } ;
HELP: .
{ $values { "obj" "an object" } }
{ $description "Prettyprints an object to the default stream with a trailing line break. Output is influenced by many variables; see " { $link "prettyprint-variables" } "." } ;
HELP: unparse
{ $values { "obj" "an object" } { "str" "Factor source string" } }
{ $description "Outputs a prettyprinted string representation of an object. Output is influenced by many variables; see " { $link "prettyprint-variables" } "." } ;
HELP: pprint-short
{ $values { "obj" "an object" } }
{ $description "Prettyprints an object to the default stream. This word rebinds printer control variables to enforce ``shorter'' output. See " { $link "prettyprint-variables" } "." } ;
HELP: short.
{ $values { "obj" "an object" } }
{ $description "Prettyprints an object to the default stream with a trailing line break. This word rebinds printer control variables to enforce ``shorter'' output." } ;
HELP: unparse-short
{ $values { "obj" "an object" } { "str" "Factor source string" } }
{ $description "Outputs a prettyprinted string representation of an object. This word rebinds printer control variables to enforce ``shorter'' output. See " { $link "prettyprint-variables" } "." } ;
HELP: .b
{ $values { "n" "an integer" } }
{ $description "Outputs an integer in binary." } ;
HELP: .o
{ $values { "n" "an integer" } }
{ $description "Outputs an integer in octal." } ;
HELP: .h
{ $values { "n" "an integer" } }
{ $description "Outputs an integer in hexadecimal." } ;
HELP: summary
{ $values { "object" "an object" } { "string" "a string" } }
{ $contract "Outputs a brief description of the object." } ;

View File

@ -0,0 +1,217 @@
! Copyright (C) 2003, 2006 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
IN: prettyprint-internals
USING: alien arrays generic hashtables io kernel math
namespaces parser sequences strings styles vectors words
prettyprint ;
! Sections
TUPLE: section start end style ;
C: section ( style length -- section )
>r position [ dup rot + dup ] change r>
[ set-section-end ] keep
[ set-section-start ] keep
[ set-section-style ] keep ;
GENERIC: section-fits? ( section -- ? )
M: section section-fits? ( section -- ? )
section-end last-newline get - text-fits? ;
GENERIC: short-section ( section -- )
GENERIC: long-section ( section -- )
GENERIC: block-empty? ( section -- ? )
: pprint-section ( section -- )
{
{ [ margin get zero? ] [ short-section ] }
{ [ dup section-fits? ] [ short-section ] }
{ [ t ] [ long-section ] }
} cond ;
! Block sections
TUPLE: block sections ;
C: block ( style -- block )
swap 0 <section> over set-delegate
V{ } clone over set-block-sections ;
: pprinter-block ( -- block ) pprinter-stack get peek ;
: add-section ( section -- )
dup block-empty?
[ drop ] [ pprinter-block block-sections push ] if ;
M: block block-empty? block-sections empty? ;
M: block section-fits? ( section -- ? )
line-limit? [
drop t
] [
delegate section-fits?
] if ;
: (<block) pprinter-stack get push ;
: <style section-style stdio [ <nested-style-stream> ] change ;
: style> stdio [ delegate ] change ;
: change-indent ( n -- )
tab-size get * indent [ + ] change ;
: <indent ( block -- ) 1 change-indent ;
: indent> ( block -- ) -1 change-indent ;
! Text section
TUPLE: text string ;
C: text ( string style -- text )
[ >r over length 1+ <section> r> set-delegate ] keep
[ set-text-string ] keep ;
M: text block-empty? drop f ;
M: text short-section
dup text-string swap section-style format ;
M: text long-section
dup section-start fresh-line short-section ;
: styled-text ( string style -- ) <text> add-section ;
: text ( string -- ) H{ } styled-text ;
! Newline section
TUPLE: newline ;
C: newline ( -- section )
H{ } 0 <section> over set-delegate ;
M: newline block-empty? drop f ;
M: newline section-fits? drop t ;
M: newline short-section section-start fresh-line ;
: newline ( -- ) <newline> add-section ;
! Inset section
TUPLE: inset ;
C: inset ( style -- block )
swap <block> over set-delegate ;
M: inset section-fits? ( section -- ? )
line-limit? [
drop t
] [
section-end last-newline get - 2 + text-fits?
] if ;
: advance ( section -- )
dup newline? [
drop
] [
section-start last-newline get = [ bl ] unless
] if ;
: pprint-block ( block -- )
dup <style
block-sections unclip pprint-section
[ dup advance pprint-section ] each
style> ;
M: inset short-section pprint-block ;
M: inset long-section
<indent
dup section-start fresh-line dup pprint-block
indent>
section-end fresh-line ;
: <inset ( style -- ) <inset> (<block) ;
! Flow section
TUPLE: flow ;
C: flow ( style -- block )
swap <block> over set-delegate ;
M: flow section-fits? ( section -- ? )
dup delegate section-fits? [
drop t
] [
dup section-end swap section-start - text-fits? not
] if ;
M: flow short-section pprint-block ;
M: flow long-section dup section-start fresh-line pprint-block ;
: <flow ( style -- ) <flow> (<block) ;
! Narrow section
TUPLE: narrow ;
C: narrow ( style -- block )
swap <block> over set-delegate ;
M: narrow section-fits? ( section -- ? )
line-limit? [
drop t
] [
section-end last-newline get - 2 + text-fits?
] if ;
M: narrow short-section pprint-block ;
: narrow-block ( block -- )
dup <style
block-sections unclip pprint-section
[ dup section-start fresh-line pprint-section ] each
style> ;
M: narrow long-section
<indent
dup section-start fresh-line dup narrow-block
indent>
section-end fresh-line ;
: <narrow ( style -- ) <narrow> (<block) ;
! Defblock section
TUPLE: defblock ;
C: defblock ( style -- block )
swap <block> over set-delegate ;
M: defblock short-section pprint-block ;
M: defblock long-section
<indent
dup section-start fresh-line pprint-block
indent> ;
: <defblock ( style -- ) <defblock> (<block) ;
: end-block ( block -- ) position get swap set-section-end ;
: (block>) ( -- )
pprinter-stack get pop dup end-block add-section ;
: last-block? ( -- ? ) pprinter-stack get length 1 = ;
: block> ( -- ) last-block? [ (block>) ] unless ;
: end-blocks ( -- ) last-block? [ (block>) end-blocks ] unless ;
: do-pprint ( -- )
[
end-printing set pprinter-block
dup block-empty? [ drop ] [ pprint-section ] if
] callcc0 ;

View File

@ -0,0 +1,103 @@
USING: help io kernel prettyprint prettyprint-internals words ;
: $prettyprinting-note
drop {
"This word should only be called from inside the "
{ $link with-pprint } " combinator."
} $notes ;
HELP: pprint-section
{ $values { "section" "a section" } }
{ $contract "Prettyprints an object delegating to an instance of " { $link section } ", performing wrapping and indentation using the formatting information in the section." } ;
HELP: section
{ $class-description "A section represents a run of text with a known length and indentation level." } ;
HELP: line-limit?
{ $values { "?" "a boolean" } }
{ $description "Tests if the line number limit has been reached, and thus if prettyprinting should stop." } ;
HELP: do-indent
{ $description "Outputs the current indent nesting to the default stream." } ;
HELP: fresh-line
{ $values { "n" "the current column position" } }
{ $description "Advances the prettyprinter by one line unless the current line is empty. If the line limit is exceeded, escapes the prettyprinter by restoring a continuation captured in " { $link do-pprint } "." } ;
HELP: <text>
{ $values { "string" "a string" } { "style" "a hashtable" } { "text" "a new text section" } }
{ $description "Creates a text section." } ;
HELP: block
{ $class-description "A block is a section consisting of whitespace-separated child sections." } ;
HELP: pprinter-block
{ $values { "block" "a block section" } }
{ $description "Outputs the block currently being constructed." }
$prettyprinting-note ;
HELP: block-empty?
{ $values { "section" "a section" } { "?" "a boolean" } }
{ $description "Tests if a section is empty. A section is empty if it is a block with no children." } ;
HELP: add-section
{ $values { "section" "a section" } }
{ $description "Adds a section to the current block." }
$prettyprinting-note ;
HELP: text
{ $values { "string" "a string" } }
{ $description "Adds a section consisting of a single string to the current block. The current style on the style stack is used; see " { $link with-style } "." }
$prettyprinting-note ;
HELP: <indent
{ $values { "section" "a section" } }
{ $description "Increases indent level by the indent level of the section." }
$prettyprinting-note ;
HELP: indent>
{ $values { "section" "a section" } }
{ $description "Decreases indent level by the indent level of the section." }
$prettyprinting-note ;
HELP: section-fits?
{ $values { "section" "a section" } { "?" "a boolean" } }
{ $description "Tests if a section should be printed on the current line." } ;
HELP: newline
{ $description "Adds a section introducing an unconditional line break to the current block." }
$prettyprinting-note ;
HELP: advance
{ $values { "section" "a section" } }
{ $description "Emits a space unless the section is the first section on the line." } ;
HELP: <inset
{ $values { "style" "a style" } }
{ $description "Begins a nested block." }
$prettyprinting-note ;
HELP: end-block
{ $values { "block" "a block" } }
{ $description "Save the current position as the end position of the block." } ;
HELP: (block>)
{ $description "Adds the current block to its containing block." }
$prettyprinting-note ;
HELP: last-block?
{ $values { "?" "a boolean" } }
{ $description "Tests if the current block is the top-level block." }
$prettyprinting-note ;
HELP: block>
{ $description "Adds the current block to its containing block, unless the current block is the top-level block in which case it does nothing." }
{ $notes "This word is used to end blocks in order for the prettyprinter to be forgiving in the case of mismatched begin/end pairs (this can happen when printing parsing words)." }
$prettyprinting-note ;
HELP: end-blocks
{ $description "Unwind all prettyprinter state to the top level block." }
$prettyprinting-note ;
HELP: do-pprint
{ $description "Recursively output all children of the top-level block. The continuation is restored and output terminates if the line length is exceeded; this test is performed in " { $link fresh-line } "." } ;

View File

@ -1,357 +0,0 @@
! Copyright (C) 2003, 2006 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
IN: prettyprint
USING: alien arrays generic hashtables io kernel math
namespaces parser sequences strings styles vectors words ;
! State
SYMBOL: position
SYMBOL: last-newline
SYMBOL: recursion-check
SYMBOL: line-count
SYMBOL: end-printing
SYMBOL: indent
SYMBOL: pprinter-stack
! Configuration
SYMBOL: tab-size
SYMBOL: margin
SYMBOL: nesting-limit
SYMBOL: length-limit
SYMBOL: line-limit
SYMBOL: string-limit
! Special trick to highlight a word in a quotation
SYMBOL: hilite-quotation
SYMBOL: hilite-index
SYMBOL: hilite-next?
global [
4 tab-size set
64 margin set
0 position set
0 indent set
0 last-newline set
1 line-count set
string-limit off
] bind
GENERIC: pprint-section* ( section -- )
TUPLE: section start end nl-after? indent style ;
C: section ( style length -- section )
>r position [ dup rot + dup ] change r>
[ set-section-end ] keep
[ set-section-start ] keep
[ set-section-style ] keep
0 over set-section-indent ;
: line-limit? ( -- ? )
line-limit get dup [ line-count get <= ] when ;
: do-indent ( -- ) indent get CHAR: \s <string> write ;
: fresh-line ( n -- )
dup last-newline get = [
drop
] [
last-newline set
line-limit? [ "..." write end-printing get continue ] when
line-count inc
terpri do-indent
] if ;
TUPLE: text string ;
C: text ( string style -- text )
[ >r over length 1+ <section> r> set-delegate ] keep
[ set-text-string ] keep ;
M: text pprint-section*
dup text-string swap section-style format ;
TUPLE: block sections ;
C: block ( style -- block )
[ >r 0 <section> r> set-delegate ] keep
V{ } clone over set-block-sections
t over set-section-nl-after?
tab-size get over set-section-indent ;
: pprinter-block ( -- block ) pprinter-stack get peek ;
: block-empty? ( section -- ? )
dup block? [ block-sections empty? ] [ drop f ] if ;
: add-section ( section -- )
dup block-empty?
[ drop ] [ pprinter-block block-sections push ] if ;
: styled-text ( string style -- ) <text> add-section ;
: text ( string -- ) H{ } styled-text ;
: <indent ( section -- ) section-indent indent [ + ] change ;
: indent> ( section -- ) section-indent indent [ swap - ] change ;
: inset-section ( section -- )
dup <indent
dup section-start fresh-line dup pprint-section*
dup indent>
dup section-nl-after?
[ section-end fresh-line ] [ drop ] if ;
: section-fits? ( section -- ? )
margin get dup zero? [
2drop t
] [
line-limit? pick block? and [
2drop t
] [
>r section-end last-newline get - indent get + r> <=
] if
] if ;
: pprint-section ( section -- )
dup section-fits? [ pprint-section* ] [ inset-section ] if ;
TUPLE: newline ;
C: newline ( -- section )
H{ } 0 <section> over set-delegate ;
M: newline pprint-section*
section-start fresh-line ;
: newline ( -- ) <newline> add-section ;
: advance ( section -- )
dup newline? [
drop
] [
section-start last-newline get = [ bl ] unless
] if ;
: <style section-style stdio [ <nested-style-stream> ] change ;
: style> stdio [ delegate ] change ;
M: block pprint-section*
dup <style
f swap block-sections [
over [ dup advance ] when pprint-section drop t
] each drop
style> ;
: <block ( style -- ) <block> pprinter-stack get push ;
: end-block ( block -- ) position get swap set-section-end ;
: (block>) ( -- )
pprinter-stack get pop dup end-block add-section ;
: last-block? ( -- ? ) pprinter-stack get length 1 = ;
: block> ( -- ) last-block? [ (block>) ] unless ;
: block; ( -- )
pprinter-block f swap set-section-nl-after? block> ;
: end-blocks ( -- ) last-block? [ (block>) end-blocks ] unless ;
: do-pprint ( -- )
[ end-printing set pprinter-block pprint-section ] callcc0 ;
GENERIC: pprint* ( obj -- )
: word-style ( word -- style )
[
dup presented set
parsing? [ bold font-style set ] when
] make-hash ;
: pprint-word ( word -- )
dup word-name swap word-style styled-text ;
M: object pprint*
"( unprintable object: " swap class word-name " )" append3
text ;
M: real pprint* number>string text ;
: ch>ascii-escape ( ch -- str )
H{
{ CHAR: \e "\\e" }
{ CHAR: \n "\\n" }
{ CHAR: \r "\\r" }
{ CHAR: \t "\\t" }
{ CHAR: \0 "\\0" }
{ CHAR: \\ "\\\\" }
{ CHAR: \" "\\\"" }
} hash ;
: ch>unicode-escape ( ch -- str )
>hex 4 CHAR: 0 pad-left "\\u" swap append ;
: unparse-ch ( ch -- )
dup quotable? [
,
] [
dup ch>ascii-escape [ ] [ ch>unicode-escape ] ?if %
] if ;
: do-string-limit ( str -- trimmed )
string-limit get [
dup length margin get > [
margin get 3 - head "..." append
] when
] when ;
: pprint-string ( str prefix -- )
[ % [ unparse-ch ] each CHAR: " , ] "" make
do-string-limit text ;
M: string pprint* "\"" pprint-string ;
M: sbuf pprint* "SBUF\" " pprint-string ;
M: word pprint*
dup "pprint-close" word-prop [ block> ] when
dup pprint-word
"pprint-open" word-prop [ H{ } <block ] when ;
M: f pprint* drop \ f pprint-word ;
M: dll pprint* dll-path "DLL\" " pprint-string ;
: nesting-limit? ( -- ? )
nesting-limit get dup [ pprinter-stack get length < ] when ;
: check-recursion ( obj quot -- )
nesting-limit? [
2drop "#" text
] [
over recursion-check get memq? [
2drop "&" text
] [
over recursion-check get push
call
recursion-check get pop*
] if
] if ; inline
: length-limit? ( seq -- trimmed ? )
length-limit get dup
[ over length over > [ head t ] [ drop f ] if ]
[ drop f ] if ;
: pprint-element ( obj -- )
dup parsing? [ \ POSTPONE: pprint-word ] when pprint* ;
: hilite-style ( -- hash )
H{
{ background { 0.9 0.9 0.9 1 } }
{ highlight t }
} ;
: pprint-hilite ( object n -- )
hilite-index get = [
hilite-style <block pprint-element block>
] [
pprint-element
] if ;
: pprint-elements ( seq -- )
length-limit? >r dup hilite-quotation get eq? [
dup length [ pprint-hilite ] 2each
] [
[ pprint-element ] each
] if r> [ "..." text ] when ;
: pprint-sequence ( seq start end -- )
swap pprint* swap pprint-elements pprint* ;
M: complex pprint*
>rect 2array \ C{ \ } pprint-sequence ;
M: quotation pprint*
[ \ [ \ ] pprint-sequence ] check-recursion ;
M: array pprint*
[ \ { \ } pprint-sequence ] check-recursion ;
M: vector pprint*
[ \ V{ \ } pprint-sequence ] check-recursion ;
M: hashtable pprint*
[ hash>alist \ H{ \ } pprint-sequence ] check-recursion ;
M: tuple pprint*
[
\ T{ pprint*
tuple>array dup first pprint*
H{ } <block 1 tail-slice pprint-elements
\ } pprint*
] check-recursion ;
M: alien pprint*
dup expired? [
drop "( alien expired )"
] [
\ ALIEN: pprint-word alien-address number>string
] if text ;
M: wrapper pprint*
dup wrapped word? [
\ \ pprint-word wrapped pprint-word
] [
wrapped 1array \ W{ \ } pprint-sequence
] if ;
: with-pprint ( quot -- )
[
V{ } clone recursion-check set
H{ } <block> f ?push pprinter-stack set
call end-blocks do-pprint
] with-scope ; inline
: pprint ( obj -- ) [ pprint* ] with-pprint ;
: . ( obj -- )
H{
{ length-limit 1000 }
{ nesting-limit 10 }
} clone [ pprint ] bind terpri ;
: unparse ( obj -- str ) [ pprint ] string-out ;
: pprint-short ( obj -- )
H{
{ line-limit 1 }
{ length-limit 15 }
{ nesting-limit 2 }
{ string-limit t }
} clone [ pprint ] bind ;
: short. ( obj -- ) pprint-short terpri ;
: unparse-short ( obj -- str ) [ pprint-short ] string-out ;
: .b ( n -- ) >bin print ;
: .o ( n -- ) >oct print ;
: .h ( n -- ) >hex print ;
: define-open ( word -- ) t "pprint-open" set-word-prop ;
: define-close ( word -- ) t "pprint-close" set-word-prop ;
{
POSTPONE: [
POSTPONE: { POSTPONE: V{ POSTPONE: H{
POSTPONE: W{
} [ define-open ] each
{
POSTPONE: ] POSTPONE: }
} [ define-close ] each

View File

@ -1,266 +0,0 @@
USING: help io kernel prettyprint words ;
IN: help
: $prettyprinting-note
drop {
"This word should only be called from inside the "
{ $link with-pprint } " combinator."
} $notes ;
HELP: position
{ $var-description "The prettyprinter's current character position." } ;
HELP: last-newline
{ $var-description "The character position of the last newline output by the prettyprinter." } ;
HELP: recursion-check
{ $var-description "The current nesting of collections being output by the prettyprinter, used to detect circularity and prevent infinite recursion." } ;
HELP: line-count
{ $var-description "The number of lines output by the prettyprinter so far, used for line limiting (see " { $link line-limit } ")." } ;
HELP: end-printing
{ $var-description "A continuation captured by " { $link do-pprint } " that breaks out of the printer." } ;
HELP: indent
{ $var-description "The prettyprinter's current indent level." } ;
HELP: pprinter-stack
{ $var-description "A stack of " { $link block } " objects currently being constructed by the prettyprinter." } ;
HELP: tab-size
{ $var-description "Prettyprinter tab size. Indent nesting is always a multiple of the tab size. The default is 4." } ;
HELP: margin
{ $var-description "The maximum line length, in characters. Lines longer than the margin are wrapped. The default is 64." } ;
HELP: nesting-limit
{ $var-description "The maximum nesting level. Structures that nest further than this will simply print as a pound sign (#). The default is " { $link f } ", denoting unlimited nesting depth." } ;
HELP: length-limit
{ $var-description "The maximum printed sequence length. Sequences longer than this are truncated, and \"...\" is output in place of remaining elements. The default is " { $link f } ", denoting unlimited sequence length." } ;
HELP: line-limit
{ $var-description "The maximum number of lines output by the prettyprinter before output is truncated with \"...\". The default is " { $link f } ", denoting unlimited line count." } ;
HELP: string-limit
{ $var-description "Toggles whenever printed strings are truncated to the margin." } ;
HELP: pprint-section*
{ $values { "section" "a section" } }
{ $contract "Prettyprints an object delegating to an instance of " { $link section } ", performing wrapping and indentation using the formatting information in the section." } ;
HELP: section
{ $class-description "A section represents a run of text with a known length and indentation level." } ;
HELP: line-limit?
{ $values { "?" "a boolean" } }
{ $description "Tests if the line number limit has been reached, and thus if prettyprinting should stop." } ;
HELP: do-indent
{ $description "Outputs the current indent nesting to the default stream." } ;
HELP: fresh-line
{ $values { "n" "the current column position" } }
{ $description "Advances the prettyprinter by one line unless the current line is empty. If the line limit is exceeded, escapes the prettyprinter by restoring a continuation captured in " { $link do-pprint } "." } ;
HELP: <text>
{ $values { "string" "a string" } { "style" "a hashtable" } { "text" "a new text section" } }
{ $description "Creates a text section." } ;
HELP: block
{ $class-description "A block is a section consisting of whitespace-separated child sections." } ;
HELP: pprinter-block
{ $values { "block" "a block section" } }
{ $description "Outputs the block currently being constructed." }
$prettyprinting-note ;
HELP: block-empty?
{ $values { "section" "a section" } { "?" "a boolean" } }
{ $description "Tests if a section is empty. A section is empty if it is a block with no children." } ;
HELP: add-section
{ $values { "section" "a section" } }
{ $description "Adds a section to the current block." }
$prettyprinting-note ;
HELP: text
{ $values { "string" "a string" } }
{ $description "Adds a section consisting of a single string to the current block. The current style on the style stack is used; see " { $link with-style } "." }
$prettyprinting-note ;
HELP: <indent
{ $values { "section" "a section" } }
{ $description "Increases indent level by the indent level of the section." }
$prettyprinting-note ;
HELP: indent>
{ $values { "section" "a section" } }
{ $description "Decreases indent level by the indent level of the section." }
$prettyprinting-note ;
HELP: inset-section
{ $values { "section" "a section" } }
{ $description "Prints a section surrounded by line breaks and with increased indent." } ;
HELP: section-fits?
{ $values { "section" "a section" } { "?" "a boolean" } }
{ $description "Tests if a section should be printed on the current line." } ;
HELP: pprint-section
{ $values { "section" "a section" } }
{ $description "Prints a section, with a line break and increase in indent level if necessary." } ;
HELP: newline
{ $description "Adds a section introducing an unconditional line break to the current block." }
$prettyprinting-note ;
HELP: advance
{ $values { "section" "a section" } }
{ $description "Emits a space unless the section is the first section on the line." } ;
HELP: <block
{ $values { "style" "a style" } }
{ $description "Begins a nested block." }
$prettyprinting-note ;
HELP: end-block
{ $values { "block" "a block" } }
{ $description "Save the current position as the end position of the block." } ;
HELP: (block>)
{ $description "Adds the current block to its containing block." }
$prettyprinting-note ;
HELP: last-block?
{ $values { "?" "a boolean" } }
{ $description "Tests if the current block is the top-level block." }
$prettyprinting-note ;
HELP: block>
{ $description "Adds the current block to its containing block, unless the current block is the top-level block in which case it does nothing." }
{ $notes "This word is used to end blocks in order for the prettyprinter to be forgiving in the case of mismatched begin/end pairs (this can happen when printing parsing words)." }
$prettyprinting-note ;
HELP: block;
{ $description "Adds the current block to its containing block. The current block will be terminated by an unconditional newline." }
$prettyprinting-note ;
HELP: end-blocks
{ $description "Unwind all prettyprinter state to the top level block." }
$prettyprinting-note ;
HELP: do-pprint
{ $description "Recursively output all children of the top-level block. The continuation is restored and output terminates if the line length is exceeded; this test is performed in " { $link fresh-line } "." } ;
HELP: pprint*
{ $values { "obj" "an object" } }
{ $contract "Adds sections to the current block corresponding to the prettyprinted representation of the object." }
$prettyprinting-note
{ $see-also text newline <block block> block; } ;
HELP: pprint-word
{ $values { "word" "a word" } }
{ $description "Adds a text section for the word. Unlike the " { $link word } " method of " { $link pprint* } ", this does not add a " { $link POSTPONE: POSTPONE: } " prefix to parsing words." }
$prettyprinting-note ;
HELP: ch>ascii-escape
{ $values { "ch" "a character" } { "str" "a string" } }
{ $description "Converts a character to an escape code." } ;
HELP: ch>unicode-escape
{ $values { "ch" "a character" } { "str" "a string" } }
{ $description "Converts a character to a Unicode escape code (" { $snippet "\\u1234"} ")." } ;
HELP: unparse-ch
{ $values { "ch" "a character" } }
{ $description "Adds the character to the sequence being constructed (see " { $link "namespaces-make" } "). If the character can appear in a string literal, it is added directly, otherwise an escape code is added." } ;
HELP: do-string-limit
{ $values { "str" "a string" } { "trimmed" "a possibly trimmed string" } }
{ $description "If " { $link string-limit } " is on, trims the string such that it does not exceed the margin, appending \"...\" if trimming took place." } ;
HELP: pprint-string
{ $values { "str" "a string" } { "prefix" "a prefix string" } }
{ $description "Outputs a text section consisting of the prefix, the string, and a final quote (\")." }
$prettyprinting-note ;
HELP: nesting-limit?
{ $values { "?" "a boolean" } }
{ $description "Tests if the " { $link nesting-limit } " has been reached." }
$prettyprinting-note ;
HELP: check-recursion
{ $values { "obj" "an object" } { "quot" "a quotation with stack effect " { $snippet "( obj -- )" } } }
{ $description "If the object is already being printed, that is, if the prettyprinter has encountered a cycle in the object graph, or if the maximum nesting depth has been reached, outputs a dummy string. Otherwise applies the quotation to the object." }
$prettyprinting-note ;
HELP: length-limit?
{ $values { "seq" "a sequence" } { "trimmed" "a trimmed sequence" } { "?" "a boolean indicating if trimming took place" } }
{ $description "If the " { $link length-limit } " is set, trims the sequence if necessary, and outputs a boolean indicating if \"...\" should be output." }
$prettyprinting-note ;
HELP: pprint-element
{ $values { "obj" "an object" } }
{ $description "Prettyprints a sequence element." }
$prettyprinting-note ;
HELP: pprint-elements
{ $values { "seq" "a sequence" } }
{ $description "Prettyprints the elements of a sequence, trimming the sequence to " { $link length-limit } " if necessary." }
$prettyprinting-note ;
HELP: pprint-sequence
{ $values { "seq" "a sequence" } { "start" "a word" } { "end" "a word" } }
{ $description "Prettyprints a sequence." }
$prettyprinting-note ;
HELP: with-pprint
{ $values { "quot" "a quotation" } }
{ $description "Sets up the prettyprinter and calls the quotation in a new scope. The quotation should add sections to the top-level block. When the quotation returns, the top-level block is printed to the default stream." } ;
HELP: pprint
{ $values { "obj" "an object" } }
{ $description "Prettyprints an object to the default stream. Output is influenced by many variables; see " { $link "prettyprint-variables" } "." } ;
HELP: .
{ $values { "obj" "an object" } }
{ $description "Prettyprints an object to the default stream with a trailing line break. Output is influenced by many variables; see " { $link "prettyprint-variables" } "." } ;
HELP: unparse
{ $values { "obj" "an object" } { "str" "Factor source string" } }
{ $description "Outputs a prettyprinted string representation of an object. Output is influenced by many variables; see " { $link "prettyprint-variables" } "." } ;
HELP: pprint-short
{ $values { "obj" "an object" } }
{ $description "Prettyprints an object to the default stream. This word rebinds printer control variables to enforce ``shorter'' output. See " { $link "prettyprint-variables" } "." } ;
HELP: short.
{ $values { "obj" "an object" } }
{ $description "Prettyprints an object to the default stream with a trailing line break. This word rebinds printer control variables to enforce ``shorter'' output." } ;
HELP: unparse-short
{ $values { "obj" "an object" } { "str" "Factor source string" } }
{ $description "Outputs a prettyprinted string representation of an object. This word rebinds printer control variables to enforce ``shorter'' output. See " { $link "prettyprint-variables" } "." } ;
HELP: .b
{ $values { "n" "an integer" } }
{ $description "Outputs an integer in binary." } ;
HELP: .o
{ $values { "n" "an integer" } }
{ $description "Outputs an integer in octal." } ;
HELP: .h
{ $values { "n" "an integer" } }
{ $description "Outputs an integer in hexadecimal." } ;
HELP: define-open
{ $values { "word" "a word" } }
{ $description "Marks up the word so that it begins a nested block when prettyprinted. Usually only used for parsing words." } ;
HELP: define-close
{ $values { "word" "a word" } }
{ $description "Marks up the word so that it ends a nested block when prettyprinted. Usually only used for parsing words." } ;

View File

@ -120,7 +120,7 @@ TUPLE: another-one ;
[ T{ another-one f } ] [ <another-one> empty-method-test ] unit-test
! Test generic see and parsing
[ "IN: temporary SYMBOL: bah\nUNION: bah fixnum alien ;\n" ]
[ "IN: temporary SYMBOL: bah\n\nUNION: bah fixnum alien ;\n" ]
[ [ \ bah see ] string-out ] unit-test
! Weird bug

View File

@ -32,15 +32,6 @@ unit-test
[ "SBUF\" hello world\"" ] [ SBUF" hello world" unparse ] unit-test
: foo ( a -- b ) dup * ; inline
[ "IN: temporary : foo ( a -- b ) dup * ; inline\n" ]
[ [ \ foo see ] string-out ] unit-test
: bar ( x -- y ) 2 + ;
[ "IN: temporary : bar ( x -- y ) 2 + ;\n" ]
[ [ \ bar see ] string-out ] unit-test
[ "( a b -- c d )" ] [
{ "a" "b" } { "c" "d" } <effect> effect>string
@ -58,6 +49,26 @@ unit-test
{ } { } <effect> effect>string
] unit-test
[ "ALIEN: 1234" ] [ 1234 <alien> unparse ] unit-test
[ "W{ \\ + }" ] [ [ W{ \ + } ] first unparse ] unit-test
[ "[ 1 2 DUP ]" ]
[
[ 1 2 dup ] dup hilite-quotation set 2 hilite-index set
[ pprint ] string-out
] unit-test
: foo ( a -- b ) dup * ; inline
[ "IN: temporary : foo ( a -- b ) dup * ; inline\n" ]
[ [ \ foo see ] string-out ] unit-test
: bar ( x -- y ) 2 + ;
[ "IN: temporary : bar ( x -- y ) 2 + ;\n" ]
[ [ \ bar see ] string-out ] unit-test
[ ] [ \ fixnum see ] unit-test
[ ] [ \ integer see ] unit-test
@ -67,21 +78,3 @@ unit-test
[ ] [ \ compound see ] unit-test
[ ] [ \ duplex-stream see ] unit-test
[ "ALIEN: 1234" ] [ 1234 <alien> unparse ] unit-test
TUPLE: cat gender declawed? castrated? ;
[ "T{ cat \n f \"m\" \n t f\n}" ]
[
[
10 margin set
T{ cat f "m" t f } unparse
] with-scope
] unit-test
[ "[ 1 2 DUP ]" ]
[
[ 1 2 dup ] dup hilite-quotation set 2 hilite-index set
[ pprint ] string-out
] unit-test

View File

@ -2,7 +2,8 @@
! See http://factorcode.org/license.txt for BSD license.
IN: definitions
USING: arrays errors generic hashtables io kernel math
namespaces parser prettyprint sequences styles words ;
namespaces parser prettyprint prettyprint-internals sequences
styles words ;
: where ( defspec -- loc )
where* dup [ first2 >r ?resource-path r> 2array ] when ;
@ -33,7 +34,7 @@ GENERIC: synopsis* ( defspec -- )
: in. ( word -- )
word-vocabulary [
H{ } <block \ IN: pprint-word write-vocab block;
H{ } clone <flow \ IN: pprint-word write-vocab block>
] when* ;
: comment. ( string -- )
@ -83,9 +84,9 @@ M: word declarations.
[
dup synopsis*
dup definition [
H{ } <block
H{ } <defblock
pprint-elements pprint-; declarations.
block;
block>
] [
2drop
] if newline
@ -104,9 +105,9 @@ M: predicate see-class*
\ PREDICATE: pprint-word
dup superclass pprint-word
dup pprint-word
H{ } <block
H{ } <defblock
"definition" word-prop pprint-elements
pprint-; block; ;
pprint-; block> ;
M: tuple-class see-class*
\ TUPLE: pprint-word
@ -117,9 +118,12 @@ M: tuple-class see-class*
M: word see-class* drop ;
: see-class ( word -- )
[
dup class?
[ see-class* newline ] [ drop ] if
] with-pprint ;
dup class? [
terpri [ see-class* ] with-pprint terpri
] [
drop
] if ;
: see-subdefs ( word -- ) subdefs [ terpri see ] each ;
M: word see dup (see) dup see-class see-subdefs ;

View File

@ -1,23 +0,0 @@
! Copyright (C) 2005, 2006 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
IN: prettyprint
USING: generic kernel namespaces prettyprint sequences strings
styles words ;
GENERIC: summary ( object -- string )
M: object summary
"an instance of the " swap class word-name " class" append3 ;
M: input summary
"Input: " swap input-string dup string?
[ "\n" split1 "..." "" ? append ] [ unparse-short ] if
append ;
M: vocab-link summary
[
vocab-link-name dup %
" vocabulary (" %
words length #
" words)" %
] "" make ;

View File

@ -1,6 +0,0 @@
IN: prettyprint
USING: help ;
HELP: summary
{ $values { "object" "an object" } { "string" "a string" } }
{ $contract "Outputs a brief description of the object." } ;