This page is under development. Comments are welcome, but please load any comments in the comments section at the bottom of the page. Please include your wiki MONIKER and date in your comment with the same courtesy that I will give you. Aside from your courtesy, your wiki MONIKER and date as a signature and minimal good faith of any internet post are the rules of this TCL-WIKI. Its very hard to reply reasonably without some background of the correspondent on his WIKI bio page. Thanks, gold 15Oct2020
Title: Little_Math_Language V2
gold 15Oct2020 Here is extension of programs A little math language by RS and A little math language revisited written by AM in 2004.
Wrote some code for charts supporting A little math language by Richard Suchenwirth in 2002-01-02 and A little math language revisited written by AM in 2004. Called Little_Math_Language V2 in TCL as partial math language interpreter. Learning experience, Added some easy eye console displays for my bad eyes. And trying some new features as a testbed. Math calculation forms seem to be working.
The analogy of using Little_Math_Language V2 to control the large TCL language is like sticking an Apple computer for the human operator in front of a Cray computer. The human mind probably can only understand and use a limited set of instructions, an interface in hardware or TCL? language as a limited set of instructions might be useful. After all, the human mind was designed to chase rabbits.
;# Title: Little_Math_Language_AM V2 in TCL
;# console extension to little_math_language
;# proc mathlang.tcl from AM, (original on tcl wiki 05-21-2004)
;# proc test code from RS
;# ref A little math language revisited, page on TCL Wiki
;# A little math language, page on TCL Wiki
;# Richard Suchenwirth 2002-01-02
;# Original program by Arjen Markus on TCL Wiki
;# Reorganized code
;# for print and self_help
;# to tcl console.
;# written on Windows 10 on TCL
;# working under TCL version 8.6
;# on TCL WIKI , 20Oct2020
;# added statements for TCLLIB library
package require math::numtheory
package require math::constants
package require math::trig
package require math
namespace path {::tcl::mathop ::tcl::mathfunc math::numtheory math::trig math::constants }
;# Tried to keep clean AM code in upper section
;# added extension to console below. -gold
;# mathlang.tcl --
;# Provide commands that allow a more usual mathematical syntax:
;#
;# mathfunc {x} {
;# sinc = sin(x)/x if x != 0
;# sinc = 0 otherwise
;# }
;# math {
;# a = x*x + y*y
;# }
;#
;# Still to do: mathfunc
;#
;# (7 july 2004) Small improvement:
;# recognise array elements
;#
namespace eval ::mathsyntax {
namespace export math
variable cached_calcs {}
variable func_names \
{abs acos asin atan atan2 ceil cos cosh double
exp floor fmod hypot int log log10 pow rand round
sin sinh sqrt srand tan tanh wide}
}
;# ToExpr --
;# Transform an expression to the form expr wants
;# Arguments:
;# expression A right-hand side of an assignment
;# Result:
;# Valid Tcl expression
;#
proc ::mathsyntax::ToExpr { expression } {
variable func_names
set rhs [string map {" " ""} $expression]
set indices [regexp -inline -all -indices {[a-zA-Z][a-zA-Z0-9_]*} $rhs]
set offset 0
foreach idx $indices {
foreach {start stop} $idx {break}
set start [expr {$start+$offset}]
set stop [expr {$stop+$offset}]
set next [expr {$stop+1}]
if { [string index $rhs $next] != "(" } {
set char [string index $rhs $start]
set rhs [string replace $rhs $start $start "\$$char" ]
incr offset
} else {
set char [string index $rhs $start]
set name [string range $rhs $start $stop]
if { [lsearch $func_names $name] < 0 } {
set rhs [string replace $rhs $start $start "\$$char" ]
}
}
}
return $rhs
}
;# Transform --
;# Transform a series of mathematical expressions into Tcl code
;# Arguments:
;# id ID to use
;# calc One or more mathematical assignments
;# Result:
;# None
;# Side effects:
;# A private procedure is created
;# Note:
;# No conditions yet
;#
proc ::mathsyntax::Transform { id calc } {
set calc [split $calc "\n"]
set body {"uplevel 2 \{"}
foreach assign $calc {
set assign [string trim $assign]
if { $assign != "" } {
regexp {([a-zA-Z][a-zA-Z0-9_()]*) *= *(.*)} $assign ==> lhs rhsfull
;#
;# Is there a condition?
;#
set cond1 [string first " if" $rhsfull]
;# PM: set cond2 [string first " otherwise" $rhsfull]
set cond ""
if { $cond1 > 0 } {
set rhs [string range $rhsfull 0 [expr {$cond1-1}]]
set cond [string range $rhsfull [expr {$cond1+3}] end]
lappend body "if { [ToExpr $cond] } \{"
} else {
set rhs $rhsfull
}
;# If the left-hand side refers to an array element,
;# we need to add a dollar-sign
;#
set lhs [string map {"(" "($"} $lhs]
;#
;# Prepare the assignment
;#
set rhs [ToExpr $rhs]
lappend body "set $lhs \[expr {$rhs}\]"
if { $cond != "" } {
lappend body "\}"
}
}
}
lappend body "\}"
proc Cached$id {} [join $body "\n"]
}
;# math --
;# Allow mathematical expressions inside Tcl code
;# Arguments:
;# calc One or more mathematical assignments
;# Result:
;# None
;# Side effects:
;# As the code is executed in the caller's scope, variables
;# in the calling procedure are set
;# The code is transformed into a procedure that is cached
;#
proc ::mathsyntax::math { calc } {
variable cached_calcs
set id [lsearch $cached_calcs $calc]
if { $id < 0 } {
lappend cached_calcs $calc
set id [expr {[llength $cached_calcs]-1}]
Transform $id $calc
}
::mathsyntax::Cached$id
}
;#
;# Simple test
;#
if {0} {
namespace import ::mathsyntax::math
set a 1
set b 1
set c ""
set d ""
set sinc ""
math {
c = a + b
d = a + cos(b+c)
}
puts "$c $d"
for {set i 0} {$i < 20} {incr i} {
math {
x = 0.1*i
sinc = 1 if x == 0
sinc = sin(x)/x if x != 0
y(i) = sinc*sinc
}
puts "$i $x $sinc $y($i)"
}
;#
;# Just to check
;#
parray y }
;# end of AM deck
;# add cosmetics below to bottom of file
;# or source Little_Math_Language_AM
;# added statements above for TCLLIB library
proc test args {
foreach {case expected} $args {
catch {uplevel 1 $case} res ;# RS
if {$res != $expected} {error "$case->$res, expected $expected"}
puts "example >> $case -> $res, & expected was $expected"
;# res , expected, variables were not recycling ???
;# proc test is RS code from TCL Wiki
}
}
console show
console eval {.console config -bg palegreen}
console eval {.console config -font {fixed 20 bold}}
console eval {wm geometry . 40x20}
console eval {wm title . "Little_Math_Language_AM in TCL , screen grab and paste from console to texteditor"}
console eval {. configure -background orange -highlightcolor brown -relief raised -border 30}
console eval { proc self_helpx {} {
set msg "Little_Math_Language_AM,
large black type on green
from TCL,
self help listing
;# Conventional text editor formulas grabbed
;# from internet screens can be pasted
;# into green screen console
;# colon is statement end
;# proc test code from RS
;# ref A little math language revisited, page on TCL Wiki
;# Original program by Arjen Markus on TCL Wiki
;# Reorganized code from
;# for print and self_help
;# to tcl console.
;# mathlang.tcl from AM, (original on tcl wiki 05-21-2004) "
tk_messageBox -title "self_helpxx" -message $msg } }
console eval {.menubar.help add command -label Self_help -command self_helpx }
proc basic_alphabet_variables {} {
global a b c d e f g h i j k l m n o p q r s t u v w x y z
global A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
foreach i {a b c d e f g h i j k l m n o p q r s t u v w x y z} {
set $i 1
}
foreach i {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
set $i 1
} }
proc demo {} {
puts " demo "
namespace import ::mathsyntax::math
;# set lower & upper case global alphabet_variables to 1
basic_alphabet_variables
set a 1
set b 1
set c ""
set d ""
set sinc ""
math {
c = a + b
d = a + cos(b+c)
}
puts "$c $d"
for {set i 0} {$i < 20} {incr i} {
math {
x = 0.1*i
sinc = 1 if x == 0
sinc = sin(x)/x if x != 0
y(i) = sinc*sinc
}
puts "$i $x $sinc $y($i)"
}
;#
;#
parray y
puts " returns to TCL 8.6 for check = [exp 5 ]"
puts " set a 1 , set b 1 , set c 1 & <c d sinc strings nulled> "
puts "math c = a + b ; d = a + cos(b+c), puts c d "
puts "$c $d"
test { isprime 7 } 1
test { + 1 1 } 2
test { - 10 1 } 9
test { * 10 1 } 10
test { / 10 2 } 5
test { math { a = 10 / 2 } } 5
test { math { b = 10. / 2 } } 5.
test { math { c = 10. / 2. } } 5.
test { math { d = 1 + 1 } } 2
test { math { e = 10 - 1 } } 9
test { math { f = 10 * 1 } } 10
test { math { g = 10 / 2 } } 5
test { math { h = 10 ** 2 } } 100
test { math { i = 10 % 2 } } 0
test { math { i = 3 * 4 / 2 } } 6
test { math { i = (3>2) } } 1
test { math { i = (3<2) } } 0
}
demo
# end of file +----------------------------------------------------------------------------------+
| LITTLE MATH LANGUAGE: SYNTAX GOAL (AM, mathlang.tcl, 2004) |
| Provide commands allowing more usual mathematical syntax in TCL |
| |
| Desired syntax (close to conventional math notation): |
| |
| math { |
| a = x*x + y*y |
| } |
| |
| Instead of standard TCL expr form: |
| |
| set a [expr { $x*$x + $y*$y }] |
| |
| With conditional support (sinc function example): |
| |
| math { |
| sinc = 1 if x == 0 |
| sinc = sin(x)/x if x != 0 |
| } |
| |
| Key idea: bare variable names (a, x, y) auto-converted to $a, $x, $y |
| Bare function names (sin, cos) recognized and left alone (not $-prefixed) |
| Lineage: RS "A little math language" (2002) --> AM "revisited" (2004) |
| --> gold console extension V2 (2020) |
+----------------------------------------------------------------------------------++----------------------------------------------------------------------------------+
| TOEXPR PROC: VARIABLE AND FUNCTION DETECTION (::mathsyntax::ToExpr) |
| Transforms a right-hand-side expression into valid TCL expr syntax |
| |
| Input: "x*x + y*y" |
| | |
| v Step 1: strip all spaces |
| "x*x+y*y" |
| | |
| v Step 2: regexp -inline -all -indices to find identifier tokens |
| Matches: x (pos 0), x (pos 2), y (pos 4), y (pos 6) |
| | |
| v Step 3: for each identifier, check char AFTER it |
| if next char is NOT "(" : --> treat as VARIABLE, prepend $ |
| "x" --> "$x" |
| if next char IS "(" : |
| check func_names list {sin cos tan sqrt exp log ...} |
| if name IS in func_names: --> leave alone (it's a function call) |
| if name NOT in func_names: --> treat as ARRAY, prepend $ |
| "y(i)" --> "$y(i)" (array element access) |
| | |
| v |
| Output: "$x*$x+$y*$y" <-- valid TCL expr fragment |
| |
| Offset tracking: each $ insertion shifts later match positions by +1 |
+----------------------------------------------------------------------------------++----------------------------------------------------------------------------------+
| TRANSFORM PROC: MULTI-LINE ASSIGNMENT WITH CONDITIONS (::mathsyntax::Transform)|
| |
| Input block (multi-line calc string): |
| sinc = 1 if x == 0 |
| sinc = sin(x)/x if x != 0 |
| |
| Processing per line: |
| Step 1: split calc on newline --> list of assignment lines |
| Step 2: for each line, regexp to capture lhs and rhsfull |
| pattern: ([a-zA-Z][a-zA-Z0-9_()]*) *= *(.*) |
| Step 3: check for " if" substring in rhsfull |
| if found: split into rhs (before " if") and cond (after) |
| --> wrap in: if { } { set lhs [expr {}] } |
| if not found: plain assignment set lhs [expr {}] |
| Step 4: lhs array detection: "y(i)" --> "y($i)" (dollar before index) |
| Step 5: ToExpr applied to both rhs and cond |
| | |
| v |
| Generated body (joined with newlines): |
| uplevel 2 { |
| if { $x == 0 } { set sinc [expr {1}] } |
| if { $x != 0 } { set sinc [expr {sin($x)/$x}] } |
| } |
| | |
| v |
| Cached as: proc Cached$id {} (compiled once per unique calc text)|
+----------------------------------------------------------------------------------++----------------------------------------------------------------------------------+
| MATH COMMAND: CACHING AND DISPATCH FLOW (::mathsyntax::math) |
| Avoids re-parsing the same math block on every call (performance) |
| |
| proc ::mathsyntax::math { calc }: |
| | |
| v |
| lsearch $cached_calcs $calc <-- has this EXACT text been seen before? |
| | |
| +----+----+ |
| | | |
| NO YES |
| | | |
| v v |
| lappend (skip parsing, |
| cached_ id already known) |
| calcs |
| | |
| v |
| Transform $id $calc <-- parse once, create Cached$id proc |
| | |
| +---------+ |
| | |
| v |
| ::mathsyntax::Cached$id <-- call the generated proc |
| (runs inside uplevel 2, so caller's variables are set directly) |
| |
| Cache key: exact calc TEXT (not a hash) -- identical text reuses same id |
| Side effect: variables in CALLING scope are modified (not math's own scope)|
+----------------------------------------------------------------------------------++----------------------------------------------------------------------------------+
| DEMO PROC: SINC FUNCTION TABLE GENERATION (proc demo {}) |
| Computes sinc(x)^2 = (sin(x)/x)^2 for x = 0, 0.1, 0.2, ... 1.9 |
| |
| basic_alphabet_variables <-- initializes a-z, A-Z all to 1 |
| | |
| v |
| set a 1; set b 1; set c ""; set d ""; set sinc "" |
| | |
| v |
| math { c = a + b ; d = a + cos(b+c) } |
| --> c = 1+1 = 2 |
| --> d = 1 + cos(1+2) = 1 + cos(3) = 1 + (-0.98999) = 0.01001 |
| | |
| v |
| FOR i = 0 to 19: |
| math { |
| x = 0.1*i |
| sinc = 1 if x == 0 <-- avoids divide-by-zero at x=0 |
| sinc = sin(x)/x if x != 0 |
| y(i) = sinc*sinc <-- store in array y, indexed by i |
| } |
| puts "$i $x $sinc $y($i)" |
| | |
| v |
| parray y <-- print full array y(0) through y(19) |
+----------------------------------------------------------------------------------++----------------------------------------------------------------------------------+ | SINC FUNCTION TABLE: EXPECTED VALUES (y(i) = sinc(x)^2) | | | | +-----+--------+-------------------+-------------------+ | | | i | x | sinc = sin(x)/x | y(i) = sinc^2 | | | +-----+--------+-------------------+-------------------+ | | | 0 | 0.0 | 1.0 (special case)| 1.0 | | | | 1 | 0.1 | 0.99833 | 0.99666 | | | | 2 | 0.2 | 0.99335 | 0.98674 | | | | 3 | 0.3 | 0.98507 | 0.97036 | | | | 5 | 0.5 | 0.95885 | 0.91940 | | | | 10 | 1.0 | 0.84147 | 0.70807 | | | | 15 | 1.5 | 0.66500 | 0.44223 | | | | 19 | 1.9 | 0.49654 | 0.24655 | | | +-----+--------+-------------------+-------------------+ | | | | Curve shape (sinc^2, classic diffraction pattern envelope): | | 1.0 |* | | | ** | | 0.7 | *** | | | *** | | 0.4 | **** | | | ***** | | 0.2 | ***** | | +---+---+---+---+---+---+--> x | | 0 0.5 1.0 1.5 2.0 | | | | Special case i=0 avoids division by zero (sin(0)/0 undefined --> set to 1) | +----------------------------------------------------------------------------------+
+----------------------------------------------------------------------------------+
| TEST PROC: ASSERTION FRAMEWORK (proc test args, RS pattern) |
| |
| proc test args { |
| foreach {case expected} $args { |
| catch {uplevel 1 $case} res |
| if {$res != $expected} {error "$case->$res, expected $expected"} |
| puts "example >> $case -> $res, & expected was $expected" |
| } |
| } |
| |
| Usage pattern: test { CASE_CODE } EXPECTED_RESULT (pairs in args) |
| |
| All 16 test assertions from demo proc: |
| +---------------------------+----------+ |
| | test case | expected | |
| +---------------------------+----------+ |
| | isprime 7 | 1 | |
| | + 1 1 | 2 | |
| | - 10 1 | 9 | |
| | * 10 1 | 10 | |
| | / 10 2 | 5 | |
| | math { a = 10 / 2 } | 5 | <-- integer division |
| | math { b = 10. / 2 } | 5. | <-- float division |
| | math { c = 10. / 2. } | 5. | <-- float / float |
| | math { d = 1 + 1 } | 2 | |
| | math { e = 10 - 1 } | 9 | |
| | math { f = 10 * 1 } | 10 | |
| | math { g = 10 / 2 } | 5 | |
| | math { h = 10 ** 2 } | 100 | <-- exponentiation |
| | math { i = 10 % 2 } | 0 | <-- modulo |
| | math { i = 3*4/2 } | 6 | |
| | math { i = (3>2) } | 1 | <-- boolean comparison |
| | math { i = (3<2) } | 0 | |
| +---------------------------+----------+ |
+----------------------------------------------------------------------------------++----------------------------------------------------------------------------------+
| NAMESPACE STRUCTURE: ::mathsyntax (Little_Math_Language_AM) |
| |
| namespace eval ::mathsyntax { |
| namespace export math |
| variable cached_calcs {} <-- list of seen calc texts |
| variable func_names { abs acos asin atan atan2 ceil cos cosh double |
| exp floor fmod hypot int log log10 pow rand round |
| sin sinh sqrt srand tan tanh wide } |
| } |
| | |
| +----+----------+----------+----------+ |
| | | | | |
| v v v v |
| ::mathsyntax:: ::mathsyntax:: ::mathsyntax:: ::mathsyntax:: |
| ToExpr Transform math Cached0, Cached1, ... |
| (string--> (parse calc (public (auto-generated per |
| expr fragment) block, build entry unique calc block, |
| proc body) point) runs in caller's scope) |
| |
| Public usage: |
| namespace import ::mathsyntax::math |
| math { a = x*x + y*y } <-- calls math directly after import |
| |
| 20 recognized math functions in func_names (used to distinguish |
| function calls like sin(x) from array accesses like y(i)) |
+----------------------------------------------------------------------------------++----------------------------------------------------------------------------------+
| EASY EYE CONSOLE EXTENSION: GOLD'S V2 ADDITIONS (20Oct2020) |
| Keeps AM's clean original code untouched; extension layered below |
| |
| Added TCLLIB packages: |
| package require math::numtheory |
| package require math::constants |
| package require math::trig |
| package require math |
| namespace path {::tcl::mathop ::tcl::mathfunc math::numtheory |
| math::trig math::constants} |
| | |
| v |
| Console "easy eye" display configuration: |
| console show |
| console eval {.console config -bg palegreen} |
| console eval {.console config -font {fixed 20 bold}} <-- large type |
| console eval {wm geometry . 40x20} |
| console eval {wm title . "...screen grab and paste to texteditor"} |
| console eval {. configure -background orange -highlightcolor brown |
| -relief raised -border 30} |
| | |
| v |
| Added Self_Help menu item: |
| console eval { proc self_helpx {} {...} } |
| console eval {.menubar.help add command -label Self_help |
| -command self_helpx} |
| | |
| v |
| proc basic_alphabet_variables {} <-- sets a-z, A-Z globals all to 1 |
| proc demo {} <-- runs full test suite + sinc table + array print |
+----------------------------------------------------------------------------------++----------------------------------------------------------------------------------+ | LINEAGE AND HISTORICAL CONTEXT | | | | +----------+-------------------------+------------------------------------+ | | | date | author | contribution | | | +----------+-------------------------+------------------------------------+ | | |2002-01-02| Richard Suchenwirth (RS)| "A little math language" -- | | | | | | New Year's Eve fun project; small | | | | | | math DSL built on expr | | | +----------+-------------------------+------------------------------------+ | | |2004-05-07| Arjen Markus (AM) | "A little math language revisited" | | | | | | mathlang.tcl proc; ToExpr, | | | | | | Transform, math commands | | | +----------+-------------------------+------------------------------------+ | | |2004-07-07| Arjen Markus (AM) | Small improvement: recognize | | | | | | array elements in expressions | | | +----------+-------------------------+------------------------------------+ | | |2020-10-20| gold | Console extension V2: TCLLIB | | | | | | packages, easy-eye console, | | | | | | self_help, demo proc, test suite | | | +----------+-------------------------+------------------------------------+ | | | | Theoretical grounding cited: John McCarthy's "A basis for a mathematical | | theory of computation" (1963) -- inspiration for small-language DSL design | | | | Analogy (Discussion section): using Little_Math_Language to control TCL | | is like "sticking an Apple computer ... in front of a Cray computer" -- | | a simple interface layered atop a powerful, complex system | +----------------------------------------------------------------------------------+
+----------------------------------------------------------------------------------+
| OVERALL PROGRAM FLOW (Little_Math_Language_AM V2, gold 20Oct2020) |
| |
| package require math::numtheory, math::constants, math::trig, math |
| namespace path {::tcl::mathop ::tcl::mathfunc math::numtheory ...} |
| | |
| v |
| Define ::mathsyntax namespace (AM's original code, untouched) |
| ToExpr, Transform, math procs |
| | |
| v |
| Define proc test (RS pattern, assertion framework) |
| | |
| v |
| console show + easy-eye console configuration (palegreen, large font) |
| Add Self_Help menu item to console menubar |
| | |
| v |
| Define proc basic_alphabet_variables (sets a-z, A-Z = 1) |
| Define proc demo {}: |
| import math; init variables |
| math { c = a+b; d = a+cos(b+c) } --> puts c, d |
| FOR loop 0..19: math {sinc table} --> puts each row |
| parray y |
| run 16 test{} assertions (arithmetic, math block, comparison) |
| | |
| v |
| demo <-- call at end of file, auto-runs on script load |
+----------------------------------------------------------------------------------+Please include your wiki MONIKER and date in your comment with the same courtesy that I will give you. Thanks, gold 15Oct2020
| Category Numerical Analysis | Category Toys | Category Calculator | Category Mathematics | Category Example | Toys and Games | Category Games | Category Application | Category GUI |