CC MODE Version 5 Documentation. Node: Syntactic Symbols

PREV Customizing Indentation UP Top NEXT Performance Issues

Chapter 7: Syntactic Symbols

Here is a complete list of the recognized syntactic symbols as described in the c-offsets-alist variable, along with a brief description. More detailed descriptions follow below.

Most syntactic symbol names follow a general naming convention. When a line begins with an open or close brace, the syntactic symbol will contain the suffix -open or -close respectively.

Usually, a distinction is made between the first line that introduces a construct and lines that continue a construct, and the syntactic symbols that represent these lines will contain the suffix -intro or -cont respectively. As a sub-classification of this scheme, a line which is the first of a particular brace block construct will contain the suffix -block-intro.

Let's look at some examples to understand how this works. Remember that you can check the syntax of any line by using C-c C-s.


  1: void
  2: swap( int& a, int& b )
  3: {
  4:     int tmp = a;
  5:     a = b;
  6:     b = tmp;
  7:     int ignored =
  8:         a + b;
  9: }

Line 1 shows a topmost-intro since it is the first line that introduces a top-level construct. Line 2 is a continuation of the top-level construct introduction so it has the syntax topmost-intro-cont. Line 3 shows a defun-open since it is the brace that opens a top-level function definition. Line 9 is a defun-close since it contains the brace that closes the top-level function definition. Line 4 is a defun-block-intro, i.e. it is the first line of a brace-block, enclosed in a top-level function definition.

Lines 5, 6, and 7 are all given statement syntax since there isn't much special about them. Note however that line 8 is given statement-cont syntax since it continues the statement begun on the previous line.

Here's another example, which illustrates some C++ class syntactic symbols:


   1: class Bass
   2:     : public Guitar,
   3:       public Amplifiable
   4: {
   5: public:
   6:     Bass()
   7:         : eString( new BassString( 0.105 )),
   8:           aString( new BassString( 0.085 )),
   9:           dString( new BassString( 0.065 )),
  10:           gString( new BassString( 0.045 ))
  11:     {
  12:         eString.tune( 'E' );
  13:         aString.tune( 'A' );
  14:         dString.tune( 'D' );
  15:         gString.tune( 'G' );
  16:     }
  17:     friend class Luthier;
  18: }

As in the previous example, line 1 has the topmost-intro syntax. Here however, the brace that opens a C++ class definition on line 4 is assigned the class-open syntax. Note that in C++, classes, structs, and unions are essentially equivalent syntactically (and are very similar semantically), so replacing the class keyword in the example above with struct or union would still result in a syntax of class-open for line 4 [1]. Similarly, line 18 is assigned class-close syntax.

Line 2 introduces the inheritance list for the class so it is assigned the inher-intro syntax, and line 3, which continues the inheritance list is given inher-cont syntax.

Hitting C-c C-s on line 5 shows the following analysis:


((inclass . 1) (access-label . 67))

The primary syntactic symbol for this line is access-label as this a label keyword that specifies access protection in C++. However, because this line is also a top-level construct inside a class definition, the analysis actually shows two syntactic symbols. The other syntactic symbol assigned to this line is inclass. Similarly, line 6 is given both inclass and topmost-intro syntax:


((inclass . 58) (topmost-intro . 60))

Line 7 introduces a C++ member initialization list and as such is given member-init-intro syntax. Note that in this case it is not assigned inclass since this is not considered a top-level construct. Lines 8 through 10 are all assigned member-init-cont since they continue the member initialization list started on line 7.

Line 11's analysis is a bit more complicated:


((inclass . 1) (inline-open))

This line is assigned a syntax of both inline-open and inclass because it opens an in-class C++ inline method definition. This is distinct from, but related to, the C++ notion of an inline function in that its definition occurs inside an enclosing class definition, which in C++ implies that the function should be inlined. If though, the definition of the Bass constructor appeared outside the class definition, the construct would be given the defun-open syntax, even if the keyword inline appeared before the method name, as in:


class Bass
    : public Guitar,
      public Amplifiable
{
public:
    Bass();
}

