Coloring Inside the Lines
The topic of parenthesis paralysis arose in some circles I frequent in relation to Lisp. This is a semi-mythical notion that somehow the use of parentheses in Lisp contributes to cognitive difficulty or the seizing of thought. It is usually used as an excuse not to learn Lisp or make fun of it.
Related to this idea, a person by the name of Alessio Stalla hacked the Lisp reader to try and present a syntax that is more "newbie friendly". The actual hack is pretty cool.
His altering of the syntax of lisp made it look like what follows. I have some fundamental disagreements about the syntax--in particular the if expression consequence and alternate both being grouped by the { } characters associated with the if form. But that conversation is for another day...
with-output-to-string (str) { print #\c str; if (> foo 45) { progn { print 3; print 4; } print 5; } }
This generated some discussion and I decided to extend a post I had done in Usenet into a blog entry.
I've hacked scheme for a long time and lisp for a little while. I believe there is such a thing as parenthesis-paralysis. After some careful introspection upon it over the years, I've come to a belief about how it arises and presents.
It appears to me to happen because of two concepts:
- Indention style
- Destructuring bind, as performed by a human reading code.
Indention style, especially in lisp, is extremely important and emacs/slime is the only thing that culturally "does it correctly out of the box". It does it correctly, because the cultural indention style of Lisp appears to be exactly defined as what SLIME produces!
Most editor's notion of auto-indention is that when you hit return, you align with the first non-whitespace character on the previous line OR you just indent X columns. Combine this with a regular Joe's idea of indention being TAB, and you have a disastrous situation. For example suppose my tab stops are set to 4 in the following example. Every place you see a period is where I hit tab:
(let ((thing 42) . (stuff 99) . (qux 100)) . (format t "Hi~%"))
Looks like crap doesn't it?
In other block structured languages, a simple tab is sufficient for code readability, e.g., C:
void foo(void) { . int thing = 42; . int stuff = 99; . int qux = 100 . printf("Hi\n"); }
Notice, from a mental model of indention, I did exactly the same behavior. I indented the bindings, left a space, then indented the body. People use the same mental model of indention for similar languages. This realization is the key to why people entering lisp for the first time have such a hard time about it.
Let's take a look at Alessio Stalla's precipitating example. If your tab stop is set to 2, then this is exactly how you would type it using a regular editor's default notion of indention and the TAB key.
with-output-to-string (str) { . print #\c str; . if (> foo 45) { . . progn { print 3; print 4; } . . print 5; . } }
We can hypothesize that the author's mental model of code indention appears to follow to the default behavior of their editor and that they write in block structured imperative languages more often than not. I believe the author of the code view this indention model as "correct". I put that in quotes, since it is purely subjective.
In C, C++, PHP, perl, Java, Python, Fortran 90, etc, etc, etc, applying the same mental indention model is just fine and leads to pretty readable code. Specifically in python, it is enshrined.
In Lisp (and somewhat scheme), the default behavior of the editor and/or the mental model of indention is completely wrong. Why? Reason number 2.
Humans use visual whitespace and alignments of similar boundary markers to block off semantically related entities. This helps them to destructure the forms into semantically meaningful sections or elements.
The correctly indented lisp form for the above let form:
(let ((thing 42) (stuff 99) (qux 100)) (format t "Hi~%"))
You can see how the parentheses of the binding forms are aligned with each other. The ( which "sticks out" on the left is the demarcation of the beginning of the binding list. The fact the (format ...) is indented to the left of the bindings, but the right of the let states the bindings are over and tells you this is the body of the let.
Another one, more complex in lisp (done with the mental model of Algol-style indention):
(do ((x 0 (1+ x)) . (y 10 (1- y)) . (z 0 (1+ z))) . ((zerop y) (+ x z)) . (format t "Hi!~%"))
That is terrible, since the test condition vanishes into the step forms. Get rid of the newline in the middle and it is a train wreck. The problem is that this is how editors want to do it by default! I've seen the modern generation of programmers lately. They use pico, notepad, gedit, or kde/gnome stuff (mere toy code editors to me) to write their code and they don't even realize there are others to choose from that are much better. The editors all do this ham fisted auto-indent behavior and can't really be taught otherwise. I can't imagine this while writing lisp. I would go insane. (Arguably, I may be insane, but that's for the courts to figure out.)
Here is the correctly indented do form. Notice how the "nubs" of the left side parentheses on the do visual block allow the eye to disambiguate the step and test forms.
(do ((x 0 (1+ x)) (y 10 (1- y)) (z 0 (1+ z))) ((zerop y) (+ x z)) (format t "Hi~%"))
The crux of the indention issue is that culturally, lisp doesn't indent at the same quanta of indention as other languages over the various semantic forms of the language. Sometimes you need 1/4 of an indention, or 1/2 of one, or a one space difference. Programmers, when presented with such a problem, spend their time laboriously aligning (instead of code writing) parenthesis (often mixing tabs and spaces) so everything looks right--because their editor doesn't do it for them! This is the first part of the paralysis. If they don't know what looks right to begin with, they simply self converge to a self-consistent form of indention and write all their code in it.
The paralysis continues when, not having been grounded in the cultural indention style of lisp or not having an editor that performs it automatically, you start reading other people's code. If you get unlucky and read a collection of people's code that never learned the correct cultural style (each having their own style), then the destructuring process gets amazingly difficult to do. You end up reading character by character, parenthesis by parenthesis, trying to figure out where semantic forms start and end. The most powerful visual/spatial recognizer in your head (like 1/4 of your brain matter is devoted to it!) sits completely idle!
I hacked my emacs/SLIME syntax coloring to be very detailed and show many different classes of semantic ideas: comments, ANSI functions, macros, special forms, type names, special variables, constants, etc, etc, etc to all be color coordinated with each other using color theory to give me insight into what I've written--and whether I'm writing it correctly!The most important change I did was to make the parenthesis color nearly the same as the background color. In a sense they vanish and I'm left with the spatial blocks as an indention structure, which works just fine. I can always resolve a specific parenthesis from the background if I have to to disambiguate a form. Usually, if I screwed something up, the indention level of my code base goes wrong and it is pretty easy to find where I missed a parenthesis.
Oddly, lisp is the only language for which I do this, all others are just white text on a black background and I prefer it that way. Syntax/Semantics highlighting is distracting to me in non lisp languages. It is a requirement for lisp!
After determined use of the colorizing/indention system slime and my hacks provide me, I occasionally find that looking at non-colorized, but correctly indented Lisp, is hard. The parentheses make a visual heaviness that is hard to ignore; I almost read it as ALL CAPS style yelling. If I'm looking at non-colorized and non-culturally indented lisp, I revert to the "hunt and peck" method of trying to destructure the semantic forms in my head. It gets frustrating very quickly.
I honestly believe parenthesis-paralysis is real. The right editor will make the paralysis completely disappear, but the wrong editor will accentuate it.
As a treat (maybe I'm using an esoteric definition of the word...), here is some horrendously written emacs lisp which augments the Lisp syntax highlighter in a certain color theme to in fact delineate various portions of the language to me. It is by no means the whole of what I want out of a Lisp IDE, but is a tiny step that saves me some heartache.
I searched through emacs' color themes for a long time, and mostly came to the conclusion that most of those theme designers had never opened, let alone been in the same state as, a book on color theory or color perception. Bold claims? Sure, but I'm an idiot writing a blog, and that should be normal fare on the Internet. This is why I don't have a comment section.
What I did was started with the idea of warm colors being near to the viewer, cool colors being far. I also took into consideration complimentary colors, color intensity, and a Western view of the general meaning of bare colors. Each axis: warm to cool, color to compliment, and low to high intensity, cultural meaning of color, has a reason to exist in the syntax highlighting. Once you are aware of these meanings it becomes much easier to visually process the code and get a handle of the dependencies in the code. This design isn't finished yet, and over time I'm evolving it as I see what I actually care about when writing Lisp.
Here are some rules about the things I wanted to emphasize or ensure I could easily separate on my screen. These rules are not the complete set I wish to do in my source, just the ones I was generally able to do given the tools I had. Also, things like special forms and macros get the same color, because I haven't found a reason to separate them.
- De-emphasize parentheses both to lower the light emanating from my screen and to make the SLIME indention drive my comprehension. Leave enough presence of the parentheses that I can disambiguate constructs if needed.
- Emphasize special variables. Messing up their use is hard to debug!
- Since code and comments are very different, separate them in color.
- Anything relating to types or compiler optimizations should stand out. They represent hard coded assumptions I shouldn't easily ignore.
- Since macros cannot be applied to apply or funcall or passed to things like mapcar, make them stand out from regular functions.
- If I type a known ANSI function/macro/symbol/condition/etc, I want to know I typed it correctly.
- Keywords often represent symbols used at interface boundaries, so make them very noticeable since one probably has them always in one's mind.
- Constants should be marked as such. Careful with strings since they can take up a lot of screen real estate.
- Anything which is little used or special in the language should be marked with a little bit of emphasis so I know something strange is going on there.
- I specifically want the boolean values to be highlighted since a hidden use of them can change my expectation about the result a function return, or to denote final clauses in cond or other contexts.
- Conditions represent odd control flow, so intensify them.
- General code should be a basic conservative color so I know nothing special is going on there. If I want to know what it does, I'll read it.
Once one has the concrete plan about what one cares about in the Lisp code, you go and start assigning colors. Emphasizing means higher intensity, and separating means putting the concepts into color compliment domains. Choosing colors on the grey/brown intensity scale mean you only care about emphasis in between them, but they don't exist as compliments to other things. A complimentary concept is like code and comments or special variables and non-special variables. Use of warm and cool colors can give a sense of depth in the code allowing once to inspect code quickly for important things, which should be up front.
So, given the semantic plan and color knowledge, I went and assigned colors. This is what I made.
Screenshot 1: Some code out of the MD5 implementation by Pierre R. Mai. Notice how the coloring regularizes the text and makes it easy for your eye to wander over the color blocks inspecting what is present.
Screenshot 2: Some code out of the MIDI library implementation by Mathieu Chabanne, Camille Constant, Emmanuel Necibar, Stephanie Recco, Robert Strandh, David Lewis, Marcus Pearce, and Christophe Rhodes. If I were looking at this code, the special variable *time* would be most important to me. Good thing it is a high intensity color.
This code was generated by me scraping the Common Lisp Hyperspec web pages, munging the symbols and whatnot into the form you see below, and then insufficiently reading the emacs lisp programming manual. It is built on top of the comidia color theme, but I suspect if you change the colors can be customized to another theme. There are some errors in the highlighting like 1+ doesn't get recognized probably because it looks like a +1+ constant in terms of the regexes and I haven't figure out exactly how to fix it. There are other limitations and omissions, like ,@*foo* not being highlighted correctly that I need to figure out as well. I fix them when they annoy me enough.
There are some tasks like highlighting certain grouping parentheses when the point is not in the extent of them that I have no idea how to do. Some of the complex highlighting I want which is more semantics based, e.g., any symbol in a condition spot in a handler-case is colored a specific color but only if it actually exists in the source, and symbols used in functional places should be colored color A, but if used in another namespace are colored according to that namespace, is impossible to do in emacs since the language structure is not fully realized in the editor. Because this simplistic highlighter is semanticly ignorant, if you have a habit of choosing common lisp symbols and using them in other namespaces, this syntax highlighter is probably going to overly upset you.
As for me, I use the non-terminal mode of emacs and I don't intersect namespace boundaries on the same symbol. I don't care if this doesn't work in terminal mode since I will most likely never use that mode. If you decide to use it, good luck.
Frankly, the most useful thing out of this code is the 978 common lisp symbols that have been categorized for your use in a different piece of code.
All the code I wrote in this blog post is under the *spins the wheel* Modified BSD License.
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; set up a color theme that is good enough for now. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (when (require 'color-theme nil 'noerror) (color-theme-comidia)) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Set up some highlighting rules for all known ANSI common lisp functions and ;; macro names, special operators, etc. This assumes color-theme-comidia. ;; This is always tinkerable.... ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defface ansi-lisp-boolean '((t (:foreground "white"))) "Color all of the ANSI Common Lisp Booleans this specific color") (defface ansi-lisp-constant '((t (:foreground "orchid1"))) "Color all of the ANSI Common Lisp Constants this specific color") (defface ansi-lisp-declaration '((t (:foreground "indianred2"))) "Color all of the ANSI Common Lisp Declarations this specific color") (defface ansi-lisp-condition-type '((t (:foreground "indianred2"))) "Color all of the ANSI Common Lisp Condition Types this specific color") (defface ansi-lisp-function '((t (:foreground "pale green"))) "Color all of the ANSI Common Lisp Functions this specific color") (defface ansi-lisp-generic-function '((t (:foreground "cyan"))) ; same as the comida color-theme which I use. "Color all of the ANSI Common Lisp Generic Functions this specific color") (defface ansi-lisp-macro '((t (:foreground "cyan"))) ; same as comida color-theme which I use. "Color all of the ANSI Common Lisp Macros this specific color") (defface ansi-lisp-special-operator '((t (:foreground "cyan"))) ; same as macros and as the comida color-theme. "Color all of the ANSI Common Lisp Special Operators this specific color") (defface ansi-lisp-type '((t (:foreground "red"))) "Color all of the ANSI Common Lisp Types this specific color") (defface ansi-lisp-unknown '((t (:foreground "red"))) "Color all of the ANSI Common Lispthis specific color") (defface ansi-lisp-global-variable '((t (:foreground "yellow2"))) "Color all of the ANSI Common Lisp Globals this specific color") (defface ansi-lisp-expression '((t (:foreground "indianred1"))) "Color all of the ANSI Common Lisp Expressions (like declare) this specific color") (defface ansi-lisp-parenthesis '((t (:foreground "#4d4d3d"))) ; grey25, but with more yellow in it "Color all of the ANSI Common Lisp Parenthesis") (defface ansi-lisp-numbers '((t (:foreground "orchid1"))) "Color all of the ANSI Common Lisp Numbers") ;;; These are functions which produce the individual entries for each kind of ;;; symbol name according to what face they should have. (defun ansi-boolean (x) (list (concatenate 'string "\\<\\(" (symbol-name x) "\\)\\>") 0 ''ansi-lisp-boolean)) (defun ansi-constant (x) (list (concatenate 'string "\\<\\(" (symbol-name x) "\\)\\>") 0 ''ansi-lisp-constant)) (defun ansi-declaration (x) (list (concatenate 'string "\\<\\(" (symbol-name x) "\\)\\>") 0 ''ansi-lisp-declaration)) (defun ansi-condition-type (x) (list (concatenate 'string "\\<\\(" (symbol-name x) "\\)\\>") 0 ''ansi-lisp-condition-type)) (defun ansi-function (x) (list (concatenate 'string "\\<\\(" (symbol-name x) "\\)\\>") 0 ''ansi-lisp-function)) (defun ansi-generic-function (x) (list (concatenate 'string "\\<\\(" (symbol-name x) "\\)\\>") 0 ''ansi-lisp-generic-function)) (defun ansi-macro (x) (list (concatenate 'string "\\<\\(" (symbol-name x) "\\)\\>") 0 ''ansi-lisp-macro)) (defun ansi-special-operator (x) (list (concatenate 'string "\\<\\(" (symbol-name x) "\\)\\>") 0 ''ansi-lisp-special-operator)) (defun ansi-type (x) (list (concatenate 'string "\\<\\(" (symbol-name x) "\\)\\>") 0 ''ansi-lisp-type)) (defun ansi-unknown (x) (list (concatenate 'string "\\<\\(" (symbol-name x) "\\)\\>") 0 ''ansi-lisp-unknown)) (defun ansi-global-variable (x) (list (concatenate 'string "\\<\\(" (symbol-name x) "\\)\\>") 0 ''ansi-lisp-global-variable)) (defun ansi-expression (x) ; used for things like declare (list (concatenate 'string "\\<\\(" (symbol-name x) "\\)\\>") 0 ''ansi-lisp-expression)) (when t ;; sometimes I need to disable this whole thing.... (add-hook 'lisp-mode-hook (lambda () (font-lock-add-keywords nil (append ;; Conventional Constant Variables '(("\\<\\([+][^ +]*[+]\\)\\>" 0 #'ansi-lisp-constant)) ;; Conventional Global Variables, including ANSI ones '(("\\<\\([*][^ *]*[*]\\)\\>" 0 #'ansi-lisp-global-variable)) ;; Lisp Numbers, simple ones, just integers '(("\\<\\([+-]?[0-9]+\\)\\>" 0 #'ansi-lisp-numbers)) ;; I'm a psycho and want my parentheis color to be controlled. '(("\\([()]\\)" 0 #'ansi-lisp-parenthesis)) ;; These are often important to see, but I don't know how to ;; highlight the matching parenthesis with it ;;'(("\\([#][']\\)" 0 'ansi-lisp-boolean)) (mapcar #'ansi-boolean '(nil t)) (mapcar #'ansi-constant '(array-dimension-limit array-rank-limit array-total-size-limit boole-1 boole-2 boole-and boole-andc1 boole-andc2 boole-c1 boole-c2 boole-clr boole-eqv boole-ior boole-nand boole-nor boole-orc1 boole-orc2 boole-set boole-xor call-arguments-limit char-code-limit double-float-epsilon double-float-negative-epsilon internal-time-units-per-second lambda-list-keywords lambda-parameters-limit least-negative-double-float least-negative-long-float least-negative-normalized-double-float least-negative-normalized-long-float least-negative-normalized-short-float least-negative-normalized-single-float least-negative-short-float least-negative-single-float least-positive-double-float least-positive-long-float least-positive-normalized-double-float least-positive-normalized-long-float least-positive-normalized-short-float least-positive-normalized-single-float least-positive-short-float least-positive-single-float long-float-epsilon long-float-negative-epsilon most-negative-double-float most-negative-fixnum most-negative-long-float most-negative-short-float most-negative-single-float most-positive-double-float most-positive-fixnum most-positive-long-float most-positive-short-float most-positive-single-float multiple-values-limit pi short-float-epsilon short-float-negative-epsilon single-float-epsilon single-float-negative-epsilon)) (mapcar #'ansi-declaration '(type compilation-speed debug declaration dynamic-extent ftype ignorable ignore inline notinline optimize safety space special speed)) (mapcar #'ansi-condition-type '(arithmetic-error cell-error condition control-error division-by-zero end-of-file file-error floating-point-inexact floating-point-invalid-operation floating-point-overflow floating-point-underflow package-error parse-error print-not-readable program-error reader-error serious-condition simple-condition simple-error simple-type-error simple-warning storage-condition stream-error style-warning type-error unbound-slot unbound-variable undefined-function warning)) (mapcar #'ansi-function '(- / \* \+ abort and atom bit character complex cons continue eql error float list logical-pathname member mod muffle-warning not null pathname rational store-value string use-value values vector /= 1- 1\+ < <= = > >= abs acons acos acosh adjoin adjust-array adjustable-array-p alpha-char-p alphanumericp append apply apropos apropos-list aref arithmetic-error-operands arithmetic-error-operation array-dimension array-dimensions array-displacement array-element-type array-has-fill-pointer-p array-in-bounds-p array-rank array-row-major-index array-total-size arrayp ash asin asinh assoc assoc-if assoc-if-not atan atanh bit-and bit-andc1 bit-andc2 bit-eqv bit-ior bit-nand bit-nor bit-not bit-orc1 bit-orc2 bit-vector-p bit-xor boole both-case-p boundp break broadcast-stream-streams butlast byte byte-position byte-size caaaar caaadr caaar caadar caaddr caadr caar cadaar cadadr cadar caddar cadddr caddr cadr call-next-method car cdaaar cdaadr cdaar cdadar cdaddr cdadr cdar cddaar cddadr cddar cdddar cddddr cdddr cddr cdr ceiling cell-error-name cerror char char-code char-downcase char-equal char-greaterp char-int char-lessp char-name char-not-equal char-not-greaterp char-not-lessp char-upcase char/= char< char<= char= char> char>= characterp cis class-of clear-input clear-output close clrhash code-char coerce compile compile-file compile-file-pathname compiled-function-p compiler-macro compiler-macro-function complement complexp compute-restarts concatenate concatenated-stream-streams conjugate consp constantly constantp copy-alist copy-list copy-pprint-dispatch copy-readtable copy-seq copy-structure copy-symbol copy-tree cos cosh count count-if count-if-not decode-float decode-universal-time delete delete-duplicates delete-file delete-if delete-if-not delete-package denominator deposit-field describe describe-object digit-char digit-char-p directory directory-namestring disassemble documentation dpb dribble echo-stream-input-stream echo-stream-output-stream ed eighth elt encode-universal-time endp enough-namestring ensure-directories-exist ensure-generic-function eq equal equalp eval evenp every exp export expt fboundp fceiling fdefinition ffloor fifth file-author file-error-pathname file-length file-namestring file-position file-string-length file-write-date fill fill-pointer find find-all-symbols find-class find-if find-if-not find-package find-restart find-symbol finish-output first float-digits float-precision float-radix float-sign floatp floor fmakunbound force-output format fourth fresh-line fround ftruncate funcall function-lambda-expression functionp gcd gensym gentemp get get-decoded-time get-dispatch-macro-character get-internal-real-time get-internal-run-time get-macro-character get-output-stream-string get-properties get-setf-expansion get-universal-time getf gethash graphic-char-p hash-table-count hash-table-p hash-table-rehash-size hash-table-rehash-threshold hash-table-size hash-table-test host-namestring identity imagpart import input-stream-p inspect integer-decode-float integer-length integerp interactive-stream-p intern intersection invalid-method-error invoke-debugger invoke-restart invoke-restart-interactively isqrt keywordp last lcm ldb ldb-test ldiff length lisp-implementation-type lisp-implementation-version list-all-packages list-length list\* listen listp load load-logical-pathname-translations log logand logandc1 logandc2 logbitp logcount logeqv logical-pathname-translations logior lognand lognor lognot logorc1 logorc2 logtest logxor long-site-name lower-case-p machine-instance machine-type machine-version macro-function macroexpand macroexpand-1 make-array make-broadcast-stream make-concatenated-stream make-condition make-dispatch-macro-character make-echo-stream make-hash-table make-list make-load-form-saving-slots make-package make-pathname make-random-state make-sequence make-string make-string-input-stream make-string-output-stream make-symbol make-synonym-stream make-two-way-stream makunbound map map-into mapc mapcan mapcar mapcon maphash mapl maplist mask-field max member-if member-if-not merge merge-pathnames method-combination-error min minusp mismatch name-char namestring nbutlast nconc next-method-p nintersection ninth notany notevery nreconc nreverse nset-difference nset-exclusive-or nstring-capitalize nstring-downcase nstring-upcase nsublis nsubst nsubst-if nsubst-if-not nsubstitute nsubstitute-if nsubstitute-if-not nth nthcdr numberp numerator nunion oddp open open-stream-p output-stream-p package-error-package package-name package-nicknames package-shadowing-symbols package-use-list package-used-by-list packagep pairlis parse-integer parse-namestring pathname-device pathname-directory pathname-host pathname-match-p pathname-name pathname-type pathname-version pathnamep peek-char phase plusp position position-if position-if-not pprint pprint-dispatch pprint-fill pprint-indent pprint-linear pprint-newline pprint-tab pprint-tabular prin1 prin1-to-string princ princ-to-string print print-not-readable-object print-object probe-file proclaim provide random random-state-p rassoc rassoc-if rassoc-if-not rationalize rationalp read read-byte read-char read-char-no-hang read-delimited-list read-from-string read-line read-preserving-whitespace read-sequence readtable-case readtablep realp realpart reduce rem remhash remove remove-duplicates remove-if remove-if-not remprop rename-file rename-package replace require rest restart-name revappend reverse room round row-major-aref rplaca rplacd sbit scale-float schar search second set set-difference set-dispatch-macro-character set-exclusive-or set-macro-character set-pprint-dispatch set-syntax-from-char seventh shadow shadowing-import short-site-name signal signum simple-bit-vector-p simple-condition-format-arguments simple-condition-format-control simple-string-p simple-vector-p sin sinh sixth sleep slot-boundp slot-exists-p slot-makunbound slot-value software-type software-version some sort special-operator-p sqrt stable-sort standard-char-p stream-element-type stream-error-stream stream-external-format streamp string-capitalize string-downcase string-equal string-greaterp string-left-trim string-lessp string-not-equal string-not-greaterp string-not-lessp string-right-trim string-trim string-upcase string/= string< string<= string= string> string>= stringp structure sublis subseq subsetp subst subst-if subst-if-not substitute substitute-if substitute-if-not subtypep svref sxhash symbol-function symbol-name symbol-package symbol-plist symbol-value symbolp synonym-stream-symbol tailp tan tanh tenth terpri third translate-logical-pathname translate-pathname tree-equal truename truncate two-way-stream-input-stream two-way-stream-output-stream type-error-datum type-error-expected-type type-of typep unbound-slot-instance unexport unintern union unread-char unuse-package upgraded-array-element-type upgraded-complex-part-type upper-case-p use-package user-homedir-pathname values-list variable vector-pop vector-push vector-push-extend vectorp warn wild-pathname-p write write-byte write-char write-line write-sequence write-string write-to-string y-or-n-p yes-or-no-p zerop)) (mapcar #'ansi-generic-function '(add-method allocate-instance change-class class-name compute-applicable-methods find-method function-keywords initialize-instance make-instance make-instances-obsolete make-load-form method-qualifiers no-applicable-method no-next-method reinitialize-instance remove-method shared-initialize slot-missing slot-unbound update-instance-for-different-class update-instance-for-redefined-class)) (mapcar #'ansi-macro '(lambda or setf assert call-method case ccase check-type cond ctypecase decf declaim defclass defconstant defgeneric define-compiler-macro define-condition define-method-combination define-modify-macro define-setf-expander define-symbol-macro defmacro defmethod defpackage defparameter defsetf defstruct deftype defun defvar destructuring-bind do do-all-symbols do-external-symbols do-symbols do\* dolist dotimes ecase etypecase formatter handler-bind handler-case ignore-errors in-package incf loop loop-finish make-method multiple-value-bind multiple-value-list multiple-value-setq nth-value otherwise pop pprint-exit-if-list-exhausted pprint-logical-block pprint-pop print-unreadable-object prog prog1 prog2 prog\* psetf psetq push pushnew remf restart-bind restart-case return rotatef shiftf step time trace typecase unless untrace when with-accessors with-compilation-unit with-condition-restarts with-hash-table-iterator with-input-from-string with-open-file with-open-stream with-output-to-string with-package-iterator with-simple-restart with-slots with-standard-io-syntax)) (mapcar #'ansi-special-operator '(function block catch eval-when flet go if labels let let\* load-time-value locally macrolet multiple-value-call multiple-value-prog1 progn progv quote return-from setq symbol-macrolet tagbody the throw unwind-protect)) (mapcar #'ansi-type '(array base-char base-string bignum bit-vector boolean broadcast-stream built-in-class class compiled-function concatenated-stream double-float echo-stream extended-char file-stream fixnum generic-function hash-table integer keyword long-float method number package random-state ratio readtable real restart satisfies sequence short-float signed-byte simple-array simple-base-string simple-bit-vector simple-string simple-vector single-float standard-char standard-class standard-generic-function standard-method standard-object stream string-stream structure-class structure-object symbol synonym-stream two-way-stream unsigned-byte)) (mapcar #'ansi-unknown '(method-combination)) (mapcar #'ansi-global-variable '(// /// \*\* \*\*\* \*break-on-signals\* \*compile-file-pathname\* \*compile-file-truename\* \*compile-print\* \*compile-verbose\* \*debug-io\* \*debugger-hook\* \*default-pathname-defaults\* \*error-output\* \*features\* \*gensym-counter\* \*load-pathname\* \*load-print\* \*load-truename\* \*load-verbose\* \*macroexpand-hook\* \*modules\* \*package\* \*print-array\* \*print-base\* \*print-case\* \*print-circle\* \*print-escape\* \*print-gensym\* \*print-length\* \*print-level\* \*print-lines\* \*print-miser-width\* \*print-pprint-dispatch\* \*print-pretty\* \*print-radix\* \*print-readably\* \*print-right-margin\* \*query-io\* \*random-state\* \*read-base\* \*read-default-float-format\* \*read-eval\* \*read-suppress\* \*readtable\* \*standard-input\* \*standard-output\* \*terminal-io\* \*trace-output\* \+\+ \+\+\+)) (mapcar #'ansi-expression '(declare)) )))))
End of Line.