10
$\begingroup$

I often use the Operator Input Forms page as a reference for operator precedence as well as which (used or unused) operators exist. However, I just noticed that at least one operator is missing from the table: Divisible can be written as the operator (\[Divides]). The immediate problem is that I now need to use trial and error to figure out its precedence, but it raises the more important question which other valid operators (with or without built-in meaning) are missing from the table.

Is there a more reliable resource on existing operators, or a way to get a list of all existing operators (ideally with precedence) using meta programming?

$\endgroup$
6
  • $\begingroup$ If anyone is interested: testing suggests that the precedence of falls between / (division) and \[Backslash] (unused). $\endgroup$ Commented Jan 20, 2017 at 9:51
  • $\begingroup$ You know about Precedence[], no? $\endgroup$ Commented Jan 20, 2017 at 12:48
  • $\begingroup$ @J.M. Nope, I didn't. I was looking for something like it earlier, but since it's undocumented... If I look at Precedence @ Divisible though that's completely inconsistent with what I've determined experimentally. $\endgroup$ Commented Jan 20, 2017 at 13:08
  • 2
    $\begingroup$ Actually it seems that Precedence doesn't know about Divisible. It seems to return 670 for any built-in function that doesn't have an operator, and it returns the same for Divisible. $\endgroup$ Commented Jan 20, 2017 at 13:16
  • 1
    $\begingroup$ ??∣ also fails. $\endgroup$ Commented Jan 25, 2017 at 13:00

2 Answers 2

8
$\begingroup$

We can read the Front End resource as I did for List of symbols without built-in meaning.

UnicodeCharacters.tr itself contains the parsing precedence information, reference:

Extract the data for infix, prefix, and postfix operators:

ucharTR = ReadList[System`Dump`unicodeCharactersTR, Word, RecordLists -> True];

operators = 
 Cases[ucharTR, {adr_, ch_, ___, "Infix" | "Prefix" | "Postfix", 
    prec_, ___} :> {adr, StringTake[ch, {3, -2}], FromDigits @ prec}];

Your missing operator is there, and its parsing precedence is 590:

Cases[operators, {_, "Divides", _}]
{{"0x2223", "Divides", 590}}

A table sorted by precedence:

Reverse @ SortBy[operators, Last] // TableForm

