From 5e6c1abb9a923d98b8615b206fb1122d9ac2f6bf Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Tue, 10 Jun 2014 07:43:59 +0800 Subject: [PATCH 1/4] Calculator: improve triggers; add 'squared' Some of the problems inherent in the old trigger regex made themselves obvious once the 'squared' operation was added. This simplification should make maintenance a smidge easier. Resolves #482. --- lib/DDG/Goodie/Calculator.pm | 35 ++++++++++++----------------------- t/Calculator.t | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/lib/DDG/Goodie/Calculator.pm b/lib/DDG/Goodie/Calculator.pm index 4c9de76da00..742e26b9c33 100644 --- a/lib/DDG/Goodie/Calculator.pm +++ b/lib/DDG/Goodie/Calculator.pm @@ -21,27 +21,6 @@ attribution github => ['https://github.com/duckduckgo', 'duckduckgo'], twitter => ['http://twitter.com/duckduckgo', 'duckduckgo']; -triggers query_nowhitespace => qr< - ^ - ( what is | calculate | solve | math )? !? - - [\( \) x X * % + / \^ \$ -]* - - (?: [0-9 \. ,]* ) - (?: gross | dozen | pi | e | c |) - [\( \) x X * % + / \^ 0-9 \. , \$ -]* - - (?(1) (?: -? [0-9 \. ,]+ |) |) - (?: [\( \) x X * % + / \^ \$ -] | times | divided by | plus | minus | cos | sin | tan | cotan | log | ln | log[_]?\d{1,3} | exp | tanh | sec | csc)+ - - (?: [0-9 \. ,]* ) - (?: gross | dozen | pi | e | c |) - - [\( \) x X * % + / \^ 0-9 \. , \$ -]* =? - - $ - >xi; - # This is probably YAGNI territory, but since I have to reference it in two places # and there are a multitude of other notation systems (although some break the # 'thousands' assumption) I am going to pretend that I do need it. @@ -85,8 +64,11 @@ my %named_operations = ( 'plus' => '+', 'divided\sby' => '/', 'ln' => 'log', # perl log() is natural log. + 'squared' => '**2', ); +my $ored_operations = join('|', keys %named_operations); + my %named_constants = ( dozen => 12, e => 2.71828182845904523536028747135266249, # This should be computed. @@ -96,12 +78,19 @@ my %named_constants = ( my $ored_constants = join('|', keys %named_constants); # For later substitutions +my $extra_trigger_words = qr/^(?:whatis|calculate|solve|math)/; +triggers query_nowhitespace => qr< + $extra_trigger_words? + (\s|$funcy|$ored_constants|$ored_operations|$numbery)* + $ + >xi; + handle query_nowhitespace => sub { my $results_html; my $results_no_html; my $query = $_; - $query =~ s/^(?:whatis|calculate|solve|math)//; + $query =~ s/$extra_trigger_words//; if ($query !~ /[xX]\s*[\*\%\+\-\/\^]/ && $query !~ /^-?[\d]{2,3}\.\d+,\s?-?[\d]{2,3}\.\d+$/) { my $tmp_result = ''; @@ -195,7 +184,7 @@ sub spacing { $text =~ s/(\s*(? 'Calculator', html => qq(
cos(2 pi) = 1
), ), + '5 squared' => test_zci( + '5 squared = 25', + heading => 'Calculator', + html => qq(
5 squared = 25
), + ), + '1.0 + 5 squared' => test_zci( + '1.0 + 5 squared = 26.0', + heading => 'Calculator', + html => qq(
1.0 + 5 squared = 26.0
), + ), + '3 squared + 4 squared' => test_zci( + '3 squared + 4 squared = 25', + heading => 'Calculator', + html => qq(
3 squared + 4 squared = 25
), + ), + '2,2 squared' => test_zci( + '2,2 squared = 4,8', + heading => 'Calculator', + html => qq(
2,2 squared = 4,8
), + ), 'sin(1.0) + 1,05' => undef, '4,24,334+22,53,828' => undef, '5234534.34.54+1' => undef, From 2c855b6dad1dad743bcc78593a1d1610fe1a2387 Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Tue, 10 Jun 2014 08:34:11 +0800 Subject: [PATCH 2/4] Calculator: improve superscript handling. This fixes the problem of the simple case, but handling, say, expressions in parens would require a lot more thought (a lexer would help!) I'll leave that problem for another day. Fixes #483. --- lib/DDG/Goodie/Calculator.pm | 7 +++---- t/Calculator.t | 6 ++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/DDG/Goodie/Calculator.pm b/lib/DDG/Goodie/Calculator.pm index 742e26b9c33..a01b293b4c7 100644 --- a/lib/DDG/Goodie/Calculator.pm +++ b/lib/DDG/Goodie/Calculator.pm @@ -53,7 +53,7 @@ foreach my $style (@known_styles) { # Luckily it will someday be able to be tokenized so this won't apply. my $all_seps = join('', map { $_->{decimal} . $_->{thousands} } @known_styles); -my $numbery = qr/^[\d$all_seps]+$/; +my $numbery = qr/[\d$all_seps]+/; my $funcy = qr/[[a-z]+\(|log[_]?\d{1,3}\(|\^/; # Stuff that looks like functions. my %named_operations = ( @@ -114,7 +114,7 @@ handle query_nowhitespace => sub { $tmp_expr =~ s#\b$name\b# $constant #ig; } - my @numbers = grep { $_ =~ $numbery } (split /\s+/, $tmp_expr); + my @numbers = grep { $_ =~ /^$numbery$/ } (split /\s+/, $tmp_expr); my $style = display_style(@numbers); return unless $style; @@ -154,8 +154,7 @@ handle query_nowhitespace => sub { $results_no_html = $results_html = $tmp_q; # Superscript (before spacing). - $results_html =~ s/\^([^\)]+)/$1<\/sup>/g; - $results_html =~ s/\^(\d+|\b(?:$ored_constants)\b)/$1<\/sup>/g; + $results_html =~ s/\^($numbery|\b$ored_constants\b)/$1<\/sup>/g; ($results_no_html, $results_html) = map { spacing($_) } ($results_no_html, $results_html); return if $results_no_html =~ /^\s/; diff --git a/t/Calculator.t b/t/Calculator.t index 9d3c5571dc5..76822aa44ba 100644 --- a/t/Calculator.t +++ b/t/Calculator.t @@ -310,6 +310,12 @@ ddg_goodie_test( heading => 'Calculator', html => qq(
2,2 squared = 4,8
), ), + '0.8^2 + 0.6^2' => test_zci( + '0.8 ^ 2 + 0.6 ^ 2 = 1', + heading => 'Calculator', + html => + qq(
0.82 + 0.62 = 1
), + ), 'sin(1.0) + 1,05' => undef, '4,24,334+22,53,828' => undef, '5234534.34.54+1' => undef, From 45175d18c8db7a774d3c1c70a3b1561354710404 Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Tue, 10 Jun 2014 09:19:03 +0800 Subject: [PATCH 3/4] Calculator: reset alarm when calculation is complete. Total run time may well exceed 1 second with the pre- and post- processing, but this makes it significantly less annoying to try things out with `duckpan query`. Also adds a test which shows the `duckpan query` parameters I was trying, showing how `squared` is applied (and may result in confusion) --- lib/DDG/Goodie/Calculator.pm | 1 + t/Calculator.t | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/DDG/Goodie/Calculator.pm b/lib/DDG/Goodie/Calculator.pm index a01b293b4c7..d6691bf69ad 100644 --- a/lib/DDG/Goodie/Calculator.pm +++ b/lib/DDG/Goodie/Calculator.pm @@ -126,6 +126,7 @@ handle query_nowhitespace => sub { # e.g. sin(100000)/100000 completely makes this go haywire. alarm(1); $tmp_result = eval($tmp_expr); + alarm(0); # Assume the string processing will be "fast enough" }; # Guard against non-result results diff --git a/t/Calculator.t b/t/Calculator.t index 76822aa44ba..b8dd76124b5 100644 --- a/t/Calculator.t +++ b/t/Calculator.t @@ -316,6 +316,22 @@ ddg_goodie_test( html => qq(
0.82 + 0.62 = 1
), ), + '0.8^2 + 0.6^2' => test_zci( + '0.8 ^ 2 + 0.6 ^ 2 = 1', + heading => 'Calculator', + html => + qq(
0.82 + 0.62 = 1
), + ), + '2 squared ^ 3' => test_zci( + '2 squared ^ 3 = 256', + heading => 'Calculator', + html => qq(
2 squared3 = 256
), + ), + '2^3 squared' => test_zci( + '2 ^ 3 squared = 512', + heading => 'Calculator', + html => qq(
23squared = 512
), + ), 'sin(1.0) + 1,05' => undef, '4,24,334+22,53,828' => undef, '5234534.34.54+1' => undef, From 300cba4744863e8fd4cc49fad1a8b6f34c6565de Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Tue, 10 Jun 2014 19:58:32 +0800 Subject: [PATCH 4/4] Calculator: style up the answer a bit. This mostly just adds style to the HTML answers to be more in line with other IAs. The test changes are because, in general, the HTML answer is exactly the same as the text answer, just wrapped up in some HTML. We're mostly concerned with if we get correct answers, not how they happen to be wrapped for HTML display. In cases where that's different (the regression) we do more thorough testing on the contents. Also, removed one repeated test. I'm starting to think this "apply_css" and maybe even "wrap_html" should be in a role somewhere. --- lib/DDG/Goodie/Calculator.pm | 21 ++++- share/goodie/calculator/style.css | 7 ++ t/Calculator.t | 135 +++++++++++++----------------- 3 files changed, 80 insertions(+), 83 deletions(-) create mode 100644 share/goodie/calculator/style.css diff --git a/lib/DDG/Goodie/Calculator.pm b/lib/DDG/Goodie/Calculator.pm index d6691bf69ad..1aabdb1dbee 100644 --- a/lib/DDG/Goodie/Calculator.pm +++ b/lib/DDG/Goodie/Calculator.pm @@ -1,6 +1,8 @@ package DDG::Goodie::Calculator; # ABSTRACT: do simple arthimetical calculations +use feature 'state'; + use DDG::Goodie; use List::Util qw( all first max ); @@ -165,18 +167,29 @@ handle query_nowhitespace => sub { # Now add = back. $results_no_html .= ' = '; - $results_html .= ' = '; - $results_html = - qq(
$results_html$tmp_result
); return $results_no_html . $tmp_result, - html => $results_html, + html => wrap_html($results_html, $tmp_result), heading => "Calculator"; } return; }; +# Add some HTML and styling to our output +# so that we can make it prettier (unabashedly stolen from +# the ReverseComplement goodie.) +sub append_css { + my $html = shift; + state $css = share("style.css")->slurp; + return "$html"; +} + +sub wrap_html { + my ($entered, $result) = @_; + return append_css("
$entered = $result
"); +} + #separates symbols with a space #spacing '1+1' -> '1 + 1' sub spacing { diff --git a/share/goodie/calculator/style.css b/share/goodie/calculator/style.css new file mode 100644 index 00000000000..2c9610c7a44 --- /dev/null +++ b/share/goodie/calculator/style.css @@ -0,0 +1,7 @@ +.zci--answer .zci--calculator { + font-size: 1.5em; + font-weight: 300; + padding-top: .25em; + padding-bottom: .25em; + /* color: #393939; */ +} diff --git a/t/Calculator.t b/t/Calculator.t index b8dd76124b5..3414b83aebf 100644 --- a/t/Calculator.t +++ b/t/Calculator.t @@ -32,305 +32,282 @@ ddg_goodie_test( 'what is 2-2' => test_zci( "2 - 2 = 0", heading => 'Calculator', - html => qq(
2 - 2 = 0
) + html => qr/./, ), 'solve 2+2' => test_zci( "2 + 2 = 4", heading => 'Calculator', - html => qq(
2 + 2 = 4
) + html => qr/./, ), '2^8' => test_zci( "2 ^ 8 = 256", heading => 'Calculator', - html => qq(
28 = 256
) + html => qr/./, ), '2 *7' => test_zci( "2 * 7 = 14", heading => 'Calculator', - html => qq(
2 * 7 = 14
) + html => qr/./, ), '1 dozen * 2' => test_zci( "1 dozen * 2 = 24", heading => 'Calculator', - html => qq(
1 dozen * 2 = 24
) + html => qr/./, ), 'dozen + dozen' => test_zci( "dozen + dozen = 24", heading => 'Calculator', - html => qq(
dozen + dozen = 24
) + html => qr/./, ), '2divided by 4' => test_zci( "2 divided by 4 = 0.5", heading => 'Calculator', - html => qq(
2 divided by 4 = 0.5
) + html => qr/./, ), '2^dozen' => test_zci( "2 ^ dozen = 4,096", heading => 'Calculator', - html => qq(
2dozen = 4,096
) + html => qr/./, ), '2^2' => test_zci( "2 ^ 2 = 4", heading => 'Calculator', - html => qq(
22 = 4
) + html => qr/./, ), '2^0.2' => test_zci( "2 ^ 0.2 = 1.14869835499704", heading => 'Calculator', - html => - qq() + html => qr/./, ), 'cos(0)' => test_zci( "cos(0) = 1", heading => 'Calculator', - html => qq(
cos(0) = 1
) + html => qr/./, ), 'tan(1)' => test_zci( "tan(1) = 1.5574077246549", heading => 'Calculator', - html => - qq() + html => qr/./, ), 'tanh(1)' => test_zci( "tanh(1) = 0.761594155955765", heading => 'Calculator', - html => - qq() + html => qr/./, ), 'cotan(1)' => test_zci( "cotan(1) = 0.642092615934331", heading => 'Calculator', - html => - qq() + html => qr/./, ), 'sin(1)' => test_zci( "sin(1) = 0.841470984807897", heading => 'Calculator', - html => - qq() + html => qr/./, ), 'csc(1)' => test_zci( "csc(1) = 1.18839510577812", heading => 'Calculator', - html => - qq() + html => qr/./, ), 'sec(1)' => test_zci( "sec(1) = 1.85081571768093", heading => 'Calculator', - html => - qq() + html => qr/./, ), 'log(3)' => test_zci( "log(3) = 1.09861228866811", heading => 'Calculator', - html => - qq() + html => qr/./, ), 'ln(3)' => test_zci( "ln(3) = 1.09861228866811", heading => 'Calculator', - html => - qq() + html => qr/./, ), 'log10(100.00)' => test_zci( "log10(100.00) = 2", heading => 'Calculator', - html => qq(
log10(100.00) = 2
) + html => qr/./, ), 'log_10(100.00)' => test_zci( "log_10(100.00) = 2", heading => 'Calculator', - html => qq(
log_10(100.00) = 2
) + html => qr/./, ), 'log_2(16)' => test_zci( "log_2(16) = 4", heading => 'Calculator', - html => qq(
log_2(16) = 4
) + html => qr/./, ), 'log_23(25)' => test_zci( "log_23(25) = 1.0265928122321", heading => 'Calculator', - html => - qq(
log_23(25) = 1.0265928122321
) + html => qr/./, ), 'log23(25)' => test_zci( "log23(25) = 1.0265928122321", heading => 'Calculator', - html => - qq(
log23(25) = 1.0265928122321
) + html => qr/./, ), '$3.43+$34.45' => test_zci( '$3.43 + $34.45 = $37.88', heading => 'Calculator', - html => qq(
\$3.43 + \$34.45 = \$37.88
) + html => qr/./, ), '$3.45+$34.45' => test_zci( '$3.45 + $34.45 = $37.90', heading => 'Calculator', - html => qq(
\$3.45 + \$34.45 = \$37.90
) + html => qr/./, ), '$3+$34' => test_zci( '$3 + $34 = $37', heading => 'Calculator', - html => qq(
\$3 + \$34 = \$37
) + html => qr/./, ), '$3,4+$34,4' => test_zci( '$3,4 + $34,4 = $37,8', heading => 'Calculator', - html => qq(
\$3,4 + \$34,4 = \$37,8
) + html => qr/./, ), '64*343' => test_zci( '64 * 343 = 21,952', heading => 'Calculator', - html => qq(
64 * 343 = 21,952
), + html => qr/./, ), '1E2 + 1' => test_zci( '(1 * 10 ^ 2) + 1 = 101', heading => 'Calculator', - html => qq(
(1 * 102) + 1 = 101
), + html => qr/./, ), '1 + 1E2' => test_zci( '1 + (1 * 10 ^ 2) = 101', heading => 'Calculator', - html => qq(
1 + (1 * 102) = 101
), + html => qr/./, ), '2 * 3 + 1E2' => test_zci( '2 * 3 + (1 * 10 ^ 2) = 106', heading => 'Calculator', - html => - qq(
2 * 3 + (1 * 102) = 106
), + html => qr/./, ), '1E2 + 2 * 3' => test_zci( '(1 * 10 ^ 2) + 2 * 3 = 106', heading => 'Calculator', - html => - qq(
(1 * 102) + 2 * 3 = 106
), + html => qr/./, ), '1E2 / 2' => test_zci( '(1 * 10 ^ 2) / 2 = 50', heading => 'Calculator', - html => qq(
(1 * 102) / 2 = 50
), + html => qr/./, ), '2 / 1E2' => test_zci( '2 / (1 * 10 ^ 2) = 0.02', heading => 'Calculator', - html => qq(
2 / (1 * 102) = 0.02
), + html => qr/./, ), '424334+2253828' => test_zci( '424334 + 2253828 = 2,678,162', heading => 'Calculator', - html => qq(
424334 + 2253828 = 2,678,162
), + html => qr/./, ), '4.243,34+22.538,28' => test_zci( '4.243,34 + 22.538,28 = 26.781,62', heading => 'Calculator', - html => - qq(
4.243,34 + 22.538,28 = 26.781,62
), + html => qr/./, ), 'sin(1,0) + 1,05' => test_zci( 'sin(1,0) + 1,05 = 1,8914709848079', heading => 'Calculator', - html => - qq(
sin(1,0) + 1,05 = 1,8914709848079
), + html => qr/./, ), '21 + 15 x 0 + 5' => test_zci( '21 + 15 x 0 + 5 = 26', heading => 'Calculator', - html => qq(
21 + 15 x 0 + 5 = 26
), + html => qr/./, ), '0.8158 - 0.8157' => test_zci( '0.8158 - 0.8157 = 0.0001', heading => 'Calculator', - html => qq(
0.8158 - 0.8157 = 0.0001
), + html => qr/./, ), '2,90 + 4,6' => test_zci( '2,90 + 4,6 = 7,50', heading => 'Calculator', - html => qq(
2,90 + 4,6 = 7,50
), + html => qr/./, ), '2,90 + sec(4,6)' => test_zci( '2,90 + sec(4,6) = -6,01642861135959', heading => 'Calculator', - html => - qq(
2,90 + sec(4,6) = -6,01642861135959
), + html => qr/./, ), '100 - 96.54' => test_zci( '100 - 96.54 = 3.46', heading => 'Calculator', - html => qq(
100 - 96.54 = 3.46
), + html => qr/./, ), '1. + 1.' => test_zci( '1. + 1. = 2', heading => 'Calculator', - html => qq(
1. + 1. = 2
), + html => qr/./, ), '1 + sin(pi)' => test_zci( '1 + sin(pi) = 1', heading => 'Calculator', - html => qq(
1 + sin(pi) = 1
), + html => qr/./, ), '1 - 1' => test_zci( '1 - 1 = 0', heading => 'Calculator', - html => qq(
1 - 1 = 0
), + html => qr/./, ), 'sin(pi/2)' => test_zci( 'sin(pi / 2) = 1', heading => 'Calculator', - html => qq(
sin(pi / 2) = 1
), + html => qr/./, ), 'sin(pi)' => test_zci( 'sin(pi) = 0', heading => 'Calculator', - html => qq(
sin(pi) = 0
), + html => qr/./, ), 'cos(2pi)' => test_zci( 'cos(2 pi) = 1', heading => 'Calculator', - html => qq(
cos(2 pi) = 1
), + html => qr/./, ), '5 squared' => test_zci( '5 squared = 25', heading => 'Calculator', - html => qq(
5 squared = 25
), + html => qr/./, ), '1.0 + 5 squared' => test_zci( '1.0 + 5 squared = 26.0', heading => 'Calculator', - html => qq(
1.0 + 5 squared = 26.0
), + html => qr/./, ), '3 squared + 4 squared' => test_zci( '3 squared + 4 squared = 25', heading => 'Calculator', - html => qq(
3 squared + 4 squared = 25
), + html => qr/./, ), '2,2 squared' => test_zci( '2,2 squared = 4,8', heading => 'Calculator', - html => qq(
2,2 squared = 4,8
), + html => qr/./, ), '0.8^2 + 0.6^2' => test_zci( '0.8 ^ 2 + 0.6 ^ 2 = 1', heading => 'Calculator', - html => - qq(
0.82 + 0.62 = 1
), - ), - '0.8^2 + 0.6^2' => test_zci( - '0.8 ^ 2 + 0.6 ^ 2 = 1', - heading => 'Calculator', - html => - qq(
0.82 + 0.62 = 1
), + html => qr#0.82 \+ 0.62 = 1#, ), '2 squared ^ 3' => test_zci( '2 squared ^ 3 = 256', heading => 'Calculator', - html => qq(
2 squared3 = 256
), + html => qr#2 squared3 = 256#, ), '2^3 squared' => test_zci( '2 ^ 3 squared = 512', heading => 'Calculator', - html => qq(
23squared = 512
), + html => qr#23squared = 512#, ), 'sin(1.0) + 1,05' => undef, '4,24,334+22,53,828' => undef,