inline
Bass::Bass()
    : eString( new BassString( 0.105 )),
      aString( new BassString( 0.085 )),
      dString( new BassString( 0.065 )),
      gString( new BassString( 0.045 ))
{
    eString.tune( 'E' );
    aString.tune( 'A' );
    dString.tune( 'D' );
    gString.tune( 'G' );
}

Returning to the previous example, line 16 is given inline-close syntax, while line 12 is given defun-block-open syntax, and lines 13 through 15 are all given statement syntax. Line 17 is interesting in that its syntactic analysis list contains three elements:


((friend) (inclass . 58) (topmost-intro . 380))

The friend syntactic symbol is a modifier that typically does not have a relative buffer position.

Template definitions introduce yet another syntactic symbol:


   1: ThingManager <int,
   2:    Framework::Callback *,
   3:    Mutex> framework_callbacks;

Here, line 1 is analyzed as a topmost-intro, but lines 2 and 3 are both analyzed as template-args-cont lines.

Here is another (totally contrived) example which illustrates how syntax is assigned to various conditional constructs:


   1: void spam( int index )
   2: {
   3:     for( int i=0; i<index; i++ )
   4:     {
   5:         if( i == 10 )
   6:         {
   7:             do_something_special();
   8:         }
   9:         else
  10:             do_something( i );
  11:     }
  12:     do {
  13:         another_thing( i-- );
  14:     }
  15:     while( i > 0 );
  16: }


Only the lines that illustrate new syntactic symbols will be discussed.

Line 4 has a brace which opens a conditional's substatement block. It is thus assigned substatement-open syntax, and since line 5 is the first line in the substatement block, it is assigned substatement-block-intro syntax. Lines 6 and 7 are assigned similar syntax. Line 8 contains the brace that closes the inner substatement block. It is given the syntax block-close, as are lines 11 and 14.

Line 9 is a little different --- since it contains the keyword else matching the if statement introduced on line 5, it is given the else-clause syntax. Note also that line 10 is slightly different too. Because else is considered a conditional introducing keyword [2], and because the following substatement is not a brace block, line 10 is assigned the substatement syntax.

One other difference is seen on line 15. The while construct that closes a do conditional is given the special syntax do-while-closure if it appears on a line by itself. Note that if the while appeared on the same line as the preceding close brace, that line would have been assigned block-close syntax instead.

Switch statements have their own set of syntactic symbols. Here's an example:


   1: void spam( enum Ingredient i )
   2: {
   3:     switch( i ) {
   4:     case Ham:
   5:         be_a_pig();
   6:         break;
   7:     case Salt:
   8:         drink_some_water();
   9:         break;
  10:     default:
  11:         {
  12:             what_is_it();
  13:             break;
  14:         }
  15:     }
  14: }

Here, lines 4, 7, and 10 are all assigned case-label syntax, while lines 5 and 8 are assigned statement-case-intro. Line 11 is treated slightly differently since it contains a brace that opens a block --- it is given statement-case-open syntax.

There are a set of syntactic symbols that are used to recognize constructs inside of brace lists. A brace list is defined as an enum or aggregate initializer list, such as might statically initialize an array of structs. For example:


  1: static char* ingredients[] =
  2: {
  3:     "Ham",
  4:     "Salt",
  5:     NULL
  6: }

Following convention, line 2 in this example is assigned brace-list-open syntax, and line 3 is assigned brace-list-intro syntax. Likewise, line 6 is assigned brace-list-close syntax. Lines 4 and 5 however, are assigned brace-list-entry syntax, as would all subsequent lines in this initializer list.

External language definition blocks also have their own syntactic symbols. In this example:


   1: extern "C" 
   2: {
   3:     int thing_one( int );
   4:     int thing_two( double );
   5: }

line 2 is given the extern-lang-open syntax, while line 5 is given the extern-lang-close syntax. The analysis for line 3 yields: ((inextern-lang) (topmost-intro . 14)), where inextern-lang is a modifier similar in purpose to inclass.

Similarly, C++ namespace constructs have their own associated syntactic symbols. In this example:


   1: namespace foo
   2: {
   3:     void xxx() {}
   4: }

line 2 is given the namespace-open syntax, while line 4 is given the namespace-close syntax. The analysis for line 3 yields: ((innamespace) (topmost-intro . 17)), where innamespace is a modifier similar in purpose to inextern-lang and inclass.