$\left( \begin{array}{lll} \text{0xF3B4} & \text{InvisiblePostfixScriptBase} & 800 \\ \text{0xF3B3} & \text{InvisiblePrefixScriptBase} & 800 \\ \text{0xF356} & \text{} & 790 \\ \text{0xF355} & \text{} & 790 \\ \text{0xF353} & \text{} & 780 \\ \text{0xF51E} & \text{InlinePart} & 763 \\ \text{0xF76D} & \text{InvisibleApplication} & 730 \\ \text{0xF3CE} & \text{HermitianConjugate} & 695 \\ \text{0xF3C9} & \text{ConjugateTranspose} & 695 \\ \text{0xF3C8} & \text{Conjugate} & 695 \\ \text{0xF3C7} & \text{Transpose} & 695 \\ \text{0xF354} & \text{} & 660 \\ \text{0xF52B} & \text{ShortDownArrow} & 650 \\ \text{0xF52A} & \text{ShortUpArrow} & 650 \\ \text{0x296F} & \text{ReverseUpEquilibrium} & 650 \\ \text{0x296E} & \text{UpEquilibrium} & 650 \\ \text{0x2961} & \text{LeftDownTeeVector} & 650 \\ \text{0x2960} & \text{LeftUpTeeVector} & 650 \\ \text{0x295D} & \text{RightDownTeeVector} & 650 \\ \text{0x295C} & \text{RightUpTeeVector} & 650 \\ \text{0x2959} & \text{LeftDownVectorBar} & 650 \\ \text{0x2958} & \text{LeftUpVectorBar} & 650 \\ \text{0x2955} & \text{RightDownVectorBar} & 650 \\ \text{0x2954} & \text{RightUpVectorBar} & 650 \\ \text{0x2951} & \text{LeftUpDownVector} & 650 \\ \text{0x294F} & \text{RightUpDownVector} & 650 \\ \text{0x2913} & \text{DownArrowBar} & 650 \\ \text{0x2912} & \text{UpArrowBar} & 650 \\ \text{0x27FA} & \text{DoubleLongLeftRightArrow} & 650 \\ \text{0x27F9} & \text{DoubleLongRightArrow} & 650 \\ \text{0x27F8} & \text{DoubleLongLeftArrow} & 650 \\ \text{0x27F7} & \text{LongLeftRightArrow} & 650 \\ \text{0x27F6} & \text{LongRightArrow} & 650 \\ \text{0x27F5} & \text{LongLeftArrow} & 650 \\ \text{0x221A} & \text{Sqrt} & 650 \\ \text{0x21F5} & \text{DownArrowUpArrow} & 650 \\ \text{0x21D5} & \text{DoubleUpDownArrow} & 650 \\ \text{0x21D3} & \text{DoubleDownArrow} & 650 \\ \text{0x21D1} & \text{DoubleUpArrow} & 650 \\ \text{0x21C5} & \text{UpArrowDownArrow} & 650 \\ \text{0x21C3} & \text{LeftDownVector} & 650 \\ \text{0x21C2} & \text{RightDownVector} & 650 \\ \text{0x21BF} & \text{LeftUpVector} & 650 \\ \text{0x21BE} & \text{RightUpVector} & 650 \\ \text{0x21A7} & \text{DownTeeArrow} & 650 \\ \text{0x21A5} & \text{UpTeeArrow} & 650 \\ \text{0x2195} & \text{UpDownArrow} & 650 \\ \text{0x2193} & \text{DownArrow} & 650 \\ \text{0x2191} & \text{UpArrow} & 650 \\ \text{0xF74C} & \text{DifferentialD} & 630 \\ \text{0xF74B} & \text{CapitalDifferentialD} & 630 \\ \text{0xF4A5} & \text{DiscreteRatio} & 620 \\ \text{0xF4A4} & \text{DifferenceDelta} & 620 \\ \text{0xF4A3} & \text{DiscreteShift} & 620 \\ \text{0x2206} & \text{Laplacian} & 620 \\ \text{0x2202} & \text{PartialD} & 620 \\ \text{0xF3D6} & \text{Gradient} & 615 \\ \text{0xF520} & \text{Square} & 610 \\ \text{0x2218} & \text{SmallCircle} & 607 \\ \text{0xF3DE} & \text{PermutationProduct} & 605 \\ \text{0x2299} & \text{CircleDot} & 605 \\ \text{0xF4A0} & \text{Cross} & 603 \\ \text{0xF3DB} & \text{TensorWedge} & 603 \\ \text{0xF3DA} & \text{TensorProduct} & 602 \\ \text{0xF3D8} & \text{Curl} & 601 \\ \text{0xF3D7} & \text{Divergence} & 601 \\ \text{0xF361} & \text{Piecewise} & 600 \\ \text{0x2213} & \text{} & 600 \\ \text{0x2212} & \text{} & 600 \\ \text{0x00B1} & \text{} & 600 \\ \text{0xF350} & \text{} & 595 \\ \text{0xF39E} & \text{ImplicitPlus} & 593 \\ \text{0x2223} & \text{Divides} & 590 \\ \text{0x2215} & \text{DivisionSlash} & 590 \\ \text{0x00F7} & \text{Divide} & 590 \\ \text{0x2216} & \text{Backslash} & 580 \\ \text{0x22C4} & \text{Diamond} & 570 \\ \text{0x22C0} & \text{Wedge} & 560 \\ \text{0x22C1} & \text{Vee} & 550 \\ \text{0x2297} & \text{CircleTimes} & 540 \\ \text{0x00B7} & \text{CenterDot} & 530 \\ \text{0x2062} & \text{InvisibleTimes} & 520 \\ \text{0x00D7} & \text{Times} & 520 \\ \text{0x22C6} & \text{Star} & 510 \\ \text{0x2210} & \text{Coproduct} & 500 \\ \text{0x2240} & \text{VerticalTilde} & 490 \\ \text{0x2322} & \text{Cap} & 470 \\ \text{0x2323} & \text{Cup} & 460 \\ \text{0xF3DD} & \text{ExpectationE} & 455 \\ \text{0xF3DC} & \text{ProbabilityPr} & 455 \\ \text{0x2296} & \text{CircleMinus} & 450 \\ \text{0x2295} & \text{CirclePlus} & 450 \\ \text{0x2213} & \text{MinusPlus} & 430 \\ \text{0x2212} & \text{Minus} & 430 \\ \text{0x00B1} & \text{PlusMinus} & 430 \\ \text{0x22C2} & \text{Intersection} & 420 \\ \text{0x2293} & \text{SquareIntersection} & 420 \\ \text{0x22C3} & \text{Union} & 410 \\ \text{0x2294} & \text{SquareUnion} & 410 \\ \text{0x228E} & \text{UnionPlus} & 410 \\ \text{0xF3D5} & \text{DirectedEdge} & 395 \\ \text{0xF3D4} & \text{UndirectedEdge} & 395 \\ \text{0xF7D9} & \text{LongEqual} & 390 \\ \text{0xF431} & \text{Equal} & 390 \\ \text{0xF42D} & \text{NotSucceedsEqual} & 390 \\ \text{0xF42B} & \text{NotPrecedesEqual} & 390 \\ \text{0xF429} & \text{NotGreaterSlantEqual} & 390 \\ \text{0xF428} & \text{NotNestedGreaterGreater} & 390 \\ \text{0xF427} & \text{NotGreaterGreater} & 390 \\ \text{0xF424} & \text{NotLessSlantEqual} & 390 \\ \text{0xF423} & \text{NotNestedLessLess} & 390 \\ \text{0xF422} & \text{NotLessLess} & 390 \\ \text{0xF413} & \text{NotRightTriangleBar} & 390 \\ \text{0xF412} & \text{NotLeftTriangleBar} & 390 \\ \text{0xF402} & \text{NotHumpDownHump} & 390 \\ \text{0xF401} & \text{NotHumpEqual} & 390 \\ \text{0xF400} & \text{NotEqualTilde} & 390 \\ \text{0xF3D1} & \text{NotVerticalBar} & 390 \\ \text{0xF3D0} & \text{VerticalBar} & 390 \\ \text{0x2AB0} & \text{SucceedsEqual} & 390 \\ \text{0x2AAF} & \text{PrecedesEqual} & 390 \\ \text{0x2AA2} & \text{NestedGreaterGreater} & 390 \\ \text{0x2AA1} & \text{NestedLessLess} & 390 \\ \text{0x2A7E} & \text{GreaterSlantEqual} & 390 \\ \text{0x2A7D} & \text{LessSlantEqual} & 390 \\ \text{0x29D0} & \text{RightTriangleBar} & 390 \\ \text{0x29CF} & \text{LeftTriangleBar} & 390 \\ \text{0x27C2} & \text{Perpendicular} & 390 \\ \text{0x22ED} & \text{NotRightTriangleEqual} & 390 \\ \text{0x22EC} & \text{NotLeftTriangleEqual} & 390 \\ \text{0x22EB} & \text{NotRightTriangle} & 390 \\ \text{0x22EA} & \text{NotLeftTriangle} & 390 \\ \text{0x22E9} & \text{NotSucceedsTilde} & 390 \\ \text{0x22E8} & \text{NotPrecedesTilde} & 390 \\ \text{0x22E1} & \text{NotSucceedsSlantEqual} & 390 \\ \text{0x22E0} & \text{NotPrecedesSlantEqual} & 390 \\ \text{0x22DB} & \text{GreaterEqualLess} & 390 \\ \text{0x22DA} & \text{LessEqualGreater} & 390 \\ \text{0x22B5} & \text{RightTriangleEqual} & 390 \\ \text{0x22B4} & \text{LeftTriangleEqual} & 390 \\ \text{0x22B3} & \text{RightTriangle} & 390 \\ \text{0x22B2} & \text{LeftTriangle} & 390 \\ \text{0x22A5} & \text{UpTee} & 390 \\ \text{0x2281} & \text{NotSucceeds} & 390 \\ \text{0x2280} & \text{NotPrecedes} & 390 \\ \text{0x227F} & \text{SucceedsTilde} & 390 \\ \text{0x227E} & \text{PrecedesTilde} & 390 \\ \text{0x227D} & \text{SucceedsSlantEqual} & 390 \\ \text{0x227C} & \text{PrecedesSlantEqual} & 390 \\ \text{0x227B} & \text{Succeeds} & 390 \\ \text{0x227A} & \text{Precedes} & 390 \\ \text{0x2279} & \text{NotGreaterLess} & 390 \\ \text{0x2278} & \text{NotLessGreater} & 390 \\ \text{0x2277} & \text{GreaterLess} & 390 \\ \text{0x2276} & \text{LessGreater} & 390 \\ \text{0x2275} & \text{NotGreaterTilde} & 390 \\ \text{0x2274} & \text{NotLessTilde} & 390 \\ \text{0x2273} & \text{GreaterTilde} & 390 \\ \text{0x2272} & \text{LessTilde} & 390 \\ \text{0x2271} & \text{NotGreaterEqual} & 390 \\ \text{0x2270} & \text{NotLessEqual} & 390 \\ \text{0x226F} & \text{NotGreater} & 390 \\ \text{0x226E} & \text{NotLess} & 390 \\ \text{0x226D} & \text{NotCupCap} & 390 \\ \text{0x226B} & \text{GreaterGreater} & 390 \\ \text{0x226A} & \text{LessLess} & 390 \\ \text{0x2269} & \text{NotGreaterFullEqual} & 390 \\ \text{0x2268} & \text{NotLessFullEqual} & 390 \\ \text{0x2267} & \text{GreaterFullEqual} & 390 \\ \text{0x2266} & \text{LessFullEqual} & 390 \\ \text{0x2265} & \text{GreaterEqual} & 390 \\ \text{0x2264} & \text{LessEqual} & 390 \\ \text{0x2262} & \text{NotCongruent} & 390 \\ \text{0x2261} & \text{Congruent} & 390 \\ \text{0x2260} & \text{NotEqual} & 390 \\ \text{0x2250} & \text{DotEqual} & 390 \\ \text{0x224F} & \text{HumpEqual} & 390 \\ \text{0x224E} & \text{HumpDownHump} & 390 \\ \text{0x224D} & \text{CupCap} & 390 \\ \text{0x2249} & \text{NotTildeTilde} & 390 \\ \text{0x2248} & \text{TildeTilde} & 390 \\ \text{0x2247} & \text{NotTildeFullEqual} & 390 \\ \text{0x2245} & \text{TildeFullEqual} & 390 \\ \text{0x2244} & \text{NotTildeEqual} & 390 \\ \text{0x2243} & \text{TildeEqual} & 390 \\ \text{0x2242} & \text{EqualTilde} & 390 \\ \text{0x2241} & \text{NotTilde} & 390 \\ \text{0x223C} & \text{Tilde} & 390 \\ \text{0x2237} & \text{Proportion} & 390 \\ \text{0x2226} & \text{NotDoubleVerticalBar} & 390 \\ \text{0x2225} & \text{DoubleVerticalBar} & 390 \\ \text{0x221D} & \text{Proportional} & 390 \\ \text{0x21CC} & \text{Equilibrium} & 390 \\ \text{0x21CB} & \text{ReverseEquilibrium} & 390 \\ \text{0xF526} & \text{ShortLeftArrow} & 380 \\ \text{0xF525} & \text{ShortRightArrow} & 380 \\ \text{0x295F} & \text{DownRightTeeVector} & 380 \\ \text{0x295E} & \text{DownLeftTeeVector} & 380 \\ \text{0x295B} & \text{RightTeeVector} & 380 \\ \text{0x295A} & \text{LeftTeeVector} & 380 \\ \text{0x2957} & \text{DownRightVectorBar} & 380 \\ \text{0x2956} & \text{DownLeftVectorBar} & 380 \\ \text{0x2953} & \text{RightVectorBar} & 380 \\ \text{0x2952} & \text{LeftVectorBar} & 380 \\ \text{0x2950} & \text{DownLeftRightVector} & 380 \\ \text{0x294E} & \text{LeftRightVector} & 380 \\ \text{0x21E5} & \text{RightArrowBar} & 380 \\ \text{0x21E4} & \text{LeftArrowBar} & 380 \\ \text{0x21D4} & \text{DoubleLeftRightArrow} & 380 \\ \text{0x21D2} & \text{DoubleRightArrow} & 380 \\ \text{0x21D0} & \text{DoubleLeftArrow} & 380 \\ \text{0x21C6} & \text{LeftArrowRightArrow} & 380 \\ \text{0x21C4} & \text{RightArrowLeftArrow} & 380 \\ \text{0x21C1} & \text{DownRightVector} & 380 \\ \text{0x21C0} & \text{RightVector} & 380 \\ \text{0x21BD} & \text{DownLeftVector} & 380 \\ \text{0x21BC} & \text{LeftVector} & 380 \\ \text{0x21A6} & \text{RightTeeArrow} & 380 \\ \text{0x21A4} & \text{LeftTeeArrow} & 380 \\ \text{0x2199} & \text{LowerLeftArrow} & 380 \\ \text{0x2198} & \text{LowerRightArrow} & 380 \\ \text{0x2197} & \text{UpperRightArrow} & 380 \\ \text{0x2196} & \text{UpperLeftArrow} & 380 \\ \text{0x2194} & \text{LeftRightArrow} & 380 \\ \text{0x2192} & \text{RightArrow} & 380 \\ \text{0x2190} & \text{LeftArrow} & 380 \\ \text{0xF42F} & \text{NotSquareSuperset} & 360 \\ \text{0xF42E} & \text{NotSquareSubset} & 360 \\ \text{0xF3D2} & \text{Distributed} & 360 \\ \text{0x22E3} & \text{NotSquareSupersetEqual} & 360 \\ \text{0x22E2} & \text{NotSquareSubsetEqual} & 360 \\ \text{0x2292} & \text{SquareSupersetEqual} & 360 \\ \text{0x2291} & \text{SquareSubsetEqual} & 360 \\ \text{0x2290} & \text{SquareSuperset} & 360 \\ \text{0x228F} & \text{SquareSubset} & 360 \\ \text{0x2289} & \text{NotSupersetEqual} & 360 \\ \text{0x2288} & \text{NotSubsetEqual} & 360 \\ \text{0x2287} & \text{SupersetEqual} & 360 \\ \text{0x2286} & \text{SubsetEqual} & 360 \\ \text{0x2285} & \text{NotSuperset} & 360 \\ \text{0x2284} & \text{NotSubset} & 360 \\ \text{0x2283} & \text{Superset} & 360 \\ \text{0x2282} & \text{Subset} & 360 \\ \text{0x220C} & \text{NotReverseElement} & 360 \\ \text{0x220B} & \text{ReverseElement} & 360 \\ \text{0x2209} & \text{NotElement} & 360 \\ \text{0x2208} & \text{Element} & 360 \\ \text{0x2204} & \text{NotExists} & 350 \\ \text{0x2203} & \text{Exists} & 350 \\ \text{0x2200} & \text{ForAll} & 350 \\ \text{0x00AC} & \text{Not} & 340 \\ \text{0x22BC} & \text{Nand} & 330 \\ \text{0x2227} & \text{And} & 330 \\ \text{0xF4A2} & \text{Xnor} & 325 \\ \text{0x22BB} & \text{Xor} & 325 \\ \text{0x22BD} & \text{Nor} & 320 \\ \text{0x2228} & \text{Or} & 320 \\ \text{0x29E6} & \text{Equivalent} & 315 \\ \text{0xF523} & \text{Implies} & 310 \\ \text{0x2970} & \text{RoundImplies} & 310 \\ \text{0xF3D3} & \text{Conditioned} & 305 \\ \text{0x2AE4} & \text{DoubleLeftTee} & 300 \\ \text{0x22A8} & \text{DoubleRightTee} & 300 \\ \text{0x22A4} & \text{DownTee} & 300 \\ \text{0x22A3} & \text{LeftTee} & 300 \\ \text{0x22A2} & \text{RightTee} & 300 \\ \text{0x220D} & \text{SuchThat} & 290 \\ \text{0xF522} & \text{Rule} & 250 \\ \text{0xF51F} & \text{RuleDelayed} & 250 \\ \text{0x2236} & \text{Colon} & 205 \\ \text{0xF432} & \text{VerticalSeparator} & 202 \\ \text{0x2235} & \text{Because} & 201 \\ \text{0x2234} & \text{Therefore} & 201 \\ \text{0xF4A1} & \text{Function} & 190 \\ \text{0xF765} & \text{InvisibleComma} & 1 \\ \end{array} \right)$

