The  CL-ECMA-48 library  was created  to serve  my needs  when writing  an interactive  program that
creates a  more complex  terminal interface.   In order to  clean the  source of  the aforementioned
program a tad, I decided  to split the functionality into a library.  While  I used a small fraction
of these control functions,  the library implements all 162 that the  ECMA-48 standard specifies, as
this was the most sensible option.  The library  also optimizes the control functions emitted to the
most compact representation, in contrast to the preceding handwritten functions which were much less
discerning and  these are also  designed to  be efficient themselves  and avoid any  allocations and
other costly operations.  Due to all of this,  the library is rather niggardly and there's no reason
any of it should consume any more space once loaded nor should it perform poorly.

This library is licensed under the GNU Affero General Public License version three.

The ECMA-48 standard specifies  control functions belonging to the C0, C1,  Fs, and control sequence
sets.  The C0 control  functions are simply a single character; the C1  and Fs control functions are
the escape  control function followed by  a single character; and  the control sequences are  the C1
control function named  CONTROL SEQUENCE INTRODUCER, any  parameters, and then a sequence  of one or
more characters.  While  the standard further distinguishes control sequences  by parameter purpose,
this library  doesn't.  There  are required,  optional, and variadic  parameters.  In  designing the
interface, required parameters are  those with no defualt value; optional  parameters have a default
value; and variadic parameters may or may not have a default value.  Default values may be passed as
NIL, whereas required  parameters must be an  UNSIGNED-BYTE or CHARACTER.  Every  function accepts a
STREAM argument as the last argument,  which defaults to *STANDARD-OUTPUT*.  Variadic parameters are
achieved by passing in a list of the parameters  and this is not checked, but all elements should be
an UNSIGNED-BYTE, CHARACTER, and  NIL only if a default value is specified;  it is also permitted to
pass  an UNSIGNED-BYTE  or CHARACTER  directly, if  only a  single parameter  is to  be used.   If a
parameter with a default value  is given its default value, it won't be  emitted by the function, as
an optimization.  It is purely to aid extended parameter strings that CHARACTER values are permitted
for the parameters.

When defining a control function, use DEFINE-CONTROL-FUNCTION, which accepts a symbol or list of two
symbols  (the  acronym  and name),  a  type  designation  (one  of  :C0,  :C1, :FS,  or  a  modified
lambda-list), the  identity (a  character, character  code, or sequence  of characters  or character
codes), and  an optional documentation  string.  The modified lambda-list  is identical to  a normal
lambda-list, except for  that &REST may have a  default argument using the same  syntax as &OPTIONAL
and that  these two lambda-list keywords  are the only  two allowed and  are not allowed to  be used
together.  The DEFINE-CONTROL-FUNCTION  delegates to more specific macros in  its implementation and
will also assign :EXPANSION and :ACRONYM keys to the plist of the symbol or symbols used to name the
function.

The programmer is expected to reference the  ECMA-48 standard when using this library.  This library
will properly function so long as the implementation provides characters for character codes zero to
one hundred and  twenty seven, inclusive.  If  the implementation uses ASCII, :ASCII  is placed into
*FEATURES* and the code  is optimized for this case, using CL:PRINC, and  provides an internal PRINC
otherwise.  In the common case of a program that is standard, on an implementation that makes use of
a full seven bit character set, there should be no additional issues.

All of the following examples can be thought of  as being expanded in the context of the COMMON-LISP
package, sans  a few  examples that will  be explained.  In  particular, forms  containing character
values are replaced  by an equivalent #.  expression that will produce identical  behavior if loaded
and  all shown  instances of  NULL are  referred to  as COMMON-LISP:NULL,  due to  the NULL  control
function necessitating shadowing the symbol.

The generated functions will  now be exemplified with the DEFUN forms generated.   To begin, the C0,
C1, and Fs functions are all rather similar, sans the particular values emitted:

(DEFINE-CONTROL-FUNCTION (CR CARRIAGE-RETURN) :C0 #X0D
  "Carriage return moves to the home or limit position of a line.")

