In Perl 6, you can use superscript indices to calculate powers of numbers, for example:
> 2⁵ 32 > 7³ 343
It also works with more than one digit in the superscript:
> 10¹² 1000000000000
You can guess that the above cases are equivalent to the following:
> 2**5 32 > 7**3 343 > 10**12 1000000000000
But the question is: How on Earth does it work? Let us find it out.
For the Numeric role, the following operation is defined:
proto sub postfix:<ⁿ>(Mu $, Mu $) is pure {*} multi sub postfix:<ⁿ>(\a, \b) { a ** b }
Aha, that is what we need, and the superscript notation is converted to the simple ** operator here.
You can visualise what exactly is passed to the operation by printing the operands:
multi sub postfix:<ⁿ>(\a, \b) { nqp::say('# a = ' ~ a); nqp::say('# b = ' ~ b); a ** b }
In this case, you’ll see the following output for the test examples above:
> 2⁵ # a = 2 # b = 5 > 10¹² # a = 10 # b = 12
Now, it is time to understand how the postfix that extracts superscripts works. Its name, ⁿ, written in superscript, should not mislead you. This is not a magic trick of the parser, this is just a name of the symbol, and it can be found in the Grammar:
token postfix:sym<ⁿ> { <sign=[⁻⁺¯]>? <dig=[⁰¹²³⁴⁵⁶⁷⁸⁹]>+ <O(|%autoincrement)> }
You see, this symbol is a sequence of superscripted digits with an optional sign before them. (Did you think of a sign before we reached this moment in the Grammar?)
Let us try negative powers, by the way:
> say 4⁻³ # a = 4 # b = -3 0.015625
Also notice that the whole construct is treated as a postfix operator. It can also be applied to variables, for example:
> my $x = 9 9 > say $x² # a = 9 # b = 2 81
So, a digit in superscript is not a part of the variable’s name.
OK, the final part of the trilogy, the code in Actions, which parses the index:
method postfix:sym<ⁿ>($/) { my $Int := $*W.find_symbol(['Int']); my $power := nqp::box_i(0, $Int); for $<dig> { $power := nqp::add_I( nqp::mul_I($power, nqp::box_i(10, $Int), $Int), nqp::box_i(nqp::index("⁰¹²³⁴⁵⁶⁷⁸⁹", $_), $Int), $Int); } $power := nqp::neg_I($power, $Int) if $<sign> eq '⁻' || $<sign> eq '¯'; make QAST::Op.new(:op<call>, :name('&postfix:<ⁿ>'), $*W.add_numeric_constant($/, 'Int', $power)); }
As you can see here, it scans the digits and updates the $power variable by adding the value at the next decimal position (it is selected in the code above).
The available characters are listed in a string, and to get its value, the offset in the string is used. The $<dig> match contains a digit, you can see it in the Grammar:
<dig=[⁰¹²³⁴⁵⁶⁷⁸⁹]>+
One thought on “🔬72. Superscripts in Perl 6”