Forum Discussion

Walter_Kacynski's avatar
Walter_Kacynski
Icon for Cirrostratus rankCirrostratus
Aug 11, 2014

Nested Procs in an iRule

Since iRules don't support the expr ceil(args) function, I'm trying to implement a TCL version that I found at https://searchcode.com/codesearch/view/16742115/

 

This implement uses multiple reusable procs. When I format my iRule with these procs, the recusion doesn't seem to process correctly and I'm wondering if there are any restrictions here?

 

The below code results in this error on my system (11.4.1 HF3)

 

01070151:3: Rule [/Common/math] error: /Common/math:37: error: [undefined procedure: isFloat][isFloat $x]
/Common/math:37: error: [undefined procedure: isInt][isInt $x]
/Common/math:59: error: [undefined procedure: checkFloat][checkFloat $x]
/Common/math:71: error: [undefined procedure: checkNumber][checkNumber $a]
/Common/math:72: error: [undefined procedure: iszero][iszero $a]
/Common/math:109: error: [undefined procedure: bits][bits $delta]
/Common/math:131: error: [undefined procedure: normalize][normalize $number]
/Common/math:145: error: [undefined procedure: opp][opp $try]
 These have been copied from
 https://searchcode.com/codesearch/view/16742115/


 returns 1 if x is a BigFloat, 0 elsewhere

proc isFloat {x} {
     a BigFloat is a list of : "F" mantissa exponent delta
    if {[llength $x]!=4} {
        return 0
    }
     the marker is the letter "F"
    if {[string equal [lindex $x 0] F]} {
        return 1
    }
    return 0
}


 checks that n is a BigInt (a number create by math::bignum::fromstr)

proc isInt {n} {
    if {[llength $n]>1} {
        return 0
    }
     if {[string is digit $n]} {
         return 1
     }
    return 1
}


 checks if each number is either a BigFloat or a BigInt
 arguments : each argument is the name of a variable to be checked

proc checkNumber {x} {
    if {![isFloat $x] && ![isInt $x]} {
        error "input is not an integer, nor a BigFloat"
    }
}


 checks each variable to be a BigFloat
 arguments : each argument is the name of a variable to be checked

proc checkFloat {number} {
    if {![isFloat $number]} {
        error "BigFloat expected"
    }
}


 returns 1 if x is null, 0 otherwise

proc iszero {x} {
    if {[isInt $x]} {
        return [expr {$x==0}]
    }
    checkFloat $x
     now we do some interval rounding : if a number's interval englobs 0,
     it is considered to be equal to zero
    foreach {dummy integer exp delta} $x {break}
    if {$delta>=abs($integer)} {return 1}
    return 0
}


 returns -A (the opposite)

proc opp {a} {
    checkNumber $a
    if {[iszero $a]} {
        return $a
    }
    if {[isInt $a]} {
        return [expr {-$a}]
    }
     recursive call
    lset a 1 [expr {-[lindex $a 1]}]
    return $a
}


 bits : computes the number of bits of an integer, approx.

proc bits {int} {
    set l [string length [set int [expr {abs($int)}]]]
     int<10**l -> log_2(int)=l*log_2(10)
    set l [expr {int($l*log(10)/log(2))+1}]
    if {$int>>$l!=0} {
        error "bad result: $l bits"
    }
    while {($int>>($l-1))==0} {
        incr l -1
    }
    return $l
}


 normalizes a number : Delta (accuracy of the BigFloat)
 has to be limited, because the memory use increase
 quickly when we do some computations, as the Mantissa and Delta
 increase together
 The solution : limit the size of Delta to 16 bits

proc normalize {number} {
    checkFloat $number
    foreach {dummy integer exp delta} $number {break}
    set l [bits $delta]
    if {$l>16} {
        incr l -16
         $l holds the supplementary size (in bits)
         now we can shift right by $l bits
         always round upper the Delta
        set delta [expr {$delta>>$l}]
        incr delta
        set integer [expr {$integer>>$l}]
        incr exp $l
    }
    return [list F $integer $exp $delta]
}


 returns the integer part of a BigFloat, as a BigInt
 the result is the same one you would have
 if you had called [expr {ceil($x)}]
 Case C1631422

proc ceil {number} {
    checkFloat $number
    set number [normalize $number]
    if {[iszero $number]} {
        return 0
    }
    foreach {dummy integer exp delta} $number {break}
    if {$exp>=0} {
        error "not enough precision to perform rounding (ceil)"
    }
     saving the sign ...
    set sign [expr {$integer<0}]
    set integer [expr {abs($integer)}]
     integer part
    set try [expr {$integer>>(-$exp)}]
    if {$sign} {
        return [opp $try]
    }
     fractional part
    if {($try<<(-$exp))!=$integer} {
        return [incr try]
    }
    return $try
}
 These have been copied from
 https://searchcode.com/codesearch/view/16742115/


 returns 1 if x is a BigFloat, 0 elsewhere