(DEFUN CARRIAGE-RETURN (&OPTIONAL (STREAM *STANDARD-OUTPUT*) &AUX (*STANDARD-OUTPUT* STREAM))
  "Carriage return moves to the home or limit position of a line."
  (DECLARE (IGNORABLE STREAM))
  (WRITE-CHAR #.(CODE-CHAR #X0D))
  (VALUES))

(DEFINE-CONTROL-FUNCTION (CSI CONTROL-SEQUENCE-INTRODUCER) :C1 #X5B
  "Introduce a control sequence.")

(DEFUN CONTROL-SEQUENCE-INTRODUCER
    (&OPTIONAL (STREAM *STANDARD-OUTPUT*) &AUX (*STANDARD-OUTPUT* STREAM))
  "Introduce a control sequence."
  (DECLARE (IGNORABLE STREAM))
  (WRITE-STRING #.(FORMAT NIL "~C~C" (CODE-CHAR #X1B) (CODE-CHAR #X5B)))
  (VALUES))

(DEFINE-CONTROL-FUNCTION (DMI DISABLE-MANUAL-INPUT) :FS #X60
  "Disable the manual input facilities of a device.")

(DEFUN DISABLE-MANUAL-INPUT (&OPTIONAL (STREAM *STANDARD-OUTPUT*) &AUX (*STANDARD-OUTPUT* STREAM))
  "Disable the manual input facilities of a device."
  (DECLARE (IGNORABLE STREAM))
  (WRITE-STRING #.(FORMAT NIL "~C~C" (CODE-CHAR #X1B) (CODE-CHAR #X60)))
  (VALUES))

The majority of the machinery of this library concerns itself with the control sequences.  To begin,
control sequences  with only required  parameters produce  rather similar and  straightforward code,
regardless of the number of parameters:

(DEFINE-CONTROL-FUNCTION (SLH SET-LINE-HOME) (N) (#X20 #X55)
  "Establish position n of the active line as the line home.")

(DEFUN SET-LINE-HOME (N &OPTIONAL (STREAM *STANDARD-OUTPUT*)
                      &AUX (*STANDARD-OUTPUT* STREAM) (*PRINT-BASE* 10) *PRINT-RADIX*)
  "Establish position n of the active line as the line home."
  (DECLARE (IGNORABLE STREAM) (TYPE (OR UNSIGNED-BYTE CHARACTER) N))
  (CONTROL-SEQUENCE-INTRODUCER)
  (PRINC N)
  (WRITE-STRING #.(FORMAT NIL "~C~C" (CODE-CHAR #X20) (CODE-CHAR #X55)))
  (VALUES))

(DEFINE-CONTROL-FUNCTION (SPI SPACING-INCREMENT) (LINE CHARACTER) (#X20 #X47)
  "Establish line and character spacing in terms of SSU.")

(DEFUN SPACING-INCREMENT (LINE CHARACTER &OPTIONAL (STREAM *STANDARD-OUTPUT*)
                          &AUX (*STANDARD-OUTPUT* STREAM) (*PRINT-BASE* 10) *PRINT-RADIX*)
  "Establish line and character spacing in terms of SSU."
  (DECLARE (IGNORABLE STREAM)
           (TYPE (OR UNSIGNED-BYTE CHARACTER) LINE CHARACTER))
  (CONTROL-SEQUENCE-INTRODUCER)
  (PRINC LINE)
  (WRITE-CHAR #.(CODE-CHAR #X3B))
  (PRINC CHARACTER)
  (WRITE-STRING #.(FORMAT NIL "~C~C" (CODE-CHAR #X20) (CODE-CHAR #X47)))
  (VALUES))

Control  sequences  containing  optional  parameters  required  the  most  interesting  and  complex
optimization  mechanisms and,  in an  effort to  produce code  as an  intelligent programmer  would,
further optimization  in the  case of  only one or  two optional  parameters, which  encompasses all
standard control sequences, was performed:

(DEFINE-CONTROL-FUNCTION (CUU CURSOR-UP) (&OPTIONAL (COUNT 1)) #X41
  "Move the cursor up count times.")

(DEFUN CURSOR-UP (&OPTIONAL (COUNT 1) (STREAM *STANDARD-OUTPUT*)
                  &AUX (*STANDARD-OUTPUT* STREAM) (*PRINT-BASE* 10) *PRINT-RADIX*)
  "Move the cursor up count times."
  (DECLARE (IGNORABLE STREAM)
           (TYPE (OR NULL UNSIGNED-BYTE CHARACTER) COUNT))
  (CONTROL-SEQUENCE-INTRODUCER)
  (OR (NULL COUNT) (EQL COUNT 1) (PRINC COUNT))
  (WRITE-CHAR #.(CODE-CHAR #X41))
  (VALUES))

(DEFINE-CONTROL-FUNCTION (CUP CURSOR-POSITION) (&OPTIONAL (LINE 1) (CHARACTER 1)) #X48
  "Reposition the cursor at line and character.")

(DEFUN CURSOR-POSITION (&OPTIONAL (LINE 1) (CHARACTER 1) (STREAM *STANDARD-OUTPUT*)
                        &AUX (*STANDARD-OUTPUT* STREAM) (*PRINT-BASE* 10) *PRINT-RADIX*)
  "Reposition the cursor at line and character."
  (DECLARE (IGNORABLE STREAM)
           (TYPE (OR NULL UNSIGNED-BYTE CHARACTER) LINE CHARACTER))
  (CONTROL-SEQUENCE-INTRODUCER)
  (OR (NULL LINE) (EQL LINE 1) (PRINC LINE))
  (OR (NULL CHARACTER)
      (EQL CHARACTER 1)
      (PROGN (WRITE-CHAR #.(CODE-CHAR #X3B)) (PRINC CHARACTER)))
  (WRITE-CHAR #.(CODE-CHAR #X48))
  (VALUES))

There is a single  control sequence, TABULATION CENTERED ON CHARACTER, which  is responsible for the
library permitting a combination of required and  optional or variadic parameters and care was taken
to generate code as an intelligent programmer would, even in this case:

(DEFINE-CONTROL-FUNCTION (TCC TABULATION-CENTERED-ON-CHARACTER)
 (LINE &OPTIONAL (CHARACTER 32)) (#X20 #X63)
 #|Documentation string omitted here because of length.|#)

(DEFUN TABULATION-CENTERED-ON-CHARACTER
    (LINE &OPTIONAL (CHARACTER 32) (STREAM *STANDARD-OUTPUT*)
     &AUX (*STANDARD-OUTPUT* STREAM) (*PRINT-BASE* 10) *PRINT-RADIX*)
  #|Documentation string omitted here because of length.|#
  (DECLARE (IGNORABLE STREAM)
           (TYPE (OR UNSIGNED-BYTE CHARACTER) LINE)
           (TYPE (OR NULL UNSIGNED-BYTE CHARACTER) CHARACTER))
  (CONTROL-SEQUENCE-INTRODUCER)
  (PRINC LINE)
  (OR (NULL CHARACTER) (EQL CHARACTER 32)
      (PROGN (WRITE-CHAR #.(CODE-CHAR #X3B)) (PRINC CHARACTER)))
  (WRITE-STRING #.(FORMAT NIL "~C~C" (CODE-CHAR #X20) (CODE-CHAR #X63)))
  (VALUES))

Further care was taken to generate efficient code in the case that more than two optional parameters
were used, which is not the case for any standard control function; similar code is generated in the
case  that required  and optional  parameters are  used  when there's  more than  a single  optional
parameter, but this is not shown in this document:

(DEFINE-CONTROL-FUNCTION EXAMPLE (&OPTIONAL (W 1) X (Y 3) Z) #X54)

(DEFUN EXAMPLE (&OPTIONAL (W 1) X (Y 3) Z (STREAM *STANDARD-OUTPUT*)
                &AUX (*STANDARD-OUTPUT* STREAM) (*PRINT-BASE* 10) *PRINT-RADIX*)
  (DECLARE (IGNORABLE STREAM)
           (TYPE (OR NULL UNSIGNED-BYTE CHARACTER) W X Y Z))
  (CONTROL-SEQUENCE-INTRODUCER)
  (OR (NULL W) (EQL W 1) (PRINC W))
  (LET ((#1=#:X680 (OR (NULL X)))
        (#2=#:Y681 (OR (NULL Y) (EQL Y 3)))
        (#3=#:Z682 (OR (NULL Z))))
    (DECLARE (TYPE BOOLEAN #1# #2# #3#)
             (DYNAMIC-EXTENT #1# #2# #3#))
    (OR (AND #1# #2# #3#) (WRITE-CHAR #.(CODE-CHAR #X3B)))
    (OR #1# (PRINC X))
    (OR (AND #2# #3#) (WRITE-CHAR #.(CODE-CHAR #X3B)))
    (OR #2# (PRINC Y))
    (OR #3# (PROGN (WRITE-CHAR #.(CODE-CHAR #X3B)) (PRINC Z))))
  (WRITE-CHAR #.(CODE-CHAR #X54))
  (VALUES))

There is a  single control function without  differing acronym and name, QUAD,  which is responsible
for permitting a symbol to be used in place of the acronym and name list; this control sequence also
shows the code generated in the case of a variadic parameter:

(DEFINE-CONTROL-FUNCTION QUAD (&REST (N 0)) (#X20 #X48)
  "Position string with layout as specified.  See ECMA-48.")

(DEFUN QUAD (&OPTIONAL N (STREAM *STANDARD-OUTPUT*)
             &AUX (*STANDARD-OUTPUT* STREAM) (*PRINT-BASE* 10) *PRINT-RADIX*)
  "Position string with layout as specified.  See ECMA-48."
  (DECLARE (IGNORABLE STREAM)
           (TYPE (OR LIST UNSIGNED-BYTE CHARACTER) N))
  (CONTROL-SEQUENCE-INTRODUCER)
  (IF (LISTP N)
      (MAPL (LAMBDA (LIST &AUX (ELT (CAR LIST)))
              (OR (NULL ELT) (EQL ELT 0) (PRINC ELT))
              (IF (CDR LIST) (WRITE-CHAR #.(CODE-CHAR #X3B))))
            N)
      (OR (NULL N) (EQL N 0) (PRINC N)))
  (WRITE-STRING #.(FORMAT NIL "~C~C" (CODE-CHAR #X20) (CODE-CHAR #X48)))
  (VALUES))

Correcting any flaws found are the only expected changes to be made.

Copyright (C) 2017,2018,2019 Prince Trippy
Verbatim copying and redistribution of this article are permitted provided this notice is preserved.