summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Brown <gmb60@cam.ac.uk>2021-04-20 11:54:40 +0100
committerGreg Brown <gmb60@cam.ac.uk>2021-04-20 11:54:40 +0100
commit15f5491028b11bd821bd141183e8b5bf8c1c46af (patch)
tree02a688b9baf9e4e6b11d83b9d76b95bb363e66ce
parent7934aa9af22e8fa3c33a45bc08e732a73c0cabf5 (diff)
Add handwritten arithmetic parser.
-rw-r--r--chomp-bench/benches/arith.rs7
-rw-r--r--chomp-bench/src/arith/mod.rs117
2 files changed, 124 insertions, 0 deletions
diff --git a/chomp-bench/benches/arith.rs b/chomp-bench/benches/arith.rs
index 4ba6ea7..489a847 100644
--- a/chomp-bench/benches/arith.rs
+++ b/chomp-bench/benches/arith.rs
@@ -24,6 +24,10 @@ fn parse_chewed(input: &str) -> i64 {
.into()
}
+fn parse_handwritten(input: &str) -> i64 {
+ parse_expr(&mut IterWrapper::new(input.chars())).unwrap()
+}
+
fn bench_parse(c: &mut Criterion) {
let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic);
let mut group = c.benchmark_group("Arith");
@@ -33,6 +37,9 @@ fn bench_parse(c: &mut Criterion) {
group.bench_with_input(BenchmarkId::new("Chewed", i), *input, |b, i| {
b.iter(|| parse_chewed(i))
});
+ group.bench_with_input(BenchmarkId::new("Handwritten", i), *input, |b, i| {
+ b.iter(|| parse_handwritten(i))
+ });
}
}
diff --git a/chomp-bench/src/arith/mod.rs b/chomp-bench/src/arith/mod.rs
index e7b7abd..37432d5 100644
--- a/chomp-bench/src/arith/mod.rs
+++ b/chomp-bench/src/arith/mod.rs
@@ -1 +1,118 @@
+use chewed::{ParseError, TakeError};
+
pub mod nibble;
+
+pub fn take_number<P: chewed::Parser + ?Sized>(input: &mut P) -> Result<i64, TakeError> {
+ let mut out = None;
+ loop {
+ match input.next() {
+ Some('0') => {
+ out = Some(out.unwrap_or_default() * 10);
+ }
+ Some('1') => {
+ out = Some(out.unwrap_or_default() * 10 + 1);
+ }
+ Some('2') => {
+ out = Some(out.unwrap_or_default() * 10 + 2);
+ }
+ Some('3') => {
+ out = Some(out.unwrap_or_default() * 10 + 3);
+ }
+ Some('4') => {
+ out = Some(out.unwrap_or_default() * 10 + 4);
+ }
+ Some('5') => {
+ out = Some(out.unwrap_or_default() * 10 + 5);
+ }
+ Some('6') => {
+ out = Some(out.unwrap_or_default() * 10 + 6);
+ }
+ Some('7') => {
+ out = Some(out.unwrap_or_default() * 10 + 7);
+ }
+ Some('8') => {
+ out = Some(out.unwrap_or_default() * 10 + 8);
+ }
+ Some('9') => {
+ out = Some(out.unwrap_or_default() * 10 + 9);
+ }
+ Some(c) => {
+ return out.ok_or_else(|| {
+ TakeError::BadBranch(
+ input.pos(),
+ c,
+ &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
+ )
+ })
+ }
+ None => return out.ok_or_else(|| TakeError::EndOfStream(input.pos())),
+ }
+ }
+}
+
+pub fn take_term<P: chewed::Parser + ?Sized>(input: &mut P) -> Result<i64, TakeError> {
+ let out = match input.peek() {
+ Some('-') => {
+ input.consume_str("-")?;
+ input.skip_while(|c| c == ' ');
+ take_number(input).map(|x| -x)
+ }
+ Some(c) if c.is_ascii_digit() => take_number(input),
+ Some('(') => {
+ input.consume_str("(")?;
+ input.skip_while(|c| c == ' ');
+ let o = take_expr(input);
+ input.consume_str(")")?;
+ o
+ }
+ Some(c) => Err(TakeError::BadBranch(
+ input.pos(),
+ c,
+ &['-', '(', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
+ )),
+ None => Err(TakeError::EndOfStream(input.pos())),
+ }?;
+ input.skip_while(|c| c == ' ');
+ Ok(out)
+}
+
+pub fn take_product<P: chewed::Parser + ?Sized>(input: &mut P) -> Result<i64, TakeError> {
+ let mut out = take_term(input)?;
+ while let Some(c) = input.peek() {
+ if c == '*' {
+ input.skip_while(|c| c == ' ');
+ out *= take_term(input)?;
+ } else if c == '/' {
+ input.skip_while(|c| c == ' ');
+ out -= take_term(input)?;
+ } else {
+ return Err(TakeError::BadBranch(input.pos(), c, &['*', '/']));
+ }
+ }
+ Ok(out)
+}
+
+pub fn take_expr<P: chewed::Parser + ?Sized>(input: &mut P) -> Result<i64, TakeError> {
+ let mut out = take_product(input)?;
+ while let Some(c) = input.peek() {
+ if c == '+' {
+ input.skip_while(|c| c == ' ');
+ out += take_product(input)?;
+ } else if c == '-' {
+ input.skip_while(|c| c == ' ');
+ out -= take_product(input)?;
+ } else {
+ return Err(TakeError::BadBranch(input.pos(), c, &['+', '-']));
+ }
+ }
+ Ok(out)
+}
+
+pub fn parse_expr<P: chewed::Parser + ?Sized>(input: &mut P) -> Result<i64, ParseError> {
+ input.skip_while(|c| c == ' ');
+ let out = take_expr(input)?;
+ match input.peek() {
+ Some(_) => Err(ParseError::InputContinues(input.pos())),
+ None => Ok(out),
+ }
+}