Skip to content

Instantly share code, notes, and snippets.

@ShimmerFairy
Created March 21, 2010 20:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ShimmerFairy/339561 to your computer and use it in GitHub Desktop.
Save ShimmerFairy/339561 to your computer and use it in GitHub Desktop.
From cc5f6714a7acfab5c18eef2da04cec92e964f3be Mon Sep 17 00:00:00 2001
From: Lue <lue@twigil.drangle.com>
Date: Sun, 21 Mar 2010 13:31:24 -0700
Subject: [PATCH] Put in support for fractional numbers (:16<A.4>) and other base scanning (:2<0o10>) into the radcalc function.
---
src/Perl6/Actions.pm | 3 +-
src/builtins/Num.pir | 176 ++++++++++++++++++++++++++++++++------------------
2 files changed, 114 insertions(+), 65 deletions(-)
diff --git a/src/Perl6/Actions.pm b/src/Perl6/Actions.pm
index a277932..94fc988 100644
--- a/src/Perl6/Actions.pm
+++ b/src/Perl6/Actions.pm
@@ -2136,11 +2136,12 @@ method rad_number($/) {
} else {
my $intpart := $<intpart>.Str;
my $fracpart := $<fracpart> ?? $<fracpart>.Str !! "0";
+ my $intfrac := $intpart ~ $fracpart; #the dot is a part of $fracpart, so no need for ~ "." ~
my $base := $<base> ?? +($<base>[0].Str) !! 0;
my $exp := $<exp> ?? +($<exp>[0].Str) !! 0;
make PAST::Op.new( :name('&radcalc'), :pasttype('call'),
- $radix, $intpart, $fracpart, $base, $exp
+ $radix, $intfrac, $base, $exp
);
}
}
diff --git a/src/builtins/Num.pir b/src/builtins/Num.pir
index 6c9167b..f5d502b 100644
--- a/src/builtins/Num.pir
+++ b/src/builtins/Num.pir
@@ -124,74 +124,122 @@ Value type, so return self.
.namespace []
.sub '&radcalc'
- .param int radix
- .param string intpart
- .param string fracpart :optional
- .param int has_fracpart :opt_flag
- .param num base :optional
+ #this code was rewritten & optimized to be as dumb as possible. --lue
+ .param int radix #to all mathematicians, I know fractional radixes are possible. We programmers are taking baby steps :)
+ .param string number
+ .param num base :optional #as in exponents, not as in radixes
.param int has_base :opt_flag
- .param num exp :optional
- .param int has_exp :opt_flag
- .local num result, fracdivisor, magnitude
- .local pmc it
-
- if radix <= 1 goto err_range
- if radix > 36 goto err_range
-
- result = 0.0
- fracdivisor = 1.0
-
- $P0 = split '', intpart
- it = iter $P0
-
- lp1: # Accumulate over decimal part
- unless it goto ex1
- $S0 = shift it
- $S0 = downcase $S0
- if $S0 == "_" goto lp1
- $I0 = index "0123456789abcdefghijklmnopqrstuvwxyz", $S0
- if $I0 == -1 goto err_char
- $N0 = $I0
- result *= radix
- result += $N0
- goto lp1
-
- ex1:
- unless has_fracpart goto nofracpart
- $I0 = length fracpart
- unless $I0 goto nofracpart
- $P0 = split '', fracpart
- $P99 = shift $P0 # remove the radix point
-
- lp2: # Accumulate over fractional part, keep length
- unless it goto ex2
- $S0 = shift it
- $S0 = downcase $S0
- if $S0 == "_" goto lp2
- $I0 = index "0123456789abcdefghijklmnopqrstuvwxyz", $S0
- if $I0 == -1 goto err_char
- $N0 = $I0
-
- result *= radix
- result += $N0
- fracdivisor *= radix
- goto lp2
-
- ex2:
- result /= fracdivisor
-
- nofracpart:
- unless has_base goto ret
- magnitude = base ** exp
- result *= magnitude
- ret:
+ .param num exponent :optional
+ .param int has_exponent :opt_flag
+ .local int iresult, fresult, fdivide
+ .local num result
+
+ if radix <= 1 goto ERANGE
+ if radix > 36 goto ERANGE #at least until we know how to represent bases > 36...
+#magcheck:
+ if has_base goto magcheck2 #if you hasn't a base, go on
+ if has_exponent goto EEXPNOBASE #if you have an exponent but no base, ERROR!
+ base = 1.0
+ exponent = 1.0 #if you have no exponent and no base, then allow dumb coding to not affect the results.
+ goto magcheckdone
+
+magcheck2:
+ if has_exponent goto magcheckdone #base+exponent? Continue
+ goto EBASENOEXP #wait, you has a base, but no exponent? No way!
+magcheckdone:
+ $S99 = substr number, 0, 1
+ if $S99 == "0" goto checkradix #maybe they entered :8<0x3F> ?
+ goto allchecksdone
+
+ #below is the nightmare of checking for radix conversion
+checkradix:
+ $S99 = substr number, 1, 1 #will they convert?
+ $S99 = upcase $S99
+ $I10 = index "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", $S99
+ if $I10 >= radix goto changeradix #is the character beyond the range of the specified radix
+ goto allchecksdone #false alarm
+changeradix:
+ if $S99 == "B" goto changeto2 #is it a binary number?
+ if $S99 == "O" goto changeto8 #octal?
+ if $S99 == "X" goto changeto16 #hex?
+ if $S99 == "D" goto changeto10 #decimal?
+ goto allchecksdone
+changeto2:
+ radix=2 #the actual radix change
+ number = substr number, 2 #this is to remove what changed the radix (0x, 0b, etc.)
+ goto allchecksdone #and, relax! The checks be over
+changeto8:
+ radix=8
+ number = substr number, 2
+ goto allchecksdone
+changeto10:
+ radix=10
+ number = substr number, 2
+ goto allchecksdone
+changeto16:
+ radix=16
+ number = substr number, 2
+ #above is the nightmare of checking for radix conversion
+
+allchecksdone:
+ iresult = 0
+ fresult = 0
+ fdivide = 1
+ $I99 = 0 #I don't want to hear about the Iter object. We DON'T NEED PMCs for a radix conversion.
+
+iloop:
+ $S0 = substr number, $I99, 1
+ $S0 = upcase $S0 #I prefer uppercase when it comes to numbers in other bases, so that's how it's going to be.
+ if $S0 == "_" goto iskip #if you seperate words, ex. DEAD_BEEF
+ if $S0 == "." goto fbefore #fraction time!
+ if $S0 == "" goto finish #all done
+ $I0 = index "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", $S0
+ if $I0 == -1 goto EINVALIDCHAR #say, you tried to pass off the number '3ZøA5'. Jerk.
+ if $I0 >= radix goto EINVALIDCHAR #if you try passing '2' with a radix of 2
+ iresult *= radix
+ iresult += $I0
+iskip:
+ $I99 += 1
+ goto iloop #aaaand-a 1, 2, 3, 4...
+
+fbefore:
+ $I99 += 1 #we wouldn't want to read the . again, now do we?
+
+floop:
+ $S0 = substr number, $I99, 1 #no, we DO NOT reset the $I99 loop var.
+ $S0 = upcase $S0
+ if $S0 == "_" goto fskip #sepearation of words, again (could also be dwords, qwords...)
+ if $S0 == "." goto EINVALIDCHAR #The IMU has yet to say you can write fractions of a fraction as 3.6.8 :)
+ if $S0 == "" goto finish #all done!
+ $I0 = index "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", $S0
+ if $I0 == -1 goto EINVALIDCHAR #we do not know if 3@8 is a valid number. Especially considering we don't allow a radix that would have @ as a digit.
+ if $I0 >= radix goto EINVALIDCHAR #remember kids, octals can't take the number 8
+ fresult *= radix
+ fresult += $I0
+ fdivide *= radix #each place value of the fraction increases the denominator. If this is confusing, try converting any old decimal (say, .141592) into a fraction BY HAND.
+fskip:
+ $I99 += 1
+ goto floop #and repeat
+
+finish:
+ $N0 = fresult / fdivide #for the fractional part
+ result = iresult + $N0
+ $N1 = pow base, exponent #for the magnitude. If no magnitude, then it's 1 pow 1
+ result = result * $N1 #if no magnitude, then it's result * 1. Pefect :)
+
.return (result)
- err_range:
- die "radix out of range (2-36)"
- err_char:
- $S0 = concat "unrecognized character: ", $S0
+#errors
+ERANGE:
+ die "DON'T PANIC! The radix is out of range (2..36 only)"
+EINVALIDCHAR:
+ $S0 = concat "DON'T PANIC! Invalid character (", $S0
+ $S0 = concat $S0, ")! Please try again :) "
die $S0
+EBASENOEXP:
+ die "DON'T PANIC! You gave us a base for the magnitude, but you forgot the exponent."
+EEXPNOBASE:
+ die "DON'T PANIC! You gave us an exponent for the magnitude, but you forgot the base."
.end
--
1.6.0.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment