# Units converter, formula applier, and expression evaluator # by peterre # Version 1.0 # # Copyright © 2001 peterre # # email: tcl@staffs.softnet.co.uk # URL: http://www.soft.net.uk/staffs/eggdrop/characters.html # Tested with eggdrop v1.6.6 and Tcl v8.3.3 # If you find bugs, or have comments, please let me know. # Description: # # The script converts between units, applies formulae, and # evaluates arithmetic expressions. You can edit in new # conversions and formulae without needing to understand Tcl. # Users' notes: # # All of the calculations are done in floating point since that is # probably what most users want. E.g., when they type "!calc 5/4" # they do not want the script to do integer division then give the # answer 1. They want the answer 1.25, without having to remember # to enter the query as !calc 5.0/4. Similarly, they do not want 8 # as the answer to !calc 2^10. # Programmers' notes: # # Tcl is an elegant and simple language. The Tcl(n) manual page # describes the basic syntax and semantics of the language with # just 11 concise rules. See them at # http://tcl.activestate.com/man/tcl8.2/TclCmd/Tcl.htm # # Those 11 rules are a good basis for understanding one of the # techniques used in this script, where procedures are defined # within foreach commands. It's the way that the argument values # are passed that needs the careful thought. # # The danger caused by user input that contains commands, e.g. # [die], is handled by use of regular expressions. Other types # of illegal input are handled partly by the use of regular # expressions, and partly by the use of catch commands. # Public and MSG commands provided with the # script (you can add more of your own): # # !convert - Lists the available commands # !f2c - Fahrenheit => Celsius # !c2f - Celsius => Fahrenheit # !in2cm - Inches => Centimetres # !cm2in - Centimetres => Inches # !mi2km - Miles => Kilometres # !km2mi - Kilometres => Miles # !lb2kg - Pounds => Kilograms # !kg2lb - Kilograms => Pounds # !cuberoot - Calculates the cube root of a number # !circle - Calculates the area of a circle # !calc - Evaluates an arithmetic expression # DCC commands provided with the script # (you can add more of your own): # # .convert - Lists the available commands # .f2c - Fahrenheit => Celsius # .c2f - Celsius => Fahrenheit # .in2cm - Inches => Centimetres # .cm2in - Centimetres => Inches # .mi2km - Miles => Kilometres # .km2mi - Kilometres => Miles # .lb2kg - Pounds => Kilograms # .kg2lb - Kilograms => Pounds # .cuberoot - Calculates the cube root of a number # .circle - Calculates the area of a circle # .calc - Evaluates an arithmetic expression ### Edit the settings below to your own requirements ### # On a public command, where should the reply go to? # C - by PRIVMSG to the channel # N - by NOTICE to the user # M - by PRIVMSG to the user set con(reply) C # Below you can add to the commands that the script deals with. Leave # f2c as the first one. There are six items to be put on each line. The # first one is the command name (some characters can cause trouble here # - letters and digits are fine). The second one is the expression that # works out the result (put it in curly braces). The last one is the # number of decimal places to which the result should be displayed. The # other three items on each line are text to be included in the output. # Put the text in curly braces. If no text is to be output in one of # the three positions, put two curly braces, thus: {} set con(commands) { f2c {($arg-32)/1.8} {Converted: } {°F => } {°C} 2 c2f {$arg*1.8+32} {Converted: } {°C => } {°F} 2 in2cm {$arg*2.54} {Converted: } { in => } { cm} 2 cm2in {$arg/2.54} {Converted: } { cm => } { in} 2 mi2km {$arg*1.609344} {Converted: } { mile(s) => } { km} 3 km2mi {$arg/1.609344} {Converted: } { km => } { mile(s)} 3 lb2kg {$arg*0.45359237} {Converted: } { lb => } { kg} 3 kg2lb {$arg/0.45359237} {Converted: } { kg => } { lb} 3 cuberoot {pow($arg,(1/3))} {The cube root of } { is } {} 3 circle {2*asin(1)*$arg*$arg} {A circle of radius } { m has area } { m²} 3 } # Note to Tcl and C programmers: # # The 1/3 in the definition of cuberoot is not an error. The # calculation will be done in floating point since the script # modifies the expression before passing it on for evaluation. ### Do not change anything below this line ### foreach {name form t1 t2 t3 prec} $con(commands) { bind dcc - $name con_dcc_$name proc con_dcc_$name {hand idx arg}\ "con_form D 0 \$idx \$arg $name\ \{$form\} \{$t1\} \{$t2\} \{$t3\} $prec" } foreach {name form t1 t2 t3 prec} $con(commands) { bind msg - !$name con_msg_$name proc con_msg_$name {nick uhost hand arg}\ "con_form M 0 \$nick \$arg $name\ \{$form\} \{$t1\} \{$t2\} \{$t3\} $prec" } foreach {name form t1 t2 t3 prec} $con(commands) { bind pub - !$name con_pub_$name proc con_pub_$name {nick uhost hand chan arg}\ "con_form $con(reply) \$chan \$nick \$arg $name\ \{$form\} \{$t1\} \{$t2\} \{$t3\} $prec" } proc con_form {method chan ident arg name form t1 t2 t3 prec} { set arg [string trim $arg] set org $arg regsub {^([0-9]+)$} $arg {\1.0} arg regsub -all {([^0-9\.g]|^)([0-9]+)([^0-9\.]|$)} $form {\1\2.0\3} form regsub -all {([^0-9\.g]|^)([0-9]+)([^0-9\.]|$)} $form {\1\2.0\3} form # The repetition of the above line is intentional if {$arg == "" || [string tolower $arg] == "help"} { if {$method == "D"} {set comm .$name} {set comm !$name} con_putout $method $chan $ident\ "The syntax for $comm is \"$comm \"" } elseif {![regexp {^(\-|\+|)([0-9]+(\.[0-9]*)?|\.[0-9]+)$} $org]} { con_putout $method $chan $ident "\"$org\" is not a valid number." } elseif {[catch {set c [expr $form]} error]} { con_putout $method $chan $ident "Error: $error" } else { con_putout $method $chan $ident\ "$t1$org$t2[format %.${prec}f $c]$t3" } } bind dcc - calc con_dcc_calc proc con_dcc_calc {hand idx arg} {con_calc D 0 $idx $arg} bind msg - !calc con_msg_calc proc con_msg_calc {nick uhost hand arg} {con_calc M 0 $nick $arg} bind pub - !calc con_pub_calc proc con_pub_calc {nick uhost hand chan arg} { global con con_calc $con(reply) $chan $nick $arg } proc con_calc {method chan ident arg} { set arg [string trim $arg] regsub -all {([^0-9\.g]|^)([0-9]+)([^0-9\.]|$)} $arg {\1\2.0\3} mrg regsub -all {([^0-9\.g]|^)([0-9]+)([^0-9\.]|$)} $mrg {\1\2.0\3} mrg # The near repetition of the above line is intentional if {$arg == "" || [string tolower $arg] == "help"} { if {$method == "D"} {set comm .calc} {set comm !calc} con_putout $method $chan $ident\ "The syntax for $comm is \"$comm \"" } elseif {[regexp {\^} $arg]} { con_putout $method $chan $ident\ "To raise x to the power y, use pow(x,y) instead of x^y." } elseif {[regexp {[^\*]\*\*[^\*]} $arg]} { con_putout $method $chan $ident\ "To raise x to the power y, use pow(x,y) instead of x**y." } elseif {[regexp {[^0-9a-z,\.\+\-\*\/\(\)\ ]} $arg match]} { con_putout $method $chan $ident\ "I do not deal with expressions that\ contain the character \"$match\"." } elseif {![catch {set c [expr $mrg]} error]} { con_putout $method $chan $ident\ "Evaluated: $arg => $c" } elseif {[lrange [split $error] 0 1] == "syntax error"} { con_putout $method $chan $ident\ "There is a syntax error in your query." } else { con_putout $method $chan $ident\ "Error: $error" } } bind dcc - convert con_dcc_conv proc con_dcc_conv {hand idx arg} {con_conv D 0 $idx $arg} bind msg - !convert con_msg_conv proc con_msg_conv {nick uhost hand arg} {con_conv M 0 $nick $arg} bind pub - !convert con_pub_conv proc con_pub_conv {nick uhost hand chan arg} { global con con_conv $con(reply) $chan $nick $arg } proc con_conv {method chan ident arg} { global con set commands "" if {$method == "D"} {set comchar .} {set comchar !} foreach {name form t1 t2 t3 prec} $con(commands)\ {lappend commands $comchar$name} set f2c f2c con_putout $method $chan $ident\ "To convert from Fahrenheit to Celsius: $comchar$f2c " con_putout $method $chan $ident\ "Other commands: [lrange $commands 1 end]" set calc calc con_putout $method $chan $ident\ "To evaluate an expression: $comchar$calc " } proc con_putout {method chan ident text} { switch -exact $method { C {puthelp "PRIVMSG $chan :$text"} N {puthelp "NOTICE $ident :$text"} M {puthelp "PRIVMSG $ident :$text"} D {putdcc $ident $text} } } putlog "Loaded pete.convert1.0.tcl by peterre"