                Listing Five

#!/bin/omnimark -sb
;
; function.xom
;-------------------------------------------------------------------
;
;   Simple program to count functions in C code
;
; Description:
;
;   Lists the names of functions in C code and reports a total count
;
;   Also shows "referents" - an output action can insert a place
;   marker for data into the output, and the place marker can be
;   given a data value before or after passing that point in the
;   output stream.  This allows output and data creation to occur
;   at different times, each time being convenient to the design
;   of the program.
;
;   Run this program with the command
;
;       omnimark -s functions.xom filename ...
;
; Note:
;
;   This program may fail when a function definition contains
;   preprocesor directives.  A more complex set of patterns is
;   required to deal with this case.

;-------------------------------------------------------------------

;Allow output actions to make forward and backward references to the
;main output data.

declare #main-output has referents-allowed

;-------------------------------------------------------------------

macro C-identifier is
(   [letter|"_"]
    [letter|digit|"_"]*
)
macro-end

;we have to recognize all preprocessor directives so that we can
;discard them reliably

macro C-preprocessor-directive is
(   line-start blank*
    "#"
    (   "\%n"           ; line continuation
    |   any-text        ; or any character except newline
    ) +
)
macro-end

; Comments and quoted text -----------------------------------------

macro C-comment is
(   "/*"
    (   [any except "*"]+
    |   ( "*" [any except "/"]
    ) *
    "*/"
)
macro-end

macro CC-comment is
(   "//" any-text* "%n"?    )
)
macro-end

macro comment is
(   C-comment | CC-comment  )
macro-end

macro quoted-text is
    (   (   '"' ( [any-text except '"']+ | '\"' )* '"' )
    |   (   "'" ( [any-text except "'"]+ | "\'" )* "'" )
    )
macro-end

macro token-separator is
(   (   comment|quoted-text|white-space+    )+
)
macro-end

; Code blocks ------------------------------------------------------

;use recursive pattern functions to match code blocks

define switch function code-block
elsewhere

define switch function code-block-interior
as
    repeat scan #current-input
        match token-separator+
        match code-block
        match [any except "{}"]
    again
    return true

define switch function code-block
as
    return #current-input matches ( "{" code-block-interior "}" )

; Parameter lists --------------------------------------------------

;use recursive pattern functions to match parameter lists

define switch function parameter-list
elsewhere

define switch function parameter-list-interior
as
    repeat scan #current-input
        match token-separator+
        match code-block
        match [any except "()"]
    again
    return true

define switch function parameter-list
as
    return #current-input matches ( "(" parameter-list-interior ")" )

;-------------------------------------------------------------------

macro C-function is
(   C-identifier => function-name
    token-separator*
    parameter-list => function-parameters
    token-separator*
    code-block => function-body
)
macro-end

;-------------------------------------------------------------------

global counter NumberOfFunctions initial { 0 }
global counter FunctionList variable initial-size 0

find C-preprocessor-directive
    ; discard all directives

find C-function
    ;Report each function only once - a function might be multiply
    ;defined in conditional code
    ;
    do when FunctionList hasnt key function-name
        increment NumberOfFunctions
        output function-name || "%n"
        set new FunctionList ^ function-name to 1
    else
        increment FunctionList ^ function-name
    done

find code-block => text

find token-separator => text

find any

;-------------------------------------------------------------------

process

    ;output a heading, including the total which we do not yet have
    output "Code contains the following "
        || referent("total")                ;output a place marker
        || " functions:%n%n"

    ;scan the input to count the functions and list the names
    submit #main-input

    output "%n"

    ;give the place marker its final value
    set referent "total" to "d" % NumberOfFunctions
    halt

;-------------------------------------------------------------------
