[Richard Suchenwirth] 2002-01-02 - [Tcl] is somehow more than just a programming language. At times it feels like a quite powerful operating system, with commands that have their own "small languages" (''sed'', ''awk'' in Unix; [expr], [regexp]... in Tcl). But while implementing ''sed''s or ''awk''s functionality in C is hard work, in Tcl the creation of a custom "small language" is made easy by strong string processing commands. Hence I chose a small math language for my New Year's Eve fun project. The accepted language is based on [expr]'s, which is well known to Tcl'ers and able to express all C arithmetics (and more), so isn't exactly small. However, I added some functionalities discussed earlier on [comp.lang.tcl], plus some sugar to make it look more similar to conventional math notation: * allow multiple expressions, separated by semicolon * allow one infix assignment per expression, like ''i = j + 1'' * allow references to existing variables without prefixed dollar sign * allow omission of parens around simple function arguments * allow omission of the * operator after numeric constants * allow one fraction to be written with horizontal bar instead of / These effects took little effort, as they were reached by string manipulations on the arguments to ''math'', which transform them to valid [expr] language, and evaluate that in caller's scope. On the other hand, no tokenization like that of [expr] was done (where ''$i+$j'' is equivalent to ''$i + $j''), so the arguments have to be list elements separated by whitespace. At the place marked "more preprocessing here", you can add your own ideas. See the usage examples at end, and enjoy! ====== proc math args { if {[llength $args]==1} {set args [lindex $args 0]} foreach mathcmd [split $args ";"] { set cmd "" if {[lindex $mathcmd 1]=="="} { set cmd [list set [lindex $mathcmd 0]] set expr [lrange $mathcmd 2 end] } else {set expr $mathcmd} set expr2 "expr \{(" set previous "" foreach i $expr { if [uplevel 1 info exists $i] {set i \$$i} if [regexp -- {--+} $i] {set i )/double(} if [isBuiltin $previous] {set i ($i)} if {[isNumeric $previous] && [regexp {^[$A-Za-z_]} $i]} { set i *$i } # more preprocessing here.. set previous $i append expr2 $i } append expr2 ")\}" if {$cmd==""} { set cmd $expr2 } else { append cmd " \[$expr2\]" } set res [uplevel 1 $cmd] } set res ;# return last expression's result } proc isBuiltin word { expr [lsearch -exact { abs cosh log sqrt acos double log10 srand asin exp pow tan atan floor rand tanh atan2 fmod round ceil hypot sin cos int sinh} $word ]>=0 } proc isNumeric x {expr ![catch {expr $x*1}]} # testing, and usage examples: math { a = 1 * (2 - 1); b = sqrt 4; c = 3 a - b ------- a + b } puts c:$c ====== ---- [GWM] The following simplification checks all the built in math functions plus any yet to be developed in Tcl9,10,11 or 999! Year 3000 compliant code? ====== proc isBuiltin word { expr [lsearch -exact [info functions] $word ]>=0 } ====== [Lars H]: Except that [http://tip.tcl.tk/232.html%|%TIP 232] changes the entire mechanism for "builtin" functions. As of Tcl 8.5, [info functions] is deprecated. ---- [Arjen Markus] Inspired by this script, I wrote a small calculator, with the ability to use a primitive kind of functions. It took me half an evening and is not quite complete (error handling, a wish interface, ...) but it works: >> a = sin(b) >> b = 1 >> a 0.841470984808 >> quit Below is the code. '''Warning:''' ''it does not accept numbers like 1.0e-3!'' ====== # calculator.tcl -- # Script to emulate a calculator, allows the on-the-spot # evaluation of expressions as well the use of macros # # Author: Arjen Markus (arjen.markus@wldelft.nl) # # Macro -- # Namespace for the user-defined macros # namespace eval ::Macro { } # Calculator -- # Namespace for the public commands # namespace eval ::Calculator { } # HandleCommand -- # Identify the type of command and handle accordingly # # Arguments: # command Command that must be handled # Return value: # {} if the command is a definition or the value of the expression. # Side effects: # Definitions are handled directly # proc ::Calculator::HandleCommand { command } { set new_command [string map { " " "" "\t" "" } $command] # # Definitions take the form "name=some expression" if { [regexp {^[A-Za-z_][A-Za-z0-9]*=[^=]} $new_command] } { HandleDefinition $new_command return "" } else { Evaluate $new_command } } # Evaluate -- # Evaluate the expression # # Arguments: # command Command that must be evaluated # Return value: # The value of the expression. # proc ::Calculator::Evaluate { command } { set new_command [ConstructMacroCalls $command] return [expr $new_command] } # ConstructMacroCalls -- # Construct the calls to a macro # # Arguments: # body The raw body of the macro # Return value: # An expression valid for use in [expr] # Note: # Beware of expressions like "sin(1.0)" - they are not macros # proc ::Calculator::ConstructMacroCalls { body } { regsub -all {([A-Za-z_][A-Za-z_0-9]*)} \ $body {[::Macro::\1]} new_body regsub -all {\[::Macro::([A-Za-z_][A-Za-z_0-9]*)\]\(} \ $new_body {\1(} new_body return $new_body } # HandleDefinition -- # Define the macro based on the given command # # Arguments: # command Command that represents a definition # Return value: # The value of the expression. # proc ::Calculator::HandleDefinition { command } { regexp {(^[A-Za-z_][A-Za-z0-9]*)=(.*)} $command matched macro body set new_body [ConstructMacroCalls $body] proc ::Macro::$macro {} [list expr $new_body] return } # main code -- # In a loop, read the expressions and evaluate them # puts "Calculator: Example: >> a=b+1 >> b=1 >> a 2 >>1.0+2.0+3.0 6.0 (Use quit to exit the program)" while { 1 } { puts -nonewline ">> " flush stdout gets stdin line if { $line == "quit" } { break } else { if { [ catch { puts [::Calculator::HandleCommand $line] } message ] != 0 } { puts "Error: [lindex [split $message "\n"] 0]" } } } ====== ---- [AM] See also: [A little math language revisited] ---- ---- [gold]10/10/2020, added appendix and pix, but above text and code unchanged. ---- ---- *** References *** ---- * [A little math language revisited] * [Playing with Recursion] by [RS] * [Functional Programming] * [recursion] * [func] * John McCarthy: A basis for a mathematical theory of computation, in: * Computer Programming and Formal Systems. * P.Braffort, D.Hirschberg (ed.), Amsterdam:North Holland 1963, * several versions, archived pdf [http://www-formal.stanford.edu/jmc/basis1.pdf] * McCarthy’s LISP and Basis for Theory of Computation, archived pdf[https://hapoc2015.sciencesconf.org/conference/hapoc2015/pages/Dai.pdf] * en.wikipedia.org search on [https://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)] * John McCarthy at Stanford web site, archived [https://web.archive.org/web/20131011125002/http://www-formal.stanford.edu/jmc/] * Towards a Mathematical Science of Computation, J. McCarthy, * Computer Science Department,Stanford University, archived pdf [https://web.archive.org/web/20130319040125/http://www-formal.stanford.edu/jmc/towards.pdf] * Elephant 2000: A Programming Language Based on Speech Acts * John McCarthy, Stanford University, archived [https://web.archive.org/web/20130319040119/http://www-formal.stanford.edu/jmc/elephant.pdf] * Elephant input and output statements are characterized * as speech acts and programs, which * can refer directly to the past. * Elephant proposal contains summary * on McCarthy mathematical theory of computation * King that learns? [A Program That Learns] * [upvar sugar] * [Salt and Sugar] * [Math sugar] * [A little math language] * [args] * [RS] * [LV] * [Radical Language Modification] * [Functional Programming] * [Custom curry] * [Playing with recursion] * [Modeling COND with expr] * [Sample Math Programs] * Professor Frisby's Mostly Adequate Guide to Functional Programming [https://github.com/MostlyAdequate/mostly-adequate-guide] * [expr shorthand for Tcl9] * [Steps towards functional programming] * [Tacit programming] * The fortran call statement appeared in Fortran2 (1958). example of call exit, fortran 4 * Thocp code, http://www.thocp.net/software/languages/fortran.htm * [Natural User Interface] * [Natural Languages] category * [Game kingdom of strategy] * wikipedia.org wiki The_Sumerian_Game * [Find all words] * [Things German] * [How to synthesize a query] * [Writing Tk programs so that the user can extend or interact with them without modifying the application] * [Ruslish] * [Accumulator Generators] * [Accumulator Generator] * [Whadjasay] * disassemble byte code [https://www.magicsplat.com/blog/disassemble/] * One Liners Programs Compendium [https://wiki.tcllang.org/page/One+Liners+Programs+Compendium++and+TCL+demo+examples+calculations%2C+numerical+analysis] * [One Liners] * [Oneliner's Pie in the Sky] * Ref. WIKI BOOKS, Tcl_Programming_Introduction, [https://en.wikibooks.org/wiki/Tcl_Programming/Introduction] * Book Section contrasts one liners programs * versus traditional procedural approach, * Multiple Wiki Books on TCL programming [https://en.wikibooks.org/wiki/Category:Book:Tcl_Programming] * [if] * [New Control Structures] * Kernighan and Pike: The practice of programming ---- **Hidden Comments Section** <> Please include your wiki MONIKER and date in your comment Thanks, [gold] 12Aug2020 ---- <> Numerical Analysis | Toys | Calculator | Mathematics| Example| Toys and Games | Games | Application | GUI ---- <> Development | Concept| Algorithm | Language | Arts and crafts of Tcl-Tk programming ----