$\endgroup$
1
  • $\begingroup$ Note that this answer misses several operators contained in UnicodeCharacters.tr. See my answer for details. $\endgroup$ Commented Aug 15, 2018 at 17:13
8
$\begingroup$

Finding All Operators and Their Precedence, Arity, Affix, and Associativity

Sources of Information

There are a few different sources of information about operators and their properties. I list all known sources (as of August 2018) in the table below. We will explore these sources in more detail in the remainder of this section.

  1. Official Documentation: The most common operators only. Usually no precedence or associativity.
  2. WolframLanguageData[]: Only has info for most common operators. For most operators, gives precedence rank, arity, affix. Less often associativity info is apparent.
  3. Precedence[]: Undocumented built-in function. When provided the long name of an operator, gives precedence. Only works with some operators and is often incorrect.
  4. UnicodeCharacters.tr: Gives explicit precedence, affix, and associativity for 338 operators that are a single Unicode character outside of ASCII character set. Note that this undocumented system file uses a different precedence numbering scheme than other sources and which applies only to the notebook interface.
  5. System`Convert`MLStringDataDump`$Operators: Undocumented list of 340 operators—characters only. Includes most operators, even single and multi-character ASCII operators. Excludes Association brackets, box operators, and obscure bracketing operators.
  6. System`Convert`MLStringDataDump`$ExtractedOperators: A proper subset of $Operators above with 272 single character unicode operators. Listed for completeness.
  7. Internal`SymbolNameQ: Undocumented function that apparently identifies a string as a "symbol name." Computing the complement of the set of Unicode characters for which SymbolNameQ evaluates to True gives a list of operators, including some single-character box operators found in no other list.

Note: No single source contains all operators, and all but $ExtractedOperators and (ironically) the official documentation are necessary to obtain all available information about all operators.

The documentation and the undocumented Precedence[] function

You will find that the table given in the Operator Input Forms documentation is sometimes incorrect and woefully incomplete. The same is true of the undocumented Precedence function. Neither includes all of the operators, or even all precedence/precedence ranks, and neither is to be trusted.

WolframLanguageData[]

I have found that the precedence rank returned by WolframLanguageData is more accurate than the Precedence function. As a bonus, WolframLanguageData gives you really nice operator synonym and FullForm data.

WolframLanguageData["Power", "PrecedenceRanks"]
{{expr1\_expr2\%expr3, Power[Subscript[expr1,expr2],expr3]}->8,
{expr1^expr2, Power[expr1,expr2]}->21,
{expr1^expr2, Power[expr1,expr2]}->21,
{expr1^expr2, Power[expr1,expr2]}->21,
{Subsuperscript[expr1, expr2, expr3],
    Power[Subscript[expr1,expr2],expr3]}->21,
{expr1\^expr2\%expr3, Power[Subscript[expr1,expr3],expr2]}->21,
{\@expr\%n,Power[expr,Power[n,-1]]}->22}

Another bonus of WolframLanguageData is that you do not already have to know the name of every function with an operator. You can ask WolframLanguageData to just give you everything in its database, including the box sublanguage. The following will generate a list for you. Be warned that it will download data for all 5,000+ functions and ultimately give you a list of 135 (as of August 2018) {name, rankdata, shortnotation} triples:

 Select[
     WolframLanguageData[
         WolframLanguageData["Entities"], 
         {"Name", "PrecedenceRanks", "ShortNotations"}
     ], 
     #[[2]] =!= Missing["NotApplicable"] &
 ]

The "ShortNotations" data provided by WolframLanguageData leaves a lot to be desired. And, unfortunately, WolframLanguageData will not give you every operator. However, this is the only source that gives all multicharacter box operators.

UnicodeCharacters.tr

The Mathematica system files includes a file named UnicodeCharacters.tr. I borrow from Mr. Wizard's answer the following code, slightly modified, to read in the data from the file:

ucharTR = ReadList[
    System`Dump`unicodeCharactersTR, 
    Word, WordSeparators -> {"\t\t"},
    RecordLists -> True
];

As its name implies, the UnicodeCharacters.tr file appears to only include characters that are single Unicode characters outside of the ASCII character set. It does not include ! or /@, for example. Most of the records in the file are not operators. But for the operators that are included, it gives us the most detailed information of any source. Each record has up to 9 columns (fields) that we might name: (1)Hex Code, (2)Long Name, (3)Alternative Input Methods, (4)Class/Affix, (5)Precedence, (6)Associativity, (7)Left Spacing, (8)Right Spacing, (9)Rendered As. This is everything we could hope to know about the linguistics of an operator. Be aware that it uses a different precedence numbering scheme from Precedence, so be sure not to mix up the two. Note also that the precedence in the UnicodeCharacters.tr refers to the precedence of the frontend only. (See the third displayed example in the section "Programmatically determining properties of operators.")

We can filter out non-operators using the Class/Affix column. Let's look at the unique values in that column.

Union[Select[ucharTR, Length[#] >= 4 &][[All, 4]]]
{"Alias", "Auto", "Close", "CompoundPrefix", "Infix",
"InfixOpen", "LargeOp:Prefix", "Letter", "NonBreakingAfterLetter", 
"NonBreakingBeforeLetter", "Open", "Postfix", "Prefix", "WhiteSp"}

These are the ones we want:

affixes = {"Close", "CompoundPrefix", "Infix", "InfixOpen",
        "LargeOp:Prefix", "Open", "Postfix", "Prefix"};
ops = Select[ucharTR, Length[#] >= 4 && MemberQ[affixes, #[[4]]]&];
Length[ops]
337

The character 〚 is the only one listed with affix InfixOpen, while the other matchfix (circumfix) operators are under Open or Close. Most of them appear to function like parentheses, but their rendering in the frontend is flakey. We also have LargeOp:Prefix operators like ∑ (\[Sum]), and then ∇ (\[Del]), which is the only operator with affix CompoundPrefix (I don't know why).

Of the sources of operators and their precedence, the UnicodeCharacters.tr is among the largest, and since it is presumably used by Mathematica internals, you can be sure that it is also the most accurate.

Mysteriously, however, it seems to be wrong about precedence in at least one case that I've found so far: UnicodeCharacters.tr gives CircleDot and PermutationProduct the same precedence, but CircleDot does have a higher precedence than PermutationProduct while still being lower than the next highest operator, SmallCircle. That is to say, at least on v11.3, CircleDot's real precedence is 606, not 605 as it's listed.

System`Convert`MLStringDataDump`$Operators

