All pastes #747456 Raw Edit

Miscellany

public text v1 · immutable
·published 2007-10-24 01:44 UTC
rendered paste body
# 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 <number>      -  Fahrenheit => Celsius
# !c2f <number>      -  Celsius => Fahrenheit
# !in2cm <number>    -  Inches => Centimetres
# !cm2in <number>    -  Centimetres => Inches
# !mi2km <number>    -  Miles => Kilometres
# !km2mi <number>    -  Kilometres => Miles
# !lb2kg <number>    -  Pounds => Kilograms
# !kg2lb <number>    -  Kilograms => Pounds
# !cuberoot <number> -  Calculates the cube root of a number
# !circle <number>   -  Calculates the area of a circle
# !calc <expression> -  Evaluates an arithmetic expression

# DCC commands provided with the script
# (you can add more of your own):
#
# .convert           -  Lists the available commands
# .f2c <number>      -  Fahrenheit => Celsius
# .c2f <number>      -  Celsius => Fahrenheit
# .in2cm <number>    -  Inches => Centimetres
# .cm2in <number>    -  Centimetres => Inches
# .mi2km <number>    -  Miles => Kilometres
# .km2mi <number>    -  Kilometres => Miles
# .lb2kg <number>    -  Pounds => Kilograms
# .kg2lb <number>    -  Kilograms => Pounds
# .cuberoot <number> -  Calculates the cube root of a number
# .circle <number>   -  Calculates the area of a circle
# .calc <expression> -  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 <number>\""
  } 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 <expression>\""
  } 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 <number>"
  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 <expression>"
}

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"