factor/core/math/parser/parser.factor

433 lines
13 KiB
Factor
Raw Normal View History

2009-11-01 01:26:05 -05:00
! (c)2009 Joe Groff bsd license
USING: accessors byte-arrays combinators kernel kernel.private
2011-11-30 19:02:37 -05:00
make math namespaces sequences sequences.private splitting
strings ;
2007-09-20 18:09:08 -04:00
IN: math.parser
: digit> ( ch -- n )
2009-11-01 01:26:05 -05:00
{
{ [ dup CHAR: 9 <= ] [ CHAR: 0 - dup 0 < [ drop 255 ] when ] }
{ [ dup CHAR: a < ] [ CHAR: A 10 - - dup 10 < [ drop 255 ] when ] }
[ CHAR: a 10 - - dup 10 < [ drop 255 ] when ]
} cond ; inline
2007-09-20 18:09:08 -04:00
ERROR: invalid-radix radix ;
2009-11-01 01:26:05 -05:00
<PRIVATE
2008-02-06 21:05:03 -05:00
2012-06-08 10:32:01 -04:00
TUPLE: number-parse
2009-11-01 01:26:05 -05:00
{ str read-only }
{ length fixnum read-only }
{ radix fixnum read-only } ;
2009-04-11 21:30:51 -04:00
2009-11-01 01:26:05 -05:00
: <number-parse> ( str radix -- i number-parse n )
[ 0 ] 2dip
[ dup length ] dip
number-parse boa
0 ; inline
2009-04-11 21:30:51 -04:00
: (next-digit) ( i number-parse n digit-quot end-quot -- n/f )
2009-11-01 01:26:05 -05:00
[ 2over length>> < ] 2dip
[ [ 2over str>> nth-unsafe >fixnum [ 1 + >fixnum ] 3dip ] prepose ] dip if ; inline
2008-02-06 22:15:47 -05:00
: require-next-digit ( i number-parse n quot -- n/f )
2009-11-01 01:26:05 -05:00
[ 3drop f ] (next-digit) ; inline
2008-02-06 21:05:03 -05:00
: next-digit ( i number-parse n quot -- n/f )
2009-11-01 01:26:05 -05:00
[ 2nip ] (next-digit) ; inline
2008-02-06 21:05:03 -05:00
: add-digit ( i number-parse n digit quot -- n/f )
2009-11-01 01:26:05 -05:00
[ [ dup radix>> ] [ * ] [ + ] tri* ] dip next-digit ; inline
2008-02-10 02:40:17 -05:00
2009-11-01 01:26:05 -05:00
: digit-in-radix ( number-parse n char -- number-parse n digit ? )
digit> pick radix>> over > ; inline
2009-04-11 21:30:51 -04:00
2009-11-01 01:26:05 -05:00
: ?make-ratio ( num denom/f -- ratio/f )
[ / ] [ drop f ] if* ; inline
2008-02-06 21:05:03 -05:00
2009-11-01 01:26:05 -05:00
TUPLE: float-parse
{ radix read-only }
{ point read-only }
{ exponent read-only } ;
2008-02-06 21:05:03 -05:00
2009-11-01 01:26:05 -05:00
: inc-point ( float-parse -- float-parse' )
[ radix>> ] [ point>> 1 + ] [ exponent>> ] tri float-parse boa ; inline
2008-02-06 21:05:03 -05:00
2009-11-01 01:26:05 -05:00
: store-exponent ( float-parse n expt -- float-parse' n )
swap [ [ drop radix>> ] [ drop point>> ] [ nip ] 2tri float-parse boa ] dip ; inline
2007-09-20 18:09:08 -04:00
2009-11-01 01:26:05 -05:00
: ?store-exponent ( float-parse n expt/f -- float-parse' n/f )
[ store-exponent ] [ drop f ] if* ; inline
2007-09-20 18:09:08 -04:00
2009-11-01 01:26:05 -05:00
: ((pow)) ( base x -- base^x )
iota 1 rot [ nip * ] curry reduce ; inline
2009-11-01 01:26:05 -05:00
: (pow) ( base x -- base^x )
dup 0 >= [ ((pow)) ] [ [ recip ] [ neg ] bi* ((pow)) ] if ; inline
: add-mantissa-digit ( float-parse i number-parse n digit quot -- float-parse' n/f )
2009-11-01 01:26:05 -05:00
[ [ inc-point ] 4dip ] dip add-digit ; inline
: make-float-dec-exponent ( float-parse n/f -- float/f )
[ [ radix>> ] [ point>> ] [ exponent>> ] tri - (pow) ] [ swap /f ] bi* ; inline
: make-float-bin-exponent ( float-parse n/f -- float/f )
[ drop [ radix>> ] [ point>> ] bi (pow) ]
[ nip swap /f ]
[ drop 2.0 swap exponent>> (pow) * ] 2tri ; inline
: ?default-exponent ( float-parse n/f -- float-parse' n/f' )
over exponent>> [
over radix>> 10 =
[ [ [ radix>> ] [ point>> ] bi 0 float-parse boa ] dip ]
[ drop f ] if
] unless ; inline
2009-11-01 01:26:05 -05:00
: ?make-float ( float-parse n/f -- float/f )
{ float-parse object } declare
?default-exponent
2009-11-01 01:26:05 -05:00
{
{ [ dup not ] [ 2drop f ] }
{ [ over radix>> 10 = ] [ make-float-dec-exponent ] }
[ make-float-bin-exponent ]
} cond ;
2009-11-01 01:26:05 -05:00
: ?neg ( n/f -- -n/f )
[ neg ] [ f ] if* ; inline
: ?add-ratio ( m n/f -- m+n/f )
dup ratio? [ + ] [ 2drop f ] if ; inline
: @abort ( i number-parse n x -- f )
2drop 2drop f ; inline
: @split ( i number-parse n -- n i number-parse n' )
-rot 0 ; inline
: @split-exponent ( i number-parse n -- n i number-parse' n' )
-rot [ str>> ] [ length>> ] bi 10 number-parse boa 0 ; inline
: <float-parse> ( i number-parse n -- float-parse i number-parse n )
[ drop nip radix>> 0 f float-parse boa ] 3keep ; inline
2009-11-01 01:26:05 -05:00
DEFER: @exponent-digit
DEFER: @mantissa-digit
DEFER: @denom-digit
DEFER: @num-digit
DEFER: @pos-digit
DEFER: @neg-digit
: @exponent-digit-or-punc ( float-parse i number-parse n char -- float-parse n/f )
2009-11-01 01:26:05 -05:00
{
{ CHAR: , [ [ @exponent-digit ] require-next-digit ] }
[ @exponent-digit ]
} case ; inline
2009-11-01 01:26:05 -05:00
: @exponent-digit ( float-parse i number-parse n char -- float-parse n/f )
{ float-parse fixnum number-parse integer fixnum } declare
digit-in-radix [ [ @exponent-digit-or-punc ] add-digit ] [ @abort ] if ;
2009-11-01 01:26:05 -05:00
: @exponent-first-char ( float-parse i number-parse n char -- float-parse n/f )
2009-11-01 01:26:05 -05:00
{
{ CHAR: - [ [ @exponent-digit ] require-next-digit ?neg ] }
{ CHAR: + [ [ @exponent-digit ] require-next-digit ] }
2009-11-01 01:26:05 -05:00
[ @exponent-digit ]
} case ; inline
2009-11-01 01:26:05 -05:00
: ->exponent ( float-parse i number-parse n -- float-parse' n/f )
2009-11-01 01:26:05 -05:00
@split-exponent [ @exponent-first-char ] require-next-digit ?store-exponent ; inline
: exponent-char? ( number-parse n char -- number-parse n char ? )
3dup nip swap radix>> {
{ 10 [ [ CHAR: e CHAR: E ] dip [ = ] curry either? ] }
[ drop [ CHAR: p CHAR: P ] dip [ = ] curry either? ]
} case ; inline
: or-exponent ( i number-parse n char quot -- n/f )
2009-11-01 01:26:05 -05:00
[ exponent-char? [ drop <float-parse> ->exponent ?make-float ] ] dip if ; inline
: or-mantissa->exponent ( float-parse i number-parse n char quot -- float-parse n/f )
2009-11-01 01:26:05 -05:00
[ exponent-char? [ drop ->exponent ] ] dip if ; inline
: @mantissa-digit-or-punc ( float-parse i number-parse n char -- float-parse n/f )
2009-11-01 01:26:05 -05:00
{
{ CHAR: , [ [ @mantissa-digit ] require-next-digit ] }
[ @mantissa-digit ]
} case ; inline
2009-10-22 21:28:00 -04:00
: @mantissa-digit ( float-parse i number-parse n char -- float-parse n/f )
{ float-parse fixnum number-parse integer fixnum } declare
2009-11-01 01:26:05 -05:00
[
digit-in-radix
[ [ @mantissa-digit-or-punc ] add-mantissa-digit ]
[ @abort ] if
] or-mantissa->exponent ;
2009-11-01 01:26:05 -05:00
: ->mantissa ( i number-parse n -- n/f )
2009-11-01 01:26:05 -05:00
<float-parse> [ @mantissa-digit ] next-digit ?make-float ; inline
: ->required-mantissa ( i number-parse n -- n/f )
2009-11-01 01:26:05 -05:00
<float-parse> [ @mantissa-digit ] require-next-digit ?make-float ; inline
: @denom-digit-or-punc ( i number-parse n char -- n/f )
2009-11-01 01:26:05 -05:00
{
{ CHAR: , [ [ @denom-digit ] require-next-digit ] }
{ CHAR: . [ ->mantissa ] }
[ [ @denom-digit ] or-exponent ]
} case ; inline
2009-11-01 01:26:05 -05:00
: @denom-digit ( i number-parse n char -- n/f )
{ fixnum number-parse integer fixnum } declare
digit-in-radix [ [ @denom-digit-or-punc ] add-digit ] [ @abort ] if ;
2009-11-01 01:26:05 -05:00
: @denom-first-digit ( i number-parse n char -- n/f )
2009-11-01 01:26:05 -05:00
{
{ CHAR: . [ ->mantissa ] }
[ @denom-digit ]
} case ; inline
2009-11-01 01:26:05 -05:00
: ->denominator ( i number-parse n -- n/f )
{ fixnum number-parse integer } declare
@split [ @denom-first-digit ] require-next-digit ?make-ratio ;
2009-11-01 01:26:05 -05:00
: @num-digit-or-punc ( i number-parse n char -- n/f )
2009-11-01 01:26:05 -05:00
{
{ CHAR: , [ [ @num-digit ] require-next-digit ] }
{ CHAR: / [ ->denominator ] }
[ @num-digit ]
} case ; inline
2009-11-01 01:26:05 -05:00
: @num-digit ( i number-parse n char -- n/f )
{ fixnum number-parse integer fixnum } declare
digit-in-radix [ [ @num-digit-or-punc ] add-digit ] [ @abort ] if ;
2009-11-01 01:26:05 -05:00
: ->numerator ( i number-parse n -- n/f )
{ fixnum number-parse integer } declare
@split [ @num-digit ] require-next-digit ?add-ratio ;
2009-11-01 01:26:05 -05:00
: @pos-digit-or-punc ( i number-parse n char -- n/f )
2009-11-01 01:26:05 -05:00
{
{ CHAR: , [ [ @pos-digit ] require-next-digit ] }
{ CHAR: + [ ->numerator ] }
{ CHAR: / [ ->denominator ] }
{ CHAR: . [ ->mantissa ] }
[ [ @pos-digit ] or-exponent ]
} case ; inline
2009-11-01 01:26:05 -05:00
: @pos-digit ( i number-parse n char -- n/f )
{ fixnum number-parse integer fixnum } declare
digit-in-radix [ [ @pos-digit-or-punc ] add-digit ] [ @abort ] if ;
2009-11-01 01:26:05 -05:00
: (->radix) ( number-parse radix -- number-parse' )
[ [ str>> ] [ length>> ] bi ] dip number-parse boa ; inline
: ->radix ( i number-parse n quot radix -- i number-parse n quot )
[ (->radix) ] curry 2dip ; inline
: with-radix-char ( i number-parse n radix-quot nonradix-quot -- n/f )
[
rot {
{ CHAR: b [ drop 2 ->radix require-next-digit ] }
{ CHAR: o [ drop 8 ->radix require-next-digit ] }
{ CHAR: x [ drop 16 ->radix require-next-digit ] }
{ f [ 3drop 2drop 0 ] }
[ [ drop ] 2dip swap call ]
} case
] 2curry next-digit ; inline
: @pos-first-digit ( i number-parse n char -- n/f )
2009-11-01 01:26:05 -05:00
{
{ CHAR: . [ ->required-mantissa ] }
2011-11-24 15:39:52 -05:00
{ CHAR: 0 [ [ @pos-digit ] [ @pos-digit-or-punc ] with-radix-char ] }
2009-11-01 01:26:05 -05:00
[ @pos-digit ]
2011-11-24 15:39:52 -05:00
} case ; inline
2009-11-01 01:26:05 -05:00
: @neg-digit-or-punc ( i number-parse n char -- n/f )
2009-11-01 01:26:05 -05:00
{
{ CHAR: , [ [ @neg-digit ] require-next-digit ] }
{ CHAR: - [ ->numerator ] }
{ CHAR: / [ ->denominator ] }
{ CHAR: . [ ->mantissa ] }
[ [ @neg-digit ] or-exponent ]
} case ; inline
2009-11-01 01:26:05 -05:00
: @neg-digit ( i number-parse n char -- n/f )
{ fixnum number-parse integer fixnum } declare
digit-in-radix [ [ @neg-digit-or-punc ] add-digit ] [ @abort ] if ;
2009-11-01 01:26:05 -05:00
: @neg-first-digit ( i number-parse n char -- n/f )
2009-11-01 01:26:05 -05:00
{
{ CHAR: . [ ->required-mantissa ] }
2011-11-24 15:39:52 -05:00
{ CHAR: 0 [ [ @neg-digit ] [ @neg-digit-or-punc ] with-radix-char ] }
2009-11-01 01:26:05 -05:00
[ @neg-digit ]
2011-11-24 15:39:52 -05:00
} case ; inline
2009-11-01 01:26:05 -05:00
: @first-char ( i number-parse n char -- n/f )
2009-11-01 01:26:05 -05:00
{
{ CHAR: - [ [ @neg-first-digit ] require-next-digit ?neg ] }
{ CHAR: + [ [ @pos-first-digit ] require-next-digit ] }
2009-11-01 01:26:05 -05:00
[ @pos-first-digit ]
} case ; inline
: @first-char-no-radix ( i number-parse n char -- n/f )
{
{ CHAR: - [ [ @neg-digit ] require-next-digit ?neg ] }
{ CHAR: + [ [ @pos-digit ] require-next-digit ] }
[ @pos-digit ]
} case ; inline
2008-02-06 21:05:03 -05:00
PRIVATE>
2007-09-20 18:09:08 -04:00
: string>number ( str -- n/f )
10 <number-parse> [ @first-char ] require-next-digit ;
2009-11-01 01:26:05 -05:00
: base> ( str radix -- n/f )
<number-parse> [ @first-char-no-radix ] require-next-digit ;
2009-11-01 01:26:05 -05:00
: bin> ( str -- n/f ) 2 base> ; inline
: oct> ( str -- n/f ) 8 base> ; inline
: dec> ( str -- n/f ) 10 base> ; inline
: hex> ( str -- n/f ) 16 base> ; inline
2009-11-01 01:26:05 -05:00
: string>digits ( str -- digits )
[ digit> ] B{ } map-as ; inline
<PRIVATE
2009-11-01 01:26:05 -05:00
: (digits>integer) ( valid? accum digit radix -- valid? accum )
2dup < [ swapd * + ] [ 2drop 2drop f 0 ] if ; inline
: each-digit ( seq radix quot -- n/f )
[ t 0 ] 3dip curry each swap [ drop f ] unless ; inline
2007-09-20 18:09:08 -04:00
PRIVATE>
2009-11-01 01:26:05 -05:00
: digits>integer ( seq radix -- n/f )
[ (digits>integer) ] each-digit ; inline
2007-09-20 18:09:08 -04:00
: >digit ( n -- ch )
dup 10 < [ CHAR: 0 + ] [ 10 - CHAR: a + ] if ; inline
2007-09-20 18:09:08 -04:00
<PRIVATE
2008-09-27 18:54:44 -04:00
: positive>base ( num radix -- str )
dup 1 <= [ invalid-radix ] when
[ dup 0 > ] swap [ /mod >digit ] curry "" produce-as nip
2009-10-28 15:40:15 -04:00
reverse! ; inline
2007-09-20 18:09:08 -04:00
PRIVATE>
2007-09-20 18:09:08 -04:00
GENERIC# >base 1 ( n radix -- str )
: number>string ( n -- str ) 10 >base ; inline
: >bin ( n -- str ) 2 >base ; inline
: >oct ( n -- str ) 8 >base ; inline
: >hex ( n -- str ) 16 >base ; inline
2008-02-06 21:05:03 -05:00
<PRIVATE
2009-11-01 01:26:05 -05:00
SYMBOL: radix
SYMBOL: negative?
: sign ( -- str ) negative? get "-" "+" ? ;
: with-radix ( radix quot -- )
radix swap with-variable ; inline
2008-09-30 19:28:11 -04:00
: (>base) ( n -- str ) radix get positive>base ;
2008-02-06 21:05:03 -05:00
PRIVATE>
2007-09-20 18:09:08 -04:00
M: integer >base
2008-09-27 18:54:44 -04:00
over 0 = [
2drop "0"
] [
over 0 > [
positive>base
2007-09-20 18:09:08 -04:00
] [
2008-09-27 18:54:44 -04:00
[ neg ] dip positive>base CHAR: - prefix
2007-09-20 18:09:08 -04:00
] if
2008-09-27 18:54:44 -04:00
] if ;
2007-09-20 18:09:08 -04:00
M: ratio >base
[
2008-09-27 18:54:44 -04:00
dup 0 < negative? set
2008-09-30 19:28:11 -04:00
abs 1 /mod
[ [ "" ] [ (>base) sign append ] if-zero ]
2008-02-06 21:05:03 -05:00
[
2008-09-27 18:54:44 -04:00
[ numerator (>base) ]
[ denominator (>base) ] bi
2008-12-03 20:12:48 -05:00
"/" glue
2008-09-27 18:54:44 -04:00
] bi* append
negative? get [ CHAR: - prefix ] when
2008-02-06 21:05:03 -05:00
] with-radix ;
2007-09-20 18:09:08 -04:00
: fix-float ( str -- newstr )
{
{
[ CHAR: e over member? ]
[ "e" split1 [ fix-float "e" ] dip 3append ]
2007-09-20 18:09:08 -04:00
} {
[ CHAR: . over member? ]
[ ]
}
2008-04-11 13:53:22 -04:00
[ ".0" append ]
2007-09-20 18:09:08 -04:00
} cond ;
<PRIVATE
: mantissa-expt-normalize ( mantissa expt -- mantissa' expt' )
dup zero?
[ over log2 52 swap - [ shift 52 2^ 1 - bitand ] [ 1022 + - ] bi-curry bi* ]
[ 1023 - ] if ;
: mantissa-expt ( float -- mantissa expt )
[ 52 2^ 1 - bitand ]
[ -0.0 double>bits bitnot bitand -52 shift ] bi
mantissa-expt-normalize ;
: float>hex-sign ( bits -- str )
-0.0 double>bits bitand zero? "" "-" ? ;
: float>hex-value ( mantissa -- str )
>hex 13 CHAR: 0 pad-head [ CHAR: 0 = ] trim-tail
2011-10-15 22:19:44 -04:00
[ "0" ] when-empty "1." prepend ;
: float>hex-expt ( mantissa -- str )
10 >base "p" prepend ;
: float>hex ( n -- str )
double>bits
[ float>hex-sign ] [
mantissa-expt [ float>hex-value ] [ float>hex-expt ] bi*
] bi 3append ;
: format-float ( n format -- string )
0 suffix >byte-array (format-float)
dup [ 0 = ] find drop head >string
fix-float ;
2012-06-08 10:32:01 -04:00
: float>base ( n radix -- str )
{
{ 16 [ float>hex ] }
{ 10 [ "%.16g" format-float ] }
2012-06-08 10:32:01 -04:00
[ invalid-radix ]
} case ; inline
PRIVATE>
: float>string ( n -- str )
10 float>base ; inline
2007-09-20 18:09:08 -04:00
M: float >base
{
{ [ over fp-nan? ] [ 2drop "0/0." ] }
{ [ over 1/0. = ] [ 2drop "1/0." ] }
{ [ over -1/0. = ] [ 2drop "-1/0." ] }
{ [ over 0.0 fp-bitwise= ] [ 2drop "0.0" ] }
{ [ over -0.0 fp-bitwise= ] [ 2drop "-0.0" ] }
[ float>base ]
2007-09-20 18:09:08 -04:00
} cond ;
: # ( n -- ) number>string % ; inline