Posted to tcl by schelte at Mon Apr 07 11:06:08 GMT 2025view raw
- namespace eval tproc {
- proc check {ns checks} {
- set args [info level -1]
- foreach {i type} $checks {
- if {$i >= [llength $args]} continue
- set arg [lindex $args $i]
- try {
- namespace eval $ns [list tproc::validate::$type $arg]
- } trap [list TCL LOOKUP COMMAND tproc::validate::$type] {} {
- return -level 2 -code error \
- "unknown type: \"$type\""
- } on ok {rc} {
- if {![string is true $rc]} {
- return -level 2 -code error \
- "invalid argument: \"$arg\", expected a $type"
- }
- } on error {msg} {
- return -level 2 -code error $msg
- }
- }
- }
- }
- namespace eval tproc::validate {
- proc int {val} {::string is integer -strict $val}
- proc double {val} {::string is double -strict $val}
- proc list {val} {::string is list $val}
- }
- proc tproc {proc args body} {
- set args [lmap arg $args {
- incr i
- lassign [split [lindex $arg 0] :] name type
- if {$type ne ""} {
- lappend checks $i $type
- }
- lreplace $arg 0 0 $name
- }]
- set ns [uplevel 1 {namespace current}]::[namespace qualifiers $proc]
- set cmd [list ::tproc::check $ns $checks]
- namespace eval $ns [list proc [namespace tail $proc] $args $cmd\n$body]
- }
- # Test
- tproc foo {a:int {b:double zzz}} {
- return [expr {$a * $b}]
- }
- namespace eval baz {
- namespace eval tproc::validate {
- proc color {val} {
- set colors {red blue green}
- if {$val ni $colors} {
- error "invalid color, must be one of [join $colors {, }]"
- }
- }
- }
- }
- tproc baz::foo {c:color} {
- return "the color is $c"
- }
- tproc oops {c:color} {
- return "the color is $c"
- }
- foreach test {
- {foo 42 3.14}
- {foo 13 xyz}
- {foo 99}
- {baz::foo red}
- {baz::foo yellow}
- {oops blue}
- } {
- set rc [catch $test result]
- if {$rc} {
- puts "fail: $test -> $result"
- } else {
- puts "pass: $test -> $result"
- }
- }