A number of syntactic symbols are associated with parenthesis lists, a.k.a argument lists, as found in function declarations and function calls. This example illustrates these:


   1: void a_function( int line1,
   2:                  int line2 );
   3: 
   4: void a_longer_function(
   5:     int line1,
   6:     int line2
   7:     );
   8: 
   9: void call_them( int line1, int line2 )
  10: {
  11:     a_function(
  12:         line1,
  13:         line2
  14:         );
  15: 
  16:     a_longer_function( line1,
  17:                        line2 );
  18: }

Lines 5 and 12 are assigned arglist-intro syntax since they are the first line following the open parenthesis, and lines 7 and 14 are assigned arglist-close syntax since they contain the parenthesis that closes the argument list.

Lines that continue argument lists can be assigned one of two syntactic symbols. For example, Lines 2 and 17 are assigned arglist-cont-nonempty syntax. What this means is that they continue an argument list, but that the line containing the parenthesis that opens the list is not empty following the open parenthesis. Contrast this against lines 6 and 13 which are assigned arglist-cont syntax. This is because the parenthesis that opens their argument lists is the last character on that line.

Note that there is no arglist-open syntax. This is because any parenthesis that opens an argument list, appearing on a separate line, is assigned the statement-cont syntax instead.

A few miscellaneous syntactic symbols that haven't been previously covered are illustrated by this C++ example:


   1: void Bass::play( int volume )
   2: const
   3: {
   4:     /* this line starts a multi-line
   5:      * comment.  This line should get `c' syntax */
   6: 
   7:     char* a_multiline_string = "This line starts a multi-line \
   8: string.  This line should get `string' syntax.";
   9: 
  10:   note:
  11:     {
  12: #ifdef LOCK
  13:         Lock acquire();
  14: #endif // LOCK
  15:         slap_pop();
  16:         cout << "I played "
  17:              << "a note\n";
  18:     }
  19: }

The lines to note in this example include:

Multi-line C preprocessor macros are now (somewhat) supported. At least CC Mode now recognizes the fact that it is inside a multi-line macro, and it properly skips such macros as syntactic whitespace. In this example:


   1: #define LIST_LOOP(cons, listp)                           \
   2:   for (cons = listp; !NILP (cons); cons = XCDR (cons))   \
   3:      if (!CONSP (cons))                                  \
   4:        signal_error ("Invalid list format", listp);      \
   5:      else

line 1 is given the syntactic symbol cpp-macro. This first line of a macro is always given this symbol. The second and subsequent lines (e.g. lines 2 through 5) are given the cpp-macro-cont syntactic symbol, with a relative buffer position pointing to the # which starts the macro definition.

In Objective-C buffers, there are three additional syntactic symbols assigned to various message calling constructs. Here's an example illustrating these:


  1: - (void)setDelegate:anObject
  2:           withStuff:stuff
  3: {
  4:     [delegate masterWillRebind:self
  5:               toDelegate:anObject
  6:               withExtraStuff:stuff];
  7: }

Here, line 1 is assigned objc-method-intro syntax, and line 2 is assigned objc-method-args-cont syntax. Lines 5 and 6 are both assigned objc-method-call-cont syntax.

Two other syntactic symbols can appear in old style, non-prototyped C code [3]:


  1: int add_three_integers(a, b, c)
  2:      int a;
  3:      int b;
  4:      int c;
  5: {
  6:     return a + b + c;
  7: }

Here, line 2 is the first line in an argument declaration list and so is given the knr-argdecl-intro syntactic symbol. Subsequent lines (i.e. lines 3 and 4 in this example), are given knr-argdecl syntax.


[1] This is the case even for C and Objective-C. For consistency, structs in all supported languages are syntactically equivalent to classes. Note however that the keyword class is meaningless in C and Objective-C.

[2] The list of conditional keywords are (in C, C++, Objective-C, and Java): for, if, do, else, while, and switch. C++ and Java have two additional conditional keywords: try and catch. Java also has the finally and synchronized keywords.

[3] a.k.a. K&R C, or Kernighan & Ritchie C

PREV Customizing Indentation UP Top NEXT Performance Issues