XEmacs 21.4.4 "Artificial Intelligence".
[chise/xemacs-chise.git.1] / src / syntax.c
1 /* XEmacs routines to deal with syntax tables; also word and list parsing.
2    Copyright (C) 1985-1994 Free Software Foundation, Inc.
3    Copyright (C) 1995 Sun Microsystems, Inc.
4
5 This file is part of XEmacs.
6
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING.  If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 /* Synched up with: FSF 19.28. */
23
24 /* This file has been Mule-ized. */
25
26 #include <config.h>
27 #include "lisp.h"
28
29 #include "buffer.h"
30 #include "syntax.h"
31 #include "extents.h"
32
33 /* Here is a comment from Ken'ichi HANDA <handa@etl.go.jp>
34    explaining the purpose of the Sextword syntax category:
35
36 Japanese words are not separated by spaces, which makes finding word
37 boundaries very difficult.  Theoretically it's impossible without
38 using natural language processing techniques.  But, by defining
39 pseudo-words as below (much simplified for letting you understand it
40 easily) for Japanese, we can have a convenient forward-word function
41 for Japanese.
42
43         A Japanese word is a sequence of characters that consists of
44         zero or more Kanji characters followed by zero or more
45         Hiragana characters.
46
47 Then, the problem is that now we can't say that a sequence of
48 word-constituents makes up a WORD.  For instance, both Hiragana "A"
49 and Kanji "KAN" are word-constituents but the sequence of these two
50 letters can't be a single word.
51
52 So, we introduced Sextword for Japanese letters.  A character of
53 Sextword is a word-constituent but a word boundary may exist between
54 two such characters.  */
55
56 /* Mule 2.4 doesn't seem to have Sextword - I'm removing it -- mrb */
57 /* Recovered by tomo */
58
59 #define ST_COMMENT_STYLE 0x101
60 #define ST_STRING_STYLE  0x102
61
62 Lisp_Object Qsyntax_table;
63 int lookup_syntax_properties;
64
65 Lisp_Object Qsyntax_table_p;
66
67 int words_include_escapes;
68
69 int parse_sexp_ignore_comments;
70
71 /* The following two variables are provided to tell additional information
72    to the regex routines.  We do it this way rather than change the
73    arguments to re_search_2() in an attempt to maintain some call
74    compatibility with other versions of the regex code. */
75
76 /* Tell the regex routines not to QUIT.  Normally there is a QUIT
77    each iteration in re_search_2(). */
78 int no_quit_in_re_search;
79
80 /* Tell the regex routines which buffer to access for SYNTAX() lookups
81    and the like. */
82 struct buffer *regex_emacs_buffer;
83
84 /* In Emacs, this is the string or buffer in which we
85    are matching.  It is used for looking up syntax properties.  */
86 Lisp_Object regex_match_object;
87
88 Lisp_Object Vstandard_syntax_table;
89
90 Lisp_Object Vsyntax_designator_chars_string;
91
92 /* This is the internal form of the parse state used in parse-partial-sexp.  */
93
94 struct lisp_parse_state
95 {
96   int depth;            /* Depth at end of parsing */
97   Emchar instring;      /* -1 if not within string, else desired terminator */
98   int incomment;        /* Nonzero if within a comment at end of parsing */
99   int comstyle;         /* comment style a=0, or b=1, or ST_COMMENT_STYLE */
100   int quoted;           /* Nonzero if just after an escape char at end of
101                            parsing */
102   Bufpos thislevelstart;/* Char number of most recent start-of-expression
103                            at current level */
104   Bufpos prevlevelstart;/* Char number of start of containing expression */
105   Bufpos location;      /* Char number at which parsing stopped */
106   int mindepth;         /* Minimum depth seen while scanning  */
107   Bufpos comstr_start;  /* Position just after last comment/string starter  */
108   Lisp_Object levelstarts;      /* Char numbers of starts-of-expression
109                                    of levels (starting from outermost).  */
110 };
111 \f
112 /* These variables are a cache for finding the start of a defun.
113    find_start_pos    is the place for which the defun start was found.
114    find_start_value  is the defun start position found for it.
115    find_start_buffer is the buffer it was found in.
116    find_start_begv   is the BEGV value when it was found.
117    find_start_modiff is the value of MODIFF when it was found.  */
118
119 static Bufpos find_start_pos;
120 static Bufpos find_start_value;
121 static struct buffer *find_start_buffer;
122 static Bufpos find_start_begv;
123 static int find_start_modiff;
124
125 /* Find a defun-start that is the last one before POS (or nearly the last).
126    We record what we find, so that another call in the same area
127    can return the same value right away.  */
128
129 static Bufpos
130 find_defun_start (struct buffer *buf, Bufpos pos)
131 {
132   Bufpos tem;
133
134   /* Use previous finding, if it's valid and applies to this inquiry.  */
135   if (buf == find_start_buffer
136       /* Reuse the defun-start even if POS is a little farther on.
137          POS might be in the next defun, but that's ok.
138          Our value may not be the best possible, but will still be usable.  */
139       && pos <= find_start_pos + 1000
140       && pos >= find_start_value
141       && BUF_BEGV (buf) == find_start_begv
142       && BUF_MODIFF (buf) == find_start_modiff)
143     return find_start_value;
144
145   /* Back up to start of line.  */
146   tem = find_next_newline (buf, pos, -1);
147
148   SETUP_SYNTAX_CACHE (tem, 1);
149   while (tem > BUF_BEGV (buf))
150     {
151       UPDATE_SYNTAX_CACHE_BACKWARD(tem);
152
153       /* Open-paren at start of line means we found our defun-start.  */
154       if (SYNTAX_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, tem)) == Sopen)
155         break;
156       /* Move to beg of previous line.  */
157       tem = find_next_newline (buf, tem, -2);
158     }
159
160   /* Record what we found, for the next try.  */
161   find_start_value  = tem;
162   find_start_buffer = buf;
163   find_start_modiff = BUF_MODIFF (buf);
164   find_start_begv   = BUF_BEGV (buf);
165   find_start_pos    = pos;
166
167   return find_start_value;
168 }
169 \f
170 DEFUN ("syntax-table-p", Fsyntax_table_p, 1, 1, 0, /*
171 Return t if OBJECT is a syntax table.
172 Any vector of 256 elements will do.
173 */
174        (object))
175 {
176   return (CHAR_TABLEP (object)
177           && XCHAR_TABLE_TYPE (object) == CHAR_TABLE_TYPE_SYNTAX)
178     ? Qt : Qnil;
179 }
180
181 static Lisp_Object
182 check_syntax_table (Lisp_Object obj, Lisp_Object default_)
183 {
184   if (NILP (obj))
185     obj = default_;
186   while (NILP (Fsyntax_table_p (obj)))
187     obj = wrong_type_argument (Qsyntax_table_p, obj);
188   return obj;
189 }
190
191 DEFUN ("syntax-table", Fsyntax_table, 0, 1, 0, /*
192 Return the current syntax table.
193 This is the one specified by the current buffer, or by BUFFER if it
194 is non-nil.
195 */
196        (buffer))
197 {
198   return decode_buffer (buffer, 0)->syntax_table;
199 }
200
201 DEFUN ("standard-syntax-table", Fstandard_syntax_table, 0, 0, 0, /*
202 Return the standard syntax table.
203 This is the one used for new buffers.
204 */
205        ())
206 {
207   return Vstandard_syntax_table;
208 }
209
210 DEFUN ("copy-syntax-table", Fcopy_syntax_table, 0, 1, 0, /*
211 Return a new syntax table which is a copy of SYNTAX-TABLE.
212 SYNTAX-TABLE defaults to the standard syntax table.
213 */
214        (syntax_table))
215 {
216   if (NILP (Vstandard_syntax_table))
217     return Fmake_char_table (Qsyntax);
218
219   syntax_table = check_syntax_table (syntax_table, Vstandard_syntax_table);
220   return Fcopy_char_table (syntax_table);
221 }
222
223 DEFUN ("set-syntax-table", Fset_syntax_table, 1, 2, 0, /*
224 Select SYNTAX-TABLE as the new syntax table for BUFFER.
225 BUFFER defaults to the current buffer if omitted.
226 */
227        (syntax_table, buffer))
228 {
229   struct buffer *buf = decode_buffer (buffer, 0);
230   syntax_table = check_syntax_table (syntax_table, Qnil);
231   buf->syntax_table = syntax_table;
232   buf->mirror_syntax_table = XCHAR_TABLE (syntax_table)->mirror_table;
233   /* Indicate that this buffer now has a specified syntax table.  */
234   buf->local_var_flags |= XINT (buffer_local_flags.syntax_table);
235   return syntax_table;
236 }
237 \f
238 /* The current syntax state */
239 struct syntax_cache syntax_cache;
240
241
242 /* 
243    Update syntax_cache to an appropriate setting for position POS
244
245    The sign of COUNT gives the relative position of POS wrt the
246    previously valid interval.  (not currently used)
247
248    `syntax_cache.*_change' are the next and previous positions at
249    which syntax_code and c_s_t will need to be recalculated.
250
251    #### Currently this code uses 'get-char-property', which will
252    return the "last smallest" extent at a given position. In cases
253    where overlapping extents are defined, this code will simply use
254    whatever is returned by get-char-property.
255
256    It might be worth it at some point to merge provided syntax tables
257    outward to the current buffer. */
258
259 void
260 update_syntax_cache (int pos, int count, int init)
261 {
262   Lisp_Object tmp_table;
263
264   if (init)
265     {
266       syntax_cache.prev_change = -1;
267       syntax_cache.next_change = -1;
268     }
269
270   if (pos > syntax_cache.prev_change &&
271       pos < syntax_cache.next_change)
272     {
273       /* do nothing */
274     }
275   else
276     {
277       if (NILP (syntax_cache.object) || EQ (syntax_cache.object, Qt))
278         {
279           int get_change_before = pos + 1;
280
281           tmp_table = Fget_char_property (make_int(pos), Qsyntax_table,
282                                           make_buffer (syntax_cache.buffer), Qnil);
283           syntax_cache.next_change =
284             XINT (Fnext_extent_change (make_int (pos > 0 ? pos : 1),
285                                        make_buffer (syntax_cache.buffer)));
286
287           if (get_change_before < 1)
288             get_change_before = 1;
289           else if (get_change_before > BUF_ZV (syntax_cache.buffer))
290             get_change_before = BUF_ZV (syntax_cache.buffer);
291
292           syntax_cache.prev_change =
293             XINT (Fprevious_extent_change (make_int (get_change_before),
294                                            make_buffer (syntax_cache.buffer)));
295         }
296       else
297         {
298           int get_change_before = pos + 1;
299
300           tmp_table = Fget_char_property (make_int(pos), Qsyntax_table,
301                                           syntax_cache.object, Qnil);
302           syntax_cache.next_change =
303             XINT (Fnext_extent_change (make_int (pos >= 0 ? pos : 0),
304                                        syntax_cache.object));
305
306           if (get_change_before < 0)
307             get_change_before = 0;
308           else if (get_change_before > XSTRING_LENGTH(syntax_cache.object))
309             get_change_before = XSTRING_LENGTH(syntax_cache.object);
310
311           syntax_cache.prev_change =
312             XINT (Fprevious_extent_change (make_int (pos >= 0 ? pos : 0),
313                                            syntax_cache.object));
314         }
315
316       if (EQ (Fsyntax_table_p (tmp_table), Qt))
317         {
318           syntax_cache.use_code = 0;
319           syntax_cache.current_syntax_table =
320             XCHAR_TABLE (tmp_table)->mirror_table;
321         } 
322       else if (CONSP (tmp_table) && INTP (XCAR (tmp_table)))
323         {
324           syntax_cache.use_code = 1;
325           syntax_cache.syntax_code = XINT (XCAR(tmp_table));
326         }
327       else 
328         {
329           syntax_cache.use_code = 0;
330           syntax_cache.current_syntax_table =
331             syntax_cache.buffer->mirror_syntax_table;
332         }
333     }
334 }
335 \f
336 /* Convert a letter which signifies a syntax code
337    into the code it signifies.
338    This is used by modify-syntax-entry, and other things. */
339
340 const unsigned char syntax_spec_code[0400] =
341 { 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
342   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
343   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
344   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
345   (char) Swhitespace, 0377, (char) Sstring, 0377,
346       (char) Smath, 0377, 0377, (char) Squote,
347   (char) Sopen, (char) Sclose, 0377, 0377,
348         0377, (char) Swhitespace, (char) Spunct, (char) Scharquote,
349   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
350   0377, 0377, 0377, 0377,
351         (char) Scomment, 0377, (char) Sendcomment, 0377,
352   (char) Sinherit, 0377, 0377, 0377, 0377, 0377, 0377, 0377,   /* @, A ... */
353   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
354   0377, 0377, 0377, 0377, 0377, 0377, 0377, (char) Sword,
355   0377, 0377, 0377, 0377, (char) Sescape, 0377, 0377, (char) Ssymbol,
356   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,   /* `, a, ... */
357   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
358   0377, 0377, 0377, 0377, 0377, 0377, 0377, (char) Sword,
359   0377, 0377, 0377, 0377, (char) Sstring_fence, 0377, 0377, 0377
360 };
361
362 const unsigned char syntax_code_spec[] =  " .w_()'\"$\\/<>@!|";
363
364 DEFUN ("syntax-designator-chars", Fsyntax_designator_chars, 0, 0, 0, /*
365 Return a string of the recognized syntax designator chars.
366 The chars are ordered by their internal syntax codes, which are
367 numbered starting at 0.
368 */
369        ())
370 {
371   return Vsyntax_designator_chars_string;
372 }
373
374 DEFUN ("char-syntax", Fchar_syntax, 1, 2, 0, /*
375 Return the syntax code of CHARACTER, described by a character.
376 For example, if CHARACTER is a word constituent,
377 the character `?w' is returned.
378 The characters that correspond to various syntax codes
379 are listed in the documentation of `modify-syntax-entry'.
380 Optional second argument SYNTAX-TABLE defaults to the current buffer's
381 syntax table.
382 */
383        (character, syntax_table))
384 {
385   Lisp_Char_Table *mirrortab;
386
387   if (NILP (character))
388     {
389       character = make_char ('\000');
390     }
391   CHECK_CHAR_COERCE_INT (character);
392   syntax_table = check_syntax_table (syntax_table, current_buffer->syntax_table);
393   mirrortab = XCHAR_TABLE (XCHAR_TABLE (syntax_table)->mirror_table);
394   return make_char (syntax_code_spec[(int) SYNTAX (mirrortab, XCHAR (character))]);
395 }
396
397 #ifdef MULE
398
399 enum syntaxcode
400 charset_syntax (struct buffer *buf, Lisp_Object charset, int *multi_p_out)
401 {
402   *multi_p_out = 1;
403   /* #### get this right */
404   return Spunct;
405 }
406
407 #endif
408
409 Lisp_Object
410 syntax_match (Lisp_Object syntax_table, Emchar ch)
411 {
412   Lisp_Object code = XCHAR_TABLE_VALUE_UNSAFE (syntax_table, ch);
413   Lisp_Object code2 = code;
414
415   if (CONSP (code))
416     code2 = XCAR (code);
417   if (SYNTAX_FROM_CODE (XINT (code2)) == Sinherit)
418     code = XCHAR_TABLE_VALUE_UNSAFE (Vstandard_syntax_table, ch);
419
420   return CONSP (code) ? XCDR (code) : Qnil;
421 }
422
423 DEFUN ("matching-paren", Fmatching_paren, 1, 2, 0, /*
424 Return the matching parenthesis of CHARACTER, or nil if none.
425 Optional second argument SYNTAX-TABLE defaults to the current buffer's
426 syntax table.
427 */
428        (character, syntax_table))
429 {
430   Lisp_Char_Table *mirrortab;
431   int code;
432
433   CHECK_CHAR_COERCE_INT (character);
434   syntax_table = check_syntax_table (syntax_table, current_buffer->syntax_table);
435   mirrortab = XCHAR_TABLE (XCHAR_TABLE (syntax_table)->mirror_table);
436   code = SYNTAX (mirrortab, XCHAR (character));
437   if (code == Sopen || code == Sclose || code == Sstring)
438     return syntax_match (syntax_table, XCHAR (character));
439   return Qnil;
440 }
441
442 \f
443
444 #ifdef MULE
445 /* Return 1 if there is a word boundary between two word-constituent
446    characters C1 and C2 if they appear in this order, else return 0.
447    There is no word boundary between two word-constituent ASCII
448    characters.  */
449 #define WORD_BOUNDARY_P(c1, c2)                 \
450   (!(CHAR_ASCII_P (c1) && CHAR_ASCII_P (c2))    \
451    && word_boundary_p (c1, c2))
452
453 extern int word_boundary_p (Emchar c1, Emchar c2);
454 #endif
455
456 /* Return the position across COUNT words from FROM.
457    If that many words cannot be found before the end of the buffer, return 0.
458    COUNT negative means scan backward and stop at word beginning.  */
459
460 Bufpos
461 scan_words (struct buffer *buf, Bufpos from, int count)
462 {
463   Bufpos limit = count > 0 ? BUF_ZV (buf) : BUF_BEGV (buf);
464   Emchar ch0, ch1;
465   enum syntaxcode code;
466
467   SETUP_SYNTAX_CACHE_FOR_BUFFER (buf, from, count);
468
469   /* #### is it really worth it to hand expand both cases? JV */
470   while (count > 0)
471     {
472       QUIT;
473
474       while (1)
475         {
476           if (from == limit)
477             return 0;
478
479           UPDATE_SYNTAX_CACHE_FORWARD (from);
480           ch0 = BUF_FETCH_CHAR (buf, from);
481           code = SYNTAX_FROM_CACHE (mirrortab, ch0);
482
483           from++;
484           if (words_include_escapes
485               && (code == Sescape || code == Scharquote))
486             break;
487           if (code == Sword)
488             break;
489         }
490
491       QUIT;
492
493       while (from != limit)
494         {
495           UPDATE_SYNTAX_CACHE_FORWARD (from);
496           ch1 = BUF_FETCH_CHAR (buf, from);
497           code = SYNTAX_FROM_CACHE (mirrortab, ch1);
498           if (!(words_include_escapes
499                 && (code == Sescape || code == Scharquote)))
500             if (code != Sword
501 #ifdef MULE
502                 || WORD_BOUNDARY_P (ch0, ch1)
503 #endif
504                 )
505               break;
506 #ifdef MULE
507           ch0 = ch1;
508 #endif
509           from++;
510         }
511       count--;
512     }
513
514   while (count < 0)
515     {
516       QUIT;
517
518       while (1)
519         {
520           if (from == limit)
521             return 0;
522
523           UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
524           ch1 = BUF_FETCH_CHAR (buf, from - 1);
525           code = SYNTAX_FROM_CACHE (mirrortab, ch1);
526           from--;
527
528           if (words_include_escapes
529               && (code == Sescape || code == Scharquote))
530             break;
531           if (code == Sword)
532             break;
533         }
534
535       QUIT;
536
537       while (from != limit)
538         {
539           UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
540           ch0 = BUF_FETCH_CHAR (buf, from - 1);
541           code = SYNTAX_FROM_CACHE (mirrortab, ch0);
542
543           if (!(words_include_escapes
544                 && (code == Sescape || code == Scharquote)))
545             if (code != Sword
546 #ifdef MULE
547                 || WORD_BOUNDARY_P (ch0, ch1)
548 #endif
549                 )
550               break;
551 #ifdef MULE
552           ch1 = ch0;
553 #endif
554           from--;
555         }
556       count++;
557     }
558
559   return from;
560 }
561
562 DEFUN ("forward-word", Fforward_word, 0, 2, "_p", /*
563 Move point forward COUNT words (backward if COUNT is negative).
564 Normally t is returned, but if an edge of the buffer is reached,
565 point is left there and nil is returned.
566
567 The characters that are moved over may be added to the current selection
568 \(i.e. active region) if the Shift key is held down, a motion key is used
569 to invoke this command, and `shifted-motion-keys-select-region' is t; see
570 the documentation for this variable for more details.
571
572 COUNT defaults to 1, and BUFFER defaults to the current buffer.
573 */
574        (count, buffer))
575 {
576   Bufpos val;
577   struct buffer *buf = decode_buffer (buffer, 0);
578   EMACS_INT n;
579
580   if (NILP (count))
581     n = 1;
582   else
583     {
584       CHECK_INT (count);
585       n = XINT (count);
586     }
587
588   val = scan_words (buf, BUF_PT (buf), n);
589   if (val)
590     {
591       BUF_SET_PT (buf, val);
592       return Qt;
593     }
594   else
595     {
596       BUF_SET_PT (buf, n > 0 ? BUF_ZV (buf) : BUF_BEGV (buf));
597       return Qnil;
598     }
599 }
600 \f
601 static void scan_sexps_forward (struct buffer *buf,
602                                 struct lisp_parse_state *,
603                                 Bufpos from, Bufpos end,
604                                 int targetdepth, int stopbefore,
605                                 Lisp_Object oldstate,
606                                 int commentstop);
607
608 static int
609 find_start_of_comment (struct buffer *buf, Bufpos from, Bufpos stop,
610                        int comstyle)
611 {
612   Emchar c;
613   enum syntaxcode code;
614
615   /* Look back, counting the parity of string-quotes,
616      and recording the comment-starters seen.
617      When we reach a safe place, assume that's not in a string;
618      then step the main scan to the earliest comment-starter seen
619      an even number of string quotes away from the safe place.
620
621      OFROM[I] is position of the earliest comment-starter seen
622      which is I+2X quotes from the comment-end.
623      PARITY is current parity of quotes from the comment end.  */
624   int parity = 0;
625   Emchar my_stringend = 0;
626   int string_lossage = 0;
627   Bufpos comment_end = from;
628   Bufpos comstart_pos = 0;
629   int comstart_parity = 0;
630   int styles_match_p = 0;
631   /* mask to match comment styles against; for ST_COMMENT_STYLE, this
632      will get set to SYNTAX_COMMENT_STYLE_B, but never get checked */
633   int mask = comstyle ? SYNTAX_COMMENT_STYLE_B : SYNTAX_COMMENT_STYLE_A;
634
635   /* At beginning of range to scan, we're outside of strings;
636      that determines quote parity to the comment-end.  */
637   while (from != stop)
638     {
639       int syncode;
640
641       /* Move back and examine a character.  */
642       from--;
643       UPDATE_SYNTAX_CACHE_BACKWARD (from);
644
645       c = BUF_FETCH_CHAR (buf, from);
646       code = SYNTAX_FROM_CACHE (mirrortab, c);
647       syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
648
649       /* is this a 1-char comment end sequence? if so, try
650          to see if style matches previously extracted mask */
651       if (code == Sendcomment)
652         {
653           styles_match_p =
654             SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode) & mask;
655         }
656
657       /* or are we looking at a 1-char comment start sequence
658          of the style matching mask? */
659       else if (code == Scomment)
660         {
661           styles_match_p =
662             SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode) & mask;
663         }
664
665       /* otherwise, is this a 2-char comment end or start sequence? */
666       else if (from > stop)
667         do
668           {
669             /* 2-char comment end sequence? */
670             if (SYNTAX_CODE_END_SECOND_P (syncode))
671               {
672                 int prev_syncode;
673                 UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
674                 prev_syncode =
675                   SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from - 1));
676
677                 if (SYNTAX_CODES_END_P (prev_syncode, syncode))
678                   {
679                     code = Sendcomment;
680                     styles_match_p =
681                       SYNTAX_CODES_COMMENT_MASK_END (prev_syncode, syncode)
682                       & mask;
683                     from--;
684                     UPDATE_SYNTAX_CACHE_BACKWARD (from);
685                     c = BUF_FETCH_CHAR (buf, from);
686
687                     /* Found a comment-end sequence, so skip past the
688                        check for a comment-start */
689                     break;
690                   }
691               }
692
693             /* 2-char comment start sequence? */
694             if (SYNTAX_CODE_START_SECOND_P (syncode))
695               {
696                 int prev_syncode;
697                 UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
698                 prev_syncode =
699                   SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from - 1));
700
701                 if (SYNTAX_CODES_START_P (prev_syncode, syncode))
702                   {
703                     code = Scomment;
704                     styles_match_p =
705                       SYNTAX_CODES_COMMENT_MASK_START (prev_syncode, syncode)
706                       & mask;
707                     from--;
708                     UPDATE_SYNTAX_CACHE_BACKWARD (from);
709                     c = BUF_FETCH_CHAR (buf, from);
710                   }
711               }
712           } while (0);
713
714       /* Ignore escaped characters.  */
715       if (char_quoted (buf, from))
716         continue;
717
718       /* Track parity of quotes.  */
719       if (code == Sstring)
720         {
721           parity ^= 1;
722           if (my_stringend == 0)
723             my_stringend = c;
724           /* If we have two kinds of string delimiters.
725              There's no way to grok this scanning backwards.  */
726           else if (my_stringend != c)
727             string_lossage = 1;
728         }
729
730       if (code == Sstring_fence || code == Scomment_fence)
731         {
732           parity ^= 1;
733           if (my_stringend == 0)
734             my_stringend =
735               code == Sstring_fence ? ST_STRING_STYLE : ST_COMMENT_STYLE;
736           /* If we have two kinds of string delimiters.
737              There's no way to grok this scanning backwards.  */
738           else if (my_stringend != (code == Sstring_fence 
739                                     ? ST_STRING_STYLE : ST_COMMENT_STYLE))
740             string_lossage = 1;
741         }
742
743       /* Record comment-starters according to that
744          quote-parity to the comment-end.  */
745       if (code == Scomment && styles_match_p)
746         {
747           comstart_parity = parity;
748           comstart_pos = from;
749         }
750
751       /* If we find another earlier comment-ender,
752          any comment-starts earlier than that don't count
753          (because they go with the earlier comment-ender).  */
754       if (code == Sendcomment && styles_match_p)
755         break;
756
757       /* Assume a defun-start point is outside of strings.  */
758       if (code == Sopen
759           && (from == stop || BUF_FETCH_CHAR (buf, from - 1) == '\n'))
760         break;
761     }
762
763   if (comstart_pos == 0)
764     from = comment_end;
765   /* If the earliest comment starter
766      is followed by uniform paired string quotes or none,
767      we know it can't be inside a string
768      since if it were then the comment ender would be inside one.
769      So it does start a comment.  Skip back to it.  */
770   else if (comstart_parity == 0 && !string_lossage)
771     from = comstart_pos;
772   else
773     {
774       /* We had two kinds of string delimiters mixed up
775          together.  Decode this going forwards.
776          Scan fwd from the previous comment ender
777          to the one in question; this records where we
778          last passed a comment starter.  */
779
780       struct lisp_parse_state state;
781       scan_sexps_forward (buf, &state, find_defun_start (buf, comment_end),
782                           comment_end - 1, -10000, 0, Qnil, 0);
783       if (state.incomment)
784         from = state.comstr_start;
785       else
786         /* We can't grok this as a comment; scan it normally.  */
787         from = comment_end;
788       UPDATE_SYNTAX_CACHE_FORWARD (from - 1);
789     }
790   return from;
791 }
792
793 static Bufpos
794 find_end_of_comment (struct buffer *buf, Bufpos from, Bufpos stop, int comstyle)
795 {
796   int c;
797   int prev_code;
798   /* mask to match comment styles against; for ST_COMMENT_STYLE, this
799      will get set to SYNTAX_COMMENT_STYLE_B, but never get checked */
800   int mask = comstyle ? SYNTAX_COMMENT_STYLE_B : SYNTAX_COMMENT_STYLE_A;
801
802   /* This is only called by functions which have already set up the
803      syntax_cache and are keeping it up-to-date */
804   while (1)
805     {
806       if (from == stop)
807         {
808           return -1;
809         }
810
811       UPDATE_SYNTAX_CACHE_FORWARD (from);
812       c = BUF_FETCH_CHAR (buf, from);
813
814       /* Test for generic comments */
815       if (comstyle == ST_COMMENT_STYLE)
816         {
817           if (SYNTAX_FROM_CACHE (mirrortab, c) == Scomment_fence)
818             {
819               from++;
820               UPDATE_SYNTAX_CACHE_FORWARD (from);
821               break;
822             }
823           from++;
824           continue; /* No need to test other comment styles in a
825                        generic comment */
826         }
827       else
828
829         if (SYNTAX_FROM_CACHE (mirrortab, c) == Sendcomment
830             && SYNTAX_CODE_MATCHES_1CHAR_P
831             (SYNTAX_CODE_FROM_CACHE (mirrortab, c), mask))
832         /* we have encountered a comment end of the same style
833            as the comment sequence which began this comment
834            section */
835           {
836             from++;
837             UPDATE_SYNTAX_CACHE_FORWARD (from);
838             break;
839           }
840
841       prev_code = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
842       from++;
843       UPDATE_SYNTAX_CACHE_FORWARD (from);
844       if (from < stop
845           && SYNTAX_CODES_MATCH_END_P
846           (prev_code,
847            SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from)),
848            mask)
849
850           )
851         /* we have encountered a comment end of the same style
852            as the comment sequence which began this comment
853            section */
854         {
855           from++;
856           UPDATE_SYNTAX_CACHE_FORWARD (from);
857           break;
858         }
859     }
860   return from;
861 }
862
863 \f
864 /* #### between FSF 19.23 and 19.28 there are some changes to the logic
865    in this function (and minor changes to find_start_of_comment(),
866    above, which is part of Fforward_comment() in FSF).  Attempts to port
867    that logic made this function break, so I'm leaving it out.  If anyone
868    ever complains about this function not working properly, take a look
869    at those changes.  --ben */
870
871 DEFUN ("forward-comment", Fforward_comment, 0, 2, 0, /*
872 Move forward across up to COUNT comments, or backwards if COUNT is negative.
873 Stop scanning if we find something other than a comment or whitespace.
874 Set point to where scanning stops.
875 If COUNT comments are found as expected, with nothing except whitespace
876 between them, return t; otherwise return nil.
877 Point is set in either case.
878 COUNT defaults to 1, and BUFFER defaults to the current buffer.
879 */
880        (count, buffer))
881 {
882   Bufpos from;
883   Bufpos stop;
884   Emchar c;
885   enum syntaxcode code;
886   int syncode;
887   EMACS_INT n;
888   struct buffer *buf = decode_buffer (buffer, 0);
889
890   if (NILP (count))
891     n = 1;
892   else
893     {
894       CHECK_INT (count);
895       n = XINT (count);
896     }
897
898   from = BUF_PT (buf);
899
900   SETUP_SYNTAX_CACHE (from, n);
901   while (n > 0)
902     {
903       QUIT;
904
905       stop = BUF_ZV (buf);
906       while (from < stop)
907         {
908           int comstyle = 0;     /* mask for finding matching comment style */
909
910           if (char_quoted (buf, from))
911             {
912               from++;
913               continue;
914             }
915
916           UPDATE_SYNTAX_CACHE_FORWARD (from);
917           c = BUF_FETCH_CHAR (buf, from);
918           code = SYNTAX_FROM_CACHE (mirrortab, c);
919           syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
920
921           if (code == Scomment)
922             {
923               /* we have encountered a single character comment start
924                  sequence, and we are ignoring all text inside comments.
925                  we must record the comment style this character begins
926                  so that later, only a comment end of the same style actually
927                  ends the comment section */
928               comstyle = SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode)
929                 == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
930             }
931
932           else if (code == Scomment_fence)
933             {
934               from++;
935               code = Scomment;
936               comstyle = ST_COMMENT_STYLE;
937             }
938
939           else if (from < stop
940                    && SYNTAX_CODE_START_FIRST_P (syncode))
941             {
942               int next_syncode;
943               UPDATE_SYNTAX_CACHE_FORWARD (from + 1);
944               next_syncode =
945                 SYNTAX_CODE_FROM_CACHE (mirrortab, 
946                                         BUF_FETCH_CHAR (buf, from + 1));
947
948               if (SYNTAX_CODES_START_P (syncode, next_syncode))
949                 {
950                   /* we have encountered a 2char comment start sequence and we
951                      are ignoring all text inside comments. we must record
952                      the comment style this sequence begins so that later,
953                      only a comment end of the same style actually ends
954                      the comment section */
955                   code = Scomment;
956                   comstyle =
957                     SYNTAX_CODES_COMMENT_MASK_START (syncode, next_syncode)
958                     == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
959                   from++;
960                 }
961             }
962
963           if (code == Scomment)
964             {
965               Bufpos newfrom = find_end_of_comment (buf, from, stop, comstyle);
966               if (newfrom < 0)
967                 {
968                   /* we stopped because from==stop */
969                   BUF_SET_PT (buf, stop);
970                   return Qnil;
971                 }
972               from = newfrom;
973
974               /* We have skipped one comment.  */
975               break;
976             }
977           else if (code != Swhitespace
978                    && code != Sendcomment
979                    && code != Scomment )
980             {
981               BUF_SET_PT (buf, from);
982               return Qnil;
983             }
984           from++;
985         }
986
987       /* End of comment reached */
988       n--;
989     }
990
991   while (n < 0)
992     {
993       QUIT;
994
995       stop = BUF_BEGV (buf);
996       while (from > stop)
997         {
998           int comstyle = 0;     /* mask for finding matching comment style */
999
1000           from--;
1001           if (char_quoted (buf, from))
1002             {
1003               from--;
1004               continue;
1005             }
1006
1007           c = BUF_FETCH_CHAR (buf, from);
1008           code = SYNTAX_FROM_CACHE (mirrortab, c);
1009           syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
1010
1011           if (code == Sendcomment)
1012             {
1013               /* we have found a single char end comment. we must record
1014                  the comment style encountered so that later, we can match
1015                  only the proper comment begin sequence of the same style */
1016               comstyle = SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode)
1017                 == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1018             }
1019
1020           else if (code == Scomment_fence)
1021             {
1022               code = Sendcomment;
1023               comstyle = ST_COMMENT_STYLE;
1024             }
1025
1026           else if (from > stop
1027                    && SYNTAX_CODE_END_SECOND_P (syncode))
1028             {
1029               int prev_syncode;
1030               UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
1031               prev_syncode =
1032                 SYNTAX_CODE_FROM_CACHE (mirrortab,
1033                                         BUF_FETCH_CHAR (buf, from - 1));
1034               if (SYNTAX_CODES_END_P (prev_syncode, syncode))
1035                 {
1036                   /* We must record the comment style encountered so that
1037                      later, we can match only the proper comment begin
1038                      sequence of the same style.  */
1039                   code = Sendcomment;
1040                   comstyle = SYNTAX_CODES_COMMENT_MASK_END
1041                     (prev_syncode, syncode) == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1042                   from--;
1043                 }
1044             }
1045
1046           if (code == Sendcomment)
1047             {
1048               from = find_start_of_comment (buf, from, stop, comstyle);
1049               break;
1050             }
1051
1052           else if (code != Swhitespace
1053                    && code != Scomment
1054                    && code != Sendcomment)
1055             {
1056               BUF_SET_PT (buf, from + 1);
1057               return Qnil;
1058             }
1059         }
1060
1061       n++;
1062     }
1063
1064   BUF_SET_PT (buf, from);
1065   return Qt;
1066 }
1067
1068 \f
1069 Lisp_Object
1070 scan_lists (struct buffer *buf, Bufpos from, int count, int depth,
1071             int sexpflag, int noerror)
1072 {
1073   Bufpos stop;
1074   Emchar c;
1075   int quoted;
1076   int mathexit = 0;
1077   enum syntaxcode code;
1078   int syncode;
1079   int min_depth = depth;    /* Err out if depth gets less than this. */
1080
1081   if (depth > 0) min_depth = 0;
1082
1083   SETUP_SYNTAX_CACHE_FOR_BUFFER (buf, from, count);
1084   while (count > 0)
1085     {
1086       QUIT;
1087
1088       stop = BUF_ZV (buf);
1089       while (from < stop)
1090         {
1091           int comstyle = 0;     /* mask for finding matching comment style */
1092
1093           UPDATE_SYNTAX_CACHE_FORWARD (from);
1094           c = BUF_FETCH_CHAR (buf, from);
1095           code = SYNTAX_FROM_CACHE (mirrortab, c);
1096           syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
1097           from++;
1098
1099           /* a 1-char comment start sequence */
1100           if (code == Scomment && parse_sexp_ignore_comments)
1101             {
1102               comstyle = SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode) ==
1103                 SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1104             }
1105
1106           /* else, a 2-char comment start sequence? */
1107           else if (from < stop
1108                    && SYNTAX_CODE_START_FIRST_P (syncode)
1109                    && parse_sexp_ignore_comments)
1110             {
1111               int next_syncode;
1112               UPDATE_SYNTAX_CACHE_FORWARD (from);
1113               next_syncode =
1114                 SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from));
1115
1116               if (SYNTAX_CODES_START_P (syncode, next_syncode))
1117                 {
1118               /* we have encountered a comment start sequence and we
1119                  are ignoring all text inside comments. we must record
1120                  the comment style this sequence begins so that later,
1121                  only a comment end of the same style actually ends
1122                  the comment section */
1123               code = Scomment;
1124                   comstyle = SYNTAX_CODES_COMMENT_MASK_START
1125                     (syncode, next_syncode) == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1126               from++;
1127             }
1128             }
1129           UPDATE_SYNTAX_CACHE_FORWARD (from);
1130
1131           if (SYNTAX_CODE_PREFIX (syncode))
1132             continue;
1133
1134           switch (code)
1135             {
1136             case Sescape:
1137             case Scharquote:
1138               if (from == stop) goto lose;
1139               from++;
1140               /* treat following character as a word constituent */
1141             case Sword:
1142             case Ssymbol:
1143               if (depth || !sexpflag) break;
1144               /* This word counts as a sexp; return at end of it. */
1145               while (from < stop)
1146                 {
1147                   UPDATE_SYNTAX_CACHE_FORWARD (from);
1148                   switch (SYNTAX_FROM_CACHE (mirrortab,
1149                                              BUF_FETCH_CHAR (buf, from)))
1150                     {
1151                     case Scharquote:
1152                     case Sescape:
1153                       from++;
1154                       if (from == stop) goto lose;
1155                       break;
1156                     case Sword:
1157                     case Ssymbol:
1158                     case Squote:
1159                       break;
1160                     default:
1161                       goto done;
1162                     }
1163                   from++;
1164                 }
1165               goto done;
1166
1167             case Scomment_fence:
1168               comstyle = ST_COMMENT_STYLE;
1169             case Scomment:
1170               if (!parse_sexp_ignore_comments)
1171                 break;
1172               UPDATE_SYNTAX_CACHE_FORWARD (from);
1173               {
1174                 Bufpos newfrom =
1175                   find_end_of_comment (buf, from, stop, comstyle);
1176                 if (newfrom < 0)
1177                   {
1178                     /* we stopped because from == stop in search forward */
1179                     from = stop;
1180                     if (depth == 0)
1181                       goto done;
1182                     goto lose;
1183                   }
1184                 from = newfrom;
1185               }
1186               break;
1187
1188             case Smath:
1189               if (!sexpflag)
1190                 break;
1191               if (from != stop && c == BUF_FETCH_CHAR (buf, from))
1192                 from++;
1193               if (mathexit)
1194                 {
1195                   mathexit = 0;
1196                   goto close1;
1197                 }
1198               mathexit = 1;
1199
1200             case Sopen:
1201               if (!++depth) goto done;
1202               break;
1203
1204             case Sclose:
1205             close1:
1206             if (!--depth) goto done;
1207             if (depth < min_depth)
1208               {
1209                 if (noerror)
1210                   return Qnil;
1211                 error ("Containing expression ends prematurely");
1212               }
1213             break;
1214
1215             case Sstring_fence:
1216             case Sstring:
1217               {
1218                 Emchar stringterm;
1219
1220                 if (code != Sstring_fence)
1221                   {
1222                 /* XEmacs change: call syntax_match on character */
1223                 Emchar ch = BUF_FETCH_CHAR (buf, from - 1);
1224                     Lisp_Object stermobj =
1225                       syntax_match (syntax_cache.current_syntax_table, ch);
1226
1227                 if (CHARP (stermobj))
1228                   stringterm = XCHAR (stermobj);
1229                 else
1230                   stringterm = ch;
1231                   }
1232                 else
1233                   stringterm = '\0'; /* avoid compiler warnings */
1234
1235                 while (1)
1236                   {
1237                     if (from >= stop)
1238                       goto lose;
1239                     UPDATE_SYNTAX_CACHE_FORWARD (from);
1240                     c = BUF_FETCH_CHAR (buf, from);
1241                     if (code == Sstring
1242                         ? c == stringterm
1243                         : SYNTAX_FROM_CACHE (mirrortab, c) == Sstring_fence)
1244                       break;
1245
1246                     switch (SYNTAX_FROM_CACHE (mirrortab, c))
1247                       {
1248                       case Scharquote:
1249                       case Sescape:
1250                         from++;
1251                         break;
1252                       default:
1253                         break;
1254                       }
1255                     from++;
1256                   }
1257                 from++;
1258                 if (!depth && sexpflag) goto done;
1259                 break;
1260               }
1261
1262             default:
1263               break;
1264             }
1265         }
1266
1267       /* Reached end of buffer.  Error if within object,
1268          return nil if between */
1269       if (depth) goto lose;
1270
1271       return Qnil;
1272
1273       /* End of object reached */
1274     done:
1275       count--;
1276     }
1277
1278   while (count < 0)
1279     {
1280       QUIT;
1281
1282       stop = BUF_BEGV (buf);
1283       while (from > stop)
1284         {
1285           int comstyle = 0;     /* mask for finding matching comment style */
1286
1287           from--;
1288           UPDATE_SYNTAX_CACHE_BACKWARD (from);
1289           quoted = char_quoted (buf, from);
1290           if (quoted)
1291             {
1292             from--;
1293               UPDATE_SYNTAX_CACHE_BACKWARD (from);
1294             }
1295
1296           c = BUF_FETCH_CHAR (buf, from);
1297           code = SYNTAX_FROM_CACHE (mirrortab, c);
1298           syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
1299
1300           if (code == Sendcomment && parse_sexp_ignore_comments)
1301             {
1302               /* we have found a single char end comment. we must record
1303                  the comment style encountered so that later, we can match
1304                  only the proper comment begin sequence of the same style */
1305               comstyle = SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode)
1306                 == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1307             }
1308
1309           else if (from > stop
1310                    && SYNTAX_CODE_END_SECOND_P (syncode)
1311                    && !char_quoted (buf, from - 1)
1312                    && parse_sexp_ignore_comments)
1313             {
1314               int prev_syncode;
1315               UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
1316               prev_syncode = SYNTAX_CODE_FROM_CACHE
1317                 (mirrortab, BUF_FETCH_CHAR (buf, from - 1));
1318
1319               if (SYNTAX_CODES_END_P (prev_syncode, syncode))
1320                 {
1321               /* we must record the comment style encountered so that
1322                  later, we can match only the proper comment begin
1323                  sequence of the same style */
1324               code = Sendcomment;
1325                   comstyle = SYNTAX_CODES_COMMENT_MASK_END
1326                     (prev_syncode, syncode) == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1327               from--;
1328             }
1329             }
1330
1331           if (SYNTAX_CODE_PREFIX (syncode))
1332             continue;
1333
1334           switch (quoted ? Sword : code)
1335             {
1336             case Sword:
1337             case Ssymbol:
1338               if (depth || !sexpflag) break;
1339               /* This word counts as a sexp; count object finished after
1340                  passing it. */
1341               while (from > stop)
1342                 {
1343                   UPDATE_SYNTAX_CACHE_BACKWARD (from);
1344                   quoted = char_quoted (buf, from - 1);
1345
1346                   if (quoted)
1347                     from--;
1348                   if (! (quoted
1349                          || (syncode =
1350                              SYNTAX_FROM_CACHE (mirrortab,
1351                                                 BUF_FETCH_CHAR (buf, from - 1)))
1352                          == Sword
1353                          || syncode == Ssymbol
1354                          || syncode == Squote))
1355                     goto done2;
1356                   from--;
1357                 }
1358               goto done2;
1359
1360             case Smath:
1361               if (!sexpflag)
1362                 break;
1363               if (from != stop && c == BUF_FETCH_CHAR (buf, from - 1))
1364                 from--;
1365               if (mathexit)
1366                 {
1367                   mathexit = 0;
1368                   goto open2;
1369                 }
1370               mathexit = 1;
1371
1372             case Sclose:
1373               if (!++depth) goto done2;
1374               break;
1375
1376             case Sopen:
1377             open2:
1378             if (!--depth) goto done2;
1379             if (depth < min_depth)
1380               {
1381                 if (noerror)
1382                   return Qnil;
1383                 error ("Containing expression ends prematurely");
1384               }
1385             break;
1386
1387             case Scomment_fence:
1388               comstyle = ST_COMMENT_STYLE;
1389             case Sendcomment:
1390               if (parse_sexp_ignore_comments)
1391                 from = find_start_of_comment (buf, from, stop, comstyle);
1392               break;
1393
1394             case Sstring_fence:
1395             case Sstring:
1396               {
1397                 Emchar stringterm;
1398
1399                 if (code != Sstring_fence)
1400                   {
1401                 /* XEmacs change: call syntax_match() on character */
1402                 Emchar ch = BUF_FETCH_CHAR (buf, from);
1403                     Lisp_Object stermobj =
1404                       syntax_match (syntax_cache.current_syntax_table, ch);
1405
1406                 if (CHARP (stermobj))
1407                   stringterm = XCHAR (stermobj);
1408                 else
1409                   stringterm = ch;
1410                   }
1411                 else
1412                   stringterm = '\0'; /* avoid compiler warnings */
1413
1414                 while (1)
1415                   {
1416                     if (from == stop) goto lose;
1417
1418                     UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
1419                     c = BUF_FETCH_CHAR (buf, from - 1);
1420
1421                     if ((code == Sstring
1422                         ? c == stringterm
1423                          : SYNTAX_FROM_CACHE (mirrortab, c) == Sstring_fence)
1424                         && !char_quoted (buf, from - 1))
1425                       {
1426                       break;
1427                       }
1428
1429                     from--;
1430                   }
1431                 from--;
1432                 if (!depth && sexpflag) goto done2;
1433                 break;
1434               }
1435             }
1436         }
1437
1438       /* Reached start of buffer.  Error if within object,
1439          return nil if between */
1440       if (depth) goto lose;
1441
1442       return Qnil;
1443
1444     done2:
1445       count++;
1446     }
1447
1448
1449   return (make_int (from));
1450
1451 lose:
1452   if (!noerror)
1453     error ("Unbalanced parentheses");
1454   return Qnil;
1455 }
1456
1457 int
1458 char_quoted (struct buffer *buf, Bufpos pos)
1459 {
1460   enum syntaxcode code;
1461   Bufpos beg = BUF_BEGV (buf);
1462   int quoted = 0;
1463   Bufpos startpos = pos;
1464
1465   while (pos > beg)
1466     {
1467       UPDATE_SYNTAX_CACHE_BACKWARD (pos - 1);
1468       code = SYNTAX_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, pos - 1));
1469
1470       if (code != Scharquote && code != Sescape)
1471         break;
1472       pos--;
1473       quoted = !quoted;
1474     }
1475
1476   UPDATE_SYNTAX_CACHE (startpos);
1477   return quoted;
1478 }
1479
1480 DEFUN ("scan-lists", Fscan_lists, 3, 5, 0, /*
1481 Scan from character number FROM by COUNT lists.
1482 Returns the character number of the position thus found.
1483
1484 If DEPTH is nonzero, paren depth begins counting from that value,
1485 only places where the depth in parentheses becomes zero
1486 are candidates for stopping; COUNT such places are counted.
1487 Thus, a positive value for DEPTH means go out levels.
1488
1489 Comments are ignored if `parse-sexp-ignore-comments' is non-nil.
1490
1491 If the beginning or end of (the accessible part of) the buffer is reached
1492 and the depth is wrong, an error is signaled.
1493 If the depth is right but the count is not used up, nil is returned.
1494
1495 If optional arg BUFFER is non-nil, scanning occurs in that buffer instead
1496 of in the current buffer.
1497
1498 If optional arg NOERROR is non-nil, scan-lists will return nil instead of
1499 signalling an error.
1500 */
1501        (from, count, depth, buffer, noerror))
1502 {
1503   struct buffer *buf;
1504
1505   CHECK_INT (from);
1506   CHECK_INT (count);
1507   CHECK_INT (depth);
1508   buf = decode_buffer (buffer, 0);
1509
1510   return scan_lists (buf, XINT (from), XINT (count), XINT (depth), 0,
1511                      !NILP (noerror));
1512 }
1513
1514 DEFUN ("scan-sexps", Fscan_sexps, 2, 4, 0, /*
1515 Scan from character number FROM by COUNT balanced expressions.
1516 If COUNT is negative, scan backwards.
1517 Returns the character number of the position thus found.
1518
1519 Comments are ignored if `parse-sexp-ignore-comments' is non-nil.
1520
1521 If the beginning or end of (the accessible part of) the buffer is reached
1522 in the middle of a parenthetical grouping, an error is signaled.
1523 If the beginning or end is reached between groupings
1524 but before count is used up, nil is returned.
1525
1526 If optional arg BUFFER is non-nil, scanning occurs in that buffer instead
1527 of in the current buffer.
1528
1529 If optional arg NOERROR is non-nil, scan-sexps will return nil instead of
1530 signalling an error.
1531 */
1532        (from, count, buffer, noerror))
1533 {
1534   struct buffer *buf = decode_buffer (buffer, 0);
1535   CHECK_INT (from);
1536   CHECK_INT (count);
1537
1538   return scan_lists (buf, XINT (from), XINT (count), 0, 1, !NILP (noerror));
1539 }
1540
1541 DEFUN ("backward-prefix-chars", Fbackward_prefix_chars, 0, 1, 0, /*
1542 Move point backward over any number of chars with prefix syntax.
1543 This includes chars with "quote" or "prefix" syntax (' or p).
1544
1545 Optional arg BUFFER defaults to the current buffer.
1546 */
1547        (buffer))
1548 {
1549   struct buffer *buf = decode_buffer (buffer, 0);
1550   Bufpos beg = BUF_BEGV (buf);
1551   Bufpos pos = BUF_PT (buf);
1552 #ifndef emacs
1553   Lisp_Char_Table *mirrortab = XCHAR_TABLE (buf->mirror_syntax_table);
1554 #endif
1555   Emchar c = '\0'; /* initialize to avoid compiler warnings */
1556
1557
1558   SETUP_SYNTAX_CACHE_FOR_BUFFER (buf, pos, -1);
1559
1560   while (pos > beg && !char_quoted (buf, pos - 1)
1561          /* Previous statement updates syntax table.  */
1562          && (SYNTAX_FROM_CACHE (mirrortab, c = BUF_FETCH_CHAR (buf, pos - 1)) == Squote
1563              || SYNTAX_CODE_PREFIX (SYNTAX_CODE_FROM_CACHE (mirrortab, c))))
1564     pos--;
1565
1566   BUF_SET_PT (buf, pos);
1567
1568   return Qnil;
1569 }
1570 \f
1571 /* Parse forward from FROM to END,
1572    assuming that FROM has state OLDSTATE (nil means FROM is start of function),
1573    and return a description of the state of the parse at END.
1574    If STOPBEFORE is nonzero, stop at the start of an atom.
1575    If COMMENTSTOP is nonzero, stop at the start of a comment.  */
1576
1577 static void
1578 scan_sexps_forward (struct buffer *buf, struct lisp_parse_state *stateptr,
1579                     Bufpos from, Bufpos end,
1580                     int targetdepth, int stopbefore,
1581                     Lisp_Object oldstate,
1582                     int commentstop)
1583 {
1584   struct lisp_parse_state state;
1585
1586   enum syntaxcode code;
1587   struct level { int last, prev; };
1588   struct level levelstart[100];
1589   struct level *curlevel = levelstart;
1590   struct level *endlevel = levelstart + 100;
1591   int depth;    /* Paren depth of current scanning location.
1592                            level - levelstart equals this except
1593                            when the depth becomes negative.  */
1594   int mindepth;         /* Lowest DEPTH value seen.  */
1595   int start_quoted = 0;         /* Nonzero means starting after a char quote */
1596   int boundary_stop = commentstop == -1;
1597   Lisp_Object tem;
1598
1599   SETUP_SYNTAX_CACHE (from, 1);
1600   if (NILP (oldstate))
1601     {
1602       depth = 0;
1603       state.instring = -1;
1604       state.incomment = 0;
1605       state.comstyle = 0;       /* comment style a by default */
1606       state.comstr_start = -1;  /* no comment/string seen.  */
1607     }
1608   else
1609     {
1610       tem = Fcar (oldstate);    /* elt 0, depth */
1611       if (!NILP (tem))
1612         depth = XINT (tem);
1613       else
1614         depth = 0;
1615
1616       oldstate = Fcdr (oldstate);
1617       oldstate = Fcdr (oldstate);
1618       oldstate = Fcdr (oldstate);
1619       tem = Fcar (oldstate);    /* elt 3, instring */
1620       state.instring = ( !NILP (tem) 
1621                          ? ( INTP (tem) ? XINT (tem) : ST_STRING_STYLE) 
1622                          : -1);
1623
1624       oldstate = Fcdr (oldstate);
1625       tem = Fcar (oldstate);    /* elt 4, incomment */
1626       state.incomment = !NILP (tem);
1627
1628       oldstate = Fcdr (oldstate);
1629       tem = Fcar (oldstate);    /* elt 5, follows-quote */
1630       start_quoted = !NILP (tem);
1631
1632       /* if the eighth element of the list is nil, we are in comment style
1633          a; if it is t, we are in comment style b; if it is 'syntax-table,
1634          we are in a generic comment */
1635       oldstate = Fcdr (oldstate);
1636       oldstate = Fcdr (oldstate);
1637       tem = Fcar (oldstate);    /* elt 7, comment style a/b/fence */
1638       state.comstyle = NILP (tem) ? 0 : ( EQ (tem, Qsyntax_table)
1639                                           ? ST_COMMENT_STYLE : 1 );
1640
1641       oldstate = Fcdr (oldstate); /* elt 8, start of last comment/string */
1642       tem = Fcar (oldstate);
1643       state.comstr_start = NILP (tem) ? -1 : XINT (tem);
1644
1645       /* elt 9, char numbers of starts-of-expression of levels
1646          (starting from outermost). */
1647       oldstate = Fcdr (oldstate);
1648       tem = Fcar (oldstate);    /* elt 9, intermediate data for
1649                                    continuation of parsing (subject
1650                                    to change). */
1651       while (!NILP (tem))       /* >= second enclosing sexps.  */
1652         {
1653           curlevel->last = XINT (Fcar (tem));
1654           if (++curlevel == endlevel)
1655             error ("Nesting too deep for parser");
1656           curlevel->prev = -1;
1657           curlevel->last = -1;
1658           tem = Fcdr (tem);
1659         }
1660     }
1661   state.quoted = 0;
1662   mindepth = depth;
1663
1664   curlevel->prev = -1;
1665   curlevel->last = -1;
1666
1667   /* Enter the loop at a place appropriate for initial state. */
1668
1669   if (state.incomment) goto startincomment;
1670   if (state.instring >= 0)
1671     {
1672       if (start_quoted) goto startquotedinstring;
1673       goto startinstring;
1674     }
1675   if (start_quoted) goto startquoted;
1676
1677   while (from < end)
1678     {
1679       Emchar c;
1680       int syncode;
1681
1682       QUIT;
1683
1684       UPDATE_SYNTAX_CACHE_FORWARD (from);
1685       c = BUF_FETCH_CHAR (buf, from);
1686       code = SYNTAX_FROM_CACHE (mirrortab, c);
1687       syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
1688       from++;
1689
1690           /* record the comment style we have entered so that only the
1691              comment-ender sequence (or single char) of the same style
1692              actually terminates the comment section. */
1693       if (code == Scomment)
1694         {
1695           state.comstyle =
1696             SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode)
1697             == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1698           state.comstr_start = from - 1;
1699         }
1700
1701       /* a generic comment delimiter? */
1702       else if (code == Scomment_fence)
1703         {
1704           state.comstyle = ST_COMMENT_STYLE;
1705           state.comstr_start = from - 1;
1706           code = Scomment;
1707         }
1708
1709       else if (from < end &&
1710                SYNTAX_CODE_START_FIRST_P (syncode))
1711         {
1712           int next_syncode;
1713           UPDATE_SYNTAX_CACHE_FORWARD (from);
1714           next_syncode =
1715             SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from));
1716
1717           if (SYNTAX_CODES_START_P (syncode, next_syncode))
1718         {
1719           code = Scomment;
1720               state.comstyle = SYNTAX_CODES_COMMENT_MASK_START
1721                 (syncode, next_syncode) == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1722               state.comstr_start = from - 1;
1723           from++;
1724               UPDATE_SYNTAX_CACHE_FORWARD (from);
1725             }
1726         }
1727
1728       if (SYNTAX_CODE_PREFIX (syncode))
1729         continue;
1730       switch (code)
1731         {
1732         case Sescape:
1733         case Scharquote:
1734           if (stopbefore) goto stop;  /* this arg means stop at sexp start */
1735           curlevel->last = from - 1;
1736         startquoted:
1737           if (from == end) goto endquoted;
1738           from++;
1739           goto symstarted;
1740           /* treat following character as a word constituent */
1741         case Sword:
1742         case Ssymbol:
1743           if (stopbefore) goto stop;  /* this arg means stop at sexp start */
1744           curlevel->last = from - 1;
1745         symstarted:
1746           while (from < end)
1747             {
1748               UPDATE_SYNTAX_CACHE_FORWARD (from);
1749               switch (SYNTAX_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from)))
1750                 {
1751                 case Scharquote:
1752                 case Sescape:
1753                   from++;
1754                   if (from == end) goto endquoted;
1755                   break;
1756                 case Sword:
1757                 case Ssymbol:
1758                 case Squote:
1759                   break;
1760                 default:
1761                   goto symdone;
1762                 }
1763               from++;
1764             }
1765         symdone:
1766           curlevel->prev = curlevel->last;
1767           break;
1768
1769         case Scomment:
1770           state.incomment = 1;
1771           if (commentstop || boundary_stop) goto done;
1772         startincomment:
1773           if (commentstop == 1)
1774             goto done;
1775           UPDATE_SYNTAX_CACHE_FORWARD (from);
1776           {
1777             Bufpos newfrom = find_end_of_comment (buf, from, end, state.comstyle);
1778             if (newfrom < 0)
1779               {
1780                 /* we terminated search because from == end */
1781                 from = end;
1782                 goto done;
1783               }
1784             from = newfrom;
1785           }
1786           state.incomment = 0;
1787           state.comstyle = 0;                /* reset the comment style */
1788           if (boundary_stop) goto done;
1789           break;
1790
1791         case Sopen:
1792           if (stopbefore) goto stop;  /* this arg means stop at sexp start */
1793           depth++;
1794           /* curlevel++->last ran into compiler bug on Apollo */
1795           curlevel->last = from - 1;
1796           if (++curlevel == endlevel)
1797             error ("Nesting too deep for parser");
1798           curlevel->prev = -1;
1799           curlevel->last = -1;
1800           if (targetdepth == depth) goto done;
1801           break;
1802
1803         case Sclose:
1804           depth--;
1805           if (depth < mindepth)
1806             mindepth = depth;
1807           if (curlevel != levelstart)
1808             curlevel--;
1809           curlevel->prev = curlevel->last;
1810           if (targetdepth == depth) goto done;
1811           break;
1812
1813         case Sstring:
1814         case Sstring_fence:
1815           state.comstr_start = from - 1;
1816             if (stopbefore) goto stop; /* this arg means stop at sexp start */
1817             curlevel->last = from - 1;
1818           if (code == Sstring_fence)
1819             {
1820               state.instring = ST_STRING_STYLE;
1821             }
1822           else
1823             {
1824               /* XEmacs change: call syntax_match() on character */
1825               Emchar ch = BUF_FETCH_CHAR (buf, from - 1);
1826               Lisp_Object stermobj =
1827                 syntax_match (syntax_cache.current_syntax_table, ch);
1828
1829               if (CHARP (stermobj))
1830                 state.instring = XCHAR (stermobj);
1831               else
1832                 state.instring = ch;
1833           }
1834           if (boundary_stop) goto done;
1835         startinstring:
1836           while (1)
1837             {
1838               enum syntaxcode temp_code;
1839
1840               if (from >= end) goto done;
1841
1842               UPDATE_SYNTAX_CACHE_FORWARD (from);
1843               c = BUF_FETCH_CHAR (buf, from);
1844               temp_code = SYNTAX_FROM_CACHE (mirrortab, c);
1845
1846               if (
1847                   state.instring != ST_STRING_STYLE &&
1848                   temp_code == Sstring &&
1849                   c == state.instring) break;
1850
1851               switch (temp_code)
1852                 {
1853                 case Sstring_fence:
1854                   if (state.instring == ST_STRING_STYLE)
1855                     goto string_end;
1856                   break;
1857                 case Scharquote:
1858                 case Sescape:
1859                   {
1860                     from++;
1861                   startquotedinstring:
1862                     if (from >= end) goto endquoted;
1863                     break;
1864                   }
1865                 default:
1866                   break;
1867                 }
1868               from++;
1869             }
1870         string_end:
1871           state.instring = -1;
1872           curlevel->prev = curlevel->last;
1873           from++;
1874           if (boundary_stop) goto done;
1875           break;
1876
1877         case Smath:
1878           break;
1879
1880         case Swhitespace:
1881         case Spunct:
1882         case Squote:
1883         case Sendcomment:
1884         case Scomment_fence:
1885         case Sinherit:
1886         case Smax:
1887           break;
1888         }
1889     }
1890   goto done;
1891
1892  stop:   /* Here if stopping before start of sexp. */
1893   from--;    /* We have just fetched the char that starts it; */
1894   goto done; /* but return the position before it. */
1895
1896  endquoted:
1897   state.quoted = 1;
1898  done:
1899   state.depth = depth;
1900   state.mindepth = mindepth;
1901   state.thislevelstart = curlevel->prev;
1902   state.prevlevelstart
1903     = (curlevel == levelstart) ? -1 : (curlevel - 1)->last;
1904   state.location = from;
1905   state.levelstarts = Qnil;
1906   while (--curlevel >= levelstart)
1907     state.levelstarts = Fcons (make_int (curlevel->last),
1908                                state.levelstarts);
1909
1910   *stateptr = state;
1911 }
1912
1913 DEFUN ("parse-partial-sexp", Fparse_partial_sexp, 2, 7, 0, /*
1914 Parse Lisp syntax starting at FROM until TO; return status of parse at TO.
1915 Parsing stops at TO or when certain criteria are met;
1916  point is set to where parsing stops.
1917 If fifth arg OLDSTATE is omitted or nil,
1918  parsing assumes that FROM is the beginning of a function.
1919 Value is a list of nine elements describing final state of parsing:
1920  0. depth in parens.
1921  1. character address of start of innermost containing list; nil if none.
1922  2. character address of start of last complete sexp terminated.
1923  3. non-nil if inside a string.
1924     (It is the character that will terminate the string,
1925      or t if the string should be terminated by an explicit
1926      `syntax-table' property.)
1927  4. t if inside a comment.
1928  5. t if following a quote character.
1929  6. the minimum paren-depth encountered during this scan.
1930  7. nil if in comment style a, or not in a comment; t if in comment style b;
1931     `syntax-table' if given by an explicit `syntax-table' property.
1932  8. character address of start of last comment or string; nil if none.
1933  9. Intermediate data for continuation of parsing (subject to change).
1934 If third arg TARGETDEPTH is non-nil, parsing stops if the depth
1935 in parentheses becomes equal to TARGETDEPTH.
1936 Fourth arg STOPBEFORE non-nil means stop when come to
1937  any character that starts a sexp.
1938 Fifth arg OLDSTATE is a nine-element list like what this function returns.
1939 It is used to initialize the state of the parse.  Its second and third
1940 elements are ignored.
1941 Sixth arg COMMENTSTOP non-nil means stop at the start of a comment. If it
1942 is `syntax-table', stop after the start of a comment or a string, or after
1943 the end of a comment or string.
1944 */
1945        (from, to, targetdepth, stopbefore, oldstate, commentstop, buffer))
1946 {
1947   struct lisp_parse_state state;
1948   int target;
1949   Bufpos start, end;
1950   struct buffer *buf = decode_buffer (buffer, 0);
1951   Lisp_Object val;
1952
1953   if (!NILP (targetdepth))
1954     {
1955       CHECK_INT (targetdepth);
1956       target = XINT (targetdepth);
1957     }
1958   else
1959     target = -100000;           /* We won't reach this depth */
1960
1961   get_buffer_range_char (buf, from, to, &start, &end, 0);
1962   scan_sexps_forward (buf, &state, start, end,
1963                       target, !NILP (stopbefore), oldstate,
1964                       (NILP (commentstop)
1965                        ? 0 : (EQ (commentstop, Qsyntax_table) ? -1 : 1)));
1966   BUF_SET_PT (buf, state.location);
1967
1968   /* reverse order */
1969   val = Qnil;
1970   val = Fcons (state.levelstarts, val);
1971   val = Fcons ((state.incomment || (state.instring >= 0))
1972                ? make_int (state.comstr_start) : Qnil, val);
1973   val = Fcons (state.comstyle  ? (state.comstyle == ST_COMMENT_STYLE
1974                                   ? Qsyntax_table : Qt) : Qnil, val);
1975   val = Fcons (make_int (state.mindepth),   val);
1976   val = Fcons (state.quoted    ? Qt : Qnil, val);
1977   val = Fcons (state.incomment ? Qt : Qnil, val);
1978   val = Fcons (state.instring < 0
1979                ? Qnil
1980                : (state.instring == ST_STRING_STYLE
1981                   ? Qt : make_int (state.instring)), val);
1982   val = Fcons (state.thislevelstart < 0 ? Qnil : make_int (state.thislevelstart), val);
1983   val = Fcons (state.prevlevelstart < 0 ? Qnil : make_int (state.prevlevelstart), val);
1984   val = Fcons (make_int (state.depth), val);
1985
1986   return val;
1987 }
1988
1989
1990 /* Updating of the mirror syntax table.
1991
1992    Each syntax table has a corresponding mirror table in it.
1993    Whenever we make a change to a syntax table, we call
1994    update_syntax_table() on it.
1995
1996    #### We really only need to map over the changed range.
1997
1998    If we change the standard syntax table, we need to map over
1999    all tables because any of them could be inheriting from the
2000    standard syntax table.
2001
2002    When `set-syntax-table' is called, we set the buffer's mirror
2003    syntax table as well.
2004    */
2005
2006 struct cmst_arg
2007 {
2008   Lisp_Object mirrortab;
2009   int check_inherit;
2010 };
2011
2012 static int
2013 cmst_mapfun (struct chartab_range *range, Lisp_Object val, void *arg)
2014 {
2015   struct cmst_arg *closure = (struct cmst_arg *) arg;
2016
2017   if (CONSP (val))
2018     val = XCAR (val);
2019   if (SYNTAX_FROM_CODE (XINT (val)) == Sinherit
2020       && closure->check_inherit)
2021     {
2022       struct cmst_arg recursive;
2023
2024       recursive.mirrortab = closure->mirrortab;
2025       recursive.check_inherit = 0;
2026       map_char_table (XCHAR_TABLE (Vstandard_syntax_table), range,
2027                                    cmst_mapfun, &recursive);
2028     }
2029   else
2030     put_char_table (XCHAR_TABLE (closure->mirrortab), range, val);
2031   return 0;
2032 }
2033
2034 static void
2035 update_just_this_syntax_table (Lisp_Char_Table *ct)
2036 {
2037   struct chartab_range range;
2038   struct cmst_arg arg;
2039
2040   arg.mirrortab = ct->mirror_table;
2041   arg.check_inherit = (CHAR_TABLEP (Vstandard_syntax_table)
2042                        && ct != XCHAR_TABLE (Vstandard_syntax_table));
2043   range.type = CHARTAB_RANGE_ALL;
2044   map_char_table (ct, &range, cmst_mapfun, &arg);
2045 }
2046
2047 /* Called from chartab.c when a change is made to a syntax table.
2048    If this is the standard syntax table, we need to recompute
2049    *all* syntax tables (yuck).  Otherwise we just recompute this
2050    one. */
2051
2052 void
2053 update_syntax_table (Lisp_Char_Table *ct)
2054 {
2055   /* Don't be stymied at startup. */
2056   if (CHAR_TABLEP (Vstandard_syntax_table)
2057       && ct == XCHAR_TABLE (Vstandard_syntax_table))
2058     {
2059       Lisp_Object syntab;
2060
2061       for (syntab = Vall_syntax_tables; !NILP (syntab);
2062            syntab = XCHAR_TABLE (syntab)->next_table)
2063         update_just_this_syntax_table (XCHAR_TABLE (syntab));
2064     }
2065   else
2066     update_just_this_syntax_table (ct);
2067 }
2068
2069 \f
2070 /************************************************************************/
2071 /*                            initialization                            */
2072 /************************************************************************/
2073
2074 void
2075 syms_of_syntax (void)
2076 {
2077   defsymbol (&Qsyntax_table_p, "syntax-table-p");
2078   defsymbol (&Qsyntax_table, "syntax-table");
2079
2080   DEFSUBR (Fsyntax_table_p);
2081   DEFSUBR (Fsyntax_table);
2082   DEFSUBR (Fstandard_syntax_table);
2083   DEFSUBR (Fcopy_syntax_table);
2084   DEFSUBR (Fset_syntax_table);
2085   DEFSUBR (Fsyntax_designator_chars);
2086   DEFSUBR (Fchar_syntax);
2087   DEFSUBR (Fmatching_paren);
2088   /* DEFSUBR (Fmodify_syntax_entry); now in Lisp. */
2089   /* DEFSUBR (Fdescribe_syntax); now in Lisp. */
2090
2091   DEFSUBR (Fforward_word);
2092
2093   DEFSUBR (Fforward_comment);
2094   DEFSUBR (Fscan_lists);
2095   DEFSUBR (Fscan_sexps);
2096   DEFSUBR (Fbackward_prefix_chars);
2097   DEFSUBR (Fparse_partial_sexp);
2098 }
2099
2100 void
2101 vars_of_syntax (void)
2102 {
2103   DEFVAR_BOOL ("parse-sexp-ignore-comments", &parse_sexp_ignore_comments /*
2104 Non-nil means `forward-sexp', etc., should treat comments as whitespace.
2105 */ );
2106   parse_sexp_ignore_comments = 0;
2107
2108   DEFVAR_BOOL ("lookup-syntax-properties", &lookup_syntax_properties /*
2109 Non-nil means `forward-sexp', etc., grant `syntax-table' property.
2110 The value of this property should be either a syntax table, or a cons
2111 of the form (SYNTAXCODE . MATCHCHAR), SYNTAXCODE being the numeric
2112 syntax code, MATCHCHAR being nil or the character to match (which is
2113 relevant only for open/close type.
2114 */ );
2115   lookup_syntax_properties = 1;
2116
2117   DEFVAR_BOOL ("words-include-escapes", &words_include_escapes /*
2118 Non-nil means `forward-word', etc., should treat escape chars part of words.
2119 */ );
2120   words_include_escapes = 0;
2121
2122   no_quit_in_re_search = 0;
2123 }
2124
2125 static void
2126 define_standard_syntax (const char *p, enum syntaxcode syn)
2127 {
2128   for (; *p; p++)
2129     Fput_char_table (make_char (*p), make_int (syn), Vstandard_syntax_table);
2130 }
2131
2132 void
2133 complex_vars_of_syntax (void)
2134 {
2135   Emchar i;
2136   const char *p;
2137   /* Set this now, so first buffer creation can refer to it. */
2138   /* Make it nil before calling copy-syntax-table
2139      so that copy-syntax-table will know not to try to copy from garbage */
2140   Vstandard_syntax_table = Qnil;
2141   Vstandard_syntax_table = Fcopy_syntax_table (Qnil);
2142   staticpro (&Vstandard_syntax_table);
2143
2144   Vsyntax_designator_chars_string = make_string_nocopy (syntax_code_spec,
2145                                                         Smax);
2146   staticpro (&Vsyntax_designator_chars_string);
2147
2148   fill_char_table (XCHAR_TABLE (Vstandard_syntax_table), make_int (Spunct));
2149
2150   for (i = 0; i <= 32; i++)     /* Control 0 plus SPACE */
2151     Fput_char_table (make_char (i), make_int (Swhitespace),
2152                      Vstandard_syntax_table);
2153   for (i = 127; i <= 159; i++)  /* DEL plus Control 1 */
2154     Fput_char_table (make_char (i), make_int (Swhitespace),
2155                      Vstandard_syntax_table);
2156
2157   define_standard_syntax ("abcdefghijklmnopqrstuvwxyz"
2158                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2159                           "0123456789"
2160                           "$%", Sword);
2161   define_standard_syntax ("\"", Sstring);
2162   define_standard_syntax ("\\", Sescape);
2163   define_standard_syntax ("_-+*/&|<>=", Ssymbol);
2164   define_standard_syntax (".,;:?!#@~^'`", Spunct);
2165
2166   for (p = "()[]{}"; *p; p+=2)
2167     {
2168       Fput_char_table (make_char (p[0]),
2169                        Fcons (make_int (Sopen), make_char (p[1])),
2170                        Vstandard_syntax_table);
2171       Fput_char_table (make_char (p[1]),
2172                        Fcons (make_int (Sclose), make_char (p[0])),
2173                        Vstandard_syntax_table);
2174     }
2175 }