Sync to HEAD
[bpt/emacs.git] / src / syntax.c
CommitLineData
8489eb67 1/* GNU Emacs routines to deal with syntax tables; also word and list parsing.
839966f3 2 Copyright (C) 1985, 87, 93, 94, 95, 97, 1998, 1999, 2004 Free Software Foundation, Inc.
8489eb67
RS
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
a4275ad1 8the Free Software Foundation; either version 2, or (at your option)
8489eb67
RS
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
3b7ad313
EN
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
8489eb67
RS
20
21
18160b98 22#include <config.h>
8489eb67
RS
23#include <ctype.h>
24#include "lisp.h"
25#include "commands.h"
26#include "buffer.h"
5c7b02ab 27#include "character.h"
e35f6ff7 28#include "keymap.h"
195d1361
RS
29
30/* Make syntax table lookup grant data in gl_state. */
31#define SYNTAX_ENTRY_VIA_PROPERTY
32
8489eb67 33#include "syntax.h"
195d1361
RS
34#include "intervals.h"
35
36/* We use these constants in place for comment-style and
37 string-ender-char to distinguish comments/strings started by
38 comment_fence and string_fence codes. */
39
40#define ST_COMMENT_STYLE (256 + 1)
41#define ST_STRING_STYLE (256 + 2)
93da5fff 42#include "category.h"
8489eb67 43
7bf5e9e4 44Lisp_Object Qsyntax_table_p, Qsyntax_table, Qscan_error;
8489eb67
RS
45
46int words_include_escapes;
195d1361 47int parse_sexp_lookup_properties;
8489eb67 48
bd25db08
KH
49/* Nonzero means `scan-sexps' treat all multibyte characters as symbol. */
50int multibyte_syntax_as_symbol;
51
8ea151b2
RS
52/* Used as a temporary in SYNTAX_ENTRY and other macros in syntax.h,
53 if not compiled with GCC. No need to mark it, since it is used
54 only very temporarily. */
55Lisp_Object syntax_temp;
56
f4ed767f
GM
57/* Non-zero means an open parenthesis in column 0 is always considered
58 to be the start of a defun. Zero means an open parenthesis in
59 column 0 has no special meaning. */
60
61int open_paren_in_column_0_is_defun_start;
62
e5d4f4dc
RS
63/* This is the internal form of the parse state used in parse-partial-sexp. */
64
65struct lisp_parse_state
66 {
195d1361
RS
67 int depth; /* Depth at end of parsing. */
68 int instring; /* -1 if not within string, else desired terminator. */
95ff8dfc 69 int incomment; /* -1 if in unnestable comment else comment nesting */
195d1361 70 int comstyle; /* comment style a=0, or b=1, or ST_COMMENT_STYLE. */
e5d4f4dc
RS
71 int quoted; /* Nonzero if just after an escape char at end of parsing */
72 int thislevelstart; /* Char number of most recent start-of-expression at current level */
73 int prevlevelstart; /* Char number of start of containing expression */
195d1361 74 int location; /* Char number at which parsing stopped. */
e5d4f4dc 75 int mindepth; /* Minimum depth seen while scanning. */
195d1361 76 int comstr_start; /* Position just after last comment/string starter. */
1a102a58
RS
77 Lisp_Object levelstarts; /* Char numbers of starts-of-expression
78 of levels (starting from outermost). */
e5d4f4dc
RS
79 };
80\f
37bef230
RS
81/* These variables are a cache for finding the start of a defun.
82 find_start_pos is the place for which the defun start was found.
83 find_start_value is the defun start position found for it.
6a140a74 84 find_start_value_byte is the corresponding byte position.
37bef230
RS
85 find_start_buffer is the buffer it was found in.
86 find_start_begv is the BEGV value when it was found.
87 find_start_modiff is the value of MODIFF when it was found. */
88
89static int find_start_pos;
90static int find_start_value;
6a140a74 91static int find_start_value_byte;
37bef230
RS
92static struct buffer *find_start_buffer;
93static int find_start_begv;
94static int find_start_modiff;
6a140a74
RS
95
96
97static int find_defun_start P_ ((int, int));
95ff8dfc 98static int back_comment P_ ((int, int, int, int, int, int *, int *));
6a140a74 99static int char_quoted P_ ((int, int));
b7dbcc19
KH
100static Lisp_Object skip_chars P_ ((int, Lisp_Object, Lisp_Object));
101static Lisp_Object skip_syntaxes P_ ((int, Lisp_Object, Lisp_Object));
6a140a74
RS
102static Lisp_Object scan_lists P_ ((int, int, int, int));
103static void scan_sexps_forward P_ ((struct lisp_parse_state *,
104 int, int, int, int,
105 int, Lisp_Object, int));
195d1361
RS
106\f
107
108struct gl_state_s gl_state; /* Global state of syntax parser. */
109
110INTERVAL interval_of ();
111#define INTERVALS_AT_ONCE 10 /* 1 + max-number of intervals
112 to scan to property-change. */
113
6a140a74
RS
114/* Update gl_state to an appropriate interval which contains CHARPOS. The
115 sign of COUNT give the relative position of CHARPOS wrt the previously
195d1361 116 valid interval. If INIT, only [be]_property fields of gl_state are
6a140a74 117 valid at start, the rest is filled basing on OBJECT.
195d1361 118
6a140a74 119 `gl_state.*_i' are the intervals, and CHARPOS is further in the search
195d1361
RS
120 direction than the intervals - or in an interval. We update the
121 current syntax-table basing on the property of this interval, and
6a140a74 122 update the interval to start further than CHARPOS - or be
195d1361 123 NULL_INTERVAL. We also update lim_property to be the next value of
6a140a74 124 charpos to call this subroutine again - or be before/after the
195d1361
RS
125 start/end of OBJECT. */
126
127void
6a140a74
RS
128update_syntax_table (charpos, count, init, object)
129 int charpos, count, init;
195d1361
RS
130 Lisp_Object object;
131{
132 Lisp_Object tmp_table;
4ffe723b 133 int cnt = 0, invalidate = 1;
195d1361
RS
134 INTERVAL i, oldi;
135
136 if (init)
137 {
bb0de084 138 gl_state.old_prop = Qnil;
195d1361
RS
139 gl_state.start = gl_state.b_property;
140 gl_state.stop = gl_state.e_property;
bb0de084
SM
141 i = interval_of (charpos, object);
142 gl_state.backward_i = gl_state.forward_i = i;
195d1361
RS
143 invalidate = 0;
144 if (NULL_INTERVAL_P (i))
145 return;
f902a008 146 /* interval_of updates only ->position of the return value, so
d80f4cc7 147 update the parents manually to speed up update_interval. */
bb0de084 148 while (!NULL_PARENT (i))
d80f4cc7
RS
149 {
150 if (AM_RIGHT_CHILD (i))
439d5cb4 151 INTERVAL_PARENT (i)->position = i->position
d80f4cc7 152 - LEFT_TOTAL_LENGTH (i) + TOTAL_LENGTH (i) /* right end */
439d5cb4
KR
153 - TOTAL_LENGTH (INTERVAL_PARENT (i))
154 + LEFT_TOTAL_LENGTH (INTERVAL_PARENT (i));
d80f4cc7 155 else
439d5cb4 156 INTERVAL_PARENT (i)->position = i->position - LEFT_TOTAL_LENGTH (i)
d80f4cc7 157 + TOTAL_LENGTH (i);
439d5cb4 158 i = INTERVAL_PARENT (i);
d80f4cc7
RS
159 }
160 i = gl_state.forward_i;
bb0de084 161 gl_state.b_property = i->position - gl_state.offset;
ee0cdb48 162 gl_state.e_property = INTERVAL_LAST_POS (i) - gl_state.offset;
195d1361
RS
163 goto update;
164 }
165 oldi = i = count > 0 ? gl_state.forward_i : gl_state.backward_i;
166
423e705d 167 /* We are guaranteed to be called with CHARPOS either in i,
6a140a74 168 or further off. */
195d1361
RS
169 if (NULL_INTERVAL_P (i))
170 error ("Error in syntax_table logic for to-the-end intervals");
6a140a74 171 else if (charpos < i->position) /* Move left. */
195d1361
RS
172 {
173 if (count > 0)
f902a008 174 error ("Error in syntax_table logic for intervals <-");
195d1361 175 /* Update the interval. */
6a140a74 176 i = update_interval (i, charpos);
bb0de084 177 if (INTERVAL_LAST_POS (i) != gl_state.b_property)
195d1361
RS
178 {
179 invalidate = 0;
195d1361 180 gl_state.forward_i = i;
ee0cdb48 181 gl_state.e_property = INTERVAL_LAST_POS (i) - gl_state.offset;
195d1361 182 }
423e705d 183 }
6a140a74 184 else if (charpos >= INTERVAL_LAST_POS (i)) /* Move right. */
195d1361
RS
185 {
186 if (count < 0)
f902a008 187 error ("Error in syntax_table logic for intervals ->");
195d1361 188 /* Update the interval. */
6a140a74 189 i = update_interval (i, charpos);
bb0de084 190 if (i->position != gl_state.e_property)
195d1361
RS
191 {
192 invalidate = 0;
195d1361 193 gl_state.backward_i = i;
bb0de084 194 gl_state.b_property = i->position - gl_state.offset;
195d1361
RS
195 }
196 }
195d1361
RS
197
198 update:
199 tmp_table = textget (i->plist, Qsyntax_table);
200
201 if (invalidate)
202 invalidate = !EQ (tmp_table, gl_state.old_prop); /* Need to invalidate? */
7d0393cf 203
423e705d
SM
204 if (invalidate) /* Did not get to adjacent interval. */
205 { /* with the same table => */
206 /* invalidate the old range. */
195d1361
RS
207 if (count > 0)
208 {
209 gl_state.backward_i = i;
bb0de084
SM
210 gl_state.b_property = i->position - gl_state.offset;
211 }
212 else
195d1361 213 {
bb0de084 214 gl_state.forward_i = i;
ee0cdb48 215 gl_state.e_property = INTERVAL_LAST_POS (i) - gl_state.offset;
195d1361
RS
216 }
217 }
37bef230 218
bb0de084 219 if (!EQ (tmp_table, gl_state.old_prop))
195d1361 220 {
bb0de084
SM
221 gl_state.current_syntax_table = tmp_table;
222 gl_state.old_prop = tmp_table;
223 if (EQ (Fsyntax_table_p (tmp_table), Qt))
224 {
225 gl_state.use_global = 0;
7d0393cf 226 }
bb0de084
SM
227 else if (CONSP (tmp_table))
228 {
229 gl_state.use_global = 1;
230 gl_state.global_code = tmp_table;
231 }
7d0393cf 232 else
bb0de084
SM
233 {
234 gl_state.use_global = 0;
235 gl_state.current_syntax_table = current_buffer->syntax_table;
236 }
195d1361
RS
237 }
238
239 while (!NULL_INTERVAL_P (i))
240 {
241 if (cnt && !EQ (tmp_table, textget (i->plist, Qsyntax_table)))
242 {
243 if (count > 0)
bb0de084
SM
244 {
245 gl_state.e_property = i->position - gl_state.offset;
246 gl_state.forward_i = i;
247 }
248 else
249 {
250 gl_state.b_property = i->position + LENGTH (i) - gl_state.offset;
251 gl_state.backward_i = i;
252 }
253 return;
195d1361 254 }
7d0393cf 255 else if (cnt == INTERVALS_AT_ONCE)
195d1361
RS
256 {
257 if (count > 0)
bb0de084
SM
258 {
259 gl_state.e_property = i->position + LENGTH (i) - gl_state.offset;
260 gl_state.forward_i = i;
261 }
262 else
263 {
264 gl_state.b_property = i->position - gl_state.offset;
265 gl_state.backward_i = i;
266 }
267 return;
195d1361
RS
268 }
269 cnt++;
270 i = count > 0 ? next_interval (i) : previous_interval (i);
271 }
bb0de084
SM
272 eassert (NULL_INTERVAL_P (i)); /* This property goes to the end. */
273 if (count > 0)
274 gl_state.e_property = gl_state.stop;
275 else
276 gl_state.b_property = gl_state.start;
195d1361
RS
277}
278\f
6a140a74
RS
279/* Returns TRUE if char at CHARPOS is quoted.
280 Global syntax-table data should be set up already to be good at CHARPOS
281 or after. On return global syntax data is good for lookup at CHARPOS. */
195d1361
RS
282
283static int
6a140a74
RS
284char_quoted (charpos, bytepos)
285 register int charpos, bytepos;
195d1361
RS
286{
287 register enum syntaxcode code;
288 register int beg = BEGV;
289 register int quoted = 0;
6a140a74
RS
290 int orig = charpos;
291
292 DEC_BOTH (charpos, bytepos);
195d1361 293
6a140a74 294 while (bytepos >= beg)
195d1361 295 {
6a140a74 296 UPDATE_SYNTAX_TABLE_BACKWARD (charpos);
b7dbcc19 297 code = SYNTAX (FETCH_CHAR_AS_MULTIBYTE (bytepos));
6a140a74
RS
298 if (! (code == Scharquote || code == Sescape))
299 break;
300
301 DEC_BOTH (charpos, bytepos);
302 quoted = !quoted;
195d1361 303 }
6a140a74
RS
304
305 UPDATE_SYNTAX_TABLE (orig);
195d1361
RS
306 return quoted;
307}
6a140a74
RS
308
309/* Return the bytepos one character after BYTEPOS.
310 We assume that BYTEPOS is not at the end of the buffer. */
311
312INLINE int
313inc_bytepos (bytepos)
314 int bytepos;
315{
ef316cf0
RS
316 if (NILP (current_buffer->enable_multibyte_characters))
317 return bytepos + 1;
318
6a140a74
RS
319 INC_POS (bytepos);
320 return bytepos;
321}
322
323/* Return the bytepos one character before BYTEPOS.
324 We assume that BYTEPOS is not at the start of the buffer. */
325
326INLINE int
327dec_bytepos (bytepos)
328 int bytepos;
329{
ef316cf0
RS
330 if (NILP (current_buffer->enable_multibyte_characters))
331 return bytepos - 1;
332
6a140a74
RS
333 DEC_POS (bytepos);
334 return bytepos;
335}
195d1361 336\f
7cc80f0a
RS
337/* Return a defun-start position before before POS and not too far before.
338 It should be the last one before POS, or nearly the last.
339
340 When open_paren_in_column_0_is_defun_start is nonzero,
1fd1cc2f 341 only the beginning of the buffer is treated as a defun-start.
7cc80f0a
RS
342
343 We record the information about where the scan started
344 and what its result was, so that another call in the same area
345 can return the same value very quickly.
195d1361
RS
346
347 There is no promise at which position the global syntax data is
348 valid on return from the subroutine, so the caller should explicitly
349 update the global data. */
37bef230
RS
350
351static int
6a140a74
RS
352find_defun_start (pos, pos_byte)
353 int pos, pos_byte;
37bef230 354{
6a140a74 355 int opoint = PT, opoint_byte = PT_BYTE;
37bef230 356
1fd1cc2f
RS
357 if (!open_paren_in_column_0_is_defun_start)
358 {
359 find_start_value_byte = BEGV_BYTE;
360 return BEGV;
361 }
362
37bef230
RS
363 /* Use previous finding, if it's valid and applies to this inquiry. */
364 if (current_buffer == find_start_buffer
365 /* Reuse the defun-start even if POS is a little farther on.
366 POS might be in the next defun, but that's ok.
367 Our value may not be the best possible, but will still be usable. */
368 && pos <= find_start_pos + 1000
369 && pos >= find_start_value
370 && BEGV == find_start_begv
371 && MODIFF == find_start_modiff)
372 return find_start_value;
373
374 /* Back up to start of line. */
6a140a74 375 scan_newline (pos, pos_byte, BEGV, BEGV_BYTE, -1, 1);
37bef230 376
195d1361
RS
377 /* We optimize syntax-table lookup for rare updates. Thus we accept
378 only those `^\s(' which are good in global _and_ text-property
379 syntax-tables. */
380 gl_state.current_syntax_table = current_buffer->syntax_table;
381 gl_state.use_global = 0;
1fd1cc2f 382 while (PT > BEGV)
37bef230 383 {
1fd1cc2f
RS
384 /* Open-paren at start of line means we may have found our
385 defun-start. */
8f924df7 386 if (SYNTAX (FETCH_CHAR_AS_MULTIBYTE (PT_BYTE)) == Sopen)
195d1361 387 {
1fd1cc2f 388 SETUP_SYNTAX_TABLE (PT + 1, -1); /* Try again... */
b7dbcc19 389 if (SYNTAX (FETCH_CHAR_AS_MULTIBYTE (PT_BYTE)) == Sopen)
1fd1cc2f
RS
390 break;
391 /* Now fallback to the default value. */
392 gl_state.current_syntax_table = current_buffer->syntax_table;
393 gl_state.use_global = 0;
195d1361 394 }
1fd1cc2f
RS
395 /* Move to beg of previous line. */
396 scan_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -2, 1);
37bef230
RS
397 }
398
399 /* Record what we found, for the next try. */
6a140a74
RS
400 find_start_value = PT;
401 find_start_value_byte = PT_BYTE;
37bef230
RS
402 find_start_buffer = current_buffer;
403 find_start_modiff = MODIFF;
404 find_start_begv = BEGV;
405 find_start_pos = pos;
406
6a140a74
RS
407 TEMP_SET_PT_BOTH (opoint, opoint_byte);
408
37bef230
RS
409 return find_start_value;
410}
411\f
f902a008
RS
412/* Return the SYNTAX_COMEND_FIRST of the character before POS, POS_BYTE. */
413
414static int
415prev_char_comend_first (pos, pos_byte)
416 int pos, pos_byte;
417{
418 int c, val;
419
420 DEC_BOTH (pos, pos_byte);
421 UPDATE_SYNTAX_TABLE_BACKWARD (pos);
422 c = FETCH_CHAR (pos_byte);
423 val = SYNTAX_COMEND_FIRST (c);
424 UPDATE_SYNTAX_TABLE_FORWARD (pos + 1);
425 return val;
426}
427
428/* Return the SYNTAX_COMSTART_FIRST of the character before POS, POS_BYTE. */
429
3f679f55
SM
430/* static int
431 * prev_char_comstart_first (pos, pos_byte)
432 * int pos, pos_byte;
433 * {
434 * int c, val;
7d0393cf 435 *
3f679f55
SM
436 * DEC_BOTH (pos, pos_byte);
437 * UPDATE_SYNTAX_TABLE_BACKWARD (pos);
438 * c = FETCH_CHAR (pos_byte);
439 * val = SYNTAX_COMSTART_FIRST (c);
440 * UPDATE_SYNTAX_TABLE_FORWARD (pos + 1);
441 * return val;
442 * } */
f902a008 443
6a140a74
RS
444/* Checks whether charpos FROM is at the end of a comment.
445 FROM_BYTE is the bytepos corresponding to FROM.
446 Do not move back before STOP.
447
448 Return a positive value if we find a comment ending at FROM/FROM_BYTE;
449 return -1 otherwise.
450
451 If successful, store the charpos of the comment's beginning
452 into *CHARPOS_PTR, and the bytepos into *BYTEPOS_PTR.
527a32d9
KH
453
454 Global syntax data remains valid for backward search starting at
455 the returned value (or at FROM, if the search was not successful). */
195d1361
RS
456
457static int
95ff8dfc 458back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_ptr)
6a140a74 459 int from, from_byte, stop;
95ff8dfc 460 int comnested, comstyle;
6a140a74 461 int *charpos_ptr, *bytepos_ptr;
195d1361
RS
462{
463 /* Look back, counting the parity of string-quotes,
464 and recording the comment-starters seen.
465 When we reach a safe place, assume that's not in a string;
466 then step the main scan to the earliest comment-starter seen
467 an even number of string quotes away from the safe place.
468
469 OFROM[I] is position of the earliest comment-starter seen
470 which is I+2X quotes from the comment-end.
471 PARITY is current parity of quotes from the comment end. */
3ee5041c 472 int string_style = -1; /* Presumed outside of any string. */
195d1361 473 int string_lossage = 0;
f7c436c1 474 /* Not a real lossage: indicates that we have passed a matching comment
7d0393cf 475 starter plus a non-matching comment-ender, meaning that any matching
f7c436c1
SM
476 comment-starter we might see later could be a false positive (hidden
477 inside another comment).
478 Test case: { a (* b } c (* d *) */
479 int comment_lossage = 0;
195d1361 480 int comment_end = from;
6a140a74 481 int comment_end_byte = from_byte;
195d1361 482 int comstart_pos = 0;
6a140a74 483 int comstart_byte;
eb35b628
RS
484 /* Place where the containing defun starts,
485 or 0 if we didn't come across it yet. */
486 int defun_start = 0;
487 int defun_start_byte = 0;
195d1361 488 register enum syntaxcode code;
95ff8dfc 489 int nesting = 1; /* current comment nesting */
ea315ed6 490 int c;
3f679f55
SM
491 int syntax = 0;
492
493 /* FIXME: A }} comment-ender style leads to incorrect behavior
494 in the case of {{ c }}} because we ignore the last two chars which are
495 assumed to be comment-enders although they aren't. */
195d1361
RS
496
497 /* At beginning of range to scan, we're outside of strings;
498 that determines quote parity to the comment-end. */
499 while (from != stop)
500 {
3f679f55
SM
501 int temp_byte, prev_syntax;
502 int com2start, com2end;
6a140a74 503
195d1361 504 /* Move back and examine a character. */
6a140a74 505 DEC_BOTH (from, from_byte);
195d1361
RS
506 UPDATE_SYNTAX_TABLE_BACKWARD (from);
507
3f679f55 508 prev_syntax = syntax;
b7dbcc19 509 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
3f679f55 510 syntax = SYNTAX_WITH_FLAGS (c);
195d1361
RS
511 code = SYNTAX (c);
512
3f679f55
SM
513 /* Check for 2-char comment markers. */
514 com2start = (SYNTAX_FLAGS_COMSTART_FIRST (syntax)
515 && SYNTAX_FLAGS_COMSTART_SECOND (prev_syntax)
516 && comstyle == SYNTAX_FLAGS_COMMENT_STYLE (prev_syntax)
517 && (SYNTAX_FLAGS_COMMENT_NESTED (prev_syntax)
518 || SYNTAX_FLAGS_COMMENT_NESTED (syntax)) == comnested);
519 com2end = (SYNTAX_FLAGS_COMEND_FIRST (syntax)
520 && SYNTAX_FLAGS_COMEND_SECOND (prev_syntax));
521
522 /* Nasty cases with overlapping 2-char comment markers:
523 - snmp-mode: -- c -- foo -- c --
524 --- c --
525 ------ c --
526 - c-mode: *||*
527 |* *|* *|
528 |*| |* |*|
529 /// */
530
531 /* If a 2-char comment sequence partly overlaps with another,
532 we don't try to be clever. */
533 if (from > stop && (com2end || com2start))
195d1361 534 {
3f679f55
SM
535 int next = from, next_byte = from_byte, next_c, next_syntax;
536 DEC_BOTH (next, next_byte);
537 UPDATE_SYNTAX_TABLE_BACKWARD (next);
b7dbcc19 538 next_c = FETCH_CHAR_AS_MULTIBYTE (next_byte);
3f679f55
SM
539 next_syntax = SYNTAX_WITH_FLAGS (next_c);
540 if (((com2start || comnested)
541 && SYNTAX_FLAGS_COMEND_SECOND (syntax)
542 && SYNTAX_FLAGS_COMEND_FIRST (next_syntax))
543 || ((com2end || comnested)
544 && SYNTAX_FLAGS_COMSTART_SECOND (syntax)
545 && comstyle == SYNTAX_FLAGS_COMMENT_STYLE (syntax)
546 && SYNTAX_FLAGS_COMSTART_FIRST (next_syntax)))
547 goto lossage;
548 /* UPDATE_SYNTAX_TABLE_FORWARD (next + 1); */
f902a008 549 }
3f679f55
SM
550
551 if (com2start && comstart_pos == 0)
552 /* We're looking at a comment starter. But it might be a comment
553 ender as well (see snmp-mode). The first time we see one, we
554 need to consider it as a comment starter,
555 and the subsequent times as a comment ender. */
556 com2end = 0;
557
558 /* Turn a 2-char comment sequences into the appropriate syntax. */
559 if (com2end)
560 code = Sendcomment;
561 else if (com2start)
562 code = Scomment;
563 /* Ignore comment starters of a different style. */
564 else if (code == Scomment
565 && (comstyle != SYNTAX_FLAGS_COMMENT_STYLE (syntax)
566 || SYNTAX_FLAGS_COMMENT_NESTED (syntax) != comnested))
1aa963c8 567 continue;
195d1361 568
9828a477
KH
569 /* Ignore escaped characters, except comment-enders. */
570 if (code != Sendcomment && char_quoted (from, from_byte))
195d1361
RS
571 continue;
572
3ee5041c 573 switch (code)
195d1361 574 {
3ee5041c
SM
575 case Sstring_fence:
576 case Scomment_fence:
577 c = (code == Sstring_fence ? ST_STRING_STYLE : ST_COMMENT_STYLE);
578 case Sstring:
579 /* Track parity of quotes. */
580 if (string_style == -1)
581 /* Entering a string. */
582 string_style = c;
583 else if (string_style == c)
584 /* Leaving the string. */
585 string_style = -1;
586 else
587 /* If we have two kinds of string delimiters.
588 There's no way to grok this scanning backwards. */
195d1361 589 string_lossage = 1;
3ee5041c 590 break;
7d0393cf 591
3ee5041c
SM
592 case Scomment:
593 /* We've already checked that it is the relevant comstyle. */
f7c436c1 594 if (string_style != -1 || comment_lossage || string_lossage)
3ee5041c
SM
595 /* There are odd string quotes involved, so let's be careful.
596 Test case in Pascal: " { " a { " } */
597 goto lossage;
598
f7c436c1
SM
599 if (!comnested)
600 {
601 /* Record best comment-starter so far. */
602 comstart_pos = from;
603 comstart_byte = from_byte;
604 }
605 else if (--nesting <= 0)
95ff8dfc
RS
606 /* nested comments have to be balanced, so we don't need to
607 keep looking for earlier ones. We use here the same (slightly
608 incorrect) reasoning as below: since it is followed by uniform
609 paired string quotes, this comment-start has to be outside of
610 strings, else the comment-end itself would be inside a string. */
611 goto done;
3ee5041c
SM
612 break;
613
02010917 614 case Sendcomment:
3f679f55 615 if (SYNTAX_FLAGS_COMMENT_STYLE (syntax) == comstyle
d070eb22 616 && ((com2end && SYNTAX_FLAGS_COMMENT_NESTED (prev_syntax))
3f679f55 617 || SYNTAX_FLAGS_COMMENT_NESTED (syntax)) == comnested)
02010917
SM
618 /* This is the same style of comment ender as ours. */
619 {
620 if (comnested)
621 nesting++;
622 else
623 /* Anything before that can't count because it would match
624 this comment-ender rather than ours. */
625 from = stop; /* Break out of the loop. */
626 }
f7c436c1
SM
627 else if (comstart_pos != 0 || c != '\n')
628 /* We're mixing comment styles here, so we'd better be careful.
629 The (comstart_pos != 0 || c != '\n') check is not quite correct
630 (we should just always set comment_lossage), but removing it
631 would imply that any multiline comment in C would go through
632 lossage, which seems overkill.
633 The failure should only happen in the rare cases such as
634 { (* } *) */
635 comment_lossage = 1;
02010917 636 break;
195d1361 637
02010917
SM
638 case Sopen:
639 /* Assume a defun-start point is outside of strings. */
640 if (open_paren_in_column_0_is_defun_start
641 && (from == stop
642 || (temp_byte = dec_bytepos (from_byte),
643 FETCH_CHAR (temp_byte) == '\n')))
644 {
645 defun_start = from;
646 defun_start_byte = from_byte;
647 from = stop; /* Break out of the loop. */
648 }
eb35b628 649 break;
02010917
SM
650
651 default:
f7c436c1 652 break;
eb35b628 653 }
195d1361
RS
654 }
655
656 if (comstart_pos == 0)
657 {
658 from = comment_end;
6a140a74 659 from_byte = comment_end_byte;
195d1361
RS
660 UPDATE_SYNTAX_TABLE_FORWARD (comment_end - 1);
661 }
f7c436c1
SM
662 /* If comstart_pos is set and we get here (ie. didn't jump to `lossage'
663 or `done'), then we've found the beginning of the non-nested comment. */
664 else if (1) /* !comnested */
195d1361
RS
665 {
666 from = comstart_pos;
6a140a74 667 from_byte = comstart_byte;
195d1361
RS
668 /* Globals are correct now. */
669 }
670 else
671 {
3ee5041c
SM
672 struct lisp_parse_state state;
673 lossage:
195d1361
RS
674 /* We had two kinds of string delimiters mixed up
675 together. Decode this going forwards.
02010917 676 Scan fwd from a known safe place (beginning-of-defun)
195d1361
RS
677 to the one in question; this records where we
678 last passed a comment starter. */
eb35b628
RS
679 /* If we did not already find the defun start, find it now. */
680 if (defun_start == 0)
681 {
682 defun_start = find_defun_start (comment_end, comment_end_byte);
683 defun_start_byte = find_start_value_byte;
684 }
02010917 685 do
195d1361 686 {
02010917
SM
687 scan_sexps_forward (&state,
688 defun_start, defun_start_byte,
689 comment_end, -10000, 0, Qnil, 0);
690 defun_start = comment_end;
691 if (state.incomment == (comnested ? 1 : -1)
692 && state.comstyle == comstyle)
693 from = state.comstr_start;
694 else
695 {
696 from = comment_end;
697 if (state.incomment)
698 /* If comment_end is inside some other comment, maybe ours
699 is nested, so we need to try again from within the
700 surrounding comment. Example: { a (* " *) */
701 {
702 /* FIXME: We should advance by one or two chars. */
703 defun_start = state.comstr_start + 2;
704 defun_start_byte = CHAR_TO_BYTE (defun_start);
705 }
706 }
707 } while (defun_start < comment_end);
708
6a140a74 709 from_byte = CHAR_TO_BYTE (from);
195d1361
RS
710 UPDATE_SYNTAX_TABLE_FORWARD (from - 1);
711 }
7d0393cf 712
95ff8dfc 713 done:
6a140a74
RS
714 *charpos_ptr = from;
715 *bytepos_ptr = from_byte;
716
1aa963c8 717 return (from == comment_end) ? -1 : from;
195d1361
RS
718}
719\f
8489eb67 720DEFUN ("syntax-table-p", Fsyntax_table_p, Ssyntax_table_p, 1, 1, 0,
fdb82f93
PJ
721 doc: /* Return t if OBJECT is a syntax table.
722Currently, any char-table counts as a syntax table. */)
723 (object)
2203e1e8 724 Lisp_Object object;
8489eb67 725{
2203e1e8 726 if (CHAR_TABLE_P (object)
e704cb4b 727 && EQ (XCHAR_TABLE (object)->purpose, Qsyntax_table))
8489eb67
RS
728 return Qt;
729 return Qnil;
730}
731
8ea151b2 732static void
8489eb67
RS
733check_syntax_table (obj)
734 Lisp_Object obj;
735{
d1be9f0f 736 if (!(CHAR_TABLE_P (obj)
e704cb4b 737 && EQ (XCHAR_TABLE (obj)->purpose, Qsyntax_table)))
d1be9f0f 738 wrong_type_argument (Qsyntax_table_p, obj);
7d0393cf 739}
8489eb67 740
8489eb67 741DEFUN ("syntax-table", Fsyntax_table, Ssyntax_table, 0, 0, 0,
fdb82f93
PJ
742 doc: /* Return the current syntax table.
743This is the one specified by the current buffer. */)
744 ()
8489eb67
RS
745{
746 return current_buffer->syntax_table;
747}
748
749DEFUN ("standard-syntax-table", Fstandard_syntax_table,
750 Sstandard_syntax_table, 0, 0, 0,
fdb82f93
PJ
751 doc: /* Return the standard syntax table.
752This is the one used for new buffers. */)
753 ()
8489eb67
RS
754{
755 return Vstandard_syntax_table;
756}
757
758DEFUN ("copy-syntax-table", Fcopy_syntax_table, Scopy_syntax_table, 0, 1, 0,
fdb82f93
PJ
759 doc: /* Construct a new syntax table and return it.
760It is a copy of the TABLE, which defaults to the standard syntax table. */)
761 (table)
8489eb67
RS
762 Lisp_Object table;
763{
8ea151b2
RS
764 Lisp_Object copy;
765
265a9e55 766 if (!NILP (table))
8ea151b2
RS
767 check_syntax_table (table);
768 else
769 table = Vstandard_syntax_table;
770
771 copy = Fcopy_sequence (table);
0f867324
RS
772
773 /* Only the standard syntax table should have a default element.
774 Other syntax tables should inherit from parents instead. */
775 XCHAR_TABLE (copy)->defalt = Qnil;
776
777 /* Copied syntax tables should all have parents.
778 If we copied one with no parent, such as the standard syntax table,
779 use the standard syntax table as the copy's parent. */
780 if (NILP (XCHAR_TABLE (copy)->parent))
781 Fset_char_table_parent (copy, Vstandard_syntax_table);
8ea151b2 782 return copy;
8489eb67
RS
783}
784
785DEFUN ("set-syntax-table", Fset_syntax_table, Sset_syntax_table, 1, 1, 0,
fdb82f93
PJ
786 doc: /* Select a new syntax table for the current buffer.
787One argument, a syntax table. */)
788 (table)
8489eb67
RS
789 Lisp_Object table;
790{
4e4dfa78 791 int idx;
8ea151b2 792 check_syntax_table (table);
8489eb67
RS
793 current_buffer->syntax_table = table;
794 /* Indicate that this buffer now has a specified syntax table. */
f6cd0527
GM
795 idx = PER_BUFFER_VAR_IDX (syntax_table);
796 SET_PER_BUFFER_VALUE_P (current_buffer, idx, 1);
8489eb67
RS
797 return table;
798}
799\f
800/* Convert a letter which signifies a syntax code
801 into the code it signifies.
195d1361 802 This is used by modify-syntax-entry, and other things. */
8489eb67
RS
803
804unsigned char syntax_spec_code[0400] =
805 { 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
806 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
807 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
808 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
195d1361 809 (char) Swhitespace, (char) Scomment_fence, (char) Sstring, 0377,
8489eb67
RS
810 (char) Smath, 0377, 0377, (char) Squote,
811 (char) Sopen, (char) Sclose, 0377, 0377,
812 0377, (char) Swhitespace, (char) Spunct, (char) Scharquote,
813 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
814 0377, 0377, 0377, 0377,
815 (char) Scomment, 0377, (char) Sendcomment, 0377,
6cb71bf6 816 (char) Sinherit, 0377, 0377, 0377, 0377, 0377, 0377, 0377, /* @, A ... */
8489eb67
RS
817 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
818 0377, 0377, 0377, 0377, 0377, 0377, 0377, (char) Sword,
819 0377, 0377, 0377, 0377, (char) Sescape, 0377, 0377, (char) Ssymbol,
820 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377, /* `, a, ... */
821 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
822 0377, 0377, 0377, 0377, 0377, 0377, 0377, (char) Sword,
195d1361 823 0377, 0377, 0377, 0377, (char) Sstring_fence, 0377, 0377, 0377
8489eb67
RS
824 };
825
195d1361 826/* Indexed by syntax code, give the letter that describes it. */
8489eb67 827
195d1361 828char syntax_code_spec[16] =
8489eb67 829 {
195d1361
RS
830 ' ', '.', 'w', '_', '(', ')', '\'', '\"', '$', '\\', '/', '<', '>', '@',
831 '!', '|'
8489eb67 832 };
93da5fff
KH
833
834/* Indexed by syntax code, give the object (cons of syntax code and
835 nil) to be stored in syntax table. Since these objects can be
836 shared among syntax tables, we generate them in advance. By
837 sharing objects, the function `describe-syntax' can give a more
838 compact listing. */
839static Lisp_Object Vsyntax_code_object;
840
8489eb67
RS
841\f
842DEFUN ("char-syntax", Fchar_syntax, Schar_syntax, 1, 1, 0,
fdb82f93
PJ
843 doc: /* Return the syntax code of CHARACTER, described by a character.
844For example, if CHARACTER is a word constituent,
845the character `w' is returned.
846The characters that correspond to various syntax codes
847are listed in the documentation of `modify-syntax-entry'. */)
848 (character)
2203e1e8 849 Lisp_Object character;
8489eb67 850{
8ea151b2 851 int char_int;
195d1361
RS
852 gl_state.current_syntax_table = current_buffer->syntax_table;
853
854 gl_state.use_global = 0;
b7826503 855 CHECK_NUMBER (character);
2203e1e8 856 char_int = XINT (character);
8ea151b2 857 return make_number (syntax_code_spec[(int) SYNTAX (char_int)]);
beefa22e
RS
858}
859
860DEFUN ("matching-paren", Fmatching_paren, Smatching_paren, 1, 1, 0,
fdb82f93
PJ
861 doc: /* Return the matching parenthesis of CHARACTER, or nil if none. */)
862 (character)
2203e1e8 863 Lisp_Object character;
beefa22e 864{
8ea151b2 865 int char_int, code;
195d1361
RS
866 gl_state.current_syntax_table = current_buffer->syntax_table;
867 gl_state.use_global = 0;
b7826503 868 CHECK_NUMBER (character);
2203e1e8 869 char_int = XINT (character);
8ea151b2 870 code = SYNTAX (char_int);
a8bd7cd8 871 if (code == Sopen || code == Sclose)
2e34157c 872 return SYNTAX_MATCH (char_int);
beefa22e 873 return Qnil;
8489eb67
RS
874}
875
c65adb44 876DEFUN ("string-to-syntax", Fstring_to_syntax, Sstring_to_syntax, 1, 1, 0,
fdb82f93
PJ
877 doc: /* Convert a syntax specification STRING into syntax cell form.
878STRING should be a string as it is allowed as argument of
879`modify-syntax-entry'. Value is the equivalent cons cell
880(CODE . MATCHING-CHAR) that can be used as value of a `syntax-table'
881text property. */)
882 (string)
c65adb44 883 Lisp_Object string;
8489eb67 884{
2e567bd3 885 register const unsigned char *p;
8489eb67 886 register enum syntaxcode code;
247e20a8 887 int val;
8ea151b2 888 Lisp_Object match;
8489eb67 889
b7826503 890 CHECK_STRING (string);
8489eb67 891
d5db4077 892 p = SDATA (string);
8489eb67
RS
893 code = (enum syntaxcode) syntax_spec_code[*p++];
894 if (((int) code & 0377) == 0377)
2e34157c 895 error ("invalid syntax description letter: %c", p[-1]);
8489eb67 896
8ea151b2 897 if (code == Sinherit)
c65adb44 898 return Qnil;
8ea151b2
RS
899
900 if (*p)
d1be9f0f 901 {
93da5fff 902 int len;
7c19e8e1 903 int character = (STRING_CHAR_AND_LENGTH
d5db4077 904 (p, SBYTES (string) - 1, len));
93da5fff 905 XSETINT (match, character);
d1be9f0f
RS
906 if (XFASTINT (match) == ' ')
907 match = Qnil;
93da5fff 908 p += len;
d1be9f0f
RS
909 }
910 else
8ea151b2 911 match = Qnil;
8489eb67 912
8ea151b2 913 val = (int) code;
8489eb67
RS
914 while (*p)
915 switch (*p++)
916 {
917 case '1':
247e20a8 918 val |= 1 << 16;
8489eb67
RS
919 break;
920
921 case '2':
247e20a8 922 val |= 1 << 17;
8489eb67
RS
923 break;
924
925 case '3':
247e20a8 926 val |= 1 << 18;
8489eb67
RS
927 break;
928
929 case '4':
247e20a8 930 val |= 1 << 19;
8489eb67
RS
931 break;
932
933 case 'p':
247e20a8 934 val |= 1 << 20;
8489eb67 935 break;
e5d4f4dc
RS
936
937 case 'b':
247e20a8 938 val |= 1 << 21;
e5d4f4dc 939 break;
95ff8dfc
RS
940
941 case 'n':
942 val |= 1 << 22;
943 break;
8489eb67 944 }
7d0393cf 945
93da5fff 946 if (val < XVECTOR (Vsyntax_code_object)->size && NILP (match))
c65adb44 947 return XVECTOR (Vsyntax_code_object)->contents[val];
93da5fff
KH
948 else
949 /* Since we can't use a shared object, let's make a new one. */
c65adb44
SM
950 return Fcons (make_number (val), match);
951}
952
fdb82f93 953/* I really don't know why this is interactive
2c9e1900 954 help-form should at least be made useful whilst reading the second arg. */
7d0393cf 955DEFUN ("modify-syntax-entry", Fmodify_syntax_entry, Smodify_syntax_entry, 2, 3,
c65adb44 956 "cSet syntax for character: \nsSet syntax for %s to: ",
a50a10a0 957 doc: /* Set syntax for character CHAR according to string NEWENTRY.
fdb82f93
PJ
958The syntax is changed only for table SYNTAX_TABLE, which defaults to
959 the current buffer's syntax table.
5c7b02ab
KH
960CHAR may be a cons (MIN . MAX), in which case, syntaxes of all characters
961in the range MIN and MAX are changed.
fdb82f93
PJ
962The first character of NEWENTRY should be one of the following:
963 Space or - whitespace syntax. w word constituent.
964 _ symbol constituent. . punctuation.
965 ( open-parenthesis. ) close-parenthesis.
966 " string quote. \\ escape.
967 $ paired delimiter. ' expression quote or prefix operator.
968 < comment starter. > comment ender.
969 / character-quote. @ inherit from `standard-syntax-table'.
970 | generic string fence. ! generic comment fence.
971
972Only single-character comment start and end sequences are represented thus.
973Two-character sequences are represented as described below.
974The second character of NEWENTRY is the matching parenthesis,
975 used only if the first character is `(' or `)'.
976Any additional characters are flags.
977Defined flags are the characters 1, 2, 3, 4, b, p, and n.
a50a10a0
PJ
978 1 means CHAR is the start of a two-char comment start sequence.
979 2 means CHAR is the second character of such a sequence.
980 3 means CHAR is the start of a two-char comment end sequence.
981 4 means CHAR is the second character of such a sequence.
fdb82f93
PJ
982
983There can be up to two orthogonal comment sequences. This is to support
984language modes such as C++. By default, all comment sequences are of style
985a, but you can set the comment sequence style to b (on the second character
986of a comment-start, or the first character of a comment-end sequence) using
987this flag:
a50a10a0
PJ
988 b means CHAR is part of comment sequence b.
989 n means CHAR is part of a nestable comment sequence.
fdb82f93 990
a50a10a0 991 p means CHAR is a prefix character for `backward-prefix-chars';
fdb82f93 992 such characters are treated as whitespace when they occur
a50a10a0
PJ
993 between expressions.
994usage: (modify-syntax-entry CHAR NEWENTRY &optional SYNTAX-TABLE) */)
fdb82f93 995 (c, newentry, syntax_table)
c65adb44
SM
996 Lisp_Object c, newentry, syntax_table;
997{
5c7b02ab
KH
998 if (CONSP (c))
999 {
8f924df7
KH
1000 CHECK_CHARACTER_CAR (c);
1001 CHECK_CHARACTER_CDR (c);
5c7b02ab
KH
1002 }
1003 else
1004 CHECK_CHARACTER (c);
c65adb44
SM
1005
1006 if (NILP (syntax_table))
1007 syntax_table = current_buffer->syntax_table;
1008 else
1009 check_syntax_table (syntax_table);
8489eb67 1010
5c7b02ab
KH
1011 newentry = Fstring_to_syntax (newentry);
1012 if (CONSP (c))
1013 SET_RAW_SYNTAX_ENTRY_RANGE (syntax_table, c, newentry);
1014 else
1015 SET_RAW_SYNTAX_ENTRY (syntax_table, XINT (c), newentry);
8489eb67
RS
1016 return Qnil;
1017}
1018\f
1019/* Dump syntax table to buffer in human-readable format */
1020
62abe9cb
SM
1021DEFUN ("internal-describe-syntax-value", Finternal_describe_syntax_value,
1022 Sinternal_describe_syntax_value, 1, 1, 0,
1023 doc: /* Insert a description of the internal syntax description SYNTAX at point. */)
1024 (syntax)
1025 Lisp_Object syntax;
8489eb67
RS
1026{
1027 register enum syntaxcode code;
e6365855 1028 char desc, start1, start2, end1, end2, prefix, comstyle, comnested;
8489eb67 1029 char str[2];
62abe9cb 1030 Lisp_Object first, match_lisp, value = syntax;
8489eb67 1031
8ea151b2
RS
1032 if (NILP (value))
1033 {
62abe9cb
SM
1034 insert_string ("default");
1035 return syntax;
8ea151b2
RS
1036 }
1037
908b7fea
KH
1038 if (CHAR_TABLE_P (value))
1039 {
62abe9cb
SM
1040 insert_string ("deeper char-table ...");
1041 return syntax;
908b7fea
KH
1042 }
1043
8ea151b2
RS
1044 if (!CONSP (value))
1045 {
62abe9cb
SM
1046 insert_string ("invalid");
1047 return syntax;
8ea151b2
RS
1048 }
1049
c1d497be
KR
1050 first = XCAR (value);
1051 match_lisp = XCDR (value);
8ea151b2
RS
1052
1053 if (!INTEGERP (first) || !(NILP (match_lisp) || INTEGERP (match_lisp)))
8489eb67 1054 {
62abe9cb
SM
1055 insert_string ("invalid");
1056 return syntax;
8489eb67
RS
1057 }
1058
e704cb4b 1059 code = (enum syntaxcode) (XINT (first) & 0377);
8ea151b2
RS
1060 start1 = (XINT (first) >> 16) & 1;
1061 start2 = (XINT (first) >> 17) & 1;
1062 end1 = (XINT (first) >> 18) & 1;
1063 end2 = (XINT (first) >> 19) & 1;
1064 prefix = (XINT (first) >> 20) & 1;
1065 comstyle = (XINT (first) >> 21) & 1;
e6365855 1066 comnested = (XINT (first) >> 22) & 1;
8489eb67
RS
1067
1068 if ((int) code < 0 || (int) code >= (int) Smax)
1069 {
1070 insert_string ("invalid");
62abe9cb 1071 return syntax;
8489eb67
RS
1072 }
1073 desc = syntax_code_spec[(int) code];
1074
1075 str[0] = desc, str[1] = 0;
1076 insert (str, 1);
1077
93da5fff
KH
1078 if (NILP (match_lisp))
1079 insert (" ", 1);
1080 else
1081 insert_char (XINT (match_lisp));
8489eb67 1082
8489eb67
RS
1083 if (start1)
1084 insert ("1", 1);
1085 if (start2)
1086 insert ("2", 1);
1087
1088 if (end1)
1089 insert ("3", 1);
1090 if (end2)
1091 insert ("4", 1);
1092
1093 if (prefix)
1094 insert ("p", 1);
e5d4f4dc
RS
1095 if (comstyle)
1096 insert ("b", 1);
e6365855
SM
1097 if (comnested)
1098 insert ("n", 1);
8489eb67
RS
1099
1100 insert_string ("\twhich means: ");
1101
0220c518 1102 switch (SWITCH_ENUM_CAST (code))
8489eb67
RS
1103 {
1104 case Swhitespace:
1105 insert_string ("whitespace"); break;
1106 case Spunct:
1107 insert_string ("punctuation"); break;
1108 case Sword:
1109 insert_string ("word"); break;
1110 case Ssymbol:
1111 insert_string ("symbol"); break;
1112 case Sopen:
1113 insert_string ("open"); break;
1114 case Sclose:
1115 insert_string ("close"); break;
1116 case Squote:
d671382e 1117 insert_string ("prefix"); break;
8489eb67
RS
1118 case Sstring:
1119 insert_string ("string"); break;
1120 case Smath:
1121 insert_string ("math"); break;
1122 case Sescape:
1123 insert_string ("escape"); break;
1124 case Scharquote:
1125 insert_string ("charquote"); break;
1126 case Scomment:
1127 insert_string ("comment"); break;
1128 case Sendcomment:
1129 insert_string ("endcomment"); break;
d671382e
SM
1130 case Sinherit:
1131 insert_string ("inherit"); break;
1132 case Scomment_fence:
1133 insert_string ("comment fence"); break;
1134 case Sstring_fence:
1135 insert_string ("string fence"); break;
8489eb67
RS
1136 default:
1137 insert_string ("invalid");
62abe9cb 1138 return syntax;
8489eb67
RS
1139 }
1140
8ea151b2 1141 if (!NILP (match_lisp))
8489eb67
RS
1142 {
1143 insert_string (", matches ");
8ea151b2 1144 insert_char (XINT (match_lisp));
8489eb67
RS
1145 }
1146
1147 if (start1)
1148 insert_string (",\n\t is the first character of a comment-start sequence");
1149 if (start2)
1150 insert_string (",\n\t is the second character of a comment-start sequence");
1151
1152 if (end1)
1153 insert_string (",\n\t is the first character of a comment-end sequence");
1154 if (end2)
1155 insert_string (",\n\t is the second character of a comment-end sequence");
e5d4f4dc
RS
1156 if (comstyle)
1157 insert_string (" (comment style b)");
e6365855
SM
1158 if (comnested)
1159 insert_string (" (nestable)");
e5d4f4dc 1160
8489eb67
RS
1161 if (prefix)
1162 insert_string (",\n\t is a prefix character for `backward-prefix-chars'");
1163
62abe9cb
SM
1164 return syntax;
1165}
8489eb67 1166\f
195d1361
RS
1167int parse_sexp_ignore_comments;
1168
8f924df7
KH
1169/* Char-table of functions that find the next or previous word
1170 boundary. */
1171Lisp_Object Vfind_word_boundary_function_table;
869bb237 1172
8489eb67
RS
1173/* Return the position across COUNT words from FROM.
1174 If that many words cannot be found before the end of the buffer, return 0.
1175 COUNT negative means scan backward and stop at word beginning. */
1176
dfcf069d 1177int
8489eb67
RS
1178scan_words (from, count)
1179 register int from, count;
1180{
1181 register int beg = BEGV;
1182 register int end = ZV;
6a140a74 1183 register int from_byte = CHAR_TO_BYTE (from);
93da5fff
KH
1184 register enum syntaxcode code;
1185 int ch0, ch1;
8f924df7 1186 Lisp_Object func, script, pos;
8489eb67
RS
1187
1188 immediate_quit = 1;
1189 QUIT;
1190
195d1361
RS
1191 SETUP_SYNTAX_TABLE (from, count);
1192
8489eb67
RS
1193 while (count > 0)
1194 {
1195 while (1)
1196 {
1197 if (from == end)
1198 {
1199 immediate_quit = 0;
1200 return 0;
1201 }
195d1361 1202 UPDATE_SYNTAX_TABLE_FORWARD (from);
b7dbcc19 1203 ch0 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
93da5fff 1204 code = SYNTAX (ch0);
6a140a74 1205 INC_BOTH (from, from_byte);
8489eb67
RS
1206 if (words_include_escapes
1207 && (code == Sescape || code == Scharquote))
1208 break;
1209 if (code == Sword)
1210 break;
8489eb67 1211 }
93da5fff
KH
1212 /* Now CH0 is a character which begins a word and FROM is the
1213 position of the next character. */
8f924df7 1214 func = CHAR_TABLE_REF (Vfind_word_boundary_function_table, ch0);
869bb237 1215 if (! NILP (Ffboundp (func)))
8489eb67 1216 {
869bb237 1217 pos = call2 (func, make_number (from - 1), make_number (end));
8f924df7
KH
1218 if (INTEGERP (pos) && XINT (pos) > from)
1219 {
1220 from = XINT (pos);
1221 from_byte = CHAR_TO_BYTE (from);
1222 }
8489eb67 1223 }
869bb237 1224 else
8cb8232a 1225 {
8cb8232a
KH
1226 script = CHAR_TABLE_REF (Vchar_script_table, ch0);
1227 while (1)
1228 {
1229 if (from == end) break;
1230 UPDATE_SYNTAX_TABLE_FORWARD (from);
b7dbcc19 1231 ch1 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
8cb8232a
KH
1232 code = SYNTAX (ch1);
1233 if ((code != Sword
1234 && (! words_include_escapes
1235 || (code != Sescape && code != Scharquote)))
1236 || ! EQ (CHAR_TABLE_REF (Vchar_script_table, ch1), script))
869bb237 1237 break;
8cb8232a
KH
1238 INC_BOTH (from, from_byte);
1239 ch0 = ch1;
1240 }
8489eb67
RS
1241 }
1242 count--;
1243 }
1244 while (count < 0)
1245 {
1246 while (1)
1247 {
1248 if (from == beg)
1249 {
1250 immediate_quit = 0;
1251 return 0;
1252 }
6a140a74 1253 DEC_BOTH (from, from_byte);
195d1361 1254 UPDATE_SYNTAX_TABLE_BACKWARD (from);
b7dbcc19 1255 ch1 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
93da5fff 1256 code = SYNTAX (ch1);
8489eb67
RS
1257 if (words_include_escapes
1258 && (code == Sescape || code == Scharquote))
1259 break;
1260 if (code == Sword)
1261 break;
8489eb67 1262 }
93da5fff
KH
1263 /* Now CH1 is a character which ends a word and FROM is the
1264 position of it. */
8f924df7 1265 func = CHAR_TABLE_REF (Vfind_word_boundary_function_table, ch1);
869bb237 1266 if (! NILP (Ffboundp (func)))
8f924df7 1267 {
869bb237 1268 pos = call2 (func, make_number (from), make_number (beg));
8f924df7
KH
1269 if (INTEGERP (pos) && XINT (pos) < from)
1270 {
1271 from = XINT (pos);
1272 from_byte = CHAR_TO_BYTE (from);
1273 }
8489eb67 1274 }
869bb237 1275 else
8489eb67 1276 {
8cb8232a
KH
1277 script = CHAR_TABLE_REF (Vchar_script_table, ch1);
1278 while (1)
1279 {
1280 int temp_byte;
6a140a74 1281
8cb8232a
KH
1282 if (from == beg)
1283 break;
1284 temp_byte = dec_bytepos (from_byte);
1285 UPDATE_SYNTAX_TABLE_BACKWARD (from);
b7dbcc19 1286 ch0 = FETCH_CHAR_AS_MULTIBYTE (temp_byte);
8cb8232a
KH
1287 code = SYNTAX (ch0);
1288 if ((code != Sword
1289 && (! words_include_escapes
1290 || (code != Sescape && code != Scharquote)))
1291 || ! EQ (CHAR_TABLE_REF (Vchar_script_table, ch0), script))
1292 break;
1293 DEC_BOTH (from, from_byte);
1294 ch1 = ch0;
1295 }
8489eb67
RS
1296 }
1297 count++;
1298 }
1299
1300 immediate_quit = 0;
1301
1302 return from;
1303}
1304
839966f3 1305DEFUN ("forward-word", Fforward_word, Sforward_word, 0, 1, "p",
fdb82f93
PJ
1306 doc: /* Move point forward ARG words (backward if ARG is negative).
1307Normally returns t.
1308If an edge of the buffer or a field boundary is reached, point is left there
1309and the function returns nil. Field boundaries are not noticed if
1310`inhibit-field-text-motion' is non-nil. */)
839966f3
KH
1311 (arg)
1312 Lisp_Object arg;
8489eb67 1313{
2c6ea900 1314 int orig_val, val;
8489eb67 1315
839966f3
KH
1316 if (NILP (arg))
1317 XSETFASTINT (arg, 1);
1318 else
1319 CHECK_NUMBER (arg);
1320
1321 val = orig_val = scan_words (PT, XINT (arg));
2c6ea900 1322 if (! orig_val)
839966f3 1323 val = XINT (arg) > 0 ? ZV : BEGV;
5878ee6f 1324
b8855607 1325 /* Avoid jumping out of an input field. */
64aa4eb1 1326 val = XFASTINT (Fconstrain_to_field (make_number (val), make_number (PT),
070fe99c 1327 Qt, Qnil, Qnil));
7d0393cf 1328
8489eb67 1329 SET_PT (val);
e6d8341f 1330 return val == orig_val ? Qt : Qnil;
8489eb67
RS
1331}
1332\f
195d1361
RS
1333Lisp_Object skip_chars ();
1334
1335DEFUN ("skip-chars-forward", Fskip_chars_forward, Sskip_chars_forward, 1, 2, 0,
fdb82f93
PJ
1336 doc: /* Move point forward, stopping before a char not in STRING, or at pos LIM.
1337STRING is like the inside of a `[...]' in a regular expression
1338except that `]' is never special and `\\' quotes `^', `-' or `\\'
1339 (but not as the end of a range; quoting is never needed there).
1340Thus, with arg "a-zA-Z", this skips letters stopping before first nonletter.
1341With arg "^a-zA-Z", skips nonletters stopping before first letter.
b222e415
DL
1342Returns the distance traveled, either zero or positive.
1343Note that char classes, e.g. `[:alpha:]', are not currently supported;
1344they will be treated as literals. */)
fdb82f93 1345 (string, lim)
195d1361
RS
1346 Lisp_Object string, lim;
1347{
b7dbcc19 1348 return skip_chars (1, string, lim);
195d1361
RS
1349}
1350
1351DEFUN ("skip-chars-backward", Fskip_chars_backward, Sskip_chars_backward, 1, 2, 0,
fdb82f93
PJ
1352 doc: /* Move point backward, stopping after a char not in STRING, or at pos LIM.
1353See `skip-chars-forward' for details.
1354Returns the distance traveled, either zero or negative. */)
1355 (string, lim)
195d1361
RS
1356 Lisp_Object string, lim;
1357{
b7dbcc19 1358 return skip_chars (0, string, lim);
195d1361
RS
1359}
1360
1361DEFUN ("skip-syntax-forward", Fskip_syntax_forward, Sskip_syntax_forward, 1, 2, 0,
fdb82f93
PJ
1362 doc: /* Move point forward across chars in specified syntax classes.
1363SYNTAX is a string of syntax code characters.
1364Stop before a char whose syntax is not in SYNTAX, or at position LIM.
1365If SYNTAX starts with ^, skip characters whose syntax is NOT in SYNTAX.
1366This function returns the distance traveled, either zero or positive. */)
1367 (syntax, lim)
195d1361
RS
1368 Lisp_Object syntax, lim;
1369{
b7dbcc19 1370 return skip_syntaxes (1, syntax, lim);
195d1361
RS
1371}
1372
1373DEFUN ("skip-syntax-backward", Fskip_syntax_backward, Sskip_syntax_backward, 1, 2, 0,
fdb82f93
PJ
1374 doc: /* Move point backward across chars in specified syntax classes.
1375SYNTAX is a string of syntax code characters.
1376Stop on reaching a char whose syntax is not in SYNTAX, or at position LIM.
1377If SYNTAX starts with ^, skip characters whose syntax is NOT in SYNTAX.
1378This function returns the distance traveled, either zero or negative. */)
1379 (syntax, lim)
195d1361
RS
1380 Lisp_Object syntax, lim;
1381{
b7dbcc19 1382 return skip_syntaxes (0, syntax, lim);
195d1361
RS
1383}
1384
6a140a74 1385static Lisp_Object
b7dbcc19
KH
1386skip_chars (forwardp, string, lim)
1387 int forwardp;
195d1361
RS
1388 Lisp_Object string, lim;
1389{
195d1361
RS
1390 register unsigned int c;
1391 unsigned char fastmap[0400];
b7dbcc19 1392 /* Store the ranges of non-ASCII characters. */
5df4982e 1393 int *char_ranges;
7e68b0ea 1394 int n_char_ranges = 0;
195d1361 1395 int negate = 0;
4101e6fe 1396 register int i, i_byte;
b7dbcc19
KH
1397 /* Set to 1 if the current buffer is multibyte and the region
1398 contains non-ASCII chars. */
1399 int multibyte;
1400 /* Set to 1 if STRING is multibyte and it contains non-ASCII
1401 chars. */
1674d9a2
RS
1402 int string_multibyte;
1403 int size_byte;
2e567bd3 1404 const unsigned char *str;
82d497fc 1405 int len;
195d1361 1406
b7826503 1407 CHECK_STRING (string);
82d497fc 1408
195d1361
RS
1409 if (NILP (lim))
1410 XSETINT (lim, forwardp ? ZV : BEGV);
1411 else
b7826503 1412 CHECK_NUMBER_COERCE_MARKER (lim);
195d1361
RS
1413
1414 /* In any case, don't allow scan outside bounds of buffer. */
195d1361
RS
1415 if (XINT (lim) > ZV)
1416 XSETFASTINT (lim, ZV);
1417 if (XINT (lim) < BEGV)
1418 XSETFASTINT (lim, BEGV);
1419
b7dbcc19 1420 multibyte = (!NILP (current_buffer->enable_multibyte_characters)
92eaa22e 1421 && (XINT (lim) - PT != CHAR_TO_BYTE (XINT (lim)) - PT_BYTE));
8f924df7 1422 string_multibyte = SBYTES (string) > SCHARS (string);
b7dbcc19 1423
195d1361
RS
1424 bzero (fastmap, sizeof fastmap);
1425
8f924df7
KH
1426 str = SDATA (string);
1427 size_byte = SBYTES (string);
4101e6fe 1428
82d497fc 1429 i_byte = 0;
13090112 1430 if (i_byte < size_byte
d5db4077 1431 && SREF (string, 0) == '^')
195d1361 1432 {
82d497fc 1433 negate = 1; i_byte++;
195d1361
RS
1434 }
1435
1436 /* Find the characters specified and set their elements of fastmap.
b7dbcc19 1437 Handle backslashes and ranges specially.
195d1361 1438
b7dbcc19
KH
1439 If STRING contains non-ASCII characters, setup char_ranges for
1440 them and use fastmap only for their leading codes. */
4101e6fe 1441
b7dbcc19 1442 if (! string_multibyte)
195d1361 1443 {
b7dbcc19 1444 int string_has_eight_bit = 0;
4101e6fe 1445
b7dbcc19
KH
1446 /* At first setup fastmap. */
1447 while (i_byte < size_byte)
1448 {
1449 c = str[i_byte++];
1450
1451 if (c == '\\')
1452 {
13090112 1453 if (i_byte == size_byte)
4101e6fe
RS
1454 break;
1455
5c7b02ab 1456 c = str[i_byte++];
195d1361 1457 }
839966f3
KH
1458 /* Treat `-' as range character only if another character
1459 follows. */
1460 if (i_byte + 1 < size_byte
82d497fc 1461 && str[i_byte] == '-')
195d1361 1462 {
9690d026 1463 unsigned int c2;
4101e6fe
RS
1464
1465 /* Skip over the dash. */
82d497fc 1466 i_byte++;
4101e6fe 1467
4101e6fe 1468 /* Get the end of the range. */
5c7b02ab 1469 c2 = str[i_byte++];
b7dbcc19
KH
1470 if (c2 == '\\'
1471 && i_byte < size_byte)
1472 c2 = str[i_byte++];
1473
8f924df7
KH
1474 if (c <= c2)
1475 {
1476 while (c <= c2)
1477 fastmap[c++] = 1;
1478 if (! ASCII_CHAR_P (c2))
1479 string_has_eight_bit = 1;
1480 }
195d1361
RS
1481 }
1482 else
b7dbcc19
KH
1483 {
1484 fastmap[c] = 1;
1485 if (! ASCII_CHAR_P (c))
1486 string_has_eight_bit = 1;
1487 }
1488 }
1489
1490 /* If the current range is multibyte and STRING contains
1491 eight-bit chars, arrange fastmap and setup char_ranges for
1492 the corresponding multibyte chars. */
1493 if (multibyte && string_has_eight_bit)
1494 {
1495 unsigned char fastmap2[0400];
1496 int range_start_byte, range_start_char;
1497
1498 bcopy (fastmap2 + 0200, fastmap + 0200, 0200);
1499 bzero (fastmap + 0200, 0200);
1500 /* We are sure that this loop stops. */
1501 for (i = 0200; ! fastmap2[i]; i++);
1502 c = unibyte_char_to_multibyte (i);
1503 fastmap[CHAR_LEADING_CODE (c)] = 1;
1504 range_start_byte = i;
1505 range_start_char = c;
f003ca54 1506 char_ranges = (int *) alloca (sizeof (int) * 128 * 2);
b7dbcc19
KH
1507 for (i = 129; i < 0400; i++)
1508 {
1509 c = unibyte_char_to_multibyte (i);
1510 fastmap[CHAR_LEADING_CODE (c)] = 1;
1511 if (i - range_start_byte != c - range_start_char)
1512 {
1513 char_ranges[n_char_ranges++] = range_start_char;
1514 char_ranges[n_char_ranges++] = ((i - 1 - range_start_byte)
1515 + range_start_char);
1516 range_start_byte = i;
1517 range_start_char = c;
8f924df7 1518 }
b7dbcc19
KH
1519 }
1520 char_ranges[n_char_ranges++] = range_start_char;
1521 char_ranges[n_char_ranges++] = ((i - 1 - range_start_byte)
1522 + range_start_char);
195d1361
RS
1523 }
1524 }
f003ca54 1525 else /* STRING is multibyte */
b7dbcc19 1526 {
f003ca54
KH
1527 char_ranges = (int *) alloca (sizeof (int) * SCHARS (string) * 2);
1528
b7dbcc19 1529 while (i_byte < size_byte)
195d1361 1530 {
b7dbcc19 1531 unsigned char leading_code;
5c7b02ab 1532
b7dbcc19
KH
1533 leading_code = str[i_byte];
1534 c = STRING_CHAR_AND_LENGTH (str + i_byte, size_byte-i_byte, len);
1535 i_byte += len;
5c7b02ab 1536
195d1361
RS
1537 if (c == '\\')
1538 {
13090112 1539 if (i_byte == size_byte)
4101e6fe
RS
1540 break;
1541
b7dbcc19 1542 leading_code = str[i_byte];
839966f3
KH
1543 c = STRING_CHAR_AND_LENGTH (str + i_byte,
1544 size_byte - i_byte, len);
82d497fc 1545 i_byte += len;
195d1361 1546 }
839966f3
KH
1547 /* Treat `-' as range character only if another character
1548 follows. */
1549 if (i_byte + 1 < size_byte
82d497fc 1550 && str[i_byte] == '-')
195d1361 1551 {
9690d026 1552 unsigned int c2;
b7dbcc19 1553 unsigned char leading_code2;
4101e6fe
RS
1554
1555 /* Skip over the dash. */
82d497fc 1556 i_byte++;
4101e6fe 1557
4101e6fe 1558 /* Get the end of the range. */
b7dbcc19 1559 leading_code2 = str[i_byte];
839966f3
KH
1560 c2 = STRING_CHAR_AND_LENGTH (str + i_byte,
1561 size_byte - i_byte, len);
82d497fc 1562 i_byte += len;
7e68b0ea 1563
b7dbcc19
KH
1564 if (c2 == '\\'
1565 && i_byte < size_byte)
1566 {
1567 leading_code2 = str[i_byte];
1568 c2 =STRING_CHAR_AND_LENGTH (str + i_byte, size_byte-i_byte, len);
1569 i_byte += len;
1570 }
1571
f003ca54
KH
1572 if (c > c2)
1573 continue;
b7dbcc19 1574 if (ASCII_CHAR_P (c))
06274af5 1575 {
5c7b02ab
KH
1576 while (c <= c2 && c < 0x80)
1577 fastmap[c++] = 1;
b7dbcc19
KH
1578 leading_code = CHAR_LEADING_CODE (c);
1579 }
1580 if (! ASCII_CHAR_P (c))
1581 {
1582 while (leading_code <= leading_code2)
1583 fastmap[leading_code++] = 1;
1584 if (c <= c2)
e39091b6 1585 {
b7dbcc19 1586 char_ranges[n_char_ranges++] = c;
e39091b6 1587 char_ranges[n_char_ranges++] = c2;
06274af5
KH
1588 }
1589 }
195d1361
RS
1590 }
1591 else
7e68b0ea 1592 {
b7dbcc19 1593 if (ASCII_CHAR_P (c))
e39091b6
KH
1594 fastmap[c] = 1;
1595 else
7e68b0ea 1596 {
b7dbcc19 1597 fastmap[leading_code] = 1;
4101e6fe
RS
1598 char_ranges[n_char_ranges++] = c;
1599 char_ranges[n_char_ranges++] = c;
7e68b0ea
RS
1600 }
1601 }
195d1361 1602 }
b7dbcc19
KH
1603
1604 /* If the current range is unibyte and STRING contains non-ASCII
1605 chars, arrange fastmap for the corresponding unibyte
1606 chars. */
1607
1608 if (! multibyte && n_char_ranges > 0)
1609 {
1610 bzero (fastmap + 0200, 0200);
1611 for (i = 0; i < n_char_ranges; i += 2)
1612 {
1613 int c1 = char_ranges[i];
1614 int c2 = char_ranges[i + 1];
1615
1616 for (; c1 <= c2; c1++)
1617 fastmap[CHAR_TO_BYTE8 (c1)] = 1;
1618 }
1619 }
195d1361
RS
1620 }
1621
9690d026 1622 /* If ^ was the first character, complement the fastmap. */
195d1361 1623 if (negate)
b7dbcc19
KH
1624 {
1625 if (! multibyte)
1626 for (i = 0; i < sizeof fastmap; i++)
1627 fastmap[i] ^= 1;
1628 else
1629 {
1630 for (i = 0; i < 0200; i++)
1631 fastmap[i] ^= 1;
1632 /* All non-ASCII chars possibly match. */
1633 for (; i < sizeof fastmap; i++)
1634 fastmap[i] = 1;
1635 }
1636 }
195d1361
RS
1637
1638 {
1639 int start_point = PT;
1640 int pos = PT;
6a140a74 1641 int pos_byte = PT_BYTE;
9af7511a
KH
1642 unsigned char *p = PT_ADDR, *endp, *stop;
1643
1644 if (forwardp)
1645 {
8f924df7
KH
1646 endp = (XINT (lim) == GPT) ? GPT_ADDR : CHAR_POS_ADDR (XINT (lim));
1647 stop = (pos < GPT && GPT < XINT (lim)) ? GPT_ADDR : endp;
9af7511a
KH
1648 }
1649 else
1650 {
8f924df7
KH
1651 endp = CHAR_POS_ADDR (XINT (lim));
1652 stop = (pos >= GPT && GPT > XINT (lim)) ? GAP_END_ADDR : endp;
9af7511a 1653 }
195d1361
RS
1654
1655 immediate_quit = 1;
b7dbcc19 1656 if (forwardp)
195d1361 1657 {
b7dbcc19 1658 if (multibyte)
8f924df7 1659 while (1)
b7dbcc19 1660 {
8f924df7 1661 int nbytes;
9af7511a 1662
8f924df7 1663 if (p >= stop)
9af7511a 1664 {
8f924df7 1665 if (p >= endp)
9af7511a 1666 break;
8f924df7
KH
1667 p = GAP_END_ADDR;
1668 stop = endp;
9af7511a 1669 }
8f924df7 1670 if (! fastmap[*p])
b7dbcc19 1671 break;
8f924df7 1672 c = STRING_CHAR_AND_LENGTH (p, MAX_MULTIBYTE_LENGTH, nbytes);
b7dbcc19 1673 if (! ASCII_CHAR_P (c))
9af7511a 1674 {
b7dbcc19
KH
1675 /* As we are looking at a multibyte character, we
1676 must look up the character in the table
1677 CHAR_RANGES. If there's no data in the table,
1678 that character is not what we want to skip. */
1679
1680 /* The following code do the right thing even if
1681 n_char_ranges is zero (i.e. no data in
1682 CHAR_RANGES). */
1683 for (i = 0; i < n_char_ranges; i += 2)
1684 if (c >= char_ranges[i] && c <= char_ranges[i + 1])
9af7511a 1685 break;
b7dbcc19
KH
1686 if (!(negate ^ (i < n_char_ranges)))
1687 break;
9af7511a 1688 }
8f924df7 1689 p += nbytes, pos++, pos_byte += nbytes;
b7dbcc19
KH
1690 }
1691 else
8f924df7
KH
1692 while (1)
1693 {
1694 if (p >= stop)
9af7511a 1695 {
8f924df7 1696 if (p >= endp)
9af7511a 1697 break;
8f924df7
KH
1698 p = GAP_END_ADDR;
1699 stop = endp;
9af7511a 1700 }
8f924df7
KH
1701 if (!fastmap[*p])
1702 break;
1703 p++, pos++, pos_byte++;
1704 }
195d1361
RS
1705 }
1706 else
1707 {
b7dbcc19 1708 if (multibyte)
8f924df7 1709 while (1)
b7dbcc19 1710 {
8f924df7 1711 unsigned char *prev_p;
9af7511a 1712
8f924df7 1713 if (p <= stop)
9af7511a 1714 {
8f924df7 1715 if (p <= endp)
9af7511a 1716 break;
8f924df7
KH
1717 p = GPT_ADDR;
1718 stop = endp;
9af7511a 1719 }
8f924df7
KH
1720 prev_p = p;
1721 while (--p >= stop && ! CHAR_HEAD_P (*p));
1722 if (! fastmap[*p])
b7dbcc19 1723 break;
8f924df7 1724 c = STRING_CHAR (p, MAX_MULTIBYTE_LENGTH);
b7dbcc19 1725 if (! ASCII_CHAR_P (c))
7e68b0ea 1726 {
b7dbcc19
KH
1727 /* See the comment in the previous similar code. */
1728 for (i = 0; i < n_char_ranges; i += 2)
1729 if (c >= char_ranges[i] && c <= char_ranges[i + 1])
1730 break;
1731 if (!(negate ^ (i < n_char_ranges)))
1732 break;
7e68b0ea 1733 }
8f924df7 1734 pos--, pos_byte -= prev_p - p;
b7dbcc19
KH
1735 }
1736 else
8f924df7
KH
1737 while (1)
1738 {
1739 if (p <= stop)
9af7511a 1740 {
8f924df7 1741 if (p <= endp)
9af7511a 1742 break;
8f924df7
KH
1743 p = GPT_ADDR;
1744 stop = endp;
9af7511a 1745 }
8f924df7
KH
1746 if (!fastmap[p[-1]])
1747 break;
1748 p--, pos--, pos_byte--;
1749 }
195d1361 1750 }
7e68b0ea 1751
b7dbcc19
KH
1752 SET_PT_BOTH (pos, pos_byte);
1753 immediate_quit = 0;
1754
1755 return make_number (PT - start_point);
1756 }
1757}
7e68b0ea 1758
b7dbcc19
KH
1759
1760static Lisp_Object
1761skip_syntaxes (forwardp, string, lim)
1762 int forwardp;
1763 Lisp_Object string, lim;
1764{
1765 register unsigned int c;
1766 unsigned char fastmap[0400];
1767 int negate = 0;
1768 register int i, i_byte;
1769 int multibyte;
1770 int size_byte;
1771 unsigned char *str;
1772
1773 CHECK_STRING (string);
1774
1775 if (NILP (lim))
1776 XSETINT (lim, forwardp ? ZV : BEGV);
1777 else
1778 CHECK_NUMBER_COERCE_MARKER (lim);
1779
1780 /* In any case, don't allow scan outside bounds of buffer. */
1781 if (XINT (lim) > ZV)
1782 XSETFASTINT (lim, ZV);
1783 if (XINT (lim) < BEGV)
1784 XSETFASTINT (lim, BEGV);
1785
ade8ee9e 1786 if (forwardp ? (PT >= XFASTINT (lim)) : (PT <= XFASTINT (lim)))
92eaa22e 1787 return Qnil;
8c6e735b 1788
b7dbcc19 1789 multibyte = (!NILP (current_buffer->enable_multibyte_characters)
92eaa22e 1790 && (XINT (lim) - PT != CHAR_TO_BYTE (XINT (lim)) - PT_BYTE));
b7dbcc19
KH
1791
1792 bzero (fastmap, sizeof fastmap);
1793
8f924df7
KH
1794 if (SBYTES (string) > SCHARS (string))
1795 /* As this is very rare case (syntax spec is ASCII only), don't
1796 consider efficiency. */
bc796a59
KH
1797 string = string_make_unibyte (string);
1798
8f924df7
KH
1799 str = SDATA (string);
1800 size_byte = SBYTES (string);
bc796a59 1801
b7dbcc19
KH
1802 i_byte = 0;
1803 if (i_byte < size_byte
8f924df7 1804 && SREF (string, 0) == '^')
b7dbcc19
KH
1805 {
1806 negate = 1; i_byte++;
1807 }
1808
b7dbcc19
KH
1809 /* Find the syntaxes specified and set their elements of fastmap. */
1810
1811 while (i_byte < size_byte)
1812 {
1813 c = str[i_byte++];
8f924df7 1814 fastmap[syntax_spec_code[c]] = 1;
b7dbcc19 1815 }
195d1361 1816
9690d026 1817 /* If ^ was the first character, complement the fastmap. */
195d1361
RS
1818 if (negate)
1819 for (i = 0; i < sizeof fastmap; i++)
9690d026 1820 fastmap[i] ^= 1;
195d1361
RS
1821
1822 {
1823 int start_point = PT;
1824 int pos = PT;
6a140a74 1825 int pos_byte = PT_BYTE;
8f924df7
KH
1826 unsigned char *p = PT_ADDR, *endp, *stop;
1827
1828 if (forwardp)
1829 {
1830 endp = (XINT (lim) == GPT) ? GPT_ADDR : CHAR_POS_ADDR (XINT (lim));
1831 stop = (pos < GPT && GPT < XINT (lim)) ? GPT_ADDR : endp;
1832 }
1833 else
1834 {
1835 endp = CHAR_POS_ADDR (XINT (lim));
1836 stop = (pos >= GPT && GPT > XINT (lim)) ? GAP_END_ADDR : endp;
1837 }
195d1361
RS
1838
1839 immediate_quit = 1;
b7dbcc19
KH
1840 SETUP_SYNTAX_TABLE (pos, forwardp ? 1 : -1);
1841 if (forwardp)
195d1361 1842 {
b7dbcc19 1843 if (multibyte)
195d1361 1844 {
8f924df7 1845 while (1)
8c6e735b 1846 {
8f924df7
KH
1847 int nbytes;
1848
1849 if (p >= stop)
1850 {
1851 if (p >= endp)
1852 break;
1853 p = GAP_END_ADDR;
1854 stop = endp;
1855 }
1856 c = STRING_CHAR_AND_LENGTH (p, MAX_MULTIBYTE_LENGTH, nbytes);
1857 if (! fastmap[(int) SYNTAX (c)])
8c6e735b 1858 break;
8f924df7 1859 p += nbytes, pos++, pos_byte += nbytes;
8c6e735b
KH
1860 UPDATE_SYNTAX_TABLE_FORWARD (pos);
1861 }
195d1361
RS
1862 }
1863 else
1864 {
8c6e735b 1865 while (1)
7e68b0ea 1866 {
8f924df7
KH
1867 if (p >= stop)
1868 {
1869 if (p >= endp)
1870 break;
1871 p = GAP_END_ADDR;
1872 stop = endp;
1873 }
1874 if (! fastmap[(int) SYNTAX (*p)])
8c6e735b 1875 break;
8f924df7 1876 p++, pos++, pos_byte++;
b7dbcc19 1877 UPDATE_SYNTAX_TABLE_FORWARD (pos);
195d1361
RS
1878 }
1879 }
1880 }
1881 else
1882 {
b7dbcc19 1883 if (multibyte)
195d1361 1884 {
8c6e735b 1885 while (1)
b7dbcc19 1886 {
8f924df7
KH
1887 unsigned char *prev_p;
1888
1889 if (p <= stop)
b7dbcc19 1890 {
8f924df7
KH
1891 if (p <= endp)
1892 break;
1893 p = GPT_ADDR;
1894 stop = endp;
b7dbcc19 1895 }
8f924df7
KH
1896 prev_p = p;
1897 while (--p >= stop && ! CHAR_HEAD_P (*p));
1898 c = STRING_CHAR (p, MAX_MULTIBYTE_LENGTH);
1899 if (! fastmap[(int) SYNTAX (c)])
1900 break;
1901 pos--, pos_byte -= prev_p - p;
1902 UPDATE_SYNTAX_TABLE_BACKWARD (pos);
b7dbcc19 1903 }
195d1361
RS
1904 }
1905 else
1906 {
8c6e735b
KH
1907 while (1)
1908 {
8f924df7
KH
1909 if (p <= stop)
1910 {
1911 if (p <= endp)
1912 break;
1913 p = GPT_ADDR;
1914 stop = endp;
1915 }
1916 if (! fastmap[(int) SYNTAX (p[-1])])
8c6e735b 1917 break;
8f924df7 1918 p--, pos--, pos_byte--;
8c6e735b
KH
1919 UPDATE_SYNTAX_TABLE_BACKWARD (pos - 1);
1920 }
195d1361
RS
1921 }
1922 }
6a140a74
RS
1923
1924 SET_PT_BOTH (pos, pos_byte);
195d1361
RS
1925 immediate_quit = 0;
1926
1927 return make_number (PT - start_point);
1928 }
1929}
1930\f
95ff8dfc
RS
1931/* Jump over a comment, assuming we are at the beginning of one.
1932 FROM is the current position.
1933 FROM_BYTE is the bytepos corresponding to FROM.
1934 Do not move past STOP (a charpos).
1935 The comment over which we have to jump is of style STYLE
1936 (either SYNTAX_COMMENT_STYLE(foo) or ST_COMMENT_STYLE).
1937 NESTING should be positive to indicate the nesting at the beginning
1938 for nested comments and should be zero or negative else.
1939 ST_COMMENT_STYLE cannot be nested.
1940 PREV_SYNTAX is the SYNTAX_WITH_FLAGS of the previous character
1941 (or 0 If the search cannot start in the middle of a two-character).
1942
1943 If successful, return 1 and store the charpos of the comment's end
1944 into *CHARPOS_PTR and the corresponding bytepos into *BYTEPOS_PTR.
1945 Else, return 0 and store the charpos STOP into *CHARPOS_PTR, the
1946 corresponding bytepos into *BYTEPOS_PTR and the current nesting
1947 (as defined for state.incomment) in *INCOMMENT_PTR.
1948
1949 The comment end is the last character of the comment rather than the
1950 character just after the comment.
1951
1952 Global syntax data is assumed to initially be valid for FROM and
1953 remains valid for forward search starting at the returned position. */
1954
1955static int
1956forw_comment (from, from_byte, stop, nesting, style, prev_syntax,
1957 charpos_ptr, bytepos_ptr, incomment_ptr)
1958 int from, from_byte, stop;
1959 int nesting, style, prev_syntax;
1960 int *charpos_ptr, *bytepos_ptr, *incomment_ptr;
1961{
1962 register int c, c1;
1963 register enum syntaxcode code;
1964 register int syntax;
1965
1966 if (nesting <= 0) nesting = -1;
1967
1968 /* Enter the loop in the middle so that we find
1969 a 2-char comment ender if we start in the middle of it. */
1970 syntax = prev_syntax;
1971 if (syntax != 0) goto forw_incomment;
1972
1973 while (1)
1974 {
1975 if (from == stop)
1976 {
1977 *incomment_ptr = nesting;
1978 *charpos_ptr = from;
1979 *bytepos_ptr = from_byte;
1980 return 0;
1981 }
8f924df7 1982 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
95ff8dfc
RS
1983 syntax = SYNTAX_WITH_FLAGS (c);
1984 code = syntax & 0xff;
1985 if (code == Sendcomment
1986 && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style
abf8a9ff
SM
1987 && (SYNTAX_FLAGS_COMMENT_NESTED (syntax) ?
1988 (nesting > 0 && --nesting == 0) : nesting < 0))
95ff8dfc
RS
1989 /* we have encountered a comment end of the same style
1990 as the comment sequence which began this comment
1991 section */
1992 break;
1993 if (code == Scomment_fence
1994 && style == ST_COMMENT_STYLE)
1995 /* we have encountered a comment end of the same style
1996 as the comment sequence which began this comment
1997 section. */
1998 break;
1999 if (nesting > 0
2000 && code == Scomment
abf8a9ff 2001 && SYNTAX_FLAGS_COMMENT_NESTED (syntax)
95ff8dfc
RS
2002 && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style)
2003 /* we have encountered a nested comment of the same style
2004 as the comment sequence which began this comment section */
2005 nesting++;
2006 INC_BOTH (from, from_byte);
2007 UPDATE_SYNTAX_TABLE_FORWARD (from);
7d0393cf 2008
95ff8dfc
RS
2009 forw_incomment:
2010 if (from < stop && SYNTAX_FLAGS_COMEND_FIRST (syntax)
2011 && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style
8f924df7 2012 && (c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte),
abf8a9ff
SM
2013 SYNTAX_COMEND_SECOND (c1))
2014 && ((SYNTAX_FLAGS_COMMENT_NESTED (syntax) ||
2015 SYNTAX_COMMENT_NESTED (c1)) ? nesting > 0 : nesting < 0))
4ffe723b
GM
2016 {
2017 if (--nesting <= 0)
2018 /* we have encountered a comment end of the same style
2019 as the comment sequence which began this comment
2020 section */
2021 break;
2022 else
2023 {
2024 INC_BOTH (from, from_byte);
2025 UPDATE_SYNTAX_TABLE_FORWARD (from);
2026 }
2027 }
95ff8dfc
RS
2028 if (nesting > 0
2029 && from < stop
2030 && SYNTAX_FLAGS_COMSTART_FIRST (syntax)
8f924df7 2031 && (c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte),
95ff8dfc 2032 SYNTAX_COMMENT_STYLE (c1) == style
abf8a9ff
SM
2033 && SYNTAX_COMSTART_SECOND (c1))
2034 && (SYNTAX_FLAGS_COMMENT_NESTED (syntax) ||
2035 SYNTAX_COMMENT_NESTED (c1)))
95ff8dfc
RS
2036 /* we have encountered a nested comment of the same style
2037 as the comment sequence which began this comment
2038 section */
2039 {
2040 INC_BOTH (from, from_byte);
2041 UPDATE_SYNTAX_TABLE_FORWARD (from);
2042 nesting++;
2043 }
2044 }
2045 *charpos_ptr = from;
2046 *bytepos_ptr = from_byte;
2047 return 1;
2048}
2049
b3cfe0c8 2050DEFUN ("forward-comment", Fforward_comment, Sforward_comment, 1, 1, 0,
177c0ea7 2051 doc: /*
ee648c11 2052Move forward across up to COUNT comments. If COUNT is negative, move backward.
fdb82f93
PJ
2053Stop scanning if we find something other than a comment or whitespace.
2054Set point to where scanning stops.
ee648c11 2055If COUNT comments are found as expected, with nothing except whitespace
fdb82f93
PJ
2056between them, return t; otherwise return nil. */)
2057 (count)
840f481c 2058 Lisp_Object count;
b3cfe0c8
RS
2059{
2060 register int from;
6a140a74 2061 int from_byte;
b3cfe0c8 2062 register int stop;
8ea151b2 2063 register int c, c1;
b3cfe0c8
RS
2064 register enum syntaxcode code;
2065 int comstyle = 0; /* style of comment encountered */
95ff8dfc 2066 int comnested = 0; /* whether the comment is nestable or not */
be720845 2067 int found;
840f481c 2068 int count1;
6a140a74 2069 int out_charpos, out_bytepos;
95ff8dfc 2070 int dummy;
840f481c 2071
b7826503 2072 CHECK_NUMBER (count);
840f481c 2073 count1 = XINT (count);
195d1361 2074 stop = count1 > 0 ? ZV : BEGV;
b3cfe0c8
RS
2075
2076 immediate_quit = 1;
2077 QUIT;
2078
2079 from = PT;
6a140a74 2080 from_byte = PT_BYTE;
b3cfe0c8 2081
195d1361 2082 SETUP_SYNTAX_TABLE (from, count1);
840f481c 2083 while (count1 > 0)
b3cfe0c8 2084 {
04882296 2085 do
b3cfe0c8 2086 {
f902a008
RS
2087 int comstart_first;
2088
04882296
KH
2089 if (from == stop)
2090 {
ef316cf0 2091 SET_PT_BOTH (from, from_byte);
b7e6e612 2092 immediate_quit = 0;
04882296
KH
2093 return Qnil;
2094 }
b7dbcc19 2095 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
b3cfe0c8 2096 code = SYNTAX (c);
f902a008 2097 comstart_first = SYNTAX_COMSTART_FIRST (c);
95ff8dfc 2098 comnested = SYNTAX_COMMENT_NESTED (c);
e6365855 2099 comstyle = SYNTAX_COMMENT_STYLE (c);
6a140a74 2100 INC_BOTH (from, from_byte);
f902a008 2101 UPDATE_SYNTAX_TABLE_FORWARD (from);
f902a008 2102 if (from < stop && comstart_first
b7dbcc19 2103 && (c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte),
8ea151b2 2104 SYNTAX_COMSTART_SECOND (c1)))
b3cfe0c8 2105 {
7d0393cf 2106 /* We have encountered a comment start sequence and we
7fc8191e 2107 are ignoring all text inside comments. We must record
b3cfe0c8
RS
2108 the comment style this sequence begins so that later,
2109 only a comment end of the same style actually ends
7fc8191e 2110 the comment section. */
b3cfe0c8 2111 code = Scomment;
8ea151b2 2112 comstyle = SYNTAX_COMMENT_STYLE (c1);
95ff8dfc 2113 comnested = comnested || SYNTAX_COMMENT_NESTED (c1);
6a140a74 2114 INC_BOTH (from, from_byte);
95ff8dfc 2115 UPDATE_SYNTAX_TABLE_FORWARD (from);
b3cfe0c8 2116 }
04882296 2117 }
abf8a9ff 2118 while (code == Swhitespace || (code == Sendcomment && c == '\n'));
6a140a74 2119
da71ab91
KH
2120 if (code == Scomment_fence)
2121 comstyle = ST_COMMENT_STYLE;
2122 else if (code != Scomment)
04882296
KH
2123 {
2124 immediate_quit = 0;
6a140a74 2125 DEC_BOTH (from, from_byte);
ef316cf0 2126 SET_PT_BOTH (from, from_byte);
04882296
KH
2127 return Qnil;
2128 }
2129 /* We're at the start of a comment. */
95ff8dfc
RS
2130 found = forw_comment (from, from_byte, stop, comnested, comstyle, 0,
2131 &out_charpos, &out_bytepos, &dummy);
2132 from = out_charpos; from_byte = out_bytepos;
2133 if (!found)
04882296 2134 {
95ff8dfc
RS
2135 immediate_quit = 0;
2136 SET_PT_BOTH (from, from_byte);
2137 return Qnil;
b3cfe0c8 2138 }
95ff8dfc
RS
2139 INC_BOTH (from, from_byte);
2140 UPDATE_SYNTAX_TABLE_FORWARD (from);
04882296 2141 /* We have skipped one comment. */
840f481c 2142 count1--;
b3cfe0c8
RS
2143 }
2144
840f481c 2145 while (count1 < 0)
b3cfe0c8 2146 {
b9145dbb 2147 while (1)
b3cfe0c8 2148 {
c65adb44 2149 int quoted;
f902a008 2150
b9145dbb
RS
2151 if (from <= stop)
2152 {
6a140a74 2153 SET_PT_BOTH (BEGV, BEGV_BYTE);
b9145dbb
RS
2154 immediate_quit = 0;
2155 return Qnil;
2156 }
b3cfe0c8 2157
6a140a74 2158 DEC_BOTH (from, from_byte);
f902a008 2159 /* char_quoted does UPDATE_SYNTAX_TABLE_BACKWARD (from). */
6a140a74 2160 quoted = char_quoted (from, from_byte);
b7dbcc19 2161 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
b3cfe0c8
RS
2162 code = SYNTAX (c);
2163 comstyle = 0;
95ff8dfc 2164 comnested = SYNTAX_COMMENT_NESTED (c);
7fc8191e
RS
2165 if (code == Sendcomment)
2166 comstyle = SYNTAX_COMMENT_STYLE (c);
b3cfe0c8 2167 if (from > stop && SYNTAX_COMEND_SECOND (c)
f902a008 2168 && prev_char_comend_first (from, from_byte)
89c6809a 2169 && !char_quoted (from - 1, dec_bytepos (from_byte)))
b3cfe0c8 2170 {
7fc8191e 2171 /* We must record the comment style encountered so that
b3cfe0c8 2172 later, we can match only the proper comment begin
7fc8191e 2173 sequence of the same style. */
6a140a74 2174 DEC_BOTH (from, from_byte);
f902a008
RS
2175 code = Sendcomment;
2176 /* Calling char_quoted, above, set up global syntax position
2177 at the new value of FROM. */
b7dbcc19 2178 c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
95ff8dfc
RS
2179 comstyle = SYNTAX_COMMENT_STYLE (c1);
2180 comnested = comnested || SYNTAX_COMMENT_NESTED (c1);
b3cfe0c8
RS
2181 }
2182
195d1361
RS
2183 if (code == Scomment_fence)
2184 {
2185 /* Skip until first preceding unquoted comment_fence. */
6a140a74 2186 int found = 0, ini = from, ini_byte = from_byte;
7d0393cf 2187
6a140a74 2188 while (1)
195d1361 2189 {
6a140a74
RS
2190 DEC_BOTH (from, from_byte);
2191 if (from == stop)
2192 break;
195d1361 2193 UPDATE_SYNTAX_TABLE_BACKWARD (from);
b7dbcc19 2194 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
6a140a74 2195 if (SYNTAX (c) == Scomment_fence
7d0393cf 2196 && !char_quoted (from, from_byte))
195d1361 2197 {
7d0393cf 2198 found = 1;
195d1361
RS
2199 break;
2200 }
2201 }
2202 if (found == 0)
2203 {
2204 from = ini; /* Set point to ini + 1. */
6a140a74 2205 from_byte = ini_byte;
195d1361
RS
2206 goto leave;
2207 }
2208 }
2209 else if (code == Sendcomment)
b3cfe0c8 2210 {
95ff8dfc 2211 found = back_comment (from, from_byte, stop, comnested, comstyle,
6a140a74 2212 &out_charpos, &out_bytepos);
1aa963c8
SM
2213 if (found == -1)
2214 {
abf8a9ff
SM
2215 if (c == '\n')
2216 /* This end-of-line is not an end-of-comment.
2217 Treat it like a whitespace.
2218 CC-mode (and maybe others) relies on this behavior. */
2219 ;
2220 else
2221 {
2222 /* Failure: we should go back to the end of this
2223 not-quite-endcomment. */
2224 if (SYNTAX(c) != code)
2225 /* It was a two-char Sendcomment. */
2226 INC_BOTH (from, from_byte);
2227 goto leave;
2228 }
1aa963c8 2229 }
2b927d02 2230 else
abf8a9ff
SM
2231 {
2232 /* We have skipped one comment. */
2233 from = out_charpos, from_byte = out_bytepos;
2234 break;
2235 }
b3cfe0c8 2236 }
bb0de084 2237 else if (code != Swhitespace || quoted)
b3cfe0c8 2238 {
195d1361 2239 leave:
b3cfe0c8 2240 immediate_quit = 0;
6a140a74 2241 INC_BOTH (from, from_byte);
ef316cf0 2242 SET_PT_BOTH (from, from_byte);
b3cfe0c8
RS
2243 return Qnil;
2244 }
2245 }
2246
840f481c 2247 count1++;
b3cfe0c8
RS
2248 }
2249
ef316cf0 2250 SET_PT_BOTH (from, from_byte);
b3cfe0c8
RS
2251 immediate_quit = 0;
2252 return Qt;
2253}
2254\f
b7dbcc19 2255/* Return syntax code of character C if C is an ASCII character
db9f48c3 2256 or `multibyte_symbol_p' is zero. Otherwise, return Ssymbol. */
bd25db08 2257
b7dbcc19
KH
2258#define SYNTAX_WITH_MULTIBYTE_CHECK(c) \
2259 ((ASCII_CHAR_P (c) || !multibyte_symbol_p) \
bd25db08
KH
2260 ? SYNTAX (c) : Ssymbol)
2261
6a140a74 2262static Lisp_Object
8489eb67
RS
2263scan_lists (from, count, depth, sexpflag)
2264 register int from;
2265 int count, depth, sexpflag;
2266{
2267 Lisp_Object val;
195d1361 2268 register int stop = count > 0 ? ZV : BEGV;
93da5fff
KH
2269 register int c, c1;
2270 int stringterm;
8489eb67
RS
2271 int quoted;
2272 int mathexit = 0;
93da5fff 2273 register enum syntaxcode code, temp_code;
195d1361 2274 int min_depth = depth; /* Err out if depth gets less than this. */
e5d4f4dc 2275 int comstyle = 0; /* style of comment encountered */
95ff8dfc 2276 int comnested = 0; /* whether the comment is nestable or not */
93da5fff 2277 int temp_pos;
7bf5e9e4 2278 int last_good = from;
195d1361 2279 int found;
c3907a7e 2280 int from_byte;
6a140a74 2281 int out_bytepos, out_charpos;
95ff8dfc 2282 int temp, dummy;
bd25db08 2283 int multibyte_symbol_p = sexpflag && multibyte_syntax_as_symbol;
8489eb67
RS
2284
2285 if (depth > 0) min_depth = 0;
2286
c3907a7e
RS
2287 if (from > ZV) from = ZV;
2288 if (from < BEGV) from = BEGV;
2289
2290 from_byte = CHAR_TO_BYTE (from);
2291
8489eb67
RS
2292 immediate_quit = 1;
2293 QUIT;
2294
195d1361 2295 SETUP_SYNTAX_TABLE (from, count);
8489eb67
RS
2296 while (count > 0)
2297 {
8489eb67
RS
2298 while (from < stop)
2299 {
f902a008 2300 int comstart_first, prefix;
195d1361 2301 UPDATE_SYNTAX_TABLE_FORWARD (from);
b7dbcc19 2302 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
bd25db08 2303 code = SYNTAX_WITH_MULTIBYTE_CHECK (c);
f902a008 2304 comstart_first = SYNTAX_COMSTART_FIRST (c);
95ff8dfc 2305 comnested = SYNTAX_COMMENT_NESTED (c);
e6365855 2306 comstyle = SYNTAX_COMMENT_STYLE (c);
f902a008 2307 prefix = SYNTAX_PREFIX (c);
7bf5e9e4
RS
2308 if (depth == min_depth)
2309 last_good = from;
6a140a74 2310 INC_BOTH (from, from_byte);
195d1361 2311 UPDATE_SYNTAX_TABLE_FORWARD (from);
f902a008 2312 if (from < stop && comstart_first
b7dbcc19 2313 && SYNTAX_COMSTART_SECOND (FETCH_CHAR_AS_MULTIBYTE (from_byte))
8489eb67 2314 && parse_sexp_ignore_comments)
e5d4f4dc 2315 {
7d0393cf 2316 /* we have encountered a comment start sequence and we
195d1361 2317 are ignoring all text inside comments. We must record
e5d4f4dc
RS
2318 the comment style this sequence begins so that later,
2319 only a comment end of the same style actually ends
2320 the comment section */
2321 code = Scomment;
b7dbcc19 2322 c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
95ff8dfc
RS
2323 comstyle = SYNTAX_COMMENT_STYLE (c1);
2324 comnested = comnested || SYNTAX_COMMENT_NESTED (c1);
6a140a74 2325 INC_BOTH (from, from_byte);
f902a008 2326 UPDATE_SYNTAX_TABLE_FORWARD (from);
e5d4f4dc 2327 }
7d0393cf 2328
f902a008 2329 if (prefix)
8489eb67
RS
2330 continue;
2331
0220c518 2332 switch (SWITCH_ENUM_CAST (code))
8489eb67
RS
2333 {
2334 case Sescape:
2335 case Scharquote:
2336 if (from == stop) goto lose;
6a140a74 2337 INC_BOTH (from, from_byte);
8489eb67
RS
2338 /* treat following character as a word constituent */
2339 case Sword:
2340 case Ssymbol:
2341 if (depth || !sexpflag) break;
195d1361 2342 /* This word counts as a sexp; return at end of it. */
8489eb67
RS
2343 while (from < stop)
2344 {
195d1361 2345 UPDATE_SYNTAX_TABLE_FORWARD (from);
7c7ca3d2
RS
2346
2347 /* Some compilers can't handle this inside the switch. */
b7dbcc19 2348 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
bd25db08 2349 temp = SYNTAX_WITH_MULTIBYTE_CHECK (c);
7c7ca3d2 2350 switch (temp)
8489eb67
RS
2351 {
2352 case Scharquote:
2353 case Sescape:
6a140a74 2354 INC_BOTH (from, from_byte);
8489eb67
RS
2355 if (from == stop) goto lose;
2356 break;
2357 case Sword:
2358 case Ssymbol:
2359 case Squote:
2360 break;
2361 default:
2362 goto done;
2363 }
6a140a74 2364 INC_BOTH (from, from_byte);
8489eb67
RS
2365 }
2366 goto done;
2367
195d1361 2368 case Scomment_fence:
95ff8dfc
RS
2369 comstyle = ST_COMMENT_STYLE;
2370 /* FALLTHROUGH */
2371 case Scomment:
8489eb67 2372 if (!parse_sexp_ignore_comments) break;
95ff8dfc
RS
2373 UPDATE_SYNTAX_TABLE_FORWARD (from);
2374 found = forw_comment (from, from_byte, stop,
2375 comnested, comstyle, 0,
2376 &out_charpos, &out_bytepos, &dummy);
2377 from = out_charpos, from_byte = out_bytepos;
2378 if (!found)
8489eb67 2379 {
95ff8dfc
RS
2380 if (depth == 0)
2381 goto done;
2382 goto lose;
8489eb67 2383 }
95ff8dfc
RS
2384 INC_BOTH (from, from_byte);
2385 UPDATE_SYNTAX_TABLE_FORWARD (from);
8489eb67
RS
2386 break;
2387
2388 case Smath:
2389 if (!sexpflag)
2390 break;
b7dbcc19 2391 if (from != stop && c == FETCH_CHAR_AS_MULTIBYTE (from_byte))
6a140a74
RS
2392 {
2393 INC_BOTH (from, from_byte);
2394 }
8489eb67
RS
2395 if (mathexit)
2396 {
2397 mathexit = 0;
2398 goto close1;
2399 }
2400 mathexit = 1;
2401
2402 case Sopen:
2403 if (!++depth) goto done;
2404 break;
2405
2406 case Sclose:
2407 close1:
2408 if (!--depth) goto done;
2409 if (depth < min_depth)
7bf5e9e4
RS
2410 Fsignal (Qscan_error,
2411 Fcons (build_string ("Containing expression ends prematurely"),
2412 Fcons (make_number (last_good),
2413 Fcons (make_number (from), Qnil))));
8489eb67
RS
2414 break;
2415
2416 case Sstring:
195d1361 2417 case Sstring_fence:
ef316cf0 2418 temp_pos = dec_bytepos (from_byte);
b7dbcc19 2419 stringterm = FETCH_CHAR_AS_MULTIBYTE (temp_pos);
8489eb67
RS
2420 while (1)
2421 {
2422 if (from >= stop) goto lose;
195d1361 2423 UPDATE_SYNTAX_TABLE_FORWARD (from);
b7dbcc19 2424 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
42ebfa31
SM
2425 if (code == Sstring
2426 ? (c == stringterm
2427 && SYNTAX_WITH_MULTIBYTE_CHECK (c) == Sstring)
bd25db08 2428 : SYNTAX_WITH_MULTIBYTE_CHECK (c) == Sstring_fence)
195d1361 2429 break;
7c7ca3d2
RS
2430
2431 /* Some compilers can't handle this inside the switch. */
bd25db08 2432 temp = SYNTAX_WITH_MULTIBYTE_CHECK (c);
7c7ca3d2 2433 switch (temp)
8489eb67
RS
2434 {
2435 case Scharquote:
2436 case Sescape:
6a140a74 2437 INC_BOTH (from, from_byte);
8489eb67 2438 }
6a140a74 2439 INC_BOTH (from, from_byte);
8489eb67 2440 }
6a140a74 2441 INC_BOTH (from, from_byte);
8489eb67
RS
2442 if (!depth && sexpflag) goto done;
2443 break;
240c43e8
SM
2444 default:
2445 /* Ignore whitespace, punctuation, quote, endcomment. */
2446 break;
8489eb67
RS
2447 }
2448 }
2449
2450 /* Reached end of buffer. Error if within object, return nil if between */
2451 if (depth) goto lose;
2452
2453 immediate_quit = 0;
2454 return Qnil;
2455
2456 /* End of object reached */
2457 done:
2458 count--;
2459 }
2460
2461 while (count < 0)
2462 {
8489eb67
RS
2463 while (from > stop)
2464 {
6a140a74 2465 DEC_BOTH (from, from_byte);
195d1361 2466 UPDATE_SYNTAX_TABLE_BACKWARD (from);
b7dbcc19 2467 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
bd25db08 2468 code = SYNTAX_WITH_MULTIBYTE_CHECK (c);
7bf5e9e4
RS
2469 if (depth == min_depth)
2470 last_good = from;
7fc8191e 2471 comstyle = 0;
95ff8dfc 2472 comnested = SYNTAX_COMMENT_NESTED (c);
7fc8191e
RS
2473 if (code == Sendcomment)
2474 comstyle = SYNTAX_COMMENT_STYLE (c);
8489eb67 2475 if (from > stop && SYNTAX_COMEND_SECOND (c)
1674d9a2 2476 && prev_char_comend_first (from, from_byte)
8489eb67 2477 && parse_sexp_ignore_comments)
e5d4f4dc 2478 {
f902a008 2479 /* We must record the comment style encountered so that
e5d4f4dc 2480 later, we can match only the proper comment begin
f902a008 2481 sequence of the same style. */
6a140a74 2482 DEC_BOTH (from, from_byte);
f902a008
RS
2483 UPDATE_SYNTAX_TABLE_BACKWARD (from);
2484 code = Sendcomment;
b7dbcc19 2485 c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
95ff8dfc
RS
2486 comstyle = SYNTAX_COMMENT_STYLE (c1);
2487 comnested = comnested || SYNTAX_COMMENT_NESTED (c1);
e5d4f4dc 2488 }
7d0393cf 2489
9828a477 2490 /* Quoting turns anything except a comment-ender
e6365855 2491 into a word character. Note that this cannot be true
f902a008 2492 if we decremented FROM in the if-statement above. */
9828a477 2493 if (code != Sendcomment && char_quoted (from, from_byte))
240c43e8
SM
2494 {
2495 DEC_BOTH (from, from_byte);
2496 code = Sword;
2497 }
9828a477 2498 else if (SYNTAX_PREFIX (c))
8489eb67
RS
2499 continue;
2500
9828a477 2501 switch (SWITCH_ENUM_CAST (code))
8489eb67
RS
2502 {
2503 case Sword:
2504 case Ssymbol:
9828a477
KH
2505 case Sescape:
2506 case Scharquote:
8489eb67 2507 if (depth || !sexpflag) break;
195d1361
RS
2508 /* This word counts as a sexp; count object finished
2509 after passing it. */
8489eb67
RS
2510 while (from > stop)
2511 {
6a140a74 2512 temp_pos = from_byte;
ef316cf0
RS
2513 if (! NILP (current_buffer->enable_multibyte_characters))
2514 DEC_POS (temp_pos);
2515 else
2516 temp_pos--;
6a140a74 2517 UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
b7dbcc19 2518 c1 = FETCH_CHAR_AS_MULTIBYTE (temp_pos);
bd25db08 2519 temp_code = SYNTAX_WITH_MULTIBYTE_CHECK (c1);
9828a477
KH
2520 /* Don't allow comment-end to be quoted. */
2521 if (temp_code == Sendcomment)
2522 goto done2;
6a140a74 2523 quoted = char_quoted (from - 1, temp_pos);
8489eb67 2524 if (quoted)
93da5fff 2525 {
6a140a74 2526 DEC_BOTH (from, from_byte);
ef316cf0 2527 temp_pos = dec_bytepos (temp_pos);
6a140a74 2528 UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
93da5fff 2529 }
b7dbcc19 2530 c1 = FETCH_CHAR_AS_MULTIBYTE (temp_pos);
bd25db08 2531 temp_code = SYNTAX_WITH_MULTIBYTE_CHECK (c1);
93da5fff
KH
2532 if (! (quoted || temp_code == Sword
2533 || temp_code == Ssymbol
2534 || temp_code == Squote))
8489eb67 2535 goto done2;
6a140a74 2536 DEC_BOTH (from, from_byte);
8489eb67
RS
2537 }
2538 goto done2;
2539
2540 case Smath:
2541 if (!sexpflag)
2542 break;
ef316cf0 2543 temp_pos = dec_bytepos (from_byte);
6a140a74 2544 UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
b7dbcc19 2545 if (from != stop && c == FETCH_CHAR_AS_MULTIBYTE (temp_pos))
6a140a74 2546 DEC_BOTH (from, from_byte);
8489eb67
RS
2547 if (mathexit)
2548 {
2549 mathexit = 0;
2550 goto open2;
2551 }
2552 mathexit = 1;
2553
2554 case Sclose:
2555 if (!++depth) goto done2;
2556 break;
2557
2558 case Sopen:
2559 open2:
2560 if (!--depth) goto done2;
2561 if (depth < min_depth)
7bf5e9e4
RS
2562 Fsignal (Qscan_error,
2563 Fcons (build_string ("Containing expression ends prematurely"),
2564 Fcons (make_number (last_good),
2565 Fcons (make_number (from), Qnil))));
8489eb67
RS
2566 break;
2567
2568 case Sendcomment:
2569 if (!parse_sexp_ignore_comments)
2570 break;
95ff8dfc 2571 found = back_comment (from, from_byte, stop, comnested, comstyle,
6a140a74 2572 &out_charpos, &out_bytepos);
1aa963c8
SM
2573 /* FIXME: if found == -1, then it really wasn't a comment-end.
2574 For single-char Sendcomment, we can't do much about it apart
2575 from skipping the char.
2576 For 2-char endcomments, we could try again, taking both
2577 chars as separate entities, but it's a lot of trouble
2578 for very little gain, so we don't bother either. -sm */
6a140a74
RS
2579 if (found != -1)
2580 from = out_charpos, from_byte = out_bytepos;
8489eb67
RS
2581 break;
2582
195d1361
RS
2583 case Scomment_fence:
2584 case Sstring_fence:
2585 while (1)
2586 {
195d1361 2587 if (from == stop) goto lose;
6b61353c 2588 DEC_BOTH (from, from_byte);
195d1361 2589 UPDATE_SYNTAX_TABLE_BACKWARD (from);
7d0393cf 2590 if (!char_quoted (from, from_byte)
b7dbcc19 2591 && (c = FETCH_CHAR_AS_MULTIBYTE (from_byte),
bd25db08 2592 SYNTAX_WITH_MULTIBYTE_CHECK (c) == code))
195d1361
RS
2593 break;
2594 }
2595 if (code == Sstring_fence && !depth && sexpflag) goto done2;
2596 break;
7d0393cf 2597
8489eb67 2598 case Sstring:
b7dbcc19 2599 stringterm = FETCH_CHAR_AS_MULTIBYTE (from_byte);
8489eb67
RS
2600 while (1)
2601 {
2602 if (from == stop) goto lose;
6b61353c
KH
2603 DEC_BOTH (from, from_byte);
2604 UPDATE_SYNTAX_TABLE_BACKWARD (from);
2605 if (!char_quoted (from, from_byte)
2606 && (stringterm
2607 == (c = FETCH_CHAR_AS_MULTIBYTE (from_byte)))
42ebfa31 2608 && SYNTAX_WITH_MULTIBYTE_CHECK (c) == Sstring)
8489eb67 2609 break;
8489eb67 2610 }
8489eb67
RS
2611 if (!depth && sexpflag) goto done2;
2612 break;
240c43e8
SM
2613 default:
2614 /* Ignore whitespace, punctuation, quote, endcomment. */
2615 break;
8489eb67
RS
2616 }
2617 }
2618
2619 /* Reached start of buffer. Error if within object, return nil if between */
2620 if (depth) goto lose;
2621
2622 immediate_quit = 0;
2623 return Qnil;
2624
2625 done2:
2626 count++;
2627 }
2628
2629
2630 immediate_quit = 0;
1e142fb7 2631 XSETFASTINT (val, from);
8489eb67
RS
2632 return val;
2633
2634 lose:
7bf5e9e4
RS
2635 Fsignal (Qscan_error,
2636 Fcons (build_string ("Unbalanced parentheses"),
2637 Fcons (make_number (last_good),
2638 Fcons (make_number (from), Qnil))));
2639
8489eb67
RS
2640 /* NOTREACHED */
2641}
2642
8489eb67 2643DEFUN ("scan-lists", Fscan_lists, Sscan_lists, 3, 3, 0,
fdb82f93
PJ
2644 doc: /* Scan from character number FROM by COUNT lists.
2645Returns the character number of the position thus found.
2646
2647If DEPTH is nonzero, paren depth begins counting from that value,
2648only places where the depth in parentheses becomes zero
2649are candidates for stopping; COUNT such places are counted.
2650Thus, a positive value for DEPTH means go out levels.
2651
2652Comments are ignored if `parse-sexp-ignore-comments' is non-nil.
2653
2654If the beginning or end of (the accessible part of) the buffer is reached
2655and the depth is wrong, an error is signaled.
2656If the depth is right but the count is not used up, nil is returned. */)
2657 (from, count, depth)
8489eb67
RS
2658 Lisp_Object from, count, depth;
2659{
b7826503
PJ
2660 CHECK_NUMBER (from);
2661 CHECK_NUMBER (count);
2662 CHECK_NUMBER (depth);
8489eb67
RS
2663
2664 return scan_lists (XINT (from), XINT (count), XINT (depth), 0);
2665}
2666
2667DEFUN ("scan-sexps", Fscan_sexps, Sscan_sexps, 2, 2, 0,
fdb82f93
PJ
2668 doc: /* Scan from character number FROM by COUNT balanced expressions.
2669If COUNT is negative, scan backwards.
2670Returns the character number of the position thus found.
2671
2672Comments are ignored if `parse-sexp-ignore-comments' is non-nil.
2673
2674If the beginning or end of (the accessible part of) the buffer is reached
2675in the middle of a parenthetical grouping, an error is signaled.
2676If the beginning or end is reached between groupings
2677but before count is used up, nil is returned. */)
2678 (from, count)
8489eb67
RS
2679 Lisp_Object from, count;
2680{
b7826503
PJ
2681 CHECK_NUMBER (from);
2682 CHECK_NUMBER (count);
8489eb67
RS
2683
2684 return scan_lists (XINT (from), XINT (count), 0, 1);
2685}
2686
2687DEFUN ("backward-prefix-chars", Fbackward_prefix_chars, Sbackward_prefix_chars,
fdb82f93
PJ
2688 0, 0, 0,
2689 doc: /* Move point backward over any number of chars with prefix syntax.
2690This includes chars with "quote" or "prefix" syntax (' or p). */)
2691 ()
8489eb67
RS
2692{
2693 int beg = BEGV;
6a140a74
RS
2694 int opoint = PT;
2695 int opoint_byte = PT_BYTE;
6ec8bbd2 2696 int pos = PT;
6a140a74 2697 int pos_byte = PT_BYTE;
93da5fff 2698 int c;
93da5fff 2699
7d0393cf 2700 if (pos <= beg)
195d1361 2701 {
f902a008
RS
2702 SET_PT_BOTH (opoint, opoint_byte);
2703
2704 return Qnil;
195d1361 2705 }
8489eb67 2706
f902a008
RS
2707 SETUP_SYNTAX_TABLE (pos, -1);
2708
6a140a74
RS
2709 DEC_BOTH (pos, pos_byte);
2710
1fd3172d 2711 while (!char_quoted (pos, pos_byte)
195d1361 2712 /* Previous statement updates syntax table. */
b7dbcc19 2713 && ((c = FETCH_CHAR_AS_MULTIBYTE (pos_byte), SYNTAX (c) == Squote)
93da5fff
KH
2714 || SYNTAX_PREFIX (c)))
2715 {
1fd3172d
RS
2716 opoint = pos;
2717 opoint_byte = pos_byte;
2718
2719 if (pos + 1 > beg)
2720 DEC_BOTH (pos, pos_byte);
93da5fff 2721 }
8489eb67 2722
6a140a74 2723 SET_PT_BOTH (opoint, opoint_byte);
8489eb67
RS
2724
2725 return Qnil;
2726}
2727\f
6a140a74 2728/* Parse forward from FROM / FROM_BYTE to END,
e5d4f4dc
RS
2729 assuming that FROM has state OLDSTATE (nil means FROM is start of function),
2730 and return a description of the state of the parse at END.
c81a3712 2731 If STOPBEFORE is nonzero, stop at the start of an atom.
644ea4df
RS
2732 If COMMENTSTOP is 1, stop at the start of a comment.
2733 If COMMENTSTOP is -1, stop at the start or end of a comment,
2734 after the beginning of a string, or after the end of a string. */
8489eb67 2735
340f92b5 2736static void
6a140a74 2737scan_sexps_forward (stateptr, from, from_byte, end, targetdepth,
c81a3712 2738 stopbefore, oldstate, commentstop)
e5d4f4dc 2739 struct lisp_parse_state *stateptr;
8489eb67 2740 register int from;
38355d47 2741 int from_byte;
8489eb67
RS
2742 int end, targetdepth, stopbefore;
2743 Lisp_Object oldstate;
c81a3712 2744 int commentstop;
8489eb67
RS
2745{
2746 struct lisp_parse_state state;
2747
2748 register enum syntaxcode code;
95ff8dfc
RS
2749 int c1;
2750 int comnested;
8489eb67
RS
2751 struct level { int last, prev; };
2752 struct level levelstart[100];
2753 register struct level *curlevel = levelstart;
2754 struct level *endlevel = levelstart + 100;
8489eb67
RS
2755 register int depth; /* Paren depth of current scanning location.
2756 level - levelstart equals this except
2757 when the depth becomes negative. */
2758 int mindepth; /* Lowest DEPTH value seen. */
2759 int start_quoted = 0; /* Nonzero means starting after a char quote */
2760 Lisp_Object tem;
93da5fff 2761 int prev_from; /* Keep one character before FROM. */
6a140a74 2762 int prev_from_byte;
1fd3172d 2763 int prev_from_syntax;
195d1361
RS
2764 int boundary_stop = commentstop == -1;
2765 int nofence;
95ff8dfc
RS
2766 int found;
2767 int out_bytepos, out_charpos;
7c7ca3d2 2768 int temp;
93da5fff
KH
2769
2770 prev_from = from;
6a140a74
RS
2771 prev_from_byte = from_byte;
2772 if (from != BEGV)
2773 DEC_BOTH (prev_from, prev_from_byte);
93da5fff
KH
2774
2775 /* Use this macro instead of `from++'. */
6a140a74
RS
2776#define INC_FROM \
2777do { prev_from = from; \
2778 prev_from_byte = from_byte; \
1fd3172d 2779 prev_from_syntax \
8f924df7 2780 = SYNTAX_WITH_FLAGS (FETCH_CHAR_AS_MULTIBYTE (prev_from_byte)); \
ef316cf0 2781 INC_BOTH (from, from_byte); \
3ceb4629
SM
2782 if (from < end) \
2783 UPDATE_SYNTAX_TABLE_FORWARD (from); \
6a140a74 2784 } while (0)
8489eb67
RS
2785
2786 immediate_quit = 1;
2787 QUIT;
2788
265a9e55 2789 if (NILP (oldstate))
8489eb67
RS
2790 {
2791 depth = 0;
2792 state.instring = -1;
2793 state.incomment = 0;
195d1361
RS
2794 state.comstyle = 0; /* comment style a by default. */
2795 state.comstr_start = -1; /* no comment/string seen. */
8489eb67
RS
2796 }
2797 else
2798 {
2799 tem = Fcar (oldstate);
265a9e55 2800 if (!NILP (tem))
8489eb67
RS
2801 depth = XINT (tem);
2802 else
2803 depth = 0;
2804
2805 oldstate = Fcdr (oldstate);
2806 oldstate = Fcdr (oldstate);
2807 oldstate = Fcdr (oldstate);
2808 tem = Fcar (oldstate);
195d1361 2809 /* Check whether we are inside string_fence-style string: */
7d0393cf
JB
2810 state.instring = (!NILP (tem)
2811 ? (INTEGERP (tem) ? XINT (tem) : ST_STRING_STYLE)
e76f1c44 2812 : -1);
8489eb67
RS
2813
2814 oldstate = Fcdr (oldstate);
2815 tem = Fcar (oldstate);
e76f1c44
GM
2816 state.incomment = (!NILP (tem)
2817 ? (INTEGERP (tem) ? XINT (tem) : -1)
95ff8dfc 2818 : 0);
8489eb67
RS
2819
2820 oldstate = Fcdr (oldstate);
2821 tem = Fcar (oldstate);
265a9e55 2822 start_quoted = !NILP (tem);
e5d4f4dc 2823
95ff8dfc 2824 /* if the eighth element of the list is nil, we are in comment
195d1361
RS
2825 style a. If it is non-nil, we are in comment style b */
2826 oldstate = Fcdr (oldstate);
e5d4f4dc 2827 oldstate = Fcdr (oldstate);
195d1361 2828 tem = Fcar (oldstate);
7d0393cf 2829 state.comstyle = NILP (tem) ? 0 : (EQ (tem, Qsyntax_table)
e76f1c44 2830 ? ST_COMMENT_STYLE : 1);
195d1361 2831
e5d4f4dc 2832 oldstate = Fcdr (oldstate);
e5d4f4dc 2833 tem = Fcar (oldstate);
195d1361 2834 state.comstr_start = NILP (tem) ? -1 : XINT (tem) ;
1a102a58
RS
2835 oldstate = Fcdr (oldstate);
2836 tem = Fcar (oldstate);
2837 while (!NILP (tem)) /* >= second enclosing sexps. */
2838 {
2839 /* curlevel++->last ran into compiler bug on Apollo */
2840 curlevel->last = XINT (Fcar (tem));
2841 if (++curlevel == endlevel)
02010917 2842 curlevel--; /* error ("Nesting too deep for parser"); */
1a102a58
RS
2843 curlevel->prev = -1;
2844 curlevel->last = -1;
2845 tem = Fcdr (tem);
2846 }
8489eb67
RS
2847 }
2848 state.quoted = 0;
2849 mindepth = depth;
2850
2851 curlevel->prev = -1;
2852 curlevel->last = -1;
2853
326b283b
RS
2854 SETUP_SYNTAX_TABLE (prev_from, 1);
2855 prev_from_syntax = SYNTAX_WITH_FLAGS (FETCH_CHAR (prev_from_byte));
3ceb4629 2856 UPDATE_SYNTAX_TABLE_FORWARD (from);
326b283b 2857
195d1361 2858 /* Enter the loop at a place appropriate for initial state. */
8489eb67 2859
326b283b
RS
2860 if (state.incomment)
2861 goto startincomment;
8489eb67
RS
2862 if (state.instring >= 0)
2863 {
195d1361 2864 nofence = state.instring != ST_STRING_STYLE;
326b283b
RS
2865 if (start_quoted)
2866 goto startquotedinstring;
8489eb67
RS
2867 goto startinstring;
2868 }
326b283b
RS
2869 else if (start_quoted)
2870 goto startquoted;
1fd3172d 2871
8489eb67
RS
2872 while (from < end)
2873 {
93da5fff 2874 INC_FROM;
1fd3172d 2875 code = prev_from_syntax & 0xff;
4c920633 2876
8f9dc2ed 2877 if (code == Scomment)
95ff8dfc 2878 {
d355bd8a 2879 state.comstyle = SYNTAX_FLAGS_COMMENT_STYLE (prev_from_syntax);
95ff8dfc
RS
2880 state.incomment = (SYNTAX_FLAGS_COMMENT_NESTED (prev_from_syntax) ?
2881 1 : -1);
2882 state.comstr_start = prev_from;
2883 }
4c920633 2884 else if (code == Scomment_fence)
e5d4f4dc
RS
2885 {
2886 /* Record the comment style we have entered so that only
2887 the comment-end sequence of the same style actually
2888 terminates the comment section. */
95ff8dfc
RS
2889 state.comstyle = ST_COMMENT_STYLE;
2890 state.incomment = -1;
195d1361 2891 state.comstr_start = prev_from;
e5d4f4dc 2892 code = Scomment;
e5d4f4dc 2893 }
4c920633 2894 else if (from < end)
1fd3172d 2895 if (SYNTAX_FLAGS_COMSTART_FIRST (prev_from_syntax))
8f924df7 2896 if (c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte),
95ff8dfc 2897 SYNTAX_COMSTART_SECOND (c1))
1fd3172d 2898 /* Duplicate code to avoid a complex if-expression
4c920633
RS
2899 which causes trouble for the SGI compiler. */
2900 {
2901 /* Record the comment style we have entered so that only
2902 the comment-end sequence of the same style actually
2903 terminates the comment section. */
79bd6490 2904 state.comstyle = SYNTAX_COMMENT_STYLE (c1);
95ff8dfc
RS
2905 comnested = SYNTAX_FLAGS_COMMENT_NESTED (prev_from_syntax);
2906 comnested = comnested || SYNTAX_COMMENT_NESTED (c1);
2907 state.incomment = comnested ? 1 : -1;
4c920633 2908 state.comstr_start = prev_from;
95ff8dfc 2909 INC_FROM;
4c920633
RS
2910 code = Scomment;
2911 }
e5d4f4dc 2912
1fd3172d 2913 if (SYNTAX_FLAGS_PREFIX (prev_from_syntax))
8489eb67 2914 continue;
0220c518 2915 switch (SWITCH_ENUM_CAST (code))
8489eb67
RS
2916 {
2917 case Sescape:
2918 case Scharquote:
2919 if (stopbefore) goto stop; /* this arg means stop at sexp start */
93da5fff 2920 curlevel->last = prev_from;
8489eb67
RS
2921 startquoted:
2922 if (from == end) goto endquoted;
93da5fff 2923 INC_FROM;
8489eb67
RS
2924 goto symstarted;
2925 /* treat following character as a word constituent */
2926 case Sword:
2927 case Ssymbol:
2928 if (stopbefore) goto stop; /* this arg means stop at sexp start */
93da5fff 2929 curlevel->last = prev_from;
8489eb67
RS
2930 symstarted:
2931 while (from < end)
2932 {
7c7ca3d2 2933 /* Some compilers can't handle this inside the switch. */
b7dbcc19 2934 temp = SYNTAX (FETCH_CHAR_AS_MULTIBYTE (from_byte));
7c7ca3d2 2935 switch (temp)
8489eb67
RS
2936 {
2937 case Scharquote:
2938 case Sescape:
93da5fff 2939 INC_FROM;
8489eb67
RS
2940 if (from == end) goto endquoted;
2941 break;
2942 case Sword:
2943 case Ssymbol:
2944 case Squote:
2945 break;
2946 default:
2947 goto symdone;
2948 }
93da5fff 2949 INC_FROM;
8489eb67
RS
2950 }
2951 symdone:
2952 curlevel->prev = curlevel->last;
2953 break;
2954
240c43e8 2955 case Scomment_fence: /* Can't happen because it's handled above. */
8489eb67 2956 case Scomment:
195d1361 2957 if (commentstop || boundary_stop) goto done;
d355bd8a
SM
2958 startincomment:
2959 /* The (from == BEGV) test was to enter the loop in the middle so
95ff8dfc 2960 that we find a 2-char comment ender even if we start in the
e6365855 2961 middle of it. We don't want to do that if we're just at the
d355bd8a 2962 beginning of the comment (think of (*) ... (*)). */
95ff8dfc
RS
2963 found = forw_comment (from, from_byte, end,
2964 state.incomment, state.comstyle,
e6365855
SM
2965 (from == BEGV || from < state.comstr_start + 3)
2966 ? 0 : prev_from_syntax,
95ff8dfc
RS
2967 &out_charpos, &out_bytepos, &state.incomment);
2968 from = out_charpos; from_byte = out_bytepos;
2969 /* Beware! prev_from and friends are invalid now.
2970 Luckily, the `done' doesn't use them and the INC_FROM
2971 sets them to a sane value without looking at them. */
2972 if (!found) goto done;
d355bd8a 2973 INC_FROM;
8489eb67 2974 state.incomment = 0;
e5d4f4dc 2975 state.comstyle = 0; /* reset the comment style */
195d1361 2976 if (boundary_stop) goto done;
8489eb67
RS
2977 break;
2978
2979 case Sopen:
2980 if (stopbefore) goto stop; /* this arg means stop at sexp start */
2981 depth++;
2982 /* curlevel++->last ran into compiler bug on Apollo */
93da5fff 2983 curlevel->last = prev_from;
8489eb67 2984 if (++curlevel == endlevel)
02010917 2985 curlevel--; /* error ("Nesting too deep for parser"); */
8489eb67
RS
2986 curlevel->prev = -1;
2987 curlevel->last = -1;
30844415 2988 if (targetdepth == depth) goto done;
8489eb67
RS
2989 break;
2990
2991 case Sclose:
2992 depth--;
2993 if (depth < mindepth)
2994 mindepth = depth;
2995 if (curlevel != levelstart)
2996 curlevel--;
2997 curlevel->prev = curlevel->last;
30844415 2998 if (targetdepth == depth) goto done;
8489eb67
RS
2999 break;
3000
3001 case Sstring:
195d1361
RS
3002 case Sstring_fence:
3003 state.comstr_start = from - 1;
8489eb67 3004 if (stopbefore) goto stop; /* this arg means stop at sexp start */
93da5fff 3005 curlevel->last = prev_from;
7d0393cf 3006 state.instring = (code == Sstring
b7dbcc19 3007 ? (FETCH_CHAR_AS_MULTIBYTE (prev_from_byte))
195d1361
RS
3008 : ST_STRING_STYLE);
3009 if (boundary_stop) goto done;
8489eb67 3010 startinstring:
195d1361 3011 {
644ea4df 3012 nofence = state.instring != ST_STRING_STYLE;
7d0393cf 3013
644ea4df
RS
3014 while (1)
3015 {
3016 int c;
195d1361 3017
644ea4df 3018 if (from >= end) goto done;
b7dbcc19 3019 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
7c7ca3d2
RS
3020 /* Some compilers can't handle this inside the switch. */
3021 temp = SYNTAX (c);
ccf89641
KH
3022
3023 /* Check TEMP here so that if the char has
3024 a syntax-table property which says it is NOT
3025 a string character, it does not end the string. */
3026 if (nofence && c == state.instring && temp == Sstring)
3027 break;
3028
7c7ca3d2 3029 switch (temp)
644ea4df
RS
3030 {
3031 case Sstring_fence:
3032 if (!nofence) goto string_end;
3033 break;
3034 case Scharquote:
3035 case Sescape:
3036 INC_FROM;
3037 startquotedinstring:
3038 if (from >= end) goto endquoted;
195d1361 3039 }
644ea4df
RS
3040 INC_FROM;
3041 }
195d1361
RS
3042 }
3043 string_end:
8489eb67
RS
3044 state.instring = -1;
3045 curlevel->prev = curlevel->last;
93da5fff 3046 INC_FROM;
195d1361 3047 if (boundary_stop) goto done;
8489eb67
RS
3048 break;
3049
3050 case Smath:
240c43e8
SM
3051 /* FIXME: We should do something with it. */
3052 break;
3053 default:
3054 /* Ignore whitespace, punctuation, quote, endcomment. */
8489eb67
RS
3055 break;
3056 }
3057 }
3058 goto done;
3059
3060 stop: /* Here if stopping before start of sexp. */
93da5fff 3061 from = prev_from; /* We have just fetched the char that starts it; */
8489eb67
RS
3062 goto done; /* but return the position before it. */
3063
3064 endquoted:
3065 state.quoted = 1;
3066 done:
3067 state.depth = depth;
3068 state.mindepth = mindepth;
3069 state.thislevelstart = curlevel->prev;
3070 state.prevlevelstart
3071 = (curlevel == levelstart) ? -1 : (curlevel - 1)->last;
3072 state.location = from;
1a102a58
RS
3073 state.levelstarts = Qnil;
3074 while (--curlevel >= levelstart)
3075 state.levelstarts = Fcons (make_number (curlevel->last),
3076 state.levelstarts);
8489eb67
RS
3077 immediate_quit = 0;
3078
e5d4f4dc 3079 *stateptr = state;
8489eb67
RS
3080}
3081
c81a3712 3082DEFUN ("parse-partial-sexp", Fparse_partial_sexp, Sparse_partial_sexp, 2, 6, 0,
fdb82f93
PJ
3083 doc: /* Parse Lisp syntax starting at FROM until TO; return status of parse at TO.
3084Parsing stops at TO or when certain criteria are met;
3085 point is set to where parsing stops.
3086If fifth arg OLDSTATE is omitted or nil,
3087 parsing assumes that FROM is the beginning of a function.
3088Value is a list of ten elements describing final state of parsing:
3089 0. depth in parens.
3090 1. character address of start of innermost containing list; nil if none.
3091 2. character address of start of last complete sexp terminated.
3092 3. non-nil if inside a string.
3093 (it is the character that will terminate the string,
3094 or t if the string should be terminated by a generic string delimiter.)
7d0393cf 3095 4. nil if outside a comment, t if inside a non-nestable comment,
fdb82f93
PJ
3096 else an integer (the current comment nesting).
3097 5. t if following a quote character.
3098 6. the minimum paren-depth encountered during this scan.
3099 7. t if in a comment of style b; symbol `syntax-table' if the comment
3100 should be terminated by a generic comment delimiter.
3101 8. character address of start of comment or string; nil if not in one.
3102 9. Intermediate data for continuation of parsing (subject to change).
3103If third arg TARGETDEPTH is non-nil, parsing stops if the depth
3104in parentheses becomes equal to TARGETDEPTH.
3105Fourth arg STOPBEFORE non-nil means stop when come to
3106 any character that starts a sexp.
3107Fifth arg OLDSTATE is a nine-element list like what this function returns.
3108 It is used to initialize the state of the parse. Elements number 1, 2, 6
3109 and 8 are ignored; you can leave off element 8 (the last) entirely.
3110Sixth arg COMMENTSTOP non-nil means stop at the start of a comment.
3111 If it is symbol `syntax-table', stop after the start of a comment or a
3112 string, or after end of a comment or a string. */)
3113 (from, to, targetdepth, stopbefore, oldstate, commentstop)
c81a3712 3114 Lisp_Object from, to, targetdepth, stopbefore, oldstate, commentstop;
8489eb67
RS
3115{
3116 struct lisp_parse_state state;
3117 int target;
3118
265a9e55 3119 if (!NILP (targetdepth))
8489eb67 3120 {
b7826503 3121 CHECK_NUMBER (targetdepth);
8489eb67
RS
3122 target = XINT (targetdepth);
3123 }
3124 else
3125 target = -100000; /* We won't reach this depth */
3126
3127 validate_region (&from, &to);
6a140a74
RS
3128 scan_sexps_forward (&state, XINT (from), CHAR_TO_BYTE (XINT (from)),
3129 XINT (to),
c81a3712 3130 target, !NILP (stopbefore), oldstate,
7d0393cf 3131 (NILP (commentstop)
195d1361 3132 ? 0 : (EQ (commentstop, Qsyntax_table) ? -1 : 1)));
8489eb67
RS
3133
3134 SET_PT (state.location);
7d0393cf 3135
8489eb67
RS
3136 return Fcons (make_number (state.depth),
3137 Fcons (state.prevlevelstart < 0 ? Qnil : make_number (state.prevlevelstart),
3138 Fcons (state.thislevelstart < 0 ? Qnil : make_number (state.thislevelstart),
7d0393cf
JB
3139 Fcons (state.instring >= 0
3140 ? (state.instring == ST_STRING_STYLE
195d1361 3141 ? Qt : make_number (state.instring)) : Qnil,
95ff8dfc
RS
3142 Fcons (state.incomment < 0 ? Qt :
3143 (state.incomment == 0 ? Qnil :
3144 make_number (state.incomment)),
8489eb67 3145 Fcons (state.quoted ? Qt : Qnil,
bff37d2a 3146 Fcons (make_number (state.mindepth),
7d0393cf 3147 Fcons ((state.comstyle
bff37d2a
RS
3148 ? (state.comstyle == ST_COMMENT_STYLE
3149 ? Qsyntax_table : Qt) :
3150 Qnil),
0beaf54f
DL
3151 Fcons (((state.incomment
3152 || (state.instring >= 0))
bff37d2a
RS
3153 ? make_number (state.comstr_start)
3154 : Qnil),
1a102a58 3155 Fcons (state.levelstarts, Qnil))))))))));
8489eb67
RS
3156}
3157\f
dfcf069d 3158void
8489eb67
RS
3159init_syntax_once ()
3160{
78f9a1f7 3161 register int i, c;
8ea151b2 3162 Lisp_Object temp;
8489eb67 3163
5ebaddf5
RS
3164 /* This has to be done here, before we call Fmake_char_table. */
3165 Qsyntax_table = intern ("syntax-table");
3166 staticpro (&Qsyntax_table);
3167
3168 /* Intern this now in case it isn't already done.
3169 Setting this variable twice is harmless.
3170 But don't staticpro it here--that is done in alloc.c. */
3171 Qchar_table_extra_slots = intern ("char-table-extra-slots");
3172
93da5fff 3173 /* Create objects which can be shared among syntax tables. */
42ebfa31 3174 Vsyntax_code_object = Fmake_vector (make_number (Smax), Qnil);
93da5fff
KH
3175 for (i = 0; i < XVECTOR (Vsyntax_code_object)->size; i++)
3176 XVECTOR (Vsyntax_code_object)->contents[i]
3177 = Fcons (make_number (i), Qnil);
3178
5ebaddf5
RS
3179 /* Now we are ready to set up this property, so we can
3180 create syntax tables. */
3181 Fput (Qsyntax_table, Qchar_table_extra_slots, make_number (0));
3182
93da5fff 3183 temp = XVECTOR (Vsyntax_code_object)->contents[(int) Swhitespace];
8489eb67 3184
5ebaddf5 3185 Vstandard_syntax_table = Fmake_char_table (Qsyntax_table, temp);
8489eb67 3186
93da5fff 3187 temp = XVECTOR (Vsyntax_code_object)->contents[(int) Sword];
8489eb67 3188 for (i = 'a'; i <= 'z'; i++)
8ea151b2 3189 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, i, temp);
8489eb67 3190 for (i = 'A'; i <= 'Z'; i++)
8ea151b2 3191 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, i, temp);
8489eb67 3192 for (i = '0'; i <= '9'; i++)
8ea151b2
RS
3193 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, i, temp);
3194
3195 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '$', temp);
3196 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '%', temp);
3197
3198 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '(',
3199 Fcons (make_number (Sopen), make_number (')')));
3200 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, ')',
3201 Fcons (make_number (Sclose), make_number ('(')));
3202 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '[',
3203 Fcons (make_number (Sopen), make_number (']')));
3204 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, ']',
3205 Fcons (make_number (Sclose), make_number ('[')));
3206 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '{',
3207 Fcons (make_number (Sopen), make_number ('}')));
3208 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '}',
3209 Fcons (make_number (Sclose), make_number ('{')));
3210 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '"',
3211 Fcons (make_number ((int) Sstring), Qnil));
3212 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '\\',
3213 Fcons (make_number ((int) Sescape), Qnil));
3214
93da5fff 3215 temp = XVECTOR (Vsyntax_code_object)->contents[(int) Ssymbol];
8489eb67 3216 for (i = 0; i < 10; i++)
78f9a1f7
KH
3217 {
3218 c = "_-+*/&|<>="[i];
3219 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, c, temp);
3220 }
8489eb67 3221
93da5fff 3222 temp = XVECTOR (Vsyntax_code_object)->contents[(int) Spunct];
8489eb67 3223 for (i = 0; i < 12; i++)
78f9a1f7
KH
3224 {
3225 c = ".,;:?!#@~^'`"[i];
3226 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, c, temp);
3227 }
bd25db08
KH
3228
3229 /* All multibyte characters have syntax `word' by default. */
3230 temp = XVECTOR (Vsyntax_code_object)->contents[(int) Sword];
5c7b02ab 3231 char_table_set_range (Vstandard_syntax_table, 0x80, MAX_CHAR, temp);
8489eb67
RS
3232}
3233
dfcf069d 3234void
8489eb67
RS
3235syms_of_syntax ()
3236{
3237 Qsyntax_table_p = intern ("syntax-table-p");
3238 staticpro (&Qsyntax_table_p);
3239
93da5fff
KH
3240 staticpro (&Vsyntax_code_object);
3241
7bf5e9e4
RS
3242 Qscan_error = intern ("scan-error");
3243 staticpro (&Qscan_error);
3244 Fput (Qscan_error, Qerror_conditions,
f3be100f 3245 Fcons (Qscan_error, Fcons (Qerror, Qnil)));
7bf5e9e4
RS
3246 Fput (Qscan_error, Qerror_message,
3247 build_string ("Scan error"));
3248
8489eb67 3249 DEFVAR_BOOL ("parse-sexp-ignore-comments", &parse_sexp_ignore_comments,
fdb82f93 3250 doc: /* Non-nil means `forward-sexp', etc., should treat comments as whitespace. */);
8489eb67 3251
195d1361 3252 DEFVAR_BOOL ("parse-sexp-lookup-properties", &parse_sexp_lookup_properties,
fdb82f93
PJ
3253 doc: /* Non-nil means `forward-sexp', etc., obey `syntax-table' property.
3254Otherwise, that text property is simply ignored.
3255See the info node `(elisp)Syntax Properties' for a description of the
3256`syntax-table' property. */);
195d1361 3257
8489eb67
RS
3258 words_include_escapes = 0;
3259 DEFVAR_BOOL ("words-include-escapes", &words_include_escapes,
fdb82f93 3260 doc: /* Non-nil means `forward-word', etc., should treat escape chars part of words. */);
8489eb67 3261
bd25db08 3262 DEFVAR_BOOL ("multibyte-syntax-as-symbol", &multibyte_syntax_as_symbol,
fdb82f93 3263 doc: /* Non-nil means `scan-sexps' treats all multibyte characters as symbol. */);
bd25db08
KH
3264 multibyte_syntax_as_symbol = 0;
3265
f4ed767f
GM
3266 DEFVAR_BOOL ("open-paren-in-column-0-is-defun-start",
3267 &open_paren_in_column_0_is_defun_start,
b222e415 3268 doc: /* *Non-nil means an open paren in column 0 denotes the start of a defun. */);
f4ed767f
GM
3269 open_paren_in_column_0_is_defun_start = 1;
3270
8f924df7
KH
3271
3272 DEFVAR_LISP ("find-word-boundary-function-table",
3273 &Vfind_word_boundary_function_table,
869bb237 3274 doc: /*
8f924df7 3275Char table of functions to search for the word boundary.
869bb237
KH
3276Each function is called with two arguments; POS and LIMIT.
3277POS and LIMIT are character positions in the current buffer.
3278
3279If POS is less than LIMIT, POS is at the first character of a word,
3280and the return value of a function is a position after the last
3281character of that word.
3282
3283If POS is not less than LIMIT, POS is at the last character of a word,
3284and the return value of a function is a position at the first
3285character of that word.
3286
3287In both cases, LIMIT bounds the search. */);
8f924df7 3288 Vfind_word_boundary_function_table = Fmake_char_table (Qnil, Qnil);
869bb237 3289
8489eb67
RS
3290 defsubr (&Ssyntax_table_p);
3291 defsubr (&Ssyntax_table);
3292 defsubr (&Sstandard_syntax_table);
3293 defsubr (&Scopy_syntax_table);
3294 defsubr (&Sset_syntax_table);
3295 defsubr (&Schar_syntax);
beefa22e 3296 defsubr (&Smatching_paren);
c65adb44 3297 defsubr (&Sstring_to_syntax);
8489eb67 3298 defsubr (&Smodify_syntax_entry);
62abe9cb 3299 defsubr (&Sinternal_describe_syntax_value);
8489eb67
RS
3300
3301 defsubr (&Sforward_word);
3302
195d1361
RS
3303 defsubr (&Sskip_chars_forward);
3304 defsubr (&Sskip_chars_backward);
3305 defsubr (&Sskip_syntax_forward);
3306 defsubr (&Sskip_syntax_backward);
3307
b3cfe0c8 3308 defsubr (&Sforward_comment);
8489eb67
RS
3309 defsubr (&Sscan_lists);
3310 defsubr (&Sscan_sexps);
3311 defsubr (&Sbackward_prefix_chars);
3312 defsubr (&Sparse_partial_sexp);
3313}
839966f3
KH
3314
3315/* arch-tag: 3e297b9f-088e-4b64-8f4c-fb0b3443e412
3316 (do not change this comment) */