1f14f4646Smrg;; Copyright (c) 2007,2008 Paulo Cesar Pereira de Andrade
2f14f4646Smrg;;
3f14f4646Smrg;; Permission is hereby granted, free of charge, to any person obtaining a
4f14f4646Smrg;; copy of this software and associated documentation files (the "Software"),
5f14f4646Smrg;; to deal in the Software without restriction, including without limitation
6f14f4646Smrg;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
7f14f4646Smrg;; and/or sell copies of the Software, and to permit persons to whom the
8f14f4646Smrg;; Software is furnished to do so, subject to the following conditions:
9f14f4646Smrg;;
10f14f4646Smrg;; The above copyright notice and this permission notice (including the next
11f14f4646Smrg;; paragraph) shall be included in all copies or substantial portions of the
12f14f4646Smrg;; Software.
13f14f4646Smrg;;
14f14f4646Smrg;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15f14f4646Smrg;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16f14f4646Smrg;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17f14f4646Smrg;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18f14f4646Smrg;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19f14f4646Smrg;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20f14f4646Smrg;; DEALINGS IN THE SOFTWARE.
21f14f4646Smrg;;
22f14f4646Smrg;; Author: Paulo Cesar Pereira de Andrade
23f14f4646Smrg;;
24f14f4646Smrg
25f14f4646Smrg;;   Perl syntax and indentation mode
26f14f4646Smrg;;   Based on the C/C++ and Lisp modes. Attempting to make simple
27f14f4646Smrg;; syntax/indentation rules, that should work correctly with most
28f14f4646Smrg;; perl code.
29f14f4646Smrg
30f14f4646Smrg;;  *cont-indent* is somewhat buggy, that if pressing C-A,Tab, will
31f14f4646Smrg;; not generate the same output as when normally typing the expression.
32f14f4646Smrg;;  This is because the parser doesn't search for a matching ';', '{',
33f14f4646Smrg;; '[' or '(' to know where the expression starts. The C mode has the
34f14f4646Smrg;; same problem. Example:
35f14f4646Smrg;;	a +
36f14f4646Smrg;;	    b;	<-- if pressing C-A,Tab will align "b;" with "a +"
37f14f4646Smrg
38f14f4646Smrg;;  Maybe most of the code here, and some code in the C mode could be
39f14f4646Smrg;; merged to have a single "default mode" parser for languages that
40f14f4646Smrg;; basically only depend on { and } for indentation.
41f14f4646Smrg
42f14f4646Smrg(require "syntax")
43f14f4646Smrg(require "indent")
44f14f4646Smrg(in-package "XEDIT")
45f14f4646Smrg
46f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
47f14f4646Smrg(defsynprop *prop-string-escape*
48f14f4646Smrg  "string-escape"
49f14f4646Smrg  :font		"*lucidatypewriter-bold-r*-12-*"
50f14f4646Smrg  :foreground	"RoyalBlue2"
51f14f4646Smrg  :underline	t)
52f14f4646Smrg
53f14f4646Smrg(defsynprop *prop-string-keyword-bold*
54f14f4646Smrg  "string-variable-bold"
55f14f4646Smrg  :font		"*lucidatypewriter-bold-r*-12-*"
56f14f4646Smrg  :foreground	"RoyalBlue4")
57f14f4646Smrg
58f14f4646Smrg(defsynprop *prop-string-keyword*
59f14f4646Smrg  "string-variable"
60f14f4646Smrg  :font		"*lucidatypewriter-medium-r*-12-*"
61f14f4646Smrg  :foreground	"RoyalBlue4")
62f14f4646Smrg
63f14f4646Smrg(defsynprop *prop-constant-escape*
64f14f4646Smrg  "constant-escape"
65f14f4646Smrg  :font		"*lucidatypewriter-medium-r*-12-*"
66f14f4646Smrg  :foreground	"VioletRed3"
67f14f4646Smrg  :underline	t)
68f14f4646Smrg
69f14f4646Smrg(defsynprop *prop-regex*
70f14f4646Smrg  "regex"
71f14f4646Smrg  :font		"*courier-medium-o*-12-*"
72f14f4646Smrg  :foreground	"black")
73f14f4646Smrg
74f14f4646Smrg(defsynprop *prop-shell*
75f14f4646Smrg  "shell"
76f14f4646Smrg  :font		"*lucidatypewriter-medium-r*-12-*"
77f14f4646Smrg  :foreground	"red3")
78f14f4646Smrg
79f14f4646Smrg(defsynprop *prop-shell-escape*
80f14f4646Smrg  "shell-escape"
81f14f4646Smrg  :font		"*lucidatypewriter-bold-r*-12-*"
82f14f4646Smrg  :foreground	"red3"
83f14f4646Smrg  :underline	t)
84f14f4646Smrg
85f14f4646Smrg(defsynprop *prop-documentation*
86f14f4646Smrg  "documentation"
87f14f4646Smrg  :font		"fixed"
88f14f4646Smrg  :foreground	"black"
89f14f4646Smrg  :background	"rgb:e/e/e"
90f14f4646Smrg)
91f14f4646Smrg
92f14f4646Smrg
93f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
94f14f4646Smrg(defsynoptions *perl-DEFAULT-style*
95f14f4646Smrg  ;; Positive number. Basic indentation
96f14f4646Smrg  (:indentation		.	4)
97f14f4646Smrg
98f14f4646Smrg  ;; Boolean. Add one indentation level to continuations?
99f14f4646Smrg  (:cont-indent		.	t)
100f14f4646Smrg
101f14f4646Smrg  ;; Boolean. Move cursor to the indent column after pressing <Enter>?
102f14f4646Smrg  (:newline-indent	.	t)
103f14f4646Smrg
104f14f4646Smrg  ;; Boolean. Set to T if tabs shouldn't be used to fill indentation.
105f14f4646Smrg  (:emulate-tabs	.	nil)
106f14f4646Smrg
107f14f4646Smrg  ;; Boolean. Only calculate indentation after pressing <Enter>?
108f14f4646Smrg  ;;		This may be useful if the parser does not always
109f14f4646Smrg  ;;		do what the user expects...
110f14f4646Smrg  (:only-newline-indent	.	nil)
111f14f4646Smrg
112f14f4646Smrg  ;; Boolean. Remove extra spaces from previous line.
113f14f4646Smrg  ;;		This should default to T when newline-indent is not NIL.
114f14f4646Smrg  (:trim-blank-lines	.	t)
115f14f4646Smrg
116f14f4646Smrg  ;; Boolean. If this hash-table entry is set, no indentation is done.
117f14f4646Smrg  ;;		Useful to temporarily disable indentation.
118f14f4646Smrg  (:disable-indent	.	nil))
119f14f4646Smrg
120f14f4646Smrg
121f14f4646Smrg
122f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
123f14f4646Smrg(defvar *perl-mode-options* *perl-DEFAULT-style*)
124f14f4646Smrg
125f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
126f14f4646Smrg;; Parenthesis are usually not required, just distinguish as:
127f14f4646Smrg;; expression:	code without an ending ';'
128f14f4646Smrg;; statement:	code ending in a ';'
129f14f4646Smrg;; block:	code enclosed in '{' and '}'
130f14f4646Smrg;; In Perl a simpler logic can be used, unlikely the C mode, as in
131f14f4646Smrg;; perl braces are mandatory
132f14f4646Smrg(defindent *perl-mode-indent* :main
133f14f4646Smrg  ;; this must be the first token
134f14f4646Smrg  (indtoken "^\\s*" :indent
135f14f4646Smrg    :code (or *offset* (setq *offset* (+ *ind-offset* *ind-length*))))
136f14f4646Smrg  ;; this may cause some other patterns to fail, due to matching single \'
137f14f4646Smrg  (indtoken "(&?(\\w+)|&(\\w+)?)'\\w+" :expression)
138f14f4646Smrg  ;; special variables
139f14f4646Smrg  (indtoken "\\$(\\d|^\\u|[][0-9!#$*()_@<>?/|,\"'])" :expression)
140f14f4646Smrg  ;; ignore comments
141f14f4646Smrg  (indtoken "#.*$" nil)
142f14f4646Smrg  ;; treat regex as expressions to avoid confusing parser
143f14f4646Smrg  (indtoken "m?/([^/]|\\\\/)+/\\w*" :expression)
144f14f4646Smrg  (indtoken "m\\{[^}]+\\}\\w*" :expression)
145f14f4646Smrg  (indtoken "m<[^>]+>\\w*" :expression)
146f14f4646Smrg  (indtoken "(s|tr)/[^/]+/([^/]|\\\\/)*/\\w*" :expression)
147f14f4646Smrg  (indtoken "//" :expression :nospec t)
148f14f4646Smrg  ;; fast resolve deferences to expressions
149f14f4646Smrg  (indtoken "[$@%&*]?\\{\\$?\\S+\\}" :expression)
150f14f4646Smrg
151f14f4646Smrg  (indtoken "($%@*)?\\w+" :expression)
152f14f4646Smrg  (indtoken ";" :semi :nospec t)
153f14f4646Smrg  (indinit (braces 0))
154f14f4646Smrg  (indtoken "{" :obrace :nospec t
155f14f4646Smrg    :code (decf braces))
156f14f4646Smrg  (indtoken "}" :cbrace :nospec t
157f14f4646Smrg    :code (incf braces))
158f14f4646Smrg  (indinit (parens&bracks 0))
159f14f4646Smrg  (indtoken ")" :cparen :nospec t :code (incf parens&bracks))
160f14f4646Smrg  (indtoken "(" :oparen	:nospec t :code (decf parens&bracks))
161f14f4646Smrg  (indtoken "]" :cbrack	:nospec t :code (incf parens&bracks))
162f14f4646Smrg  (indtoken "[" :obrack	:nospec t :code (decf parens&bracks))
163f14f4646Smrg  ;; if in the same line, reduce now, this must be done because the
164f14f4646Smrg  ;; delimiters are identical
165f14f4646Smrg  (indtoken "'([^\\']|\\\\.)*'" :expression)
166f14f4646Smrg  (indtoken "\"([^\\\"]|\\\\.)*\"" :expression)
167f14f4646Smrg  (indtoken "\"" :cstring1 :nospec t :begin :string1)
168f14f4646Smrg  (indtoken "'" :cstring2 :nospec t :begin :string2)
169f14f4646Smrg  ;; This must be the last rule
170f14f4646Smrg  (indtoken "\\s*$"		:eol)
171f14f4646Smrg
172f14f4646Smrg  (indtable :string1
173f14f4646Smrg    ;; Ignore escaped characters
174f14f4646Smrg    (indtoken "\\." nil)
175f14f4646Smrg    ;; Return to the toplevel when the start of the string is found
176f14f4646Smrg    (indtoken "\"" :ostring1 :nospec t :switch -1))
177f14f4646Smrg  (indtable :string2
178f14f4646Smrg    (indtoken "\\." nil)
179f14f4646Smrg    (indtoken "'" :ostring2 :nospec t :switch -1))
180f14f4646Smrg
181f14f4646Smrg  ;; This avoids some problems with *cont-indent* adding an indentation
182f14f4646Smrg  ;; level to an expression after an empty line
183f14f4646Smrg  (indreduce nil
184f14f4646Smrg    t
185f14f4646Smrg    ((:indent :eol)))
186f14f4646Smrg
187f14f4646Smrg  ;; Reduce to a single expression token
188f14f4646Smrg  (indreduce :expression
189f14f4646Smrg    t
190f14f4646Smrg    ((:indent :expression)
191f14f4646Smrg      (:expression :eol)
192f14f4646Smrg      (:expression :parens)
193f14f4646Smrg      (:expression :bracks)
194f14f4646Smrg      (:expression :expression)
195f14f4646Smrg      ;; multiline strings
196f14f4646Smrg      (:ostring1 (not :ostring1) :cstring1)
197f14f4646Smrg      (:ostring2 (not :ostring2) :cstring2)
198f14f4646Smrg      ;; parenthesis and brackets
199f14f4646Smrg      (:oparen (not :oparen) :cparen)
200f14f4646Smrg      (:obrack (not :obrack) :cbrack)))
201f14f4646Smrg
202f14f4646Smrg  ;; Statements end in a semicollon
203f14f4646Smrg  (indreduce :statement
204f14f4646Smrg    t
205f14f4646Smrg    ((:semi)
206f14f4646Smrg      (:indent :semi)
207f14f4646Smrg      (:expression :statement)
208f14f4646Smrg      (:statement :eol)
209f14f4646Smrg      ;; Doesn't necessarily end in a semicollon
210f14f4646Smrg      (:expression :block)))
211f14f4646Smrg
212f14f4646Smrg  (indreduce :block
213f14f4646Smrg    t
214f14f4646Smrg    ((:obrace (not :obrace) :cbrace)
215f14f4646Smrg      (:block :eol)))
216f14f4646Smrg  (indreduce :obrace
217f14f4646Smrg    (< *ind-offset* *ind-start*)
218f14f4646Smrg    ((:indent :obrace))
219f14f4646Smrg    (setq *indent* (offset-indentation (+ *ind-offset* *ind-length*) :resolve t))
220f14f4646Smrg    (indent-macro-reject-left))
221f14f4646Smrg
222f14f4646Smrg  ;; Try to do an smart indentation on open parenthesis and brackets
223f14f4646Smrg  (indreduce :parens
224f14f4646Smrg    t
225f14f4646Smrg    ((:oparen (not :oparen) :cparen))
226f14f4646Smrg    (when (and
227f14f4646Smrg	    (< *ind-offset* *ind-start*)
228f14f4646Smrg	    (> (+ *ind-offset* *ind-length*) *ind-start*))
229f14f4646Smrg      (setq *indent* (1+ (offset-indentation *ind-offset* :align t)))
230f14f4646Smrg      (indent-macro-reject-left)))
231f14f4646Smrg  (indreduce :bracks
232f14f4646Smrg    t
233f14f4646Smrg    ((:obrack (not :obrack) :cbrack))
234f14f4646Smrg    (when (and
235f14f4646Smrg	    (< *ind-offset* *ind-start*)
236f14f4646Smrg	    (> (+ *ind-offset* *ind-length*) *ind-start*))
237f14f4646Smrg      (setq *indent* (1+ (offset-indentation *ind-offset* :align t)))
238f14f4646Smrg      (indent-macro-reject-left)))
239f14f4646Smrg
240f14f4646Smrg  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
241f14f4646Smrg  ;; Assuming previous lines have correct indentation, try to
242f14f4646Smrg  ;; fast resolve brace indentation
243f14f4646Smrg  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
244f14f4646Smrg  ;; Line ended with an open brace
245f14f4646Smrg  (indreduce :obrace
246f14f4646Smrg    (< *ind-offset* *ind-start*)
247f14f4646Smrg    ((:expression :obrace))
248f14f4646Smrg    (setq *indent* (offset-indentation *ind-offset* :resolve t))
249f14f4646Smrg    (indent-macro-reject-left))
250f14f4646Smrg  ;; Line starts with an open brace
251f14f4646Smrg  (indreduce nil
252f14f4646Smrg    (< *ind-offset* *ind-start* (+ *ind-offset* *ind-length*))
253f14f4646Smrg    ;; Just set initial indentation
254f14f4646Smrg    ((:indent :obrace))
255f14f4646Smrg    (setq
256f14f4646Smrg      *indent* (- (offset-indentation *ind-offset* :resolve t) *base-indent*))
257f14f4646Smrg    (indent-macro-reject-left))
258f14f4646Smrg
259f14f4646Smrg  (indresolve :statement
260f14f4646Smrg    (when (< *ind-offset* *ind-start*)
261f14f4646Smrg      (while (> braces 0)
262f14f4646Smrg	(setq
263f14f4646Smrg	  *indent*	(- *indent* *base-indent*)
264f14f4646Smrg	  braces	(1- braces)))))
265f14f4646Smrg
266f14f4646Smrg  (indresolve :obrace
267f14f4646Smrg    (and (< *ind-offset* *ind-start*)
268f14f4646Smrg      (incf *indent* *base-indent*)))
269f14f4646Smrg  (indresolve :cbrace
270f14f4646Smrg    (decf *indent* *base-indent*))
271f14f4646Smrg  (indresolve :expression
272f14f4646Smrg    (and
273f14f4646Smrg      *cont-indent*
274f14f4646Smrg      (> *indent* 0)
275f14f4646Smrg      (zerop parens&bracks)
276f14f4646Smrg      (< *ind-offset* *ind-start*)
277f14f4646Smrg      (> (+ *ind-offset* *ind-length*) *ind-start*)
278f14f4646Smrg      (incf *indent* *base-indent*)))
279f14f4646Smrg
280f14f4646Smrg  (indresolve (:oparen :obrack)
281f14f4646Smrg    (and (< *ind-offset* *ind-start*)
282f14f4646Smrg      (setq *indent* (1+ (offset-indentation *ind-offset* :align t)))))
283f14f4646Smrg)
284f14f4646Smrg
285f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
286f14f4646Smrg(defun perl-offset-indent (&aux char (point (point)))
287f14f4646Smrg  ;; Skip spaces forward
288f14f4646Smrg  (while (member (setq char (char-after point)) indent-spaces)
289f14f4646Smrg    (incf point))
290f14f4646Smrg  (if (member char '(#\})) (1+ point) point))
291f14f4646Smrg
292f14f4646Smrg(compile 'perl-offset-indent)
293f14f4646Smrg
294f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
295f14f4646Smrg(defun perl-should-indent (options &aux char point start offset)
296f14f4646Smrg  (when (hash-table-p options)
297f14f4646Smrg    ;; check if previous line has extra spaces
298f14f4646Smrg    (and (gethash :trim-blank-lines options)
299f14f4646Smrg      (indent-clear-empty-line))
300f14f4646Smrg
301f14f4646Smrg    ;; indentation disabled?
302f14f4646Smrg    (and (gethash :disable-indent options)
303f14f4646Smrg      (return-from perl-should-indent))
304f14f4646Smrg
305f14f4646Smrg    (setq
306f14f4646Smrg      point	(point)
307f14f4646Smrg      char	(char-before point)
308f14f4646Smrg      start	(scan point :eol :left))
309f14f4646Smrg
310f14f4646Smrg    ;; if at bol and should indent only when starting a line
311f14f4646Smrg    (and (gethash :only-newline-indent options)
312f14f4646Smrg      (return-from perl-should-indent (= point start)))
313f14f4646Smrg
314f14f4646Smrg    ;; at the start of a line
315f14f4646Smrg    (and (= point start)
316f14f4646Smrg      (return-from perl-should-indent (gethash :newline-indent options)))
317f14f4646Smrg
318f14f4646Smrg    ;; if first character
319f14f4646Smrg    (and (= point (1+ start))
320f14f4646Smrg      (return-from perl-should-indent t))
321f14f4646Smrg
322f14f4646Smrg    ;; check if is the first non-blank character in a new line
323f14f4646Smrg    (when (and
324f14f4646Smrg	    (gethash :cont-indent options)
325f14f4646Smrg	    (= point (scan point :eol :right))
326f14f4646Smrg	    (alphanumericp char))
327f14f4646Smrg      (setq offset (1- point))
328f14f4646Smrg      (while (and
329f14f4646Smrg	       (> offset start)
330f14f4646Smrg	       (member (char-before offset) indent-spaces))
331f14f4646Smrg	(decf offset))
332f14f4646Smrg      ;; line has only one character with possible spaces before it
333f14f4646Smrg      (and (<= offset start)
334f14f4646Smrg	(return-from perl-should-indent t)))
335f14f4646Smrg
336f14f4646Smrg    ;; if one of these was typed, should check indentation
337f14f4646Smrg    (if (member char '(#\})) (return-from perl-should-indent t))
338f14f4646Smrg  )
339f14f4646Smrg  ;; Should not indent
340f14f4646Smrg  nil)
341f14f4646Smrg
342f14f4646Smrg(compile 'perl-should-indent)
343f14f4646Smrg
344f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
345f14f4646Smrg(defun perl-indent (syntax syntable)
346f14f4646Smrg  (let*
347f14f4646Smrg    ((options (syntax-options syntax))
348f14f4646Smrg      *base-indent*
349f14f4646Smrg      *cont-indent*)
350f14f4646Smrg
351f14f4646Smrg    (or (perl-should-indent options) (return-from perl-indent))
352f14f4646Smrg    (setq
353f14f4646Smrg      *base-indent* (gethash :indentation options 4)
354f14f4646Smrg      *cont-indent* (gethash :cont-indent options t))
355f14f4646Smrg
356f14f4646Smrg    (indent-macro
357f14f4646Smrg      *perl-mode-indent*
358f14f4646Smrg      (perl-offset-indent)
359f14f4646Smrg      (gethash :emulate-tabs options))))
360f14f4646Smrg
361f14f4646Smrg(compile 'perl-indent)
362f14f4646Smrg
363f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
364f14f4646Smrg;; some example macros to easily add new patterns for strings and possibly
365f14f4646Smrg;; regex or other patterns
366f14f4646Smrg(defmacro perl-q-string-token (token)
367f14f4646Smrg  `(syntoken (string-concat "\\<q(q|w)?\\s*\\" ,token)
368f14f4646Smrg     :icase t :contained t :begin
369f14f4646Smrg     (intern (string-concat "string" ,token) 'keyword)))
370f14f4646Smrg(defmacro perl-q-string-table (start end)
371f14f4646Smrg  `(syntable (intern (string-concat "string" ,start) 'keyword)
372f14f4646Smrg     *prop-string* #'default-indent
373f14f4646Smrg     (syntoken ,end :nospec t :switch -1)
374f14f4646Smrg     (synaugment :inside-string)))
375f14f4646Smrg
376f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
377f14f4646Smrg(defsyntax *perl-mode* :main nil #'perl-indent *perl-mode-options*
378f14f4646Smrg  ;; keywords
379f14f4646Smrg  (syntoken
380f14f4646Smrg    (string-concat
381f14f4646Smrg      "\\<("
382f14f4646Smrg      "and|for|foreach|gt|if|else|elsif|eq|goto|le|lt|last|ne|"
383f14f4646Smrg      "neg|next|not|or|return|shift|sub|unless|unshift|until|while"
384f14f4646Smrg      ")\\>")
385f14f4646Smrg    :property *prop-keyword*)
386f14f4646Smrg
387f14f4646Smrg  ;; pseudo keywords
388f14f4646Smrg  (syntoken
389f14f4646Smrg    (string-concat
390f14f4646Smrg      "\\<("
391f14f4646Smrg      "BEGIN|END|bless|blessed|defined|delete|eval|local|my|our|"
392f14f4646Smrg      "package|require|undef|use"
393f14f4646Smrg      ")\\>")
394f14f4646Smrg    :property *prop-preprocessor*)
395f14f4646Smrg  ;; this may cause some other patterns to fail, due to matching single \'
396f14f4646Smrg  (syntoken "(&?(\\w+)|&(\\w+)?)'\\w+" :property *prop-preprocessor*)
397f14f4646Smrg
398f14f4646Smrg  ;; numbers
399f14f4646Smrg  (syntoken
400f14f4646Smrg    (string-concat
401f14f4646Smrg      "\\<("
402f14f4646Smrg      ;; Integers
403f14f4646Smrg      "(\\d+|0x\\x+)|"
404f14f4646Smrg      ;; Floats
405f14f4646Smrg      "\\d+\\.?\\d*(e[+-]?\\d+)?"
406f14f4646Smrg      ")\\>")
407f14f4646Smrg    :icase t
408f14f4646Smrg    :property *prop-number*)
409f14f4646Smrg
410f14f4646Smrg  ;; special variables
411f14f4646Smrg  (syntoken "\\$(\\d|^\\u|[][0-9!#$*()_@<>?/|,\"'])" :property *prop-keyword*)
412f14f4646Smrg
413f14f4646Smrg  ;; also match variables
414f14f4646Smrg  (syntable :inside-string nil nil
415f14f4646Smrg    ;; escaped characters
416f14f4646Smrg
417f14f4646Smrg    ;; XXX This pattern was matching the empty string and entering an
418f14f4646Smrg    ;; infinite loop in code like:
419f14f4646Smrg#|
420f14f4646Smrg---%<---
421f14f4646Smrg"		<-- *** if an backslash is added it fails. Inverting
422f14f4646Smrga";                 *** the pattern fixed the problem, but was the wrong
423f14f4646Smrg---%<---	    *** solution. Note that C-G stops the interpreter, and
424f14f4646Smrg		    *** special care must be taken with patterns matching
425f14f4646Smrg		    *** empty strings.
426f14f4646Smrg|#
427f14f4646Smrg
428f14f4646Smrg    (syntoken "\\\\\\d{3}|\\\\." :property *prop-string-escape*)
429f14f4646Smrg    (syntoken "(\\{\\$|\\$\\{)" :property *prop-string-keyword-bold* :begin :string-varbrace)
430f14f4646Smrg    (syntoken "[$@]" :property *prop-string-keyword-bold* :begin :string-variable)
431f14f4646Smrg    (syntoken "\\$(\\d|^\\u|[][0-9!#$*()_@<>?/|,\"'])" :property *prop-string-keyword-bold*))
432f14f4646Smrg
433f14f4646Smrg  ;; variables insided strings
434f14f4646Smrg  (syntable :string-variable *prop-string-keyword* nil
435f14f4646Smrg    (syntoken "\\w+" :switch -1))
436f14f4646Smrg  (syntable :string-varbrace *prop-string-keyword* nil
437f14f4646Smrg    (syntoken		"}"
438f14f4646Smrg      :nospec		t
439f14f4646Smrg      :property	*prop-string-keyword-bold*
440f14f4646Smrg      :switch		-1)
441f14f4646Smrg    (synaugment :inside-string))
442f14f4646Smrg
443f14f4646Smrg  ;; comments
444f14f4646Smrg  (syntoken "#.*$" :property *prop-comment*)
445f14f4646Smrg
446f14f4646Smrg  ;; regex
447f14f4646Smrg  (syntoken "(\\<m)?/([^/]|\\\\/)+/\\w*"	:property *prop-regex*)
448f14f4646Smrg  (syntoken "\\<m\\{[^}]+\\}\\w*"		:property *prop-regex*)
449f14f4646Smrg  (syntoken "\\<m<[^>]+>\\w*"			:property *prop-regex*)
450f14f4646Smrg  (syntoken "\\<(s|tr)/[^/]+/([^/]|\\\\/)*/\\w*":property *prop-regex*)
451f14f4646Smrg  ;; just to avoid confusing the parser on something like split //, ...
452f14f4646Smrg  (syntoken "//" :nospec t			:property *prop-regex*)
453f14f4646Smrg
454f14f4646Smrg  ;; strings
455f14f4646Smrg  (syntoken "\"" :nospec t :contained t :begin :string)
456f14f4646Smrg  (syntable :string *prop-string* #'default-indent
457f14f4646Smrg    (syntoken "\"" :nospec t :switch -1)
458f14f4646Smrg    (synaugment :inside-string))
459f14f4646Smrg
460f14f4646Smrg  ;; more strings
461f14f4646Smrg  (perl-q-string-token "{")
462f14f4646Smrg  (perl-q-string-table "{" "}")
463f14f4646Smrg  (perl-q-string-token "[")
464f14f4646Smrg  (perl-q-string-table "[" "]")
465f14f4646Smrg  (perl-q-string-token "(")
466f14f4646Smrg  (perl-q-string-table "(" ")")
467f14f4646Smrg  (perl-q-string-token "/")
468f14f4646Smrg  (perl-q-string-table "/" "/")
469f14f4646Smrg
470f14f4646Smrg  ;; yet more strings
471f14f4646Smrg  (syntoken "'" :nospec t :contained t :begin :constant)
472f14f4646Smrg  (syntable :constant *prop-constant* #'default-indent
473f14f4646Smrg    (syntoken "'"	:nospec t :switch -1)
474f14f4646Smrg    (syntoken "\\\\." :property *prop-string-escape*))
475f14f4646Smrg
476f14f4646Smrg  ;; shell commands
477f14f4646Smrg  (syntoken "`" :nospec t :contained t :begin :shell)
478f14f4646Smrg  (syntable :shell *prop-shell* #'default-indent
479f14f4646Smrg    (syntoken "`"	:nospec t :switch -1)
480f14f4646Smrg    (synaugment :inside-string))
481f14f4646Smrg
482f14f4646Smrg  ;; punctuation
483f14f4646Smrg  (syntoken "[][$@%(){}/*+:;=<>,&!|^~\\.?-]" :property *prop-punctuation*)
484f14f4646Smrg  (syntoken "\\<x\\>" :property *prop-punctuation*)
485f14f4646Smrg
486f14f4646Smrg  ;; primitive faked heredoc support, doesn't match the proper string, just
487f14f4646Smrg  ;; expects an uppercase identifier in a single line
488f14f4646Smrg  (syntoken "<<\"[A-Z][A-Z0-9_]+\"" :property *prop-string* :begin :heredoc)
489f14f4646Smrg  (syntoken "<<'[A-Z][A-Z0-9_]+'" :property *prop-constant* :begin :heredoc)
490f14f4646Smrg  (syntoken "<<[A-Z][A-Z0-9_]+" :property *prop-preprocessor* :begin :heredoc)
491f14f4646Smrg  (syntable :heredoc *prop-documentation* #'default-indent
492f14f4646Smrg    (syntoken "^[A-Z][A-Z0-9_]+$" :switch -1))
493f14f4646Smrg
494f14f4646Smrg  (syntoken "^=(pod|item|over|head\\d)\\>.*$" :property *prop-documentation* :begin :info)
495f14f4646Smrg  (syntable :info *prop-documentation* nil
496f14f4646Smrg    (syntoken "^=cut\\>.*$"	:switch -1)
497f14f4646Smrg    (syntoken "^.*$"))
498f14f4646Smrg
499f14f4646Smrg  (syntoken "^(__END__|__DATA__)$" :property *prop-documentation*
500f14f4646Smrg    :begin :documentation)
501f14f4646Smrg
502f14f4646Smrg  (syntoken "__\\u+__" :property *prop-preprocessor*)
503f14f4646Smrg
504f14f4646Smrg  (syntable :documentation *prop-documentation* nil
505f14f4646Smrg    (syntoken "^.*$"))
506f14f4646Smrg
507f14f4646Smrg)
508