(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