1
+ # By Nathan Weizenbaum (http://nex3.leeweiz.net)
2
+ # MIT License (http://www.opensource.org/licenses/mit-license.php)
3
+ #
4
+ # CodeRay scanner for Lisp.
5
+ # The keywords are mostly geared towards Emacs Lisp,
6
+ # but it should work fine for Common Lisp
7
+ # and reasonably well for Scheme.
8
+
9
+ require 'rubygems'
10
+ require 'coderay'
11
+
12
+ module CodeRay ::Scanners
13
+ class Lisp < Scanner
14
+ register_for :lisp
15
+
16
+ NON_SYMBOL_CHARS = '();\s\[\]'
17
+ SYMBOL_RE = /[^#{ NON_SYMBOL_CHARS } ]+/
18
+ EXPONENT_RE = /(e[\- +]?[0-9]+)?/
19
+
20
+ GEN_DEFINES = %w{
21
+ defun defun* defsubst defmacro defadvice define-skeleton define-minor-mode
22
+ define-global-minor-mode define-globalized-minor-mode define-derived-mode
23
+ define-generic-mode define-compiler-macro define-modify-macro defsetf
24
+ define-setf-expander define-method-combination defgeneric defmethod
25
+ }
26
+ TYPE_DEFINES = %w{
27
+ defgroup deftheme deftype defstruct defclass define-condition
28
+ define-widget defface defpackage
29
+ }
30
+ VAR_DEFINES = %w{
31
+ defvar defconst defconstant defcustom defparameter define-symbol-macro
32
+ }
33
+ KEYWORDS = ( GEN_DEFINES + TYPE_DEFINES + VAR_DEFINES + %w{
34
+ lambda autoload progn prog1 prog2 save-excursion save-window-excursion
35
+ save-selected-window save-restriction save-match-data save-current-buffer
36
+ with-current-buffer combine-after-change-calls with-output-to-string
37
+ with-temp-file with-temp-buffer with-temp-message with-syntax-table let
38
+ let* while if read-if catch condition-case unwind-protect
39
+ with-output-to-temp-buffer eval-after-load dolist dotimes when unless
40
+ } ) . inject ( { } ) { |memo , str | memo [ str ] = nil ; memo }
41
+
42
+ DEFINES = WordList . new .
43
+ add ( GEN_DEFINES , :function ) .
44
+ add ( TYPE_DEFINES , :class ) .
45
+ add ( VAR_DEFINES , :variable )
46
+
47
+ def scan_tokens ( tokens , options )
48
+ defined = false
49
+ until eos?
50
+ kind = nil
51
+ match = nil
52
+
53
+ if scan ( /\s +/m )
54
+ kind = :space
55
+ else
56
+ if scan ( /[\( \) \[ \] ]/ )
57
+ kind = :delimiter
58
+ elsif scan ( /'+#{ SYMBOL_RE } / )
59
+ kind = :symbol
60
+ elsif scan ( /\& #{ SYMBOL_RE } / )
61
+ kind = :reserved
62
+ elsif scan ( /:#{ SYMBOL_RE } / )
63
+ kind = :constant
64
+ elsif scan ( /\? #{ SYMBOL_RE } / )
65
+ kind = :char
66
+ elsif match = scan ( /"(\\ "|[^"])+"/m )
67
+ tokens << [ :open , :string ] << [ '"' , :delimiter ] <<
68
+ [ match [ 1 ...-1 ] , :content ] << [ '"' , :delimiter ] << [ :close , :string ]
69
+ next
70
+ elsif scan ( /[\- +]?[0-9]*\. [0-9]+#{ EXPONENT_RE } / )
71
+ kind = :float
72
+ elsif scan ( /[\- +]?[0-9]+#{ EXPONENT_RE } / )
73
+ kind = :integer
74
+ elsif scan ( /;.*$/ )
75
+ kind = :comment
76
+ elsif scan ( SYMBOL_RE )
77
+ kind = :plain
78
+
79
+ if defined
80
+ kind = defined
81
+ else
82
+ sym = matched
83
+ if KEYWORDS . include? sym
84
+ kind = :reserved
85
+ defined = DEFINES [ sym ]
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ match ||= matched
92
+ raise_inspect 'Empty token' , tokens unless match
93
+
94
+ defined = [ :reserved , :comment , :space ] . include? ( kind ) && defined
95
+
96
+ tokens << [ match , kind ]
97
+ end
98
+
99
+ tokens
100
+ end
101
+ end
102
+ end
0 commit comments