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