Skip to content

hyotang666/trivial-formatter

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
src
 
 
 
 
 
 

TRIVIAL-FORMATTER 10.0.0

What is this?

Code formatter for common lisp. Please see trivial-formatter's source code. This file is formatted by trivial-formatter itself.

Alternatives.

cl-syntax for SLIME user.

Reader Syntax Coventions for Common Lisp and SLIME

trivial-indent for SLIME user.

A very simple library to allow indentation hints for SWANK.

cl-indentify

Library and command line utility to automatically indent Common Lisp source files.

Usage

(trivial-formatter:fmt :your-system :supersede)

For detail, see spec files.

Line width.

The default depends on implementation. (Probably 80 though.) You can control it with an ordinary common lisp pretty printing system way, i.e. using *PRINT-RIGHT-MARGIN*.

(let ((*print-right-margin* 100)) ; <-- Specify line width with 100.
  (trivial-formatter:fmt :your-system :supersede))

Strict mode.

When you invoke debugger, some implementations (e.g. ECL) are into :cl-user package. In such cases, the LOOP macro backtrace form becomes ugly.

(LOOP YOUR-PROJECT::FOR YOUR-PROJECT::I YOUR-PROJECT::UPFROM 0 ...)

Using keyword symbols as loop macro keywords avoid such ugly forms.

(LOOP :FOR YOUR-PROJECT::I :UPFROM 0 ...)

In strict mode, trivial-formatter formats loop macro keywords into keyword symbols. To enable it binds *STRICT-LOOP-KEYWORD-P* with T.

* (let ((trivial-formatter:*strict-loop-keyword-p* t))
    (trivial-formatter:fmt :your-system :supersede))

From developer

Reader.

If your source codes have special reader macros, trivial-foramtter signals an error about unknown reader macros. In such cases, you can extend the reader with an ordinary common lisp way because trivial-formatter heavily depends on readtable especially NAMED-READTABLES. To extend it, your reader macro functions must return an intermediate object.

(let ((*readtable* (named-readtables:copy-named-readtable 'as-code)))
  (set-macro-character #\! (lambda (stream char) `(,char ,(read stream))))
  (read-as-code))
!hoge
=> (#\! HOGE)

For details, see CLHS and NAMED-READTABLES.

Printer.

When you make intermediate objects, you need to make pretty print functions for it. Trivial-formatter heavily depends on a pretty-printing system so you can extend with an ordinary common lisp way.

(defun !-printer (stream exp)
  (format stream "~<~A~S~:>" exp))
(set-pprint-dispatch '(cons (eql #\!) (cons * null)) '!-printer)
(print-as-code '(#\! hoge))
=> !hoge

For detail, see CLHS.

Load external formatters.

Trivial-formatter can load external formatters. You can write extension codes as external formatters.

External formatters file must be named "formatters.lisp". Trivial-formatter searches file from *external-formatters-directories*. The default is quicklisp's local-projects directory and roswell's local-projects directory.

Since to share and/or to control versions of external formatters file by a version control system (e.g. git), external-formatter directories that are directly under local-projects directories of quicklisp and roswell were added as the default search paths. (e.g. ~/.roswell/local-projects/external-formatter/)

Hence external formatters file directly placed under local-projects directories of quicklisp or roswell is deprecated from version 10.

Package trivial-formatter-user.

In trivial-formatter-user, you can use deformatter, pprint-fun-call, and pprint-linear-elt with ordinary common lisp symbols.

PPRINT-FUN-CALL

PPRINT-FUN-CALL cares about key-value pairs.

(pprint-fun-call nil '(asdf:component-pathname component :direction :output :if-does-not-exist :create :if-exists if-exists))
(ASDF:COMPONENT-PATHNAME COMPONENT
                         :DIRECTION :OUTPUT
                         :IF-DOES-NOT-EXIST :CREATE
                         :IF-EXISTS IF-EXISTS))
NIL

PPRINT-LINEAR-ELT

PPRINT-LINEAR-ELT set indent current and put every CDR element with a newline.

(pprint-linear-elt nil '(asdf:component-pathname component :direction :output :if-does-not-exist :create :if-exists if-exists))
(ASDF:COMPONENT-PATHNAME COMPONENT
                         :DIRECTION
                         :OUTPUT
                         :IF-DOES-NOT-EXIST
                         :CREATE
                         :IF-EXISTS
                         IF-EXISTS))
NIL

DEFORMATTER

DEFORMATTER care about package existence and symbol confliction and set-pprint-dispatch.

(macroexpand '(deformatter package symbol (stream exp)
                (format stream "~A" exp)))

(WHEN (FIND-PACKAGE "PACKAGE")
  (DEFUN #:PPRINT-SYMBOL3440 (STREAM EXP) (FORMAT STREAM "~A" EXP))
  (SET-PPRINT-DISPATCH
   `(CONS (MEMBER ,(UIOP/PACKAGE:FIND-SYMBOL* "SYMBOL" "PACKAGE")))
   '#:PPRINT-SYMBOL3440)
  '#:PPRINT-SYMBOL3440)

TRIVIAL-FORMATTER-USER:SET-PPRINT-DISPATCH

When you want to set pretty-printing functions temporarily, you need to bind *PRINT-PPRINT-DISPATCH* and use TRIVIAL-FORMATTER-USER:SET-PPRINT-DISPATCH instead of CL:SET-PPRINT-DISPATCH otherwise temporal function never worked.

CL:SET-PPRINT-DISPATCH is shadowed in the package :TRIVIAL-FORMATTER-USER.

(deformatter sxql where (stream exp)
  (let ((*print-pprint-dispatch* (copy-pprint-dispatch)))
    (set-pprint-dispatch '(cons (member :and :or)) 'pprint-linear-elt)
    (pprint-fun-call stream exp)))

Product's goal

License

MIT

Developed with

SBCL

Tested with

  • SBCL/2.2.4

  • ECL/20.4.24

  • CMUCL/21D

  • CCL/1.12.1 ; Failed but pprit portability issue.

  • Allegro/10.1 ; Failed but pprit portability issue.

  • CLISP/2.49 ; Failed, not supported. For details see below.

  • ABCL/1.9.0 ; Failed, not supported. For details see below.

Note

Trivial-formatter works portable at least implementations above. But it never means works samely. For example, the IF format is different. SBCL prints a newline even if elements are short, but other implementations may not.

#+sbcl
(if a
    b
    c)

#+(or ecl ccl)
(if a b c)

Known issue.

CLISP

CLISP is not supported. CLISP say

The Lisp Pretty Printer implementation is not perfect yet.

ABCL

PPRINT-NEWLINE issue.

ABCL is not supported due to its issues. Related issues are this.

Reading behavior for double colon keyword symbol.

Trivial formatter formats double colon keyword symbol to single colon keyword symbol as canonicalization.

CLHS says

::aaaaa undefined

Many implementations canonicalize it, but abcl does not canonicalize.

#+(or sbcl ccl ecl clisp cmucl acl)
* ::double-coloned
:DOUBLE-COLONED

#+abcl
* ::double-coloned
:|:DOUBLE-COLONED|

If you depend on this behavior, trivial-formatter does not fit you.

Reader.

When the reader macro conflicts, such reader macros are ignored silently. You can add new reader macros, but can not modify already existing reader macros.

FORMAT-CONTROL

Trivial-formatter can not adjust ~newline format control indentation.

Installation

To install trivial-formatter, roswell is recommended.

$ ros install hyotang666/trivial-formatter

To load trivial-formatter to running lisp environment, evaluate below in the REPL.

* (ql:quickload :trivial-formatter)

About

Code formatter for common lisp.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published