summaryrefslogtreecommitdiff
path: root/lisp/modules/progmodes/lisp.lsp
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/modules/progmodes/lisp.lsp')
-rw-r--r--lisp/modules/progmodes/lisp.lsp384
1 files changed, 384 insertions, 0 deletions
diff --git a/lisp/modules/progmodes/lisp.lsp b/lisp/modules/progmodes/lisp.lsp
new file mode 100644
index 0000000..ebf2c10
--- /dev/null
+++ b/lisp/modules/progmodes/lisp.lsp
@@ -0,0 +1,384 @@
+;;
+;; Copyright (c) 2002 by The XFree86 Project, Inc.
+;;
+;; Permission is hereby granted, free of charge, to any person obtaining a
+;; copy of this software and associated documentation files (the "Software"),
+;; to deal in the Software without restriction, including without limitation
+;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
+;; and/or sell copies of the Software, and to permit persons to whom the
+;; Software is furnished to do so, subject to the following conditions:
+;;
+;; The above copyright notice and this permission notice shall be included in
+;; all copies or substantial portions of the Software.
+;;
+;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+;; THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+;; OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+;; SOFTWARE.
+;;
+;; Except as contained in this notice, the name of the XFree86 Project shall
+;; not be used in advertising or otherwise to promote the sale, use or other
+;; dealings in this Software without prior written authorization from the
+;; XFree86 Project.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/lisp.lsp,v 1.9 2003/01/30 02:46:26 paulo Exp $
+;;
+
+(require "syntax")
+(require "indent")
+(in-package "XEDIT")
+
+(defsynprop *prop-special*
+ "special"
+ :font "*courier-bold-r*12*"
+ :foreground "NavyBlue"
+)
+
+(defsynprop *prop-quote*
+ "quote"
+ :font "*courier-bold-r*12*"
+ :foreground "Red3"
+)
+
+(defsynprop *prop-package*
+ "package"
+ :font "*lucidatypewriter-medium-r*12*"
+ :foreground "Gold4"
+)
+
+(defsynprop *prop-unreadable*
+ "unreadable"
+ :font "*courier-medium-r*12*"
+ :foreground "Gray25"
+ :underline t
+)
+
+(defsynoptions *lisp-DEFAULT-style*
+ ;; Positive number. Basic indentation.
+ (:indentation . 2)
+
+ ;; Boolean. Move cursor to the indent column after pressing <Enter>?
+ (:newline-indent . t)
+
+ ;; Boolean. Use spaces instead of tabs to fill indentation?
+ (:emulate-tabs . nil)
+
+ ;; Boolean. Remove extra spaces from previous line.
+ ;; This should default to T when newline-indent is not NIL.
+ (:trim-blank-lines . t)
+
+ ;; Boolean. If this hash-table entry is set, no indentation is done.
+ ;; Useful to temporarily disable indentation.
+ (:disable-indent . nil)
+)
+
+(defvar *lisp-mode-options* *lisp-DEFAULT-style*)
+
+(defindent *lisp-mode-indent* :main
+ ;; this must be the first token
+ (indtoken "^\\s*" :indent
+ :code (or *offset* (setq *offset* (+ *ind-offset* *ind-length*))))
+ ;; ignore single line comments
+ (indtoken ";.*$" nil)
+ ;; multiline comments
+ (indtoken "|#" :comment :nospec t :begin :comment)
+ ;; characters
+ (indtoken "#\\\\(\\W|\\w+(-\\w+)?)" :character)
+ ;; numbers
+ (indtoken
+ (string-concat
+ "(\\<|[+-])\\d+("
+ ;; integers
+ "(\\>|\\.(\\s|$))|"
+ ;; ratios
+ "/\\d+\\>|"
+ ;;floats
+ "\\.?\\d*([SsFfDdLlEe][+-]?\\d+)?\\>"
+ ")")
+ :number)
+ ;; symbols, with optional package
+ (indtoken
+ (string-concat
+ ;; optional package name and ending ':'
+ "([A-Za-z_0-9%-]+:)?"
+ ;; internal symbol if after package name, or keyword
+ ":?"
+ ;; symbol name
+ "[][{}A-Za-z_0-9!$%&/<=>^~*+-]+")
+ :symbol)
+ ;; strings in the same line
+ (indtoken "\"([^\\\"]|\\\\.)*\"" :string)
+ ;; multiline strings
+ (indtoken "\"" :cstring :nospec t :begin :string)
+ ;; "quoted" symbols in the same line
+ (indtoken "\\|([^\\|]|\\\\.)*\\|" :symbol)
+ ;; multiline
+ (indtoken "|" :csymbol :nospec t :begin :symbol)
+ (indtoken "#" :hash :nospec t)
+
+ (indinit (parens 0))
+ (indtoken "(" :oparen :nospec t :code (incf parens))
+ (indtoken ")" :cparen :nospec t :code (decf parens))
+
+ (indtable :comment
+ ;; multiline comments can nest
+ (indtoken "|#" nil :nospec t :begin :comment)
+ (indtoken "#|" nil :nospec t :switch -1))
+
+ (indtable :string
+ ;; Ignore escaped characters
+ (indtoken "\\." nil)
+ ;; Return to the toplevel when the start of the string is found
+ (indtoken "\"" :ostring :nospec t :switch -1))
+
+ (indtable :symbol
+ ;; Ignore escaped characters
+ (indtoken "\\." nil)
+ ;; Return to the toplevel when the start of the symbol is found
+ (indtoken "|" :osymbol :nospec t :switch -1))
+
+ ;; ignore comments
+ (indreduce nil
+ t
+ ((:comment)))
+
+ ;; reduce multiline strings
+ (indreduce :string
+ t
+ ((:ostring (not :ostring) :cstring)))
+
+ ;; reduce multiline symbols
+ (indreduce :symbol
+ t
+ ((:osymbol (not :osymbol) :csymbol)))
+
+ ;; reduce basic types, don't care if inside list or not
+ (indreduce :element
+ t
+ ((:number)
+ (:string)
+ (:character)
+ (:element :element)
+ (:indent :element)))
+
+ (indreduce :symbol
+ t
+ ((:symbol :symbol)
+ (:symbol :element)
+ (:indent :symbol)))
+
+ ;; the "real" indentation value, to make easier parsing code like:
+ ;; (foo (bar (baz (blah
+ ;; ^ ^
+ ;; | |
+ ;; indent |
+ ;; effective indentation to be used
+ (indinit (indent 0))
+
+ ;; indentation values of opening parenthesis.
+ (indinit stack)
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; if before current line and open parenthesis >= 0, use indentation
+ ;; of current line to calculate relative indentation.
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indreduce :oparen ;; simple list?
+ (and (>= parens 0) (< *ind-offset* *ind-start*))
+ ((:indent :oparen))
+ (setq
+ *indent* (offset-indentation (+ *ind-offset* *ind-length*) :resolve t)
+ indent *indent*)
+ (indent-macro-reject-left))
+
+ ;; reduce list if there isn't indentation change
+ (indreduce :element
+ t
+ ((:oparen (not :oparen) :cparen)))
+
+ (indresolve :oparen
+ (setq
+ *indent*
+ (offset-indentation
+ (+ *ind-offset* *ind-length* -1 *base-indent*) :align t))
+ (push *indent* stack)
+ (incf indent *base-indent*)
+ (if (< *indent* indent) (setq *indent* indent)))
+
+ (indresolve :cparen
+ (decf indent *base-indent*)
+ (setq *indent* (pop stack))
+ (if (null stack)
+ (setq *indent* indent)
+ (setq *indent* (car stack))))
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Find a "good" offset to start parsing backwards, so that it should
+;; always generate the same results.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun lisp-offset-indent (&aux char (point (scan (point) :eol :left)))
+ ;; skip spaces
+ (while (member (setq char (char-after point)) indent-spaces)
+ (incf point))
+ (if (member char '(#\))) (1+ point) point))
+
+(defun lisp-should-indent (options &aux char point start)
+ (when (hash-table-p options)
+ ;; check if previous line has extra spaces
+ (and (gethash :trim-blank-lines options)
+ (indent-clear-empty-line))
+
+ ;; indentation disabled?
+ (and (gethash :disable-indent options)
+ (return-from lisp-should-indent))
+
+ (setq
+ point (point)
+ char (char-before (point))
+ start (scan point :eol :left))
+
+ ;; at the start of a line
+ (and (= point start)
+ (return-from lisp-should-indent (gethash :newline-indent options)))
+
+ ;; if first character
+ (and (= point (1+ start)) (return-from lisp-should-indent t))
+
+ ;; if closing parenthesis and first nonblank char
+ (when (and (characterp char) (char= char #\)))
+ (decf point)
+ (while
+ (and (> point start) (member (char-before point) indent-spaces))
+ (decf point))
+ (return-from lisp-should-indent (<= point start)))
+ )
+ ;; should not indent
+ nil)
+
+(defun lisp-indent (syntax syntable)
+ (let*
+ ((options (syntax-options syntax))
+ *base-indent*)
+
+ (or (lisp-should-indent options) (return-from lisp-indent))
+
+ (setq *base-indent* (gethash :indentation options 2))
+
+ (indent-macro
+ *lisp-mode-indent*
+ (lisp-offset-indent)
+ (gethash :emulate-tabs options))))
+
+(compile 'lisp-indent)
+
+(defsyntax *lisp-mode* :main nil #'lisp-indent *lisp-mode-options*
+ ;; highlight car and parenthesis
+ (syntoken "\\(+\\s*[][{}A-Za-z_0-9!$%&/<=>?^~*:+-]*\\)*"
+ :property *prop-keyword*)
+ (syntoken "\\)+" :property *prop-keyword*)
+
+ ;; nil and t
+ (syntoken "\\<(nil|t)\\>" :icase t :property *prop-special*)
+
+ (syntoken "|" :nospec t :begin :unreadable :contained t)
+
+ ;; keywords
+ (syntoken ":[][{}A-Za-z_0-9!$%&/<=>^~+-]+" :property *prop-constant*)
+
+ ;; special symbol.
+ (syntoken "\\*[][{}A-Za-z_0-9!$%&7=?^~+-]+\\*"
+ :property *prop-special*)
+
+ ;; special identifiers
+ (syntoken "&(aux|key|optional|rest)\\>" :icase t :property *prop-constant*)
+
+ ;; numbers
+ (syntoken
+ ;; since lisp is very liberal in what can be a symbol, this pattern
+ ;; will not always work as expected, since \< and \> will not properly
+ ;; work for all characters that may be in a symbol name
+ (string-concat
+ "(\\<|[+-])\\d+("
+ ;; integers
+ "(\\>|\\.(\\s|$))|"
+ ;; ratios
+ "/\\d+\\>|"
+ ;;floats
+ "\\.?\\d*([SsFfDdLlEe][+-]?\\d+)?\\>"
+ ")")
+ :property *prop-number*)
+
+ ;; characters
+ (syntoken "#\\\\(\\W|\\w+(-\\w+)?)" :property *prop-constant*)
+
+ ;; quotes
+ (syntoken "[`'.]|,@?" :property *prop-quote*)
+
+ ;; package names
+ (syntoken "[A-Za-z_0-9%-]+::?" :property *prop-package*)
+
+ ;; read time evaluation
+ (syntoken "#\\d+#" :property *prop-preprocessor*)
+ (syntoken "#([+'cCsS-]|\\d+[aA=])?" :begin :preprocessor :contained t)
+
+ (syntoken "\\c" :property *prop-control*)
+
+ ;; symbols, do nothing, just resolve conflicting matches
+ (syntoken "[][{}A-Za-z_0-9!$%&/<=>^~*+-]+")
+
+ (syntable :simple-comment *prop-comment* nil
+ (syntoken "$" :switch -1)
+ (syntoken "XXX|FIXME|TODO" :property *prop-annotation*))
+
+ (syntable :comment *prop-comment* nil
+ ;; comments can nest
+ (syntoken "#|" :nospec t :begin :comment)
+ ;; return to previous state
+ (syntoken "|#" :nospec t :switch -1)
+ (syntoken "XXX|FIXME|TODO" :property *prop-annotation*))
+
+ (syntable :unreadable *prop-unreadable* nil
+ ;; ignore escaped characters
+ (syntoken "\\\\.")
+ (syntoken "|" :nospec t :switch -1))
+
+ (syntable :string *prop-string* nil
+ ;; ignore escaped characters
+ (syntoken "\\\\.")
+ (syntoken "\"" :nospec t :switch -1))
+
+ (syntable :preprocessor *prop-preprocessor* nil
+ ;; a symbol
+ (syntoken "[][{}A-Za-z_0-9!$%&/<=>^~:*+-]+" :switch -1)
+
+ ;; conditional expression
+ (syntoken "(" :nospec t :begin :preprocessor-expression :contained t)
+
+ (syntable :preprocessor-expression *prop-preprocessor* nil
+ ;; recursive
+ (syntoken "(" :nospec t :begin :preprocessor-recursive :contained t)
+ (syntoken ")" :nospec t :switch -2)
+
+ (syntable :preprocessor-recursive *prop-preprocessor* nil
+ (syntoken "(" :nospec t
+ :begin :preprocessor-recursive
+ :contained t)
+ (syntoken ")" :nospec t :switch -1)
+ (synaugment :comments-and-strings))
+ (synaugment :comments-and-strings))
+ (synaugment :comments-and-strings))
+
+ (syntable :comments-and-strings nil nil
+ (syntoken "\"" :nospec t :begin :string :contained t)
+ (syntoken "#|" :nospec t :begin :comment :contained t)
+ (syntoken ";" :begin :simple-comment :contained t))
+
+ (synaugment :comments-and-strings)
+)