[net.emacs] New version of syntax.c$ParenScan

chris@umcp-cs (12/19/83)

Here's a new and improved version of ParenScan, the paren matcher, with
new and improved bugs (just kidding) for your Gosling Emacs (either #85
or #264).  This one understands comments.  I've also included two minor
changes that make dump-syntax-table perform name completion.

While it can now correctly match the {} in

	foo () {
	    /* comment's got an apostrophe */
	    ...
	}

ParenScan still has trouble with ``nested'' comments such as

	bar () {
	/*  int i;	/* not used */
	}

because it can't tell, when scanning backwards, that the second "/*" is
still within an enclosing "/*".  This is usually not a problem.

You will have to modify your electric-c mode to define /* and */ as
comment characters, with

    (modify-syntax-entry "  { */")	; /, with *, begins comments: /*
    (modify-syntax-entry "   }/*")	; *, with /, ends comments:    */

Equivalent changes should be made to mlisp and lisp modes to make them
start comments with ; and end them with \n.

First, ParenScan:
-----------------------------------------------------------------------
/* Primitive function for paren matching.  Leaves dot at enclosing left
   paren, or at top of buffer if none.  Stops at a zero-level newline if
   StopAtNewline is set.  Returns (to MLisp) 1 if it finds a match,
   0 if not. */
/* Bug:  if the stop-comment auxil character is also a quote, paren, or
   prefix quote, reverse scans will sometimes be wrong. */
static
ParenScan (StopAtNewline, forward) {
    register struct SyntaxTable *s = bf_mode.md_syntax;
    register    enum SyntaxKinds k;
    register    c,
                pc,
                on_on = 1,
                ParenLevel = 0;
    char    parenstack[200];
    int     InString = 0,
            InComment = 0,
            MatchingQuote,
            delta = 1;

    MLvalue -> exp_type = IsInteger;
    MLvalue -> exp_int = 0;
    if (forward != 0 && forward != 1) {
	error ("panic: ParenScan");
	return 0;
    }
    if (StopAtNewline) {	/* Skip over whitespace */
	pc = dot - 1;
	if (forward)
	    while (pc < NumCharacters) {
		if ((c = CharAt (pc)) != ' ' && c != '\t' && c != '\n')
		    break;
		pc++;
	    }
	else
	    while (pc > FirstCharacter) {
		if ((c = CharAt (pc)) != ' ' && c != '\t' && c != '\n')
		    break;
		pc--;
	    }
	SetDot (pc + 1);
    }
 /* Scan through buffer until done or error */
    while (on_on && !err) {
	if (forward) {
	    if (dot > NumCharacters)
		return 0;
	    DotRight (delta);
	    delta = 1;
	}
	else
	    if (dot <= FirstCharacter)
		return 0;
	c = CharAt (dot - 1);
    /* If in a comment, see if we are now out.  If not in a comment, see
       if we are now in.  The comparisons to forward account for the fact
       that reverse scans start comments on seeing the end-comment character,
       and end comments on seeing the start-comment character. */
    /* The bug is in here:  we should look at (dot-2) for two-character
       end-comment strings when doing reverse scans, not at (dot-1).  I
       don't think it's worth the effort. */
	if (InComment != forward && s -> s_table[c].BeginComment ||
		InComment == forward && s -> s_table[c].EndComment) {
	    if ((pc = s -> s_table[c].CommentAux) == 0) {
		InComment = !InComment;
		goto advance;
	    }
	    if (dot <= NumCharacters && pc == CharAt (dot)) {
		if (forward)
		    delta++;
		InComment = !InComment;
		goto advance;
	    }
	}
    /* If in a comment, just continue on */
	if (InComment)
	    goto advance;
    /* See what kind of character we have.  Carefully check for quoted
       quotes (and even for quoted quoters but no further; this takes
       care of things like "'\''" and "'\\'"). */
	k = s -> s_table[c].s_kind;
	if (dot > 2 && s -> s_table[CharAt (dot-2)].s_kind == PrefixQuote &&
		(dot==3 || s -> s_table[CharAt (dot-3)].s_kind != PrefixQuote))
		    k = WordChar;
	if (k == PairedQuote) {
	    if (!InString)	/* start quotes */
		InString++, MatchingQuote = c;
	    else		/* end quotes (if it's the same quote) */
		if (c == MatchingQuote)
		    InString = 0;
	}
	if (c == '\n' && ParenLevel == 0 && StopAtNewline)
	    return 0;
	if (!InString && (k == EndParen || k == BeginParen)) {
	    if (forward == (k == BeginParen))/* Push a paren */
		parenstack[ParenLevel++] = s -> s_table[c].MatchingParen;
	    else {		/* Pop a paren */
		if (ParenLevel == 0)
		    on_on = 0;	/* Should maybe be error? */
		else {
		    if (parenstack[--ParenLevel] != c)
			error ("Parenthesis mismatch.");
		    if (ParenLevel == 0 && !StopAtNewline)
			on_on = 0;
		}
	    }
	}
advance: 
	if (!forward) {
	    DotLeft (delta);
	    delta = 1;
	}
    }
    MLvalue -> exp_int = 1;
    return 0;
}
-----------------------------------------------------------------------
Now, the diff listing, not including ParenScan:
-----------------------------------------------------------------------
RCS file: RCS/syntax.c,v
retrieving revision 1.1
diff -b -c -r1.1 syntax.c
*** /tmp/,RCSt1005994	Mon Dec 19 05:10:26 1983
--- syntax.c	Mon Dec 19 04:58:56 1983
***************
*** 13,14
  static
! char *SyntaxTableNames[MaxSyntaxTables];

--- 13,14 -----
  static
! char *SyntaxTableNames[MaxSyntaxTables + 1];
***************
*** 231,236
      char c;
      
!     p = locate (getnbstr (": dump-syntax-table "));
!     if (p == 0)
  	return 0;
      SetBfn ("Syntax table");

--- 266,272 -----
      char c;
      
!     i = getword (SyntaxTableNames, ": dump-syntax-table ");
!     if (i < 0)
  	return 0;
+     p = SyntaxTables[i];
      SetBfn ("Syntax table");