This is an undocumented list of 340 operators without any additional information. The list is only available in the notebook frontend. The undocumented function System`Convert`MLStringDataDump`OperatorQ[] works by checking membership in this list.

This is the only list, excluding the documentation and WolframLanguageData, that includes all single and multi-character ASCII operators. As with most other sources, no box operators are included. It also excludes Association brackets, obscure bracketing symbols, many LargeOp:Prefix operators, and a handful of other operators found in UnicodeCharacters.tr.

The complement of Internal`SymbolNameQ characters

Using the definition of LetterLikeQ along with the opaque Internal`SymbolNameQ, we can search the list of Unicode characters to find the complement of the set of characters we already know. Our search criteria is as follows:

crit[c_] := ! Internal`SymbolNameQ[c] && ! DigitQ[c] &&  ! 
StringMatchQ[c, WhitespaceCharacter] && ! MemberQ[ops, c];

Then we make a table of code/character pairs:

Select[
    Table[{i, FromCharacterCode[i]}, {i, 65535}], 
    crit[#[[2]]]&
]

The result is a bit of a surprise: We get a seemingly random handful of ASCII characters, Association brackets, several of the LargeOp:Prefix operators also missing in other lists and a small handful of other Unicode operators, and, most interestingly, single character versions of multicharacter box operators that are not in any other source. These box operators are:

0xf7c0: \), 0xf7c1: \!, 0xf7c2: \@, 0xf7c5: \%, 
0xf7c6: \^, 0xf7c7: \&, 0xf7c8: \*, 0xf7c9: \(, 
0xf7ca: \_, 0xf7cb: \+, 0xf7cc: \/, 0xf7cd: \`

The list also includes the nonoperator characters 0xf767: \[ErrorIndicator], 0x2043: \[SkeletonIndicator], the ASCII delete character 0x007f (127), the ASCII bell character 0x0007, and 0xf3ad, which is an unassigned noncharacter Unicode codepoint "for private use."

Other "Operators"

There are other lexemes (strings) that could be considered operators that are still missing:

  • string-related characters like " and escape characters \n, \t, etc.
  • The character representation operators: \[name], \:nn, and \.nnnn.
  • The number representation operators: ^^, *^, and ``, though the single ` character does appear in the previous subsection.

Programmatically determining properties of operators

Putting these different sources together gives you a database, but there are still some gaps in the dataset. As far as I know, though, these are the only operators anyone who isn't an employee of Wolfram knows about.

You ask specifically about whether there is "a way to get a list of all existing operators (ideally with precedence) using meta programming." We have answered the first half of the question in the affirmative, having seen how to compile a list of operators, though it took some effort to get them all.

Is there a programmatic way to fill in the gaps in the resulting dataset once you know the operators, that is, what lexical tokens make up the operators? It's easy to programmatically determine affix (prefix, infix, etc.) and arity (unary, binary, etc.) from the usage data that can be obtained from WolframLanguageData, and we have everything we need for those operators listed in UnicodeCharacters.tr.

Precedence is a lot harder. I have implemented a strategy that compares two operators by instantiating expressions involving the two operators and inspecting the result. The remainer of this section gives just a few examples to illustrate the fundamental reason why this strategy doesn't work.

Two synonyms of the same operator:

FullForm[Hold[a\[Conditioned]b<->c]]
FullForm[Hold[a\[Conditioned]b\[TwoWayRule]c]]
Hold[Conditioned[a,TwoWayRule[b,c]]]
Hold[TwoWayRule[Conditioned[a,b],c]]

The only binary operator that cannot be parsed like this:

a \[DirectedEdge] b \[UndirectedEdge] c
Syntax::tsntxi: "a\[DirectedEdge]b\[UndirectedEdge]c" is incomplete; more input is needed.

This next one is not uncommon:

FullForm[Hold[a \[LeftTee] b \[UpTee] c]]
FullForm[ToExpression["Hold[a \[LeftTee] b \[UpTee] c]"]]
Hold[LeftTee[a,UpTee[b,c]]]
Hold[UpTee[LeftTee[a,b],c]]

After you try it in a notebook, try it on the command line, too. (There are several differences between the notebook and command line regarding parsing.)

Many operators do not have a FullForm (unevaluated interpretation) equal to the functions they represent. Consider Divide:

(* Give the Head of the first element within Hold. *)
Hold[a/b][[1, 0]]
Times

Considering this one fact alone, whatever strategy one uses to inspect an instantiated expression to determine precedence will have to account for every special case of how the FullForm of each operator might interact with that of another in a nongeneric way. The amount of manual labor one has to do to account for this is $Ω(n^2)$, where $n$ is the number operators.

Then there is the case of GreaterSlantEqual and LessSlantEqual, both the symbols and their corresponding functions. The short version is, these two symbols are now disassociated from their corresponding functions, which functions now have no operator notation despite the insistance of the documentation. To be clear, I think it's the right move to remap the *SlantEqual symbols to >= and <=, but...

Conclusion

At the end of the day, there just is no public language specification for Mathematica/Wolfram Language. In a sense, the language is defined to be whatever Mathematica does. And what Mathematica does on the command line may differ from what it does in the notebook, which may differ from the behavior of ToExpression and friends, all of which may differ from the documentation, which itself may be inconsistent—and all of this in just a single version (v11.3) on a single platform (macos).

So the answer is, no, you cannot determine precedence programmatically, or any other way, for that matter.

$\endgroup$
1
  • $\begingroup$ Someone at Wolfram must have noticed that *SlantEqual doesn't resolve to the function it's supposed to... so they fixed the documentation to match the bug! The docs for the *SlantEqual functions have been scrubbed from the web despite shipping with v11.3. The only remaining reference: "[Special character operators] usually have names that correspond to the names of the functions they represent. ...Exceptions are [GreaterSlantEqual], [LessSlantEqual] and [RoundImplies]." The bug for RoundImplies apparently wasn't ready for v11.3. (Try it.) Maybe we will see it in the next version? $\endgroup$ Commented Aug 15, 2018 at 2:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.