(Fcopy_syntax_table): Set default to nil.
[bpt/emacs.git] / src / syntax.c
1 /* GNU Emacs routines to deal with syntax tables; also word and list parsing.
2 Copyright (C) 1985, 1987, 1993, 1994, 1995 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21
22 #include <config.h>
23 #include <ctype.h>
24 #include "lisp.h"
25 #include "commands.h"
26 #include "buffer.h"
27 #include "syntax.h"
28
29 Lisp_Object Qsyntax_table_p, Qsyntax_table;
30
31 static void scan_sexps_forward ();
32 static int char_quoted ();
33
34 int words_include_escapes;
35
36 /* Used as a temporary in SYNTAX_ENTRY and other macros in syntax.h,
37 if not compiled with GCC. No need to mark it, since it is used
38 only very temporarily. */
39 Lisp_Object syntax_temp;
40
41 /* This is the internal form of the parse state used in parse-partial-sexp. */
42
43 struct lisp_parse_state
44 {
45 int depth; /* Depth at end of parsing */
46 int instring; /* -1 if not within string, else desired terminator. */
47 int incomment; /* Nonzero if within a comment at end of parsing */
48 int comstyle; /* comment style a=0, or b=1 */
49 int quoted; /* Nonzero if just after an escape char at end of parsing */
50 int thislevelstart; /* Char number of most recent start-of-expression at current level */
51 int prevlevelstart; /* Char number of start of containing expression */
52 int location; /* Char number at which parsing stopped. */
53 int mindepth; /* Minimum depth seen while scanning. */
54 int comstart; /* Position just after last comment starter. */
55 };
56 \f
57 /* These variables are a cache for finding the start of a defun.
58 find_start_pos is the place for which the defun start was found.
59 find_start_value is the defun start position found for it.
60 find_start_buffer is the buffer it was found in.
61 find_start_begv is the BEGV value when it was found.
62 find_start_modiff is the value of MODIFF when it was found. */
63
64 static int find_start_pos;
65 static int find_start_value;
66 static struct buffer *find_start_buffer;
67 static int find_start_begv;
68 static int find_start_modiff;
69
70 /* Find a defun-start that is the last one before POS (or nearly the last).
71 We record what we find, so that another call in the same area
72 can return the same value right away. */
73
74 static int
75 find_defun_start (pos)
76 int pos;
77 {
78 int tem;
79 int shortage;
80
81 /* Use previous finding, if it's valid and applies to this inquiry. */
82 if (current_buffer == find_start_buffer
83 /* Reuse the defun-start even if POS is a little farther on.
84 POS might be in the next defun, but that's ok.
85 Our value may not be the best possible, but will still be usable. */
86 && pos <= find_start_pos + 1000
87 && pos >= find_start_value
88 && BEGV == find_start_begv
89 && MODIFF == find_start_modiff)
90 return find_start_value;
91
92 /* Back up to start of line. */
93 tem = scan_buffer ('\n', pos, BEGV, -1, &shortage, 1);
94
95 while (tem > BEGV)
96 {
97 /* Open-paren at start of line means we found our defun-start. */
98 if (SYNTAX (FETCH_CHAR (tem)) == Sopen)
99 break;
100 /* Move to beg of previous line. */
101 tem = scan_buffer ('\n', tem, BEGV, -2, &shortage, 1);
102 }
103
104 /* Record what we found, for the next try. */
105 find_start_value = tem;
106 find_start_buffer = current_buffer;
107 find_start_modiff = MODIFF;
108 find_start_begv = BEGV;
109 find_start_pos = pos;
110
111 return find_start_value;
112 }
113 \f
114 DEFUN ("syntax-table-p", Fsyntax_table_p, Ssyntax_table_p, 1, 1, 0,
115 "Return t if OBJECT is a syntax table.\n\
116 Currently, any char-table counts as a syntax table.")
117 (object)
118 Lisp_Object object;
119 {
120 if (CHAR_TABLE_P (object)
121 && XCHAR_TABLE (object)->purpose == Qsyntax_table)
122 return Qt;
123 return Qnil;
124 }
125
126 static void
127 check_syntax_table (obj)
128 Lisp_Object obj;
129 {
130 if (!(CHAR_TABLE_P (obj)
131 && XCHAR_TABLE (obj)->purpose == Qsyntax_table))
132 wrong_type_argument (Qsyntax_table_p, obj);
133 }
134
135 DEFUN ("syntax-table", Fsyntax_table, Ssyntax_table, 0, 0, 0,
136 "Return the current syntax table.\n\
137 This is the one specified by the current buffer.")
138 ()
139 {
140 return current_buffer->syntax_table;
141 }
142
143 DEFUN ("standard-syntax-table", Fstandard_syntax_table,
144 Sstandard_syntax_table, 0, 0, 0,
145 "Return the standard syntax table.\n\
146 This is the one used for new buffers.")
147 ()
148 {
149 return Vstandard_syntax_table;
150 }
151
152 DEFUN ("copy-syntax-table", Fcopy_syntax_table, Scopy_syntax_table, 0, 1, 0,
153 "Construct a new syntax table and return it.\n\
154 It is a copy of the TABLE, which defaults to the standard syntax table.")
155 (table)
156 Lisp_Object table;
157 {
158 Lisp_Object copy;
159
160 if (!NILP (table))
161 check_syntax_table (table);
162 else
163 table = Vstandard_syntax_table;
164
165 copy = Fcopy_sequence (table);
166
167 /* Only the standard syntax table should have a default element.
168 Other syntax tables should inherit from parents instead. */
169 XCHAR_TABLE (copy)->defalt = Qnil;
170
171 /* Copied syntax tables should all have parents.
172 If we copied one with no parent, such as the standard syntax table,
173 use the standard syntax table as the copy's parent. */
174 if (NILP (XCHAR_TABLE (copy)->parent))
175 Fset_char_table_parent (copy, Vstandard_syntax_table);
176 return copy;
177 }
178
179 DEFUN ("set-syntax-table", Fset_syntax_table, Sset_syntax_table, 1, 1, 0,
180 "Select a new syntax table for the current buffer.\n\
181 One argument, a syntax table.")
182 (table)
183 Lisp_Object table;
184 {
185 check_syntax_table (table);
186 current_buffer->syntax_table = table;
187 /* Indicate that this buffer now has a specified syntax table. */
188 current_buffer->local_var_flags
189 |= XFASTINT (buffer_local_flags.syntax_table);
190 return table;
191 }
192 \f
193 /* Convert a letter which signifies a syntax code
194 into the code it signifies.
195 This is used by modify-syntax-entry, and other things. */
196
197 unsigned char syntax_spec_code[0400] =
198 { 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
199 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
200 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
201 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
202 (char) Swhitespace, 0377, (char) Sstring, 0377,
203 (char) Smath, 0377, 0377, (char) Squote,
204 (char) Sopen, (char) Sclose, 0377, 0377,
205 0377, (char) Swhitespace, (char) Spunct, (char) Scharquote,
206 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
207 0377, 0377, 0377, 0377,
208 (char) Scomment, 0377, (char) Sendcomment, 0377,
209 (char) Sinherit, 0377, 0377, 0377, 0377, 0377, 0377, 0377, /* @, A ... */
210 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
211 0377, 0377, 0377, 0377, 0377, 0377, 0377, (char) Sword,
212 0377, 0377, 0377, 0377, (char) Sescape, 0377, 0377, (char) Ssymbol,
213 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377, /* `, a, ... */
214 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
215 0377, 0377, 0377, 0377, 0377, 0377, 0377, (char) Sword,
216 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377
217 };
218
219 /* Indexed by syntax code, give the letter that describes it. */
220
221 char syntax_code_spec[14] =
222 {
223 ' ', '.', 'w', '_', '(', ')', '\'', '\"', '$', '\\', '/', '<', '>', '@'
224 };
225 \f
226 /* Look up the value for CHARACTER in syntax table TABLE's parent
227 and its parents. SYNTAX_ENTRY calls this, when TABLE itself has nil
228 for CHARACTER. It's actually used only when not compiled with GCC. */
229
230 Lisp_Object
231 syntax_parent_lookup (table, character)
232 Lisp_Object table;
233 int character;
234 {
235 Lisp_Object value;
236
237 while (1)
238 {
239 table = XCHAR_TABLE (table)->parent;
240 if (NILP (table))
241 return Qnil;
242
243 value = XCHAR_TABLE (table)->contents[character];
244 if (!NILP (value))
245 return value;
246 }
247 }
248
249 DEFUN ("char-syntax", Fchar_syntax, Schar_syntax, 1, 1, 0,
250 "Return the syntax code of CHARACTER, described by a character.\n\
251 For example, if CHARACTER is a word constituent,\n\
252 the character `w' is returned.\n\
253 The characters that correspond to various syntax codes\n\
254 are listed in the documentation of `modify-syntax-entry'.")
255 (character)
256 Lisp_Object character;
257 {
258 int char_int;
259 CHECK_NUMBER (character, 0);
260 char_int = XINT (character);
261 return make_number (syntax_code_spec[(int) SYNTAX (char_int)]);
262 }
263
264 DEFUN ("matching-paren", Fmatching_paren, Smatching_paren, 1, 1, 0,
265 "Return the matching parenthesis of CHARACTER, or nil if none.")
266 (character)
267 Lisp_Object character;
268 {
269 int char_int, code;
270 CHECK_NUMBER (character, 0);
271 char_int = XINT (character);
272 code = SYNTAX (char_int);
273 if (code == Sopen || code == Sclose)
274 return make_number (SYNTAX_MATCH (char_int));
275 return Qnil;
276 }
277
278 /* This comment supplies the doc string for modify-syntax-entry,
279 for make-docfile to see. We cannot put this in the real DEFUN
280 due to limits in the Unix cpp.
281
282 DEFUN ("modify-syntax-entry", foo, bar, 2, 3, 0,
283 "Set syntax for character CHAR according to string S.\n\
284 The syntax is changed only for table TABLE, which defaults to\n\
285 the current buffer's syntax table.\n\
286 The first character of S should be one of the following:\n\
287 Space or - whitespace syntax. w word constituent.\n\
288 _ symbol constituent. . punctuation.\n\
289 ( open-parenthesis. ) close-parenthesis.\n\
290 \" string quote. \\ escape.\n\
291 $ paired delimiter. ' expression quote or prefix operator.\n\
292 < comment starter. > comment ender.\n\
293 / character-quote. @ inherit from `standard-syntax-table'.\n\
294 \n\
295 Only single-character comment start and end sequences are represented thus.\n\
296 Two-character sequences are represented as described below.\n\
297 The second character of S is the matching parenthesis,\n\
298 used only if the first character is `(' or `)'.\n\
299 Any additional characters are flags.\n\
300 Defined flags are the characters 1, 2, 3, 4, b, and p.\n\
301 1 means CHAR is the start of a two-char comment start sequence.\n\
302 2 means CHAR is the second character of such a sequence.\n\
303 3 means CHAR is the start of a two-char comment end sequence.\n\
304 4 means CHAR is the second character of such a sequence.\n\
305 \n\
306 There can be up to two orthogonal comment sequences. This is to support\n\
307 language modes such as C++. By default, all comment sequences are of style\n\
308 a, but you can set the comment sequence style to b (on the second character\n\
309 of a comment-start, or the first character of a comment-end sequence) using\n\
310 this flag:\n\
311 b means CHAR is part of comment sequence b.\n\
312 \n\
313 p means CHAR is a prefix character for `backward-prefix-chars';\n\
314 such characters are treated as whitespace when they occur\n\
315 between expressions.")
316 (char, s, table)
317 */
318
319 DEFUN ("modify-syntax-entry", Fmodify_syntax_entry, Smodify_syntax_entry, 2, 3,
320 /* I really don't know why this is interactive
321 help-form should at least be made useful whilst reading the second arg
322 */
323 "cSet syntax for character: \nsSet syntax for %s to: ",
324 0 /* See immediately above */)
325 (c, newentry, syntax_table)
326 Lisp_Object c, newentry, syntax_table;
327 {
328 register unsigned char *p;
329 register enum syntaxcode code;
330 int val;
331 Lisp_Object match;
332
333 CHECK_NUMBER (c, 0);
334 CHECK_STRING (newentry, 1);
335
336 if (NILP (syntax_table))
337 syntax_table = current_buffer->syntax_table;
338 else
339 check_syntax_table (syntax_table);
340
341 p = XSTRING (newentry)->data;
342 code = (enum syntaxcode) syntax_spec_code[*p++];
343 if (((int) code & 0377) == 0377)
344 error ("invalid syntax description letter: %c", c);
345
346 if (code == Sinherit)
347 {
348 SET_RAW_SYNTAX_ENTRY (syntax_table, c, Qnil);
349 return Qnil;
350 }
351
352 if (*p)
353 {
354 XSETINT (match, *p++);
355 if (XFASTINT (match) == ' ')
356 match = Qnil;
357 }
358 else
359 match = Qnil;
360
361 val = (int) code;
362 while (*p)
363 switch (*p++)
364 {
365 case '1':
366 val |= 1 << 16;
367 break;
368
369 case '2':
370 val |= 1 << 17;
371 break;
372
373 case '3':
374 val |= 1 << 18;
375 break;
376
377 case '4':
378 val |= 1 << 19;
379 break;
380
381 case 'p':
382 val |= 1 << 20;
383 break;
384
385 case 'b':
386 val |= 1 << 21;
387 break;
388 }
389
390 SET_RAW_SYNTAX_ENTRY (syntax_table, c,
391 Fcons (make_number (val), match));
392
393 return Qnil;
394 }
395 \f
396 /* Dump syntax table to buffer in human-readable format */
397
398 static void
399 describe_syntax (value)
400 Lisp_Object value;
401 {
402 register enum syntaxcode code;
403 char desc, match, start1, start2, end1, end2, prefix, comstyle;
404 char str[2];
405 Lisp_Object first, match_lisp;
406
407 Findent_to (make_number (16), make_number (1));
408
409 if (NILP (value))
410 {
411 insert_string ("inherit");
412 return;
413 }
414
415 if (!CONSP (value))
416 {
417 insert_string ("invalid");
418 return;
419 }
420
421 first = XCONS (value)->car;
422 match_lisp = XCONS (value)->cdr;
423
424 if (!INTEGERP (first) || !(NILP (match_lisp) || INTEGERP (match_lisp)))
425 {
426 insert_string ("invalid");
427 return;
428 }
429
430 code = (enum syntaxcode) (first & 0377);
431 start1 = (XINT (first) >> 16) & 1;
432 start2 = (XINT (first) >> 17) & 1;
433 end1 = (XINT (first) >> 18) & 1;
434 end2 = (XINT (first) >> 19) & 1;
435 prefix = (XINT (first) >> 20) & 1;
436 comstyle = (XINT (first) >> 21) & 1;
437
438 if ((int) code < 0 || (int) code >= (int) Smax)
439 {
440 insert_string ("invalid");
441 return;
442 }
443 desc = syntax_code_spec[(int) code];
444
445 str[0] = desc, str[1] = 0;
446 insert (str, 1);
447
448 str[0] = !NILP (match_lisp) ? XINT (match_lisp) : ' ';
449 insert (str, 1);
450
451 if (start1)
452 insert ("1", 1);
453 if (start2)
454 insert ("2", 1);
455
456 if (end1)
457 insert ("3", 1);
458 if (end2)
459 insert ("4", 1);
460
461 if (prefix)
462 insert ("p", 1);
463 if (comstyle)
464 insert ("b", 1);
465
466 insert_string ("\twhich means: ");
467
468 switch (SWITCH_ENUM_CAST (code))
469 {
470 case Swhitespace:
471 insert_string ("whitespace"); break;
472 case Spunct:
473 insert_string ("punctuation"); break;
474 case Sword:
475 insert_string ("word"); break;
476 case Ssymbol:
477 insert_string ("symbol"); break;
478 case Sopen:
479 insert_string ("open"); break;
480 case Sclose:
481 insert_string ("close"); break;
482 case Squote:
483 insert_string ("quote"); break;
484 case Sstring:
485 insert_string ("string"); break;
486 case Smath:
487 insert_string ("math"); break;
488 case Sescape:
489 insert_string ("escape"); break;
490 case Scharquote:
491 insert_string ("charquote"); break;
492 case Scomment:
493 insert_string ("comment"); break;
494 case Sendcomment:
495 insert_string ("endcomment"); break;
496 default:
497 insert_string ("invalid");
498 return;
499 }
500
501 if (!NILP (match_lisp))
502 {
503 insert_string (", matches ");
504 insert_char (XINT (match_lisp));
505 }
506
507 if (start1)
508 insert_string (",\n\t is the first character of a comment-start sequence");
509 if (start2)
510 insert_string (",\n\t is the second character of a comment-start sequence");
511
512 if (end1)
513 insert_string (",\n\t is the first character of a comment-end sequence");
514 if (end2)
515 insert_string (",\n\t is the second character of a comment-end sequence");
516 if (comstyle)
517 insert_string (" (comment style b)");
518
519 if (prefix)
520 insert_string (",\n\t is a prefix character for `backward-prefix-chars'");
521
522 insert_string ("\n");
523 }
524
525 static Lisp_Object
526 describe_syntax_1 (vector)
527 Lisp_Object vector;
528 {
529 struct buffer *old = current_buffer;
530 set_buffer_internal (XBUFFER (Vstandard_output));
531 describe_vector (vector, Qnil, describe_syntax, 0, Qnil, Qnil);
532 call0 (intern ("help-mode"));
533 set_buffer_internal (old);
534 return Qnil;
535 }
536
537 DEFUN ("describe-syntax", Fdescribe_syntax, Sdescribe_syntax, 0, 0, "",
538 "Describe the syntax specifications in the syntax table.\n\
539 The descriptions are inserted in a buffer, which is then displayed.")
540 ()
541 {
542 internal_with_output_to_temp_buffer
543 ("*Help*", describe_syntax_1, current_buffer->syntax_table);
544
545 return Qnil;
546 }
547 \f
548 /* Return the position across COUNT words from FROM.
549 If that many words cannot be found before the end of the buffer, return 0.
550 COUNT negative means scan backward and stop at word beginning. */
551
552 scan_words (from, count)
553 register int from, count;
554 {
555 register int beg = BEGV;
556 register int end = ZV;
557 register int code;
558 int charcode;
559
560 immediate_quit = 1;
561 QUIT;
562
563 while (count > 0)
564 {
565 while (1)
566 {
567 if (from == end)
568 {
569 immediate_quit = 0;
570 return 0;
571 }
572 charcode = FETCH_CHAR (from);
573 code = SYNTAX (charcode);
574 if (words_include_escapes
575 && (code == Sescape || code == Scharquote))
576 break;
577 if (code == Sword)
578 break;
579 from++;
580 }
581 while (1)
582 {
583 if (from == end) break;
584 charcode = FETCH_CHAR (from);
585 code = SYNTAX (charcode);
586 if (!(words_include_escapes
587 && (code == Sescape || code == Scharquote)))
588 if (code != Sword)
589 break;
590 from++;
591 }
592 count--;
593 }
594 while (count < 0)
595 {
596 while (1)
597 {
598 if (from == beg)
599 {
600 immediate_quit = 0;
601 return 0;
602 }
603 charcode = FETCH_CHAR (from - 1);
604 code = SYNTAX (charcode);
605 if (words_include_escapes
606 && (code == Sescape || code == Scharquote))
607 break;
608 if (code == Sword)
609 break;
610 from--;
611 }
612 while (1)
613 {
614 if (from == beg) break;
615 charcode = FETCH_CHAR (from - 1);
616 code = SYNTAX (charcode);
617 if (!(words_include_escapes
618 && (code == Sescape || code == Scharquote)))
619 if (code != Sword)
620 break;
621 from--;
622 }
623 count++;
624 }
625
626 immediate_quit = 0;
627
628 return from;
629 }
630
631 DEFUN ("forward-word", Fforward_word, Sforward_word, 1, 1, "p",
632 "Move point forward ARG words (backward if ARG is negative).\n\
633 Normally returns t.\n\
634 If an edge of the buffer is reached, point is left there\n\
635 and nil is returned.")
636 (count)
637 Lisp_Object count;
638 {
639 int val;
640 CHECK_NUMBER (count, 0);
641
642 if (!(val = scan_words (point, XINT (count))))
643 {
644 SET_PT (XINT (count) > 0 ? ZV : BEGV);
645 return Qnil;
646 }
647 SET_PT (val);
648 return Qt;
649 }
650 \f
651 DEFUN ("forward-comment", Fforward_comment, Sforward_comment, 1, 1, 0,
652 "Move forward across up to N comments. If N is negative, move backward.\n\
653 Stop scanning if we find something other than a comment or whitespace.\n\
654 Set point to where scanning stops.\n\
655 If N comments are found as expected, with nothing except whitespace\n\
656 between them, return t; otherwise return nil.")
657 (count)
658 Lisp_Object count;
659 {
660 register int from;
661 register int stop;
662 register int c, c1;
663 register enum syntaxcode code;
664 int comstyle = 0; /* style of comment encountered */
665 int found;
666 int count1;
667
668 CHECK_NUMBER (count, 0);
669 count1 = XINT (count);
670
671 immediate_quit = 1;
672 QUIT;
673
674 from = PT;
675
676 while (count1 > 0)
677 {
678 stop = ZV;
679 do
680 {
681 if (from == stop)
682 {
683 SET_PT (from);
684 immediate_quit = 0;
685 return Qnil;
686 }
687 c = FETCH_CHAR (from);
688 code = SYNTAX (c);
689 from++;
690 comstyle = 0;
691 if (from < stop && SYNTAX_COMSTART_FIRST (c)
692 && (c1 = FETCH_CHAR (from),
693 SYNTAX_COMSTART_SECOND (c1)))
694 {
695 /* We have encountered a comment start sequence and we
696 are ignoring all text inside comments. We must record
697 the comment style this sequence begins so that later,
698 only a comment end of the same style actually ends
699 the comment section. */
700 code = Scomment;
701 comstyle = SYNTAX_COMMENT_STYLE (c1);
702 from++;
703 }
704 }
705 while (code == Swhitespace || code == Sendcomment);
706 if (code != Scomment)
707 {
708 immediate_quit = 0;
709 SET_PT (from - 1);
710 return Qnil;
711 }
712 /* We're at the start of a comment. */
713 while (1)
714 {
715 if (from == stop)
716 {
717 immediate_quit = 0;
718 SET_PT (from);
719 return Qnil;
720 }
721 c = FETCH_CHAR (from);
722 from++;
723 if (SYNTAX (c) == Sendcomment
724 && SYNTAX_COMMENT_STYLE (c) == comstyle)
725 /* we have encountered a comment end of the same style
726 as the comment sequence which began this comment
727 section */
728 break;
729 if (from < stop && SYNTAX_COMEND_FIRST (c)
730 && (c1 = FETCH_CHAR (from),
731 SYNTAX_COMEND_SECOND (c1))
732 && SYNTAX_COMMENT_STYLE (c) == comstyle)
733 /* we have encountered a comment end of the same style
734 as the comment sequence which began this comment
735 section */
736 { from++; break; }
737 }
738 /* We have skipped one comment. */
739 count1--;
740 }
741
742 while (count1 < 0)
743 {
744 stop = BEGV;
745 while (from > stop)
746 {
747 int quoted;
748
749 from--;
750 quoted = char_quoted (from);
751 if (quoted)
752 from--;
753 c = FETCH_CHAR (from);
754 code = SYNTAX (c);
755 comstyle = 0;
756 if (code == Sendcomment)
757 comstyle = SYNTAX_COMMENT_STYLE (c);
758 if (from > stop && SYNTAX_COMEND_SECOND (c)
759 && (c1 = FETCH_CHAR (from - 1),
760 SYNTAX_COMEND_FIRST (c1))
761 && !char_quoted (from - 1))
762 {
763 /* We must record the comment style encountered so that
764 later, we can match only the proper comment begin
765 sequence of the same style. */
766 code = Sendcomment;
767 comstyle = SYNTAX_COMMENT_STYLE (c1);
768 from--;
769 }
770
771 if (code == Sendcomment && !quoted)
772 {
773 #if 0
774 if (code != SYNTAX (c))
775 /* For a two-char comment ender, we can assume
776 it does end a comment. So scan back in a simple way. */
777 {
778 if (from != stop) from--;
779 while (1)
780 {
781 if ((c = FETCH_CHAR (from),
782 SYNTAX (c) == Scomment)
783 && SYNTAX_COMMENT_STYLE (c) == comstyle)
784 break;
785 if (from == stop)
786 {
787 immediate_quit = 0;
788 SET_PT (from);
789 return Qnil;
790 }
791 from--;
792 if (SYNTAX_COMSTART_SECOND (c)
793 && (c1 = FETCH_CHAR (from),
794 SYNTAX_COMSTART_FIRST (c1))
795 && SYNTAX_COMMENT_STYLE (c) == comstyle
796 && !char_quoted (from))
797 break;
798 }
799 break;
800 }
801 #endif /* 0 */
802
803 /* Look back, counting the parity of string-quotes,
804 and recording the comment-starters seen.
805 When we reach a safe place, assume that's not in a string;
806 then step the main scan to the earliest comment-starter seen
807 an even number of string quotes away from the safe place.
808
809 OFROM[I] is position of the earliest comment-starter seen
810 which is I+2X quotes from the comment-end.
811 PARITY is current parity of quotes from the comment end. */
812 {
813 int parity = 0;
814 char my_stringend = 0;
815 int string_lossage = 0;
816 int comment_end = from;
817 int comstart_pos = 0;
818 int comstart_parity = 0;
819 int scanstart = from - 1;
820
821 /* At beginning of range to scan, we're outside of strings;
822 that determines quote parity to the comment-end. */
823 while (from != stop)
824 {
825 /* Move back and examine a character. */
826 from--;
827
828 c = FETCH_CHAR (from);
829 code = SYNTAX (c);
830
831 /* If this char is the second of a 2-char comment sequence,
832 back up and give the pair the appropriate syntax. */
833 if (from > stop && SYNTAX_COMEND_SECOND (c)
834 && SYNTAX_COMEND_FIRST (FETCH_CHAR (from - 1)))
835 {
836 code = Sendcomment;
837 from--;
838 c = FETCH_CHAR (from);
839 }
840
841 /* If this char starts a 2-char comment start sequence,
842 treat it like a 1-char comment starter. */
843 if (from < scanstart && SYNTAX_COMSTART_FIRST (c)
844 && SYNTAX_COMSTART_SECOND (FETCH_CHAR (from + 1))
845 && comstyle == SYNTAX_COMMENT_STYLE (FETCH_CHAR (from + 1)))
846 code = Scomment;
847
848 /* Ignore escaped characters. */
849 if (char_quoted (from))
850 continue;
851
852 /* Track parity of quotes. */
853 if (code == Sstring)
854 {
855 parity ^= 1;
856 if (my_stringend == 0)
857 my_stringend = c;
858 /* If we have two kinds of string delimiters.
859 There's no way to grok this scanning backwards. */
860 else if (my_stringend != c)
861 string_lossage = 1;
862 }
863
864 /* Record comment-starters according to that
865 quote-parity to the comment-end. */
866 if (code == Scomment)
867 {
868 comstart_parity = parity;
869 comstart_pos = from;
870 }
871
872 /* If we find another earlier comment-ender,
873 any comment-starts earlier than that don't count
874 (because they go with the earlier comment-ender). */
875 if (code == Sendcomment
876 && SYNTAX_COMMENT_STYLE (FETCH_CHAR (from)) == comstyle)
877 break;
878
879 /* Assume a defun-start point is outside of strings. */
880 if (code == Sopen
881 && (from == stop || FETCH_CHAR (from - 1) == '\n'))
882 break;
883 }
884
885 if (comstart_pos == 0)
886 from = comment_end;
887 /* If the earliest comment starter
888 is followed by uniform paired string quotes or none,
889 we know it can't be inside a string
890 since if it were then the comment ender would be inside one.
891 So it does start a comment. Skip back to it. */
892 else if (comstart_parity == 0 && !string_lossage)
893 from = comstart_pos;
894 else
895 {
896 /* We had two kinds of string delimiters mixed up
897 together. Decode this going forwards.
898 Scan fwd from the previous comment ender
899 to the one in question; this records where we
900 last passed a comment starter. */
901 struct lisp_parse_state state;
902 scan_sexps_forward (&state, find_defun_start (comment_end),
903 comment_end - 1, -10000, 0, Qnil, 0);
904 if (state.incomment)
905 from = state.comstart;
906 else
907 /* We can't grok this as a comment; scan it normally. */
908 from = comment_end;
909 }
910 }
911 /* We have skipped one comment. */
912 break;
913 }
914 else if ((code != Swhitespace && code != Scomment) || quoted)
915 {
916 immediate_quit = 0;
917 SET_PT (from + 1);
918 return Qnil;
919 }
920 }
921
922 count1++;
923 }
924
925 SET_PT (from);
926 immediate_quit = 0;
927 return Qt;
928 }
929 \f
930 int parse_sexp_ignore_comments;
931
932 Lisp_Object
933 scan_lists (from, count, depth, sexpflag)
934 register int from;
935 int count, depth, sexpflag;
936 {
937 Lisp_Object val;
938 register int stop;
939 register int c;
940 unsigned char stringterm;
941 int quoted;
942 int mathexit = 0;
943 register enum syntaxcode code;
944 int min_depth = depth; /* Err out if depth gets less than this. */
945 int comstyle = 0; /* style of comment encountered */
946
947 if (depth > 0) min_depth = 0;
948
949 immediate_quit = 1;
950 QUIT;
951
952 while (count > 0)
953 {
954 stop = ZV;
955 while (from < stop)
956 {
957 c = FETCH_CHAR (from);
958 code = SYNTAX (c);
959 from++;
960 if (from < stop && SYNTAX_COMSTART_FIRST (c)
961 && SYNTAX_COMSTART_SECOND (FETCH_CHAR (from))
962 && parse_sexp_ignore_comments)
963 {
964 /* we have encountered a comment start sequence and we
965 are ignoring all text inside comments. we must record
966 the comment style this sequence begins so that later,
967 only a comment end of the same style actually ends
968 the comment section */
969 code = Scomment;
970 comstyle = SYNTAX_COMMENT_STYLE (FETCH_CHAR (from));
971 from++;
972 }
973
974 if (SYNTAX_PREFIX (c))
975 continue;
976
977 switch (SWITCH_ENUM_CAST (code))
978 {
979 case Sescape:
980 case Scharquote:
981 if (from == stop) goto lose;
982 from++;
983 /* treat following character as a word constituent */
984 case Sword:
985 case Ssymbol:
986 if (depth || !sexpflag) break;
987 /* This word counts as a sexp; return at end of it. */
988 while (from < stop)
989 {
990 switch (SWITCH_ENUM_CAST (SYNTAX (FETCH_CHAR (from))))
991 {
992 case Scharquote:
993 case Sescape:
994 from++;
995 if (from == stop) goto lose;
996 break;
997 case Sword:
998 case Ssymbol:
999 case Squote:
1000 break;
1001 default:
1002 goto done;
1003 }
1004 from++;
1005 }
1006 goto done;
1007
1008 case Scomment:
1009 if (!parse_sexp_ignore_comments) break;
1010 while (1)
1011 {
1012 if (from == stop)
1013 {
1014 if (depth == 0)
1015 goto done;
1016 goto lose;
1017 }
1018 c = FETCH_CHAR (from);
1019 if (SYNTAX (c) == Sendcomment
1020 && SYNTAX_COMMENT_STYLE (c) == comstyle)
1021 /* we have encountered a comment end of the same style
1022 as the comment sequence which began this comment
1023 section */
1024 break;
1025 from++;
1026 if (from < stop && SYNTAX_COMEND_FIRST (c)
1027 && SYNTAX_COMEND_SECOND (FETCH_CHAR (from))
1028 && SYNTAX_COMMENT_STYLE (c) == comstyle)
1029 /* we have encountered a comment end of the same style
1030 as the comment sequence which began this comment
1031 section */
1032 { from++; break; }
1033 }
1034 break;
1035
1036 case Smath:
1037 if (!sexpflag)
1038 break;
1039 if (from != stop && c == FETCH_CHAR (from))
1040 from++;
1041 if (mathexit)
1042 {
1043 mathexit = 0;
1044 goto close1;
1045 }
1046 mathexit = 1;
1047
1048 case Sopen:
1049 if (!++depth) goto done;
1050 break;
1051
1052 case Sclose:
1053 close1:
1054 if (!--depth) goto done;
1055 if (depth < min_depth)
1056 error ("Containing expression ends prematurely");
1057 break;
1058
1059 case Sstring:
1060 stringterm = FETCH_CHAR (from - 1);
1061 while (1)
1062 {
1063 if (from >= stop) goto lose;
1064 if (FETCH_CHAR (from) == stringterm) break;
1065 switch (SWITCH_ENUM_CAST (SYNTAX (FETCH_CHAR (from))))
1066 {
1067 case Scharquote:
1068 case Sescape:
1069 from++;
1070 }
1071 from++;
1072 }
1073 from++;
1074 if (!depth && sexpflag) goto done;
1075 break;
1076 }
1077 }
1078
1079 /* Reached end of buffer. Error if within object, return nil if between */
1080 if (depth) goto lose;
1081
1082 immediate_quit = 0;
1083 return Qnil;
1084
1085 /* End of object reached */
1086 done:
1087 count--;
1088 }
1089
1090 while (count < 0)
1091 {
1092 stop = BEGV;
1093 while (from > stop)
1094 {
1095 from--;
1096 if (quoted = char_quoted (from))
1097 from--;
1098 c = FETCH_CHAR (from);
1099 code = SYNTAX (c);
1100 comstyle = 0;
1101 if (code == Sendcomment)
1102 comstyle = SYNTAX_COMMENT_STYLE (c);
1103 if (from > stop && SYNTAX_COMEND_SECOND (c)
1104 && SYNTAX_COMEND_FIRST (FETCH_CHAR (from - 1))
1105 && !char_quoted (from - 1)
1106 && parse_sexp_ignore_comments)
1107 {
1108 /* we must record the comment style encountered so that
1109 later, we can match only the proper comment begin
1110 sequence of the same style */
1111 code = Sendcomment;
1112 comstyle = SYNTAX_COMMENT_STYLE (FETCH_CHAR (from - 1));
1113 from--;
1114 }
1115
1116 if (SYNTAX_PREFIX (c))
1117 continue;
1118
1119 switch (SWITCH_ENUM_CAST (quoted ? Sword : code))
1120 {
1121 case Sword:
1122 case Ssymbol:
1123 if (depth || !sexpflag) break;
1124 /* This word counts as a sexp; count object finished after passing it. */
1125 while (from > stop)
1126 {
1127 quoted = char_quoted (from - 1);
1128 if (quoted)
1129 from--;
1130 if (! (quoted || SYNTAX (FETCH_CHAR (from - 1)) == Sword
1131 || SYNTAX (FETCH_CHAR (from - 1)) == Ssymbol
1132 || SYNTAX (FETCH_CHAR (from - 1)) == Squote))
1133 goto done2;
1134 from--;
1135 }
1136 goto done2;
1137
1138 case Smath:
1139 if (!sexpflag)
1140 break;
1141 if (from != stop && c == FETCH_CHAR (from - 1))
1142 from--;
1143 if (mathexit)
1144 {
1145 mathexit = 0;
1146 goto open2;
1147 }
1148 mathexit = 1;
1149
1150 case Sclose:
1151 if (!++depth) goto done2;
1152 break;
1153
1154 case Sopen:
1155 open2:
1156 if (!--depth) goto done2;
1157 if (depth < min_depth)
1158 error ("Containing expression ends prematurely");
1159 break;
1160
1161 case Sendcomment:
1162 if (!parse_sexp_ignore_comments)
1163 break;
1164 #if 0
1165 if (code != SYNTAX (c))
1166 /* For a two-char comment ender, we can assume
1167 it does end a comment. So scan back in a simple way. */
1168 {
1169 if (from != stop) from--;
1170 while (1)
1171 {
1172 if (SYNTAX (c = FETCH_CHAR (from)) == Scomment
1173 && SYNTAX_COMMENT_STYLE (c) == comstyle)
1174 break;
1175 if (from == stop)
1176 {
1177 if (depth == 0)
1178 goto done2;
1179 goto lose;
1180 }
1181 from--;
1182 if (SYNTAX_COMSTART_SECOND (c)
1183 && SYNTAX_COMSTART_FIRST (FETCH_CHAR (from))
1184 && SYNTAX_COMMENT_STYLE (c) == comstyle
1185 && !char_quoted (from))
1186 break;
1187 }
1188 break;
1189 }
1190 #endif /* 0 */
1191
1192 /* Look back, counting the parity of string-quotes,
1193 and recording the comment-starters seen.
1194 When we reach a safe place, assume that's not in a string;
1195 then step the main scan to the earliest comment-starter seen
1196 an even number of string quotes away from the safe place.
1197
1198 OFROM[I] is position of the earliest comment-starter seen
1199 which is I+2X quotes from the comment-end.
1200 PARITY is current parity of quotes from the comment end. */
1201 {
1202 int parity = 0;
1203 char my_stringend = 0;
1204 int string_lossage = 0;
1205 int comment_end = from;
1206 int comstart_pos = 0;
1207 int comstart_parity = 0;
1208 int scanstart = from - 1;
1209
1210 /* At beginning of range to scan, we're outside of strings;
1211 that determines quote parity to the comment-end. */
1212 while (from != stop)
1213 {
1214 /* Move back and examine a character. */
1215 from--;
1216
1217 c = FETCH_CHAR (from);
1218 code = SYNTAX (c);
1219
1220 /* If this char is the second of a 2-char comment sequence,
1221 back up and give the pair the appropriate syntax. */
1222 if (from > stop && SYNTAX_COMEND_SECOND (c)
1223 && SYNTAX_COMEND_FIRST (FETCH_CHAR (from - 1)))
1224 {
1225 code = Sendcomment;
1226 from--;
1227 c = FETCH_CHAR (from);
1228 }
1229
1230 /* If this char starts a 2-char comment start sequence,
1231 treat it like a 1-char comment starter. */
1232 if (from < scanstart && SYNTAX_COMSTART_FIRST (c)
1233 && SYNTAX_COMSTART_SECOND (FETCH_CHAR (from + 1))
1234 && comstyle == SYNTAX_COMMENT_STYLE (FETCH_CHAR (from + 1)))
1235 code = Scomment;
1236
1237 /* Ignore escaped characters. */
1238 if (char_quoted (from))
1239 continue;
1240
1241 /* Track parity of quotes. */
1242 if (code == Sstring)
1243 {
1244 parity ^= 1;
1245 if (my_stringend == 0)
1246 my_stringend = c;
1247 /* If we have two kinds of string delimiters.
1248 There's no way to grok this scanning backwards. */
1249 else if (my_stringend != c)
1250 string_lossage = 1;
1251 }
1252
1253 /* Record comment-starters according to that
1254 quote-parity to the comment-end. */
1255 if (code == Scomment)
1256 {
1257 comstart_parity = parity;
1258 comstart_pos = from;
1259 }
1260
1261 /* If we find another earlier comment-ender,
1262 any comment-starts earlier than that don't count
1263 (because they go with the earlier comment-ender). */
1264 if (code == Sendcomment
1265 && SYNTAX_COMMENT_STYLE (FETCH_CHAR (from)) == comstyle)
1266 break;
1267
1268 /* Assume a defun-start point is outside of strings. */
1269 if (code == Sopen
1270 && (from == stop || FETCH_CHAR (from - 1) == '\n'))
1271 break;
1272 }
1273
1274 if (comstart_pos == 0)
1275 from = comment_end;
1276 /* If the earliest comment starter
1277 is followed by uniform paired string quotes or none,
1278 we know it can't be inside a string
1279 since if it were then the comment ender would be inside one.
1280 So it does start a comment. Skip back to it. */
1281 else if (comstart_parity == 0 && !string_lossage)
1282 from = comstart_pos;
1283 else
1284 {
1285 /* We had two kinds of string delimiters mixed up
1286 together. Decode this going forwards.
1287 Scan fwd from the previous comment ender
1288 to the one in question; this records where we
1289 last passed a comment starter. */
1290 struct lisp_parse_state state;
1291 scan_sexps_forward (&state, find_defun_start (comment_end),
1292 comment_end - 1, -10000, 0, Qnil, 0);
1293 if (state.incomment)
1294 from = state.comstart;
1295 else
1296 /* We can't grok this as a comment; scan it normally. */
1297 from = comment_end;
1298 }
1299 }
1300 break;
1301
1302 case Sstring:
1303 stringterm = FETCH_CHAR (from);
1304 while (1)
1305 {
1306 if (from == stop) goto lose;
1307 if (!char_quoted (from - 1)
1308 && stringterm == FETCH_CHAR (from - 1))
1309 break;
1310 from--;
1311 }
1312 from--;
1313 if (!depth && sexpflag) goto done2;
1314 break;
1315 }
1316 }
1317
1318 /* Reached start of buffer. Error if within object, return nil if between */
1319 if (depth) goto lose;
1320
1321 immediate_quit = 0;
1322 return Qnil;
1323
1324 done2:
1325 count++;
1326 }
1327
1328
1329 immediate_quit = 0;
1330 XSETFASTINT (val, from);
1331 return val;
1332
1333 lose:
1334 error ("Unbalanced parentheses");
1335 /* NOTREACHED */
1336 }
1337
1338 static int
1339 char_quoted (pos)
1340 register int pos;
1341 {
1342 register enum syntaxcode code;
1343 register int beg = BEGV;
1344 register int quoted = 0;
1345
1346 while (pos > beg
1347 && ((code = SYNTAX (FETCH_CHAR (pos - 1))) == Scharquote
1348 || code == Sescape))
1349 pos--, quoted = !quoted;
1350 return quoted;
1351 }
1352
1353 DEFUN ("scan-lists", Fscan_lists, Sscan_lists, 3, 3, 0,
1354 "Scan from character number FROM by COUNT lists.\n\
1355 Returns the character number of the position thus found.\n\
1356 \n\
1357 If DEPTH is nonzero, paren depth begins counting from that value,\n\
1358 only places where the depth in parentheses becomes zero\n\
1359 are candidates for stopping; COUNT such places are counted.\n\
1360 Thus, a positive value for DEPTH means go out levels.\n\
1361 \n\
1362 Comments are ignored if `parse-sexp-ignore-comments' is non-nil.\n\
1363 \n\
1364 If the beginning or end of (the accessible part of) the buffer is reached\n\
1365 and the depth is wrong, an error is signaled.\n\
1366 If the depth is right but the count is not used up, nil is returned.")
1367 (from, count, depth)
1368 Lisp_Object from, count, depth;
1369 {
1370 CHECK_NUMBER (from, 0);
1371 CHECK_NUMBER (count, 1);
1372 CHECK_NUMBER (depth, 2);
1373
1374 return scan_lists (XINT (from), XINT (count), XINT (depth), 0);
1375 }
1376
1377 DEFUN ("scan-sexps", Fscan_sexps, Sscan_sexps, 2, 2, 0,
1378 "Scan from character number FROM by COUNT balanced expressions.\n\
1379 If COUNT is negative, scan backwards.\n\
1380 Returns the character number of the position thus found.\n\
1381 \n\
1382 Comments are ignored if `parse-sexp-ignore-comments' is non-nil.\n\
1383 \n\
1384 If the beginning or end of (the accessible part of) the buffer is reached\n\
1385 in the middle of a parenthetical grouping, an error is signaled.\n\
1386 If the beginning or end is reached between groupings\n\
1387 but before count is used up, nil is returned.")
1388 (from, count)
1389 Lisp_Object from, count;
1390 {
1391 CHECK_NUMBER (from, 0);
1392 CHECK_NUMBER (count, 1);
1393
1394 return scan_lists (XINT (from), XINT (count), 0, 1);
1395 }
1396
1397 DEFUN ("backward-prefix-chars", Fbackward_prefix_chars, Sbackward_prefix_chars,
1398 0, 0, 0,
1399 "Move point backward over any number of chars with prefix syntax.\n\
1400 This includes chars with \"quote\" or \"prefix\" syntax (' or p).")
1401 ()
1402 {
1403 int beg = BEGV;
1404 int pos = point;
1405
1406 while (pos > beg && !char_quoted (pos - 1)
1407 && (SYNTAX (FETCH_CHAR (pos - 1)) == Squote
1408 || SYNTAX_PREFIX (FETCH_CHAR (pos - 1))))
1409 pos--;
1410
1411 SET_PT (pos);
1412
1413 return Qnil;
1414 }
1415 \f
1416 /* Parse forward from FROM to END,
1417 assuming that FROM has state OLDSTATE (nil means FROM is start of function),
1418 and return a description of the state of the parse at END.
1419 If STOPBEFORE is nonzero, stop at the start of an atom.
1420 If COMMENTSTOP is nonzero, stop at the start of a comment. */
1421
1422 static void
1423 scan_sexps_forward (stateptr, from, end, targetdepth,
1424 stopbefore, oldstate, commentstop)
1425 struct lisp_parse_state *stateptr;
1426 register int from;
1427 int end, targetdepth, stopbefore;
1428 Lisp_Object oldstate;
1429 int commentstop;
1430 {
1431 struct lisp_parse_state state;
1432
1433 register enum syntaxcode code;
1434 struct level { int last, prev; };
1435 struct level levelstart[100];
1436 register struct level *curlevel = levelstart;
1437 struct level *endlevel = levelstart + 100;
1438 char prev;
1439 register int depth; /* Paren depth of current scanning location.
1440 level - levelstart equals this except
1441 when the depth becomes negative. */
1442 int mindepth; /* Lowest DEPTH value seen. */
1443 int start_quoted = 0; /* Nonzero means starting after a char quote */
1444 Lisp_Object tem;
1445
1446 immediate_quit = 1;
1447 QUIT;
1448
1449 if (NILP (oldstate))
1450 {
1451 depth = 0;
1452 state.instring = -1;
1453 state.incomment = 0;
1454 state.comstyle = 0; /* comment style a by default */
1455 }
1456 else
1457 {
1458 tem = Fcar (oldstate);
1459 if (!NILP (tem))
1460 depth = XINT (tem);
1461 else
1462 depth = 0;
1463
1464 oldstate = Fcdr (oldstate);
1465 oldstate = Fcdr (oldstate);
1466 oldstate = Fcdr (oldstate);
1467 tem = Fcar (oldstate);
1468 state.instring = !NILP (tem) ? XINT (tem) : -1;
1469
1470 oldstate = Fcdr (oldstate);
1471 tem = Fcar (oldstate);
1472 state.incomment = !NILP (tem);
1473
1474 oldstate = Fcdr (oldstate);
1475 tem = Fcar (oldstate);
1476 start_quoted = !NILP (tem);
1477
1478 /* if the eight element of the list is nil, we are in comment
1479 style a. if it is non-nil, we are in comment style b */
1480 oldstate = Fcdr (oldstate);
1481 oldstate = Fcdr (oldstate);
1482 tem = Fcar (oldstate);
1483 state.comstyle = !NILP (tem);
1484 }
1485 state.quoted = 0;
1486 mindepth = depth;
1487
1488 curlevel->prev = -1;
1489 curlevel->last = -1;
1490
1491 /* Enter the loop at a place appropriate for initial state. */
1492
1493 if (state.incomment) goto startincomment;
1494 if (state.instring >= 0)
1495 {
1496 if (start_quoted) goto startquotedinstring;
1497 goto startinstring;
1498 }
1499 if (start_quoted) goto startquoted;
1500
1501 while (from < end)
1502 {
1503 code = SYNTAX (FETCH_CHAR (from));
1504 from++;
1505 if (code == Scomment)
1506 state.comstart = from-1;
1507
1508 else if (from < end && SYNTAX_COMSTART_FIRST (FETCH_CHAR (from - 1))
1509 && SYNTAX_COMSTART_SECOND (FETCH_CHAR (from)))
1510 {
1511 /* Record the comment style we have entered so that only
1512 the comment-end sequence of the same style actually
1513 terminates the comment section. */
1514 code = Scomment;
1515 state.comstyle = SYNTAX_COMMENT_STYLE (FETCH_CHAR (from));
1516 state.comstart = from-1;
1517 from++;
1518 }
1519
1520 if (SYNTAX_PREFIX (FETCH_CHAR (from - 1)))
1521 continue;
1522 switch (SWITCH_ENUM_CAST (code))
1523 {
1524 case Sescape:
1525 case Scharquote:
1526 if (stopbefore) goto stop; /* this arg means stop at sexp start */
1527 curlevel->last = from - 1;
1528 startquoted:
1529 if (from == end) goto endquoted;
1530 from++;
1531 goto symstarted;
1532 /* treat following character as a word constituent */
1533 case Sword:
1534 case Ssymbol:
1535 if (stopbefore) goto stop; /* this arg means stop at sexp start */
1536 curlevel->last = from - 1;
1537 symstarted:
1538 while (from < end)
1539 {
1540 switch (SWITCH_ENUM_CAST (SYNTAX (FETCH_CHAR (from))))
1541 {
1542 case Scharquote:
1543 case Sescape:
1544 from++;
1545 if (from == end) goto endquoted;
1546 break;
1547 case Sword:
1548 case Ssymbol:
1549 case Squote:
1550 break;
1551 default:
1552 goto symdone;
1553 }
1554 from++;
1555 }
1556 symdone:
1557 curlevel->prev = curlevel->last;
1558 break;
1559
1560 startincomment:
1561 if (commentstop)
1562 goto done;
1563 if (from != BEGV)
1564 {
1565 /* Enter the loop in the middle so that we find
1566 a 2-char comment ender if we start in the middle of it. */
1567 prev = FETCH_CHAR (from - 1);
1568 goto startincomment_1;
1569 }
1570 /* At beginning of buffer, enter the loop the ordinary way. */
1571
1572 case Scomment:
1573 state.incomment = 1;
1574 if (commentstop)
1575 goto done;
1576 while (1)
1577 {
1578 if (from == end) goto done;
1579 prev = FETCH_CHAR (from);
1580 if (SYNTAX (prev) == Sendcomment
1581 && SYNTAX_COMMENT_STYLE (prev) == state.comstyle)
1582 /* Only terminate the comment section if the endcomment
1583 of the same style as the start sequence has been
1584 encountered. */
1585 break;
1586 from++;
1587 startincomment_1:
1588 if (from < end && SYNTAX_COMEND_FIRST (prev)
1589 && SYNTAX_COMEND_SECOND (FETCH_CHAR (from))
1590 && SYNTAX_COMMENT_STYLE (prev) == state.comstyle)
1591 /* Only terminate the comment section if the end-comment
1592 sequence of the same style as the start sequence has
1593 been encountered. */
1594 { from++; break; }
1595 }
1596 state.incomment = 0;
1597 state.comstyle = 0; /* reset the comment style */
1598 break;
1599
1600 case Sopen:
1601 if (stopbefore) goto stop; /* this arg means stop at sexp start */
1602 depth++;
1603 /* curlevel++->last ran into compiler bug on Apollo */
1604 curlevel->last = from - 1;
1605 if (++curlevel == endlevel)
1606 error ("Nesting too deep for parser");
1607 curlevel->prev = -1;
1608 curlevel->last = -1;
1609 if (targetdepth == depth) goto done;
1610 break;
1611
1612 case Sclose:
1613 depth--;
1614 if (depth < mindepth)
1615 mindepth = depth;
1616 if (curlevel != levelstart)
1617 curlevel--;
1618 curlevel->prev = curlevel->last;
1619 if (targetdepth == depth) goto done;
1620 break;
1621
1622 case Sstring:
1623 if (stopbefore) goto stop; /* this arg means stop at sexp start */
1624 curlevel->last = from - 1;
1625 state.instring = FETCH_CHAR (from - 1);
1626 startinstring:
1627 while (1)
1628 {
1629 if (from >= end) goto done;
1630 if (FETCH_CHAR (from) == state.instring) break;
1631 switch (SWITCH_ENUM_CAST (SYNTAX (FETCH_CHAR (from))))
1632 {
1633 case Scharquote:
1634 case Sescape:
1635 from++;
1636 startquotedinstring:
1637 if (from >= end) goto endquoted;
1638 }
1639 from++;
1640 }
1641 state.instring = -1;
1642 curlevel->prev = curlevel->last;
1643 from++;
1644 break;
1645
1646 case Smath:
1647 break;
1648 }
1649 }
1650 goto done;
1651
1652 stop: /* Here if stopping before start of sexp. */
1653 from--; /* We have just fetched the char that starts it; */
1654 goto done; /* but return the position before it. */
1655
1656 endquoted:
1657 state.quoted = 1;
1658 done:
1659 state.depth = depth;
1660 state.mindepth = mindepth;
1661 state.thislevelstart = curlevel->prev;
1662 state.prevlevelstart
1663 = (curlevel == levelstart) ? -1 : (curlevel - 1)->last;
1664 state.location = from;
1665 immediate_quit = 0;
1666
1667 *stateptr = state;
1668 }
1669
1670 /* This comment supplies the doc string for parse-partial-sexp,
1671 for make-docfile to see. We cannot put this in the real DEFUN
1672 due to limits in the Unix cpp.
1673
1674 DEFUN ("parse-partial-sexp", Ffoo, Sfoo, 2, 6, 0,
1675 "Parse Lisp syntax starting at FROM until TO; return status of parse at TO.\n\
1676 Parsing stops at TO or when certain criteria are met;\n\
1677 point is set to where parsing stops.\n\
1678 If fifth arg STATE is omitted or nil,\n\
1679 parsing assumes that FROM is the beginning of a function.\n\
1680 Value is a list of eight elements describing final state of parsing:\n\
1681 0. depth in parens.\n\
1682 1. character address of start of innermost containing list; nil if none.\n\
1683 2. character address of start of last complete sexp terminated.\n\
1684 3. non-nil if inside a string.\n\
1685 (it is the character that will terminate the string.)\n\
1686 4. t if inside a comment.\n\
1687 5. t if following a quote character.\n\
1688 6. the minimum paren-depth encountered during this scan.\n\
1689 7. t if in a comment of style `b'.\n\
1690 If third arg TARGETDEPTH is non-nil, parsing stops if the depth\n\
1691 in parentheses becomes equal to TARGETDEPTH.\n\
1692 Fourth arg STOPBEFORE non-nil means stop when come to\n\
1693 any character that starts a sexp.\n\
1694 Fifth arg STATE is an eight-list like what this function returns.\n\
1695 It is used to initialize the state of the parse. Its second and third
1696 elements are ignored.
1697 Sixth args COMMENTSTOP non-nil means stop at the start of a comment.")
1698 (from, to, targetdepth, stopbefore, state, commentstop)
1699 */
1700
1701 DEFUN ("parse-partial-sexp", Fparse_partial_sexp, Sparse_partial_sexp, 2, 6, 0,
1702 0 /* See immediately above */)
1703 (from, to, targetdepth, stopbefore, oldstate, commentstop)
1704 Lisp_Object from, to, targetdepth, stopbefore, oldstate, commentstop;
1705 {
1706 struct lisp_parse_state state;
1707 int target;
1708
1709 if (!NILP (targetdepth))
1710 {
1711 CHECK_NUMBER (targetdepth, 3);
1712 target = XINT (targetdepth);
1713 }
1714 else
1715 target = -100000; /* We won't reach this depth */
1716
1717 validate_region (&from, &to);
1718 scan_sexps_forward (&state, XINT (from), XINT (to),
1719 target, !NILP (stopbefore), oldstate,
1720 !NILP (commentstop));
1721
1722 SET_PT (state.location);
1723
1724 return Fcons (make_number (state.depth),
1725 Fcons (state.prevlevelstart < 0 ? Qnil : make_number (state.prevlevelstart),
1726 Fcons (state.thislevelstart < 0 ? Qnil : make_number (state.thislevelstart),
1727 Fcons (state.instring >= 0 ? make_number (state.instring) : Qnil,
1728 Fcons (state.incomment ? Qt : Qnil,
1729 Fcons (state.quoted ? Qt : Qnil,
1730 Fcons (make_number (state.mindepth),
1731 Fcons (state.comstyle ? Qt : Qnil,
1732 Qnil))))))));
1733 }
1734 \f
1735 init_syntax_once ()
1736 {
1737 register int i;
1738 Lisp_Object temp;
1739
1740 /* This has to be done here, before we call Fmake_char_table. */
1741 Qsyntax_table = intern ("syntax-table");
1742 staticpro (&Qsyntax_table);
1743
1744 /* Intern this now in case it isn't already done.
1745 Setting this variable twice is harmless.
1746 But don't staticpro it here--that is done in alloc.c. */
1747 Qchar_table_extra_slots = intern ("char-table-extra-slots");
1748
1749 /* Now we are ready to set up this property, so we can
1750 create syntax tables. */
1751 Fput (Qsyntax_table, Qchar_table_extra_slots, make_number (0));
1752
1753 temp = Fcons (make_number ((int) Swhitespace), Qnil);
1754
1755 Vstandard_syntax_table = Fmake_char_table (Qsyntax_table, temp);
1756
1757 temp = Fcons (make_number ((int) Sword), Qnil);
1758 for (i = 'a'; i <= 'z'; i++)
1759 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, i, temp);
1760 for (i = 'A'; i <= 'Z'; i++)
1761 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, i, temp);
1762 for (i = '0'; i <= '9'; i++)
1763 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, i, temp);
1764
1765 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '$', temp);
1766 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '%', temp);
1767
1768 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '(',
1769 Fcons (make_number (Sopen), make_number (')')));
1770 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, ')',
1771 Fcons (make_number (Sclose), make_number ('(')));
1772 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '[',
1773 Fcons (make_number (Sopen), make_number (']')));
1774 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, ']',
1775 Fcons (make_number (Sclose), make_number ('[')));
1776 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '{',
1777 Fcons (make_number (Sopen), make_number ('}')));
1778 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '}',
1779 Fcons (make_number (Sclose), make_number ('{')));
1780 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '"',
1781 Fcons (make_number ((int) Sstring), Qnil));
1782 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, '\\',
1783 Fcons (make_number ((int) Sescape), Qnil));
1784
1785 temp = Fcons (make_number ((int) Ssymbol), Qnil);
1786 for (i = 0; i < 10; i++)
1787 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, "_-+*/&|<>="[i], temp);
1788
1789 temp = Fcons (make_number ((int) Spunct), Qnil);
1790 for (i = 0; i < 12; i++)
1791 SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, ".,;:?!#@~^'`"[i], temp);
1792 }
1793
1794 syms_of_syntax ()
1795 {
1796 Qsyntax_table_p = intern ("syntax-table-p");
1797 staticpro (&Qsyntax_table_p);
1798
1799 DEFVAR_BOOL ("parse-sexp-ignore-comments", &parse_sexp_ignore_comments,
1800 "Non-nil means `forward-sexp', etc., should treat comments as whitespace.");
1801
1802 words_include_escapes = 0;
1803 DEFVAR_BOOL ("words-include-escapes", &words_include_escapes,
1804 "Non-nil means `forward-word', etc., should treat escape chars part of words.");
1805
1806 defsubr (&Ssyntax_table_p);
1807 defsubr (&Ssyntax_table);
1808 defsubr (&Sstandard_syntax_table);
1809 defsubr (&Scopy_syntax_table);
1810 defsubr (&Sset_syntax_table);
1811 defsubr (&Schar_syntax);
1812 defsubr (&Smatching_paren);
1813 defsubr (&Smodify_syntax_entry);
1814 defsubr (&Sdescribe_syntax);
1815
1816 defsubr (&Sforward_word);
1817
1818 defsubr (&Sforward_comment);
1819 defsubr (&Sscan_lists);
1820 defsubr (&Sscan_sexps);
1821 defsubr (&Sbackward_prefix_chars);
1822 defsubr (&Sparse_partial_sexp);
1823 }