{"id":32388907,"url":"https://github.com/danlentz/printv","last_synced_at":"2026-02-24T20:33:09.638Z","repository":{"id":8069687,"uuid":"9482134","full_name":"danlentz/printv","owner":"danlentz","description":"A batteries-included tracing and debug-logging macro","archived":false,"fork":false,"pushed_at":"2021-12-10T19:43:10.000Z","size":499,"stargazers_count":69,"open_issues_count":1,"forks_count":6,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-10-25T03:56:54.504Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danlentz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-04-16T20:41:39.000Z","updated_at":"2025-06-12T08:14:08.000Z","dependencies_parsed_at":"2022-08-31T18:22:10.604Z","dependency_job_id":null,"html_url":"https://github.com/danlentz/printv","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/danlentz/printv","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danlentz%2Fprintv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danlentz%2Fprintv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danlentz%2Fprintv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danlentz%2Fprintv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danlentz","download_url":"https://codeload.github.com/danlentz/printv/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danlentz%2Fprintv/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29798951,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-24T19:56:48.334Z","status":"ssl_error","status_checked_at":"2026-02-24T19:55:43.372Z","response_time":75,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-25T03:56:53.477Z","updated_at":"2026-02-24T20:33:09.622Z","avatar_url":"https://github.com/danlentz.png","language":"Common Lisp","readme":"printv\n======\n\n\u003e PRINTV is a 'batteries-included' tracing and debug-logging macro based\n\u003e on __\"The Handy PRINTV\"__ by *Dan Corkill, Copyright (c) 2006-2010*,\n\u003e and open-source licensed under the terms of Apache License version 2.\n\n\n### Motivation\n\nThere are a variety of sophisticated debugging facilities available to\nthe common-lisp programmer, and as well a number of very capable\nlogging utilities that range from very simple tools to fairly complex\napplication-grade suites.  In spite of this, one utility which I\nalmost always wind up turning to is the *\"Handy PRINTV Macro\"* that is\ndistributed as part of the GBBopen suite.  It is an effective\nsubistitute for tracing evaluation with ad-hoc printing forms that\ndoesn't require one to compose legions of cumbersome logging, PRINT or\nFORMAT calls that explicitly enumerate each value one wishes to\nincorporate into the trace.  It also provides a standardized format\nfor your debug-logging output that is both consistent and easy to\ninterpret via quick \"eyeball\" inspection. Debug-logging a trace of the\nevaluation of program forms may be easily incorporated into existing\ncode by simply enclosing the form or forms within a PRINTV macro-call\n(implicit progn).  __PRINTV always respects multiple-values__.\n\nAs I have wound up copying this utility from project to project,\nincorporating various extensions and tweaks along the way, it occurred\nto me that it was probably time to spin off my \"extended\" PRINTV into\na standalone library.  Also included is the Clozure Associates' *PPMX*\nmacro-expansion macro which is very useful in its own right, and has\nproven invaluable during development and debugging of PRINTV.\n\nThe extended features implemented (in addition to making PRINTV available\nindependently of the massive GBBopen project) include:\n\n* Thread Safety\n* Tracing lexical variable assignments in LET and LET* forms\n* Tracing conditional evaluations inside COND forms\n* Support a variety of output destinations, including to files and to arbitrary\n  user-defined streams.\n* Character-macro to support DWIM *'PRINTV the following form'* reader extension\n  instead of unsightly, cumbersome, and error-prone nesting of (PRINTV\n  ...) s-expression structure that becomes increasingly problematic\n  to understand and even more-so to (eventually) remove\n* Support for fully disabling printv with global or dynamic extent in order to \n  allow printv instrumented code to operate at full performance that should be\n  nearly identical to that of equivalent to code without such\n  instrumentation.\n* Support enablement and disablement of PRINTV output to user-selected stream\n  (initially \\*TRACE-OUTPUT\\*) effective for global or dynamic extent.\n* Support for additional typographic functionality that can generate\n  output that is both attractive and utilitarian for structuring\n  trace output in a manner that is easy to discern by eye and to navigate when\n  seeking a particular segment of output.   Included are: \n   * Major Separator (thick horizontal rule)\n   * Minor Separator (thin horizontal rule)\n   * Human Readable Timestamp\n   * Banner Text     (FIGLET generated)  \n* Macro bindings on keywords (:PRINTV, :PPMX) in addition to standard\n  symbols (PRINTV:PRINTV, PRINTV:PPMX) for ease of use globally\n* Inclusion of PPMX macro-expander distributed by Clozure Associates with\n  their excellent Common-Lisp implementation: *Clozure Common Lisp*\n\n### Usage\n\nTo start, here's a quick example that shows a few features in action:\n\n    CL-USER\u003e (:printv                \n               :|.. printv-output ..|\n               \"\"\n               :hr\n               :ts\n               :hr\n               \"\"\n               \"This is an example composition testing various PRINTV functionalities.\"\n               \"Self-evaluation of strings may be used within a PRINTV form to augment\"\n               \"the log output with annotations or other commentary.\"\n               \"\" \n               :hr\n               \"\" \n               (+ 2 3)              \n               *print-case*\n               *package*\n               'symbol\n               (let* ((x 0) (y (1+ x)) (z (1+ y)))\n                 (values x y z)))\n\nprints the following:\n\n    #||\n                      _       _                          _               _         \n           _ __  _ __(_)_ __ | |___   __      ___  _   _| |_ _ __  _   _| |_       \n          | '_ \\| '__| | '_ \\| __\\ \\ / /____ / _ \\| | | | __| '_ \\| | | | __|      \n     _ _  | |_) | |  | | | | | |_ \\ V /_____| (_) | |_| | |_| |_) | |_| | |_   _ _ \n    (_|_) | .__/|_|  |_|_| |_|\\__| \\_/       \\___/ \\__,_|\\__| .__/ \\__,_|\\__| (_|_)\n          |_|                                               |_|                    \n    ||#\n    ;;;\n    ;;; ------------------------------------------------------------------------ ;;;\n    ;;; .............. Wednesday, April 17, 2013 06:39:03 PM EDT ............... ;;;\n    ;;; ------------------------------------------------------------------------ ;;;\n    ;;; \n    ;;; This is an example composition testing various PRINTV functionalities.\n    ;;; Self-evaluation of strings may be used within a printv form to augment\n    ;;; the log output with annotations or other commentary.\n    ;;; \n    ;;; ------------------------------------------------------------------------ ;;;\n    ;;;\n    ;;;   (+ 2 3) =\u003e 5\n    ;;;   *PRINT-CASE* =\u003e :UPCASE\n    ;;;   *PACKAGE* =\u003e #\u003cPACKAGE \"COMMON-LISP-USER\"\u003e\n    ;;;   'SYMBOL =\u003e SYMBOL\n    ;;;   (LET* ((X 0) (Y (1+ X)) (Z (1+ Y)))\n            (VALUES X Y Z)) =\u003e\n               [ [X=0]  [Y=1]  [Z=2] ]\n    ;;;   =\u003e 0, 1, 2\n\nand returns multiple-values:\n\n    0\n    1\n    2\n\n#### Basic Form Evaluation and Tracing \n\nThe fundamental purpose of PRINTV is to log a trace of the forms\nwithin its lexical scope and to log the result of their evaluation to\na specified output destination.  This allows the user to closely monitor\nthe execution of program code, to better understand its\noperation, and to quickly identify problems that may occur due to\nundexpected results.\n\nHere is a simple example that illustrates this basic functionality.\nSay one has written the following code to perform some desired\ncalculation:\n\n    (defvar *y*) \n    (defparameter *x* 2)\n    (setf *y* (sqrt *x*))\n    (setf *y* (/ 1 *y*))\n    \nEvaluation of this block of code results in the value `0.70710677.`\nTo understand more clearly (or simply to track), it's operation, one\ncan simply enclose the code within PRINTV. The principlal benefit of\ninstrumenting your code with PRINTV derives from the capability to produce\na trace, form by form as they are evaluated, and possibly to archive this\ndetailed record of its execution to persistent storage media.\n\n    (:printv\n      (defvar *y*) \n      (defparameter *x* 2)\n      (setf *y* (sqrt *x*))\n      (setf *y* (/ 1 *y*)))\n      \nThis produces the following text to PRINTV's output stream, and still\nresults in the same returned value:  `0.70710677.`\n\n    ;;;   (DEFVAR *Y*) =\u003e *Y*\n    ;;;   (DEFPARAMETER *X* 2) =\u003e *X*\n    ;;;   (SETF *Y* (SQRT *X*)) =\u003e 1.4142135\n    ;;;   (SETF *Y* (/ 1 *Y*)) =\u003e 0.70710677\n\nTo take a step further, one might augment the block of code as\nfollows:\n\n    (:printv\n      (defvar *y*) \n      (defparameter *x* 2)\n      (setf *y* (sqrt *x*))\n      *x*\n      *y*\n      (setf *y* (/ 1 *y*)))\n\nprints:\n\n    ;;;   (DEFVAR *Y*) =\u003e *Y*\n    ;;;   (DEFPARAMETER *X* 2.0) =\u003e *X*\n    ;;;   (SETF *Y* (SQRT *X*)) =\u003e 1.4142135\n    ;;;   *X* =\u003e 2.0\n    ;;;   *Y* =\u003e 1.4142135\n    ;;;   (SETF *Y* (/ 1 *Y*)) =\u003e 0.70710677\n\nand correctly returns the value of the final form:\n\n    0.70710677\n\nThe semantics (apart from tracing the evaluation of enclosed forms)\nare exactly as in PROGN -- the idea being that PRINTV may be used\nliberally just about anywhere, without changing the meaning or\noperation of the forms enclosed.  Notice that bound symbols are also\n'forms'.  Inclusion of `*x*` and `*y*` forms within the implicit-progn\nof a PRINTV expression (in any but the last position) has no effect on\nthe overall semantics of the given of code, but a record of the\nsymbol-name and its value at the current point of execution is\nincorporated into the text sent to PRINTV's output stream.  This is\nthe essence of logging with PRINTV.\n\nSelf-evaluating forms, such as literal strings, are also useful within\nthe context of PRINTV.  Since they evaluate to themselves, there is\nlittle point in showing the result of this evaluation. Instead,\nself-evaluating forms are handled specially in order to provide\nadiitional capabilities that may be useful within PRINTV's logging\noutput.  In the case of literal strings, the text of the string is\npresented directly within the logging output, providing a quick means\nof augmenting your logs with descriptive annotations or other helpful\nexposition:\n\n    (:printv\n      \"This was my homework assignment over spring break\"\n      \"\"\n      (defvar *y*) \n      (defparameter *x* 2)\n      (setf *y* (sqrt *x*))\n      (setf *y* (/ 1 *y*)))\n\nThis produces a slightly more informative output to the PRINTV\nstream. Notice that the empty string `\"\"` was used to insert a 'blank'\nline within the logged output:\n\n    ;;; This was my homework assignment over spring break\n    ;;; \n    ;;;   (DEFVAR *Y*) =\u003e *Y*\n    ;;;   (DEFPARAMETER *X* 2) =\u003e *X*\n    ;;;   (SETF *Y* (SQRT *X*)) =\u003e 1.4142135\n    ;;;   (SETF *Y* (/ 1 *Y*)) =\u003e 0.70710677\n    \nOne might make use of other simple self-evaluating forms, such as\nnumbers and 'ordinary' keywords (more on these later) to further\nannotate the output:\n\n    (:printv :FINAL-ANSWER  \"\" 0.70710677)\n\nPrints:\n\n    ;;;   :FINAL-ANSWER\n    ;;; \n    ;;;   0.70710677\n\nFinally, you may rely on PRINTV to always respect multiple-values,\nwhich are denoted in logging output as a series of comma-separated\nforms. Multiple-values are always correctly returned when produced by\nevaluation of any form, both within PRINTV's implicit progn and when\nproduced as the result of the final form:\n\n    (:printv\n       (values 1 2 3)\n       (values 'a 'b 'c))\n\nLogs the following:           \n\n    ;;;   (VALUES 1 2 3) =\u003e 1, 2, 3\n    ;;;   (VALUES 'A 'B 'C) =\u003e A, B, C\n\nAnd returns as multiple-values:\n\n    A\n    B\n    C\n    \n#### LET, LET*, and COND: Binding Forms and Conditional Clauses\n\nFor certain, more structurally complex forms, simply printing the\nvalue to which it evaluates does not necessarily provide much insight\nas to how that value may have been arrived at.  Lexical binding forms,\nLET and LET*, are such a case.  These forms often perform significant\ncalculations as part of their initial binding lists that are\nsubsequently referenced by the following series of form(s) within\ntheir implicit progn. For example, the following code should return\na humanly readable string that denotes the time exactly 24 hours ago:\n\n    (let* ((a (get-universal-time))\n           (b (- a 86400)))\n       (format-universal-time nil b))\n\nMight return `\"Tuesday, April 16, 2013 11:45:50 AM EDT\"`, but the\nsignificant calculation that is performed to arrive at this result is\nnot apparent from just the result of this evaluation. To get a better\nunderstanding what is happening, one needs to see the values that are\nlexically bound to the symbols `a` and `b`. PRINTV supports this kind\nof introspection by providing special handling of LET and LET* binding\nforms:\n\n    (printv\n      (let* ((a (get-universal-time))\n             (b (- a 86400)))\n         (format-universal-time nil b)))\n\nLogs the following:\n\n    ;;;   (LET* ((A (GET-UNIVERSAL-TIME)) (B (- A 86400)))\n            (FORMAT-UNIVERSAL-TIME NIL B)) =\u003e\n               [ [A=3575203206]  [B=3575116806] ]\n    ;;;   =\u003e \"Tuesday, April 16, 2013 12:00:06 PM EDT\"\n\nNotice the values bound to `a` and `b` are logged with a printed\nrepresentation denoted by square brackets.\n\nSimilarly, within COND forms, evaluation of the clause heads is of\ninterest not only to track what they evaluate *to*, but also to see\n*which* ones are evaluated *at all* (COND clauses provide\nshort-circuiting semantics).  PRINTV provides special handling for\nthese as well:\n\n    (printv\n      (cond\n        ((null     :x) (values \"no\"  1))\n        ((stringp  :x) (values \"no\"  2))\n        ((symbolp  :x) (values \"no\"  3))\n        ((keywordp :x) (values \"yes\" 4))\n        (t             (values \"no\"  5))))\n\nLogs the following:\n          \n    ;;;   (COND ((NULL :X) (VALUES \"no\" 1)) ((STRINGP :X) (VALUES \"no\" 2))\n                ((SYMBOLP :X) (VALUES \"no\" 3)) ((KEYWORDP :X) (VALUES \"yes\" 4))\n                (T (VALUES \"no\" 5))) =\u003e\n           [(NULL :X) -\u003e NIL]\n           [(STRINGP :X) -\u003e NIL]\n           [(SYMBOLP :X) -\u003e T]          \n    ;;;   =\u003e \"no\", 3\n\nAnd returns multiple-values:\n\n    \"no\"\n    3\n\nNotice that each cond clause evaluated, depicted by a bracketed\n*[clause -\u003e result]* representation, is displayed below the COND form\nand before the final result in the printv output text.  By examining\nthis output, it is evident that the successful clause head was\n`(symbolp :x)` which, of course, is true, but that the more specific\nclause head `(keywordp :x)` that, most likely, was the author's\nintent, was never evaluated. This is a common error which can be fixed\nby reordering the two clauses, but one that can be sometimes be\nelusive in actual code.  With PRINTV, it was immediately obvious what\nthe problem was and how to resolve it:\n\n    (printv\n      (cond\n        ((null     :x) (values \"no\"  1))\n        ((stringp  :x) (values \"no\"  2))\n        ((keywordp :x) (values \"yes\" 3))        \n        ((symbolp  :x) (values \"no\"  4))\n        (t             (values \"no\"  5))))\n\nNow logs:\n          \n    ;;;   (COND ((NULL :X) (VALUES \"no\" 1)) ((STRINGP :X) (VALUES \"no\" 2))\n                ((KEYWORDP :X) (VALUES \"yes\" 3)) ((SYMBOLP :X) (VALUES \"no\" 4))\n                (T (VALUES \"no\" 5))) =\u003e\n           [(NULL :X) -\u003e NIL]\n           [(STRINGP :X) -\u003e NIL]\n           [(KEYWORDP :X) -\u003e T]          \n    ;;;   =\u003e \"yes\", 3\n\nAnd, as was the programmer's intent, now correctly returns:\n\n    \"yes\"\n    3\n\nThis type of programmer error does not cause a warning that can be\nreported by the compiler, or flag an error to alert the user at runtime. In\nfact, the original version of the code is entirely *valid*, but it does not\ncorrectly represent the programmer's intent.  This is just one of many\nsituations in which the 'tracing' functionality of PRINTV is\nindespensible for quick identification and resolution of errors in\ncode semantics.\n\nBy helping to quickly identify and locate the faulty\nsemantics caused by improper order of the COND clauses, PRINTV has\npossibly saved you enough time for a coffee-break!\n    \n#### Extended Typography: Bells, and Whistles\n\nAs you begin to spend time crafting and annotating your debug-logging\noutput, you may start to think of what you include within PRINTV as a\nprimitive text markup DSL. And so you may begin to wish for a little\nmore typographic panache.  This, at least, occurs for me now and\nagain. It is very important, though, that in our pursuit of ever more\nbeautiful output we do not introduce artifacts that could change the\nmeaning or operation of code enclosed in a PRINTV form.  This limits\nour options when choosing the manner in which we can implement such\nfeatures.\n\nYou may have noticed from the first example shiown in section USAGE\nthat the keywords `:hr` and `:ts` were used to insert into the log\na thin horizontal rule and timestamp, respectively.  For these kinds of\nsimple, self-contained tags, it is reasonable that we can choose a few\nkeywords such as these and handle them specially when encountered\nwithin the implicit progn of a PRINTV. In doing so, we can simply\nspecify that they will not be evaluated, effectively returning\n`(values)`. Inclusion of these tags will have no effect, even if\nthey are used in the final (value-returning) position of the\nprogn. Further, the special keywords that control these typographic\nfeatures are user-configurable (see CONFIGURABLES, below).\n\nBe aware, however, that although useful, these typographic extensions\nmay potentially at odds with the PRINTV philosophy of pure\ntransparency. In particular, although the formatting tags are\n\"invisible\" within the scope of PRINTV and returned values of\nevaluated forms are automatically passed through to be correctly\nreturned form PRINTV when these tags appear in the tail position of\nPRINTV's implicit progn, this will not the case if PRINTV is disabled\n(see `disable-printv`) or when the printv is removed.  The safest\napproach is to never use these tags in the tail position -- making\nsure that the final form is the one returning the desired values.  In\npractice I have not found this to be difficult to manage, and,\npractically speaking, I have not come up with a *totally* safe\nalternative means to specify extended formatting.  In my opinion, the\nbenefits are worth this small inconvenience; also, one is always free\nto ignore the extended formatting features entirely, or even disable\nthem by configuring them to be uninterned symbols, like so:\n\n    (setf *major-separator*      (gensym))\n    (setf *minor-separator*      (gensym))\n    (setf *timestamp-designator* (gensym))    \n\nThe following are a few examples which illustrate some PRINTV's that\nuse the timestamp, thin and thick rule features:\n\n    (printv :hr \"Section 1.\" :hr)\n\nPrints:\n\n    ;;; ------------------------------------------------------------------------ ;;;\n    ;;; Section 1.\n    ;;; ------------------------------------------------------------------------ ;;;\n    \nReturns:\n\n    \"Section 1.\"\n\nSimilarly:\n\n    (printv :ff :hr (* 2 (+ 3 (/ -1 4))) :hr :ff)\n\nPrints:\n\n    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n    ;;; ======================================================================== ;;;\n    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n    ;;; ------------------------------------------------------------------------ ;;;\n    ;;;   (* 2 (+ 3 (/ -1 4))) =\u003e 11/2\n    ;;; ------------------------------------------------------------------------ ;;;\n    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n    ;;; ======================================================================== ;;;\n    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\nReturns:\n\n    11/2\n\nAnother special markup tag (by default) is `:ts`, which causes a\nhuman-readable timestamp to be incorporated into the printv log output:\n\n    (printv :hr :ts :hr (machine-version) :hr)\n\nPrints:\n\n    ;;; ------------------------------------------------------------------------ ;;;\n    ;;; .............. Wednesday, April 17, 2013 06:39:03 PM EDT ............... ;;;\n    ;;; ------------------------------------------------------------------------ ;;;\n    ;;;   (MACHINE-VERSION) =\u003e \"Intel(R) Xeon(R) CPU           E5462  @ 2.80GHz\"\n    ;;; ------------------------------------------------------------------------ ;;;\n\nReturns:\n\n    \"Intel(R) Xeon(R) CPU           E5462  @ 2.80GHz\"\n\n  \n\n#### Enablement and Control of Output\n\nA principle design-goal of this PRINTV library is to provide a number\nof options of the destinations to where the output may be directed,\nand a robust api with which the user may flexibly select one\nwithin some particular dynamic extent of program\nexecution. Under-the-hood, output control is implemeted using two\nspecial-variables: `*default-printv-output*` and `*printv-output*`\n(see CONFIGURABLES).\n\n\n\n\n\n#### Macro debugging with PPMX\n\nAs one last example, using the magic of PPMX we can finally have a\nlook at the internal operation of PRINTV itself -- this is very\nfrequently necessary when developing extensions to PRINTV.  We will show\nthe macroexpansion of a form used in one of our previous examples:\n\n    (ppmx (printv :hr :ts :hr (machine-version) :hr))\n\nPrints:\n    \n    ;;; Form: (PRINTV :HR :TS :HR (MACHINE-VERSION) :HR)\n    ;;;\n    ;;; Macro expansion:\n\n    (FLET ((EXP-1 ()\n             (LET ((*PRINT-READABLY* NIL) #:G3262)\n               (MINOR-SEPARATOR)\n               (TIMESTAMP)\n               (MINOR-SEPARATOR)\n               (FORM-PRINTER '(MACHINE-VERSION))\n               (VALUES-PRINTER\n                (SETF #:G3262 (FUNCALL # (MULTIPLE-VALUE-LIST (MACHINE-VERSION)))))\n               (MINOR-SEPARATOR)\n               (VALUES-LIST #:G3262))))\n      (ETYPECASE *PRINTV-OUTPUT*\n        (NULL (PROGN :HR :TS :HR (MACHINE-VERSION) :HR))\n        (PATHNAME\n         (BORDEAUX-THREADS:WITH-RECURSIVE-LOCK-HELD (*PRINTV-LOCK*)\n           (WITH-OPEN-FILE\n               (LOGFILE *PRINTV-OUTPUT* :DIRECTION :OUTPUT :IF-DOES-NOT-EXIST\n                :CREATE :IF-EXISTS :APPEND)\n             (WITH-PRINTV-OUTPUT-TO (LOGFILE)\n               (EXP-1)))))\n        (STREAM\n         (BORDEAUX-THREADS:WITH-RECURSIVE-LOCK-HELD (*PRINTV-LOCK*)\n           (EXP-1)))))\n    ;;;\n    ;;;     \n\n    \n#### Configurables\n\n* `*default-printv-output*` [`*trace-output*`]\n\u003e Controls the default stream to which PRINTV/PPMX output will be\n\u003e directed under the following circumstances: initially on program\n\u003e load, subsequent to any evaluation of `(enable-printv)` or to `(enable-printv-output)`\n\u003e with stream argument unspecified, or within the dynamic extent of\n\u003e `(with-printv-output-to () ...)` macro-call (i.e., *stream*\n\u003e argument (second form) as NIL).\n\n* `*printv-output*` [`*default-printv-output*`]\n\u003e The stream to which PRINTV/PPMX is currently directed. Types of\n\u003e valid values this may hold include streams (log to stream), pathnames\n\u003e (log to file), or null (disable printv). May be\n\u003e affected using functions `enable-printv-output` and\n\u003e `disable-printv-output` or within the dynamic extent of macro\n\u003e call `with-printv-output-to`. See also the more powerful\n\u003e 'enablement' controls provided by: `enable-printv`,\n\u003e `disable-printv`, `with-printv-enabled`, and `with-printv-disabled.`\n\n* `*major-separator*` [`:ff`]\n\u003e A special keyword defined to create a *thick* horizontal rule in\n\u003e printed output; it is not evaluated.\n\n* `*minor-separator*` [`:hr`]\n\u003e A special keyword defined to create a *thin* horizontal rule in\n\u003e printed output; it is not evaluated.\n\n* `*printv-macro-char*` [`#\\^`]\n\u003e Character to use as macro-character to implement reader extension\n\u003e that will behave as if the next form that follows is enclosed within\n\u003e a PRINTV macro-call.  Return values and program operation will be\n\u003e unffected, and so may be used as-needed without fear of corrupting\n\u003e live code.\n\n* `*ppmx-macro-char*` [`#\\$`]\n\u003e Character to use as macro-character to implement reader extension\n\u003e that will behave as if the next form that follows is enclosed within\n\u003e a PPMX macro-call.  Note that this expansion will NOT be evaluated, and\n\u003e so program operation may necessarily be affected if indescriminantly\n\u003e introduced into live code.\n\n* `*figlet-executable*` [`\"figlet\"`]\n\u003e Specifies the path where the desired FIGLET executable is found on\n\u003e this system. If provided as simple command name without\n\u003e specification of absolute directory (the default), this command will\n\u003e be invoked if valid executable with this name can be found in the\n\u003e default shell search path of the user who owns the current common-lisp\n\u003e process. \n\n* `*figlet-font*` [`\"standard\"`]\n\u003e The name of the (ASCII) 'figlet font' to be used in FIGLET output.\n\n### Extension\n\nFor the most part, extensions to PRINTV may be incorporated by\nimplementing additional clauses within the function `EXPANDER`, using\nthe existing clauses as a template to get started. Typically,\nevaluated forms will involve construction of two function-call\ns-expressions: a `FORM-PRINTER` to show what is being evaluated, and a\n`VALUES-PRINTER` to show the result.\n\nFor all expansions except those intended as simple typographic\ncommands, it is very important that as a result of expansion the\nsymbol designated by `RESULT-SYM` is set to a multiple-value-list of\nthe evaluation of the original form transformed by the function\ndesignated by `VALUES-TRANS-FN`, and is supplied as the argument to\nthe `VALUES-PRINTER` function call.\n\nFor example, the `EXPANDER` clause of a simple, evaluated form appears\nas follows. Note the first line which, if it evaluates to a non-nil\nvalue, designates the expansion that follows to be the one\napplicable for the given form:\n\n    ((or (consp form) (and (symbolp form) (not (keywordp form))))\n      `((form-printer   ',form)\n        (values-printer (setf ,result-sym (funcall ,values-trans-fn\n                                           (multiple-value-list ,form))))))\n                                             \n### Desiredata\n\n1. Cleaner formatted output from LET, LET*, and COND. \n2. Consistent \";;;\" prefixing for every line of output for LET, LET*,\n   COND, and other multi-line evaluated forms.\n3. Dynamic tracking of indent according to structure.\n4. Forms with implicit progn occurring within printv should have that\n   progn recursively PRINTVed, with incorporation of formatting\n   described by items 1, 2, and 3, above.\n5. Test Suite\n\n\n\n","funding_links":[],"categories":["Online editors ##"],"sub_categories":["Third-party APIs"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanlentz%2Fprintv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanlentz%2Fprintv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanlentz%2Fprintv/lists"}