Author: Florent Merlet <[email protected]>
State: Draft
Type: Project
Vote: Pending
Created: 31-Oct-2026
Tcl-Version: 9.2
Tcl-Branch: tip-759h-tcl91-user-op
Keywords: expr, operator, mathop
Post-History:
Abstract
In an expression, a backslash directly followed by a letter names an
operator defined by a command of tcl::mathop:
proc tcl::mathop::dot {u v} {
set s 0
foreach a $u b $v {set s [expr {$s + $a*$b}]}
return $s
}
expr {$U \dot $V} ;# tcl::mathop::dot $U $V
expr {\neg $U} ;# tcl::mathop::neg $U
expr {$a + $U \dot $V * 2} ;# $a + (($U \dot $V) * 2)
It is the eighth part of TIP 759 (expression syntax in Tcl). It does not depend on the other parts, except for one rule with the raw commands of the sixth part.
Rationale
expr has a fixed set of operators. Vectors, matrices, sets, intervals,
complex numbers or dates have natural operations, which today must be
written as function calls, dot($U, $V), or commands. An infix operator
keeps the formula readable: $U \dot $V, $A \times $B, $p \in $zone.
The backslash makes the operator visible as an operator, as in LaTeX, and
cannot be confused with an operand.
The form is free: a bare backslash is an error in an expression today
(invalid character "\"); backslash-newline is white space and stays so.
Specification
Lexical form. In an expression,
\directly followed by a letter, then letters, digits and underscores, is a user-defined operator. A backslash followed by anything else is still an error, except backslash-newline (white space).Unary and binary. After an operand, the operator is binary:
x \name yis the result oftcl::mathop::name x y. At the start of the expression or after another operator (including(and,), it is unary:\name xis the result oftcl::mathop::name x. This is the rule of+and-.Precedence. The binary form has the precedence of
*,/and%, and groups left to right:$a + $U \dot $V * 2is$a + (($U \dot $V) * 2). The unary form has the precedence of the other unary operators:\neg $U \dot $Vis(\neg $U) \dot $V. The precedence is fixed: it does not depend on the definition of the command, which may be defined after the expression is compiled.Resolution. The command
tcl::mathop::nameis resolved as the functions oftcl::mathfunc: relative to the current namespace, then in the global namespace. A package can define operators in its own namespace. An undefined operator is the errorinvalid command name "tcl::mathop::name", at run time.Value. The value is the result of the command, converted to a number at the end of the expression as a function result is; a list stays a list.
Raw commands (sixth part). A raw command ends at a word that begins with a user-defined operator:
llength $L \plus 1is[llength $L] \plus 1.Tcl_ParseExpr gives a
TCL_TOKEN_OPERATORtoken\name, with one or two operands.
Limits
- The operator is read by the expression parser. In an expression
written between double quotes,
expr "$a \times $b", the Tcl parser performs backslash substitution first (rule [4] of Tcl(n)), and\tbecomes a tab. Expressions are braced, as recommended. - Some names look like backslash sequences:
\times,\neg,\x31,\u…. In a braced expression they are operators. Rule [9] of Tcl(n) says so. {*}does not apply to the operands of an operator.
Compatibility
Only expressions that are errors today change meaning: \x31 + 1 was
invalid character "\"; it is now a call of the operator \x31.
Implementation
Patch 0008-TIP-759-8-8-user-defined-operators-name-in-expressio.patch:
tclCompExpr.c:ParseLexemereads\nameasUSER_OP;ParseExprresolves it toUNARY_USERorBINARY_USERand keeps the name with the function names.CompileExprTreepushestcl::mathop::name(for the binary form, below the left operand, withreverse 2), theninvokeStk 2or3:loadScalar %v0 # U push "tcl::mathop::dot" reverse 2 loadScalar %v1 # V invokeStk 3tclParse.c:Tcl_ParseCommandends a raw command at\name.Tests:
tests/exprUserOp.test. Documentation:expr.n(two entries in the list of operators),Tcl.n(rule [9]).
Alternatives considered
- A declaration in the expression,
\dot is operator = dotproduct: the command oftcl::mathopneeds no new syntax, follows the namespace rules of the functions, and accepts aliases (interp alias {} tcl::mathop::dot {} dotproduct). A declaration may come later with the register of names known toexpr. - A precedence per operator: the bytecode is compiled once and cached; a precedence given by the definition, which can come later, would make compiled code wrong or need invalidation. Parentheses give any other grouping.
- Unicode operators (
⋅,×): harder to type, and they need a table of characters.
Copyright
This document has been placed in the public domain.
