C-header Parser

(RJM) Here some code is presented that can be used to read C-header files. This should apparently aid in developing and managing less error-prone bilingual C/Tcl projects. Further comments is in the code below. Extensions and ideas are welcomed.

New: Modifications resulting in usage of command aliases instead of global variables, according to an idea from constants (modified "def" approach).

# C-header file parser
# will parse simple #define commands and enums as long as these are in a section
# that act as a parse delimiter areas as well as a comment in C as to indicate
# that sections are also used by tcl programs.
# Using C macros aid in readability of Tcl/C-projects as well as reduce potential
# error sources.

# Delimiters: //tcl on and //tcl off  (yet not /*tcl on*/)
# all #defines are stored in ordinary variables in order to keep accesses as
# straightforward as possible. Normally no conflicts occurs when #defines are
# capitalized. In procs, defines shall be dereferenced with prefixed ::, which
# aid also in quick visual recognition besides not needing 'global' commands.

proc h-parse {filename} {
    global _H_PARSE_
    set _H_PARSE_ off
    set fid [open $filename r]
    set line_tcl ""
    set multiline 0
    while {![eof $fid]} {
        set line [gets $fid]
        # ToDo: /*...*/ comment remover
        if {[regexp ^//tcl $line]} {
            set _H_PARSE_ [lindex $line 1]
            continue
        }
        regsub //(.*?)$ $line "" line
        if {$_H_PARSE_ != "on"} continue
        append line_tcl [string trimright $line \;]
        if {[regexp \{ $line]} {set multiline 1}
        if {[regexp \} $line]} {set multiline 0}
        if {$multiline} continue
        eval _C_$line_tcl
        set line_tcl ""
    }
}

proc def {name args} {
    if {[catch {set value [expr $args]}]} {
        set value $args
    }
    interp alias {} $name {} CONST $value
}
proc CONST {a} {
    return $a
}

proc _C_ {} {
}
# assign enums as macros (alternatively consider storage in array because it
# corresponds to its nature in C. However this compromises readability
# of typical enum usage in switch statements)
proc _C_enum {name ids} {
    # name ignored
    # ids: do assignments in enums without spaces!
    set index 0
    foreach element [split [regsub -all { |\t} $ids ""] ,] {
        lassign [split $element =] name value
        if {$value != ""} {
            set index $value
        }
        def $name $index
        incr index
    }
}
proc _C_#define {args} {
    lassign $args name value
    def $name $value
}


if 0 {
proc test {} {
    h-parse test.h
    
    puts "===== list of globals with \"A\" ====="
    foreach var [info globals *A*] {
        puts $var\t\t[set ::$var]
    }
    puts "===== some usage examples ====="
    for {set i 0} {$i < 25} {incr i} {
        # note the omission of outer brackets - command substitution!
        switch -- $i default {
            puts -nonewline .
        } [ALPHA] {
            puts alpha
        } [GAMMA] {
            puts gamma
        } [OMEGA] {
            puts omega
        } [LAMBDA] {
            puts lamda
        }
    }
    if {[MACRO_EMPTY] == "" } {puts "empty macro"}
    if {[MACRO_VALUE] != "" } {puts "macro: [MACRO_VALUE]"}
    if {[MACRO_STRING] != "" } {puts "macro: [MACRO_STRING]"}
}
}

And here a sample header file is quoted that is used for the test case above:

//tcl on
enum hallo {
    ALPHA, BETA, 
        GAMMA=6, 
    ETA , OMEGA = 21
};

//tcl off

/* stuff that tcl ignores */
blah, blah

//tcl on -- copy this file to the tcl-project when modified!
enum shortenum {KAPPA = 10, LAMBDA};
#define MACRO_EMPTY
#define MACRO_COMMENT   // test comment
// here a line with some whitespaces
  
#define MACRO_STRING        "TESTVAL"
#define MACRO_VALUE  123