Version 1 of C-header Parser

Updated 2012-01-13 21:39:48 by AK

(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.

# C-header file parser
# will parse simple #define commands and enums as long as these are in a section
# that acts 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]
    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
        set line _C_[string trimright $line \;]
        eval $line
    }
}

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 " " $ids ""] ,] {
        lassign [split $element =] name value
        if {$value != ""} {
            set index $value
        }
        set ::$name $index
        incr index
    }
}
proc _C_#define {args} {
    lassign $args name value
    set ::$name $value
}


if 1 {
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 < 20} {incr i} {
        # note the omission of outer brackets - variable substitution!
        switch -- $i default {
            puts -nonewline .
        } $::ALPHA {
            puts alpha
        } $::BETA {
            puts beta
        } $::GAMMA {
            puts gamma
        } $::ETA {
            puts eta
        } $::OMEGA {
            puts omega
        }
    }
    if {$::MACRO_EMPTY == "" } {puts "empty macro"}
    if {$::MACRO_VALUE != "" } {puts "macro: $::MACRO_VALUE"}
}
}

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
#define MACRO_EMPTY
#define MACRO_COMMENT   // test comment
// here a line with some whitespaces
  
#define MACRO_STRING "TESTVAL"
#define MACRO_VALUE  123