Skip to content

Commit 8d0fa38

Browse files
committed
Merge pull request #18 from agentzh/calc
added a simple infix arithmetic calculator.
2 parents e03747e + c93ef3d commit 8d0fa38

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed

categories/interpreters/calc.p6

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#!/usr/bin/env perl6
2+
3+
use v6;
4+
5+
=begin pod
6+
7+
=TITLE Simple Infix Arithmetic Calculator
8+
9+
Operators supported: +, -, *, /, and ^.
10+
11+
Usage:
12+
13+
=begin code
14+
15+
./calc.p6 '(3-(2-1))*8^2/4'
16+
17+
./calc.p6 < expression.txt
18+
19+
=end code
20+
21+
For benchmark results as compared to equivalent calculators
22+
implemented atop Perl 5's Parse::RecDescent and
23+
Regexp::Grammars, please check out the following page for details:
24+
25+
L<https://gist.github.com/agentzh/c5108a959309f015c4f6>
26+
27+
FIXME: error reporting on invalid inputs still needs love.
28+
29+
Contributed by Yichun Zhang, inspired by the calc demo in bison's user manual.
30+
31+
=end pod
32+
33+
my grammar Arith {
34+
rule TOP {
35+
| <.ws> <expr> { make $<expr>.made }
36+
| { self.panic("Bad expression") }
37+
}
38+
39+
rule expr {
40+
| <term> + % <add-op> { self.do_calc($/, $<term>, $<add-op>) }
41+
| { self.panic("Bad expression") }
42+
}
43+
44+
token add-op {
45+
| < + - >
46+
}
47+
48+
rule term {
49+
| <factor> + % <mul-op> { make self.do_calc($/, $<factor>, $<mul-op>) }
50+
| { self.panic($/, "Bad term") }
51+
}
52+
53+
token mul-op {
54+
| < * / >
55+
}
56+
57+
rule factor {
58+
| <atom> + % '^'
59+
{
60+
make [**] map { $_.made }, @<atom>;
61+
}
62+
| { self.panic($/, "Bad factor") }
63+
}
64+
65+
rule atom {
66+
| <number> { make +$<number> }
67+
| '(' ~ ')' <expr> { make $<expr>.made }
68+
| { self.panic($/, "Bad atom") }
69+
}
70+
71+
rule number {
72+
<.sign> ? <.pos-num>
73+
| { self.panic($/, "Bad number") }
74+
}
75+
76+
token sign { < + - > }
77+
token pos-num {
78+
| <.digit>+ [ \. <digit>+ ]?
79+
| \. <.digit>+
80+
| { self.panic($/, "Bad number") }
81+
}
82+
83+
method do_calc($/, $operands, $operators) {
84+
my $res = $operands[0].made;
85+
my $n = $operands.elems;
86+
loop (my $i = 1; $i < $n; $i++) {
87+
my $op = $operators[$i - 1];
88+
my $num = $operands[$i].made;
89+
90+
given $op {
91+
when '+' { $res += $num; }
92+
when '-' { $res -= $num; }
93+
when '*' { $res *= $num; }
94+
default { # when '/'
95+
$res /= $num;
96+
}
97+
}
98+
}
99+
make $res;
100+
}
101+
102+
method panic($/, $msg) {
103+
my $c = $/.CURSOR;
104+
my $pos := $c.pos;
105+
die "$msg found at pos $pos";
106+
}
107+
}
108+
109+
my $input = (@*ARGS[0] // slurp);
110+
111+
try Arith.parse($input);
112+
if $! {
113+
say "Parse failed: ", $!.message;
114+
115+
} elsif $/ {
116+
say $();
117+
118+
} else {
119+
say "Parse failed.";
120+
}

0 commit comments

Comments
 (0)