proc isFloat {x} {
     a BigFloat is a list of : "F" mantissa exponent delta
    if {[llength $x]!=4} {
        return 0
    }
     the marker is the letter "F"
    if {[string equal [lindex $x 0] F]} {
        return 1
    }
    return 0
}


 checks that n is a BigInt (a number create by math::bignum::fromstr)

proc isInt {n} {
    if {[llength $n]>1} {
        return 0
    }
     if {[string is digit $n]} {
         return 1
     }
    return 1
}


 checks if each number is either a BigFloat or a BigInt
 arguments : each argument is the name of a variable to be checked

proc checkNumber {x} {
    if {![isFloat $x] && ![isInt $x]} {
        error "input is not an integer, nor a BigFloat"
    }
}


 checks each variable to be a BigFloat
 arguments : each argument is the name of a variable to be checked

proc checkFloat {number} {
    if {![isFloat $number]} {
        error "BigFloat expected"
    }
}


 returns 1 if x is null, 0 otherwise

proc iszero {x} {
    if {[isInt $x]} {
        return [expr {$x==0}]
    }
    checkFloat $x
     now we do some interval rounding : if a number's interval englobs 0,
     it is considered to be equal to zero
    foreach {dummy integer exp delta} $x {break}
    if {$delta>=abs($integer)} {return 1}
    return 0
}


 returns -A (the opposite)

proc opp {a} {
    checkNumber $a
    if {[iszero $a]} {
        return $a
    }
    if {[isInt $a]} {
        return [expr {-$a}]
    }
     recursive call
    lset a 1 [expr {-[lindex $a 1]}]
    return $a
}


 bits : computes the number of bits of an integer, approx.

proc bits {int} {
    set l [string length [set int [expr {abs($int)}]]]
     int<10**l -> log_2(int)=l*log_2(10)
    set l [expr {int($l*log(10)/log(2))+1}]
    if {$int>>$l!=0} {
        error "bad result: $l bits"
    }
    while {($int>>($l-1))==0} {
        incr l -1
    }
    return $l
}


 normalizes a number : Delta (accuracy of the BigFloat)
 has to be limited, because the memory use increase
 quickly when we do some computations, as the Mantissa and Delta
 increase together
 The solution : limit the size of Delta to 16 bits

proc normalize {number} {
    checkFloat $number
    foreach {dummy integer exp delta} $number {break}
    set l [bits $delta]
    if {$l>16} {
        incr l -16
         $l holds the supplementary size (in bits)
         now we can shift right by $l bits
         always round upper the Delta
        set delta [expr {$delta>>$l}]
        incr delta
        set integer [expr {$integer>>$l}]
        incr exp $l
    }
    return [list F $integer $exp $delta]
}


 returns the integer part of a BigFloat, as a BigInt
 the result is the same one you would have
 if you had called [expr {ceil($x)}]
 Case C1631422

proc ceil {number} {
    checkFloat $number
    set number [normalize $number]
    if {[iszero $number]} {
        return 0
    }
    foreach {dummy integer exp delta} $number {break}
    if {$exp>=0} {
        error "not enough precision to perform rounding (ceil)"
    }
     saving the sign ...
    set sign [expr {$integer<0}]
    set integer [expr {abs($integer)}]
     integer part
    set try [expr {$integer>>(-$exp)}]
    if {$sign} {
        return [opp $try]
    }
     fractional part
    if {($try<<(-$exp))!=$integer} {
        return [incr try]
    }
    return $try
}
  • did you forget call command?

    call

    https://devcentral.f5.com/wiki/irules.call.ashx

    e.g.

     config
    
    [root@ve11a:Active:In Sync] config  tmsh list ltm rule qux
    ltm rule qux {
        proc test1 { x } {
      log local0. "test1: $x"
    }
    proc test2 { y } {
      log local0. "test2: $y"
      call test1 $y
    }
    when RULE_INIT {
      if { [TMM::cmp_group] == 0 and [TMM::cmp_unit] == 0 } {
        call test1 1
        call test2 2
      }
    }
    }
    
     /var/log/ltm
    
    [root@ve11a:Active:In Sync] config  tail -f /var/log/ltm
    Aug 16 22:54:09 ve11a info tmm[29362]: Rule /Common/qux : test1: 1
    Aug 16 22:54:09 ve11a info tmm[29362]: Rule /Common/qux : test2: 2
    Aug 16 22:54:09 ve11a info tmm[29362]: Rule /Common/qux : test1: 2