Skip to content
A DSL wrapping cl:format's syntax with something more lispy
Common Lisp
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
COPYING.md
README.md
format-string-builder.asd
format-string-builder.lisp
package.lisp

README.md

A simple package implementing a DSL for generating format strings.

Introduction

Hello World

CL-USER> (define-message hello (name)
           "Hello " :str)
CL-USER> (hello "world")
Hello world
NIL
(make-format-string '(:str)) #| ==> "~a" |#
(make-format-string '((:map () :str))) #| ==> "~{~a~}" |#

(define-message print-comma-separated (values)
  (:map () :str))

Api Reference

(make-format-string spec) #| function |#

Takes a format string specification and turns it into a string.

(format* stream spec &rest args) #| macro |#

Use like CL:FORMAT, except translate a format specification to a string at macroexpansion time.

(define-message (stream-symbol &rest format-args) &body spec) #| macro |#

Defines a function that takes a stream and the arguments to be formatted and then formats the arguments to the stream. The spec is compiled to a string at macroexpansion time, so this should be reasonably efficient.

TODO: document the API for defining directives.

DSL Reference

A spec consists of operators and literals. Literals are either strings, characters or integers and they are formatted as-is via princ. There are two kinds of operators: simple operators and compound ones. Simple operators correspond to format control directives and represented in the spec by keywords such as ~A or by lists (keyword . modifiers) and they expand to the corresponding directives. Compound operators correspond to format directives that can contain other directives such as ~{~}. In a spec, these are formatted like flet function definitions:

(keyword (&rest modifiers) &body spec)

Compound operators are further divided into sectioned operators and non-sectioned ones. The difference is that, in non sectioned operators, the body is treated just as a normal spec. In sectioned ones, the body is treated as a list of items to be divided with ~;. See CLHS 22.3 for a full guide to the modifiers for the various format directives.

Simple Format Operations

  • :str --- Translates to ~a, format a lisp value for humans
  • :repr --- Translates to ~s, format a lisp value in a way that can be read by the reader (?)
  • :float --- Translates to ~f, format a float.
  • :dec --- Translates to ~d, format a number as a base 10 number.
  • :decimal --- Translates to ~d, format a number as a base 10 number.
  • :hex --- Translates to ~x, format a number as a base 16 number.
  • :hexadecimal --- Translates to ~x, format a number as a base 16 number.
  • :oct --- Translates to ~o, format a number as a base 8 number.
  • :octal --- Translates to ~o, format a number as a base 8 number.
  • :currency --- Translates to ~$, format a number in a manner suitable for currency
  • :exit --- Translates to ~^, leaves a iteration construct
  • :end-section --- Translates to ~;, divides sections of a construct (TODO: maybe this will go away?)
  • :goto --- Translates to ~*, moves within the list of arguments
  • :fresh-line --- Translates to ~&, ensures we're at the beginning of a line and, possibly adds Modifier-1 linebreaks
  • :ensure-line --- Translates to ~&, alias for :fresh-line
  • :new-line --- Adds a linebreak

Compound Format Operations

Iteration

  • :map --- Translates to {}, iterate over a list passed in
  • :rest --- Translates to @{}, iterate over the rest of the arguments
  • :ap --- Translates to :{}, apply a list to the corresponding enclosed format directives
  • :apply --- Alias for :ap
  • :aprest --- Translates to :@{}, apply the rest of the arguments to the corresponding enclosed format directives
  • :apply-rest --- Translates to :@{}, apply the rest of the arguments to the corresponding enclosed format directives

Conditional Output (Sectioned Operators)

  • :y-or-n --- Translates to :[], if the argument is nil, print first spec otherwise print second
  • TODO: add others here...

Case Control

  • :lowercase --- Translates to (), lowercase all alphabetic characters.
  • :downcase --- Translates to (), alias for :lowercase
  • :uppercase --- Translates to :@(), uppercase all alphabetic characters.
  • :upcase --- Translates to :@(), alias for :uppercase
  • :titlecase --- Translates to :(), titlecase all alphabetic characters.
  • :capitalize --- Translates to :(), alias for :titlecase
  • :initialcap --- Translates to @(), uppercase first character

Justification

TODO: finish documenting this and switch them to sectioned operators.

  • :spread --- Translates to <>
  • :ljust --- Translates to @<>
  • :left --- Translates to @<>
  • :rjust --- Translates to :<>
  • :right --- Translates to :<>
  • :cjust --- Translates to :@<>
  • :center --- Translates to :@<>

Miscellaneous

These are not part of Format, but are defined just to be helpful

  • :own-line --- Translates to &%, ensure that the included text is on its own line, without unnecessary gaps.
You can’t perform that action at this time.