Updated 2011-01-16 08:32:47 by RLE

US 2003-04-10
 #
 # "fulfills" checks a string against a regular expression and
 #  a couple of criteria. It returns 1, if the regular expression
 #  matches and all criteria are fulfilled, else returns 0.
 #
 # It is useful for the check of test results,
 #  or for evaluation of reports.
 #
 # The first argument is the string to be checked
 # The second argument is a regex to split string into substrings
 #  (it defaults to .* that matches the entire string)
 #
 # If the given regex is incorrect, "fulfills" throws an error
 #
 # All following args contain criteria, that apply to the respective
 #  substring, the first to everything the regex matches, the second
 #  to the first parenthized subexpression,... (see regexp man page)
 #
 # "fulfills" returns a truth value as follows:
 #
 # regexp doesn't match --> 0
 # more criteria given than substrings delivered by regexp --> 0
 #
 # Criteria:
 #
 # An empty criterium means "don't check this substring" and evals to "true".
 # /string/ --> string match against "string"
 # %string% --> string match against "string" -nocase
 # ~string~ --> string equal "string" -nocase
 # =string= --> string equal "string"
 #
 # If the criterium begins with one of the following, it uses expr to check.
 #   -eq --> numerical check: substring is equal
 #   -gt --> numerical check: substring is greater than
 #   -lt --> numerical check: substring is less than
 #   -ne --> numerical check: substring is not equal
 #   -ge --> numerical check: substring is greater or equal
 #   -le --> numerical check: substring is less or equal
 #   These criteria may contain placeholders of the form @1 ... @9 which
 #    expand to the corresponding substring.
 #   --  --> expr evals substring to a truth value
 #
 # Any criterium, that doesn't have one of the forms described above,
 #  evals "string equal criterium substring"
 #
 # Evaluation stops with first false value
 # If the string fulfills all criteria, "fulfills" returns 1.
 #
 # Note, that the regex and the criteria must be quoted, if they
 #  contain whitespace or special characters.
 #

 proc fulfills {str {re .*} args} {
   set matcher [list regexp "$re" $str]
   set n 0
   foreach arg $args {
     lappend matcher p($n)
     incr n
   }
   if {[catch $matcher result]} {
   } else {
   if {!$result} {return 0}
   }
   set tv 1
   set n 0
   foreach arg $args {
     if {![string length $arg]} {
       # empty arg, always true
       continue
     }
     if {![string length $p($n)]} {
       # empty subexpr, always false
       set tv 0
       break
     }
     set f [string index $arg 0]
     set m [string range $arg 1 end-1]
     set l [string index $arg end]
     switch -- $f {
       /   {
         if {[string equal $f $l]} {
           # string match
           set tv [string match $m $p($n)]
         } else {
           # string equal
           set tv [string equal $arg $p($n)]
         }
       }
       %   {
         if {[string equal $f $l]} {
           # string match
           set tv [string match -nocase $m $p($n)]
         } else {
           # string equal
           set tv [string equal $arg $p($n)]
         }
       }
       =   {
         # string equal
         if {[string equal $f $l]} {
           # string equal
           set tv [string equal $m $p($n)]
         } else {
           # string equal
           set tv [string equal $arg $p($n)]
         }
       }
       ~   {
         if {[string equal $f $l]} {
           # string match
           set tv [string equal -nocase $m $p($n)]
         } else {
           # string equal
           set tv [string equal $arg $p($n)]
         }
       }
       -   {
         # expr check
         regsub -all {@([1-9])} [string range $arg 3 end] \$p(\\1) ec
         switch -glob -- $arg {
           --   {
             set e "($p($n)) != 0"
           }
           -eq* {
             set e "($p($n)) == ($ec)"
           }
           -ne* {
             set e "($p($n)) != ($ec)"
           }
           -gt* {
             set e "($p($n)) > ($ec)"
           }
           -ge* {
             set e "($p($n)) >= ($ec)"
           }
           -lt* {
             set e "($p($n)) < ($ec)"
           }
           -le* {
             set e "($p($n)) <= ($ec)"
           }
           default {
             set e [string equal $arg $p($n)]
           }
         }
         if {[catch "expr $e" tv]} {
           # error in expr
           puts "error in expr $e  ---  $tv"
         }
       }
       default {
         # string equal
         set tv [string equal $arg $p($n)]
       }
     }
     if {!$tv} break
     incr n
   }
   return $tv
 }

 # Examples (uncomment to try):
 #
 # puts "[fulfills abc\$123 {abc\$([0-9]+)$} /a*/ /*2*/ c d e f]"
 # puts "[fulfills abc\$123 {abc\$([0-9]+)$} /a*/ /*2*/]"
 # puts "[fulfills abc123 {abc([0-9]+)} a b]"
 # puts "[fulfills Abc123 {Abc([0-9]+)} %a*% 123]"
 # puts "[fulfills Abc123 {Abc([0-9]+)} %a*% -eq123]"
 # puts "[fulfills Abc123 {Abc([0-9]+)} %a*% "-lt 200"]"
 # puts "[fulfills Abc123 {Abc([0-9]+)} %a*% "-ge 200"]"
 # puts "[fulfills Abc123 {Abc([0-9]+)} %a*% -123]"
 # puts "[fulfills Abc123 {Abc([0-9]+)} %a*% -a23]"
 # puts "[fulfills Abc1+2+3 {^Abc(.+)$} %a*% --]"
 # puts "[fulfills Abc1+2-3 {^Abc(.+)$} %a*% --]"
 # puts "[fulfills Abc1+2-3 {^Abc(.+)$} %a*% -lt@1+1]"
 # puts "[fulfills Abc1+2x3 {^Abc(.+)x([0-9])$} %a*% -lt5 -eq@1]"
 # puts "[fulfills Abc123]"
 #