Add assignment 4

This commit is contained in:
Seungmin Jeon
2022-09-15 21:58:55 +09:00
parent 27f0b78a2c
commit 8e5abc7048
11 changed files with 509 additions and 1 deletions

View File

@@ -6,3 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.64"
pest = "2.3.0"
pest_derive = "2.3.0"

34
scripts/grade-04.sh Executable file
View File

@@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -e
set -uo pipefail
IFS=$'\n\t'
# Imports library.
BASEDIR=$(dirname "$0")
source $BASEDIR/grade-utils.sh
RUNNERS=(
"cargo"
"cargo --release"
"cargo_asan"
"cargo_asan --release"
"cargo_tsan"
"cargo_tsan --release"
)
# Lints.
cargo fmt --check
cargo clippy
# Executes test for each runner.
for RUNNER in "${RUNNERS[@]}"; do
echo "Running with $RUNNER..."
TESTS=("--lib assignment04_grade")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi
done
exit 0

9
scripts/prepare-submissions.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -e
BASEDIR=$(dirname "$0")/..
mkdir -p $BASEDIR/target
zip -rj $BASEDIR/target/assignment04.zip src/assignments/assignment04

View File

@@ -0,0 +1,46 @@
//! Calculator.
use std::collections::HashMap;
use anyhow::*;
use super::syntax::{Command, Expression};
/// Calculator's context.
#[derive(Debug, Default, Clone)]
pub struct Context {
anonymous_counter: usize,
variables: HashMap<String, f64>,
}
impl Context {
/// Creates a new context.
pub fn new() -> Self {
Self::default()
}
/// Returns the current anonymous variable counter.
pub fn current_counter(&self) -> usize {
self.anonymous_counter
}
/// Calculates the given expression.
pub fn calc_expression(&self, expression: &Expression) -> Result<f64> {
todo!("fill here")
}
/// Calculates the given command.
///
/// If there is no variable lhs in the command (i.e. `command.variable = None`), its value should be stored at `$0`, `$1`, `$2`, ... respectively.
///
/// # Example
///
/// After calculating commad `3 + 5` => Context's variables = `{($0,8)}`
///
/// After calculating commad `v = 3 - 2` => Context's variables = `{($0,8),(v,1))}`
///
/// After calculating commad `3 ^ 2` => Context's variables = `{($0,8),(v,1),($1,9)}`
pub fn calc_command(&mut self, command: &Command) -> Result<(String, f64)> {
todo!("fill here")
}
}

View File

@@ -0,0 +1,20 @@
//! Assignment 4: Designing a calculator.
//!
//! The primary goal of this assignment is twofold:
//! (1) understanding the `pest` third-party library from documentations; and
//! (2) using programming concepts you've learned so far to implement a simple arithmetic calculator.
//!
//! For `pest`, read the following documentations (that contain 90% of the solution):
//! - <https://pest.rs/>
//! - <https://pest.rs/book/>
//! - <https://docs.rs/pest/latest/pest>
//!
//! For calculator, just reading `syntax.rs` would suffice for you to understand what to do.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-04.sh` works fine.
//! See `assignment04_grade.rs` and `/scripts/grade-04.sh` for the test script.
//! Run `/scripts/prepare-submissions.sh` and submit `/target/assignment04.zip` to <https://gg.kaist.ac.kr>.
pub mod context;
pub mod parser;
pub mod syntax;

View File

@@ -0,0 +1,19 @@
//! Parser.
use super::syntax::*;
use anyhow::Result;
#[allow(missing_docs)]
#[allow(missing_debug_implementations)]
mod inner {
use pest_derive::*;
#[derive(Parser)]
#[grammar = "assignments/assignment04/syntax.pest"]
pub(crate) struct SyntaxParser;
}
/// Parses command.
pub fn parse_command(line: &str) -> Result<Command> {
todo!("fill here")
}

View File

@@ -0,0 +1,17 @@
num = @{ int ~ ("." ~ ASCII_DIGIT*)? ~ (^"e" ~ int)? }
int = { ("+" | "-")? ~ ASCII_DIGIT+ }
var = { ("$" | ASCII_ALPHA) ~ (ASCII_ALPHA | ASCII_DIGIT)* }
operation = _{ add | subtract | multiply | divide | power }
add = { "+" }
subtract = { "-" }
multiply = { "*" }
divide = { "/" }
power = { "^" }
expr = { term ~ (operation ~ term)* }
term = _{ num | var | "(" ~ expr ~ ")" }
command = _{ SOI ~ (var ~ "=")? ~ expr ~ EOI }
WHITESPACE = _{ " " | "\t" }

View File

@@ -0,0 +1,43 @@
//! Syntax.
/// Command of the form "<expression>" or "<var> = <expression>".
#[derive(Debug, Clone, PartialEq)]
pub struct Command {
/// Variable (lhs).
pub variable: Option<String>,
/// Expression (rhs).
pub expression: Expression,
}
/// Binary operators.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinOp {
/// Add.
Add,
/// Subtract.
Subtract,
/// Multiply.
Multiply,
/// Divide.
Divide,
/// Power.
Power,
}
/// Expression.
#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
/// Number.
Num(f64),
/// Variable.
Variable(String),
/// Binary operation.
BinOp {
/// Operator.
op: BinOp,
/// Lhs.
lhs: Box<Expression>,
/// Rhs.
rhs: Box<Expression>,
},
}

View File

@@ -0,0 +1,315 @@
#[cfg(test)]
mod test {
use crate::assignments::assignment04::syntax::*;
use super::super::assignment04::*;
#[test]
fn test_parse() {
assert_eq!(
parser::parse_command("$1 = (132 + 77) * 3 ^ 8").unwrap(),
Command {
variable: Some("$1".into()),
expression: Expression::BinOp {
op: BinOp::Multiply,
lhs: Expression::BinOp {
op: BinOp::Add,
lhs: Expression::Num(132.0).into(),
rhs: Expression::Num(77.0).into(),
}
.into(),
rhs: Expression::BinOp {
op: BinOp::Power,
lhs: Expression::Num(3.0).into(),
rhs: Expression::Num(8.0).into(),
}
.into(),
}
}
);
assert_eq!(
parser::parse_command("132 + 77").unwrap(),
Command {
variable: None,
expression: Expression::BinOp {
op: BinOp::Add,
lhs: Expression::Num(132.0).into(),
rhs: Expression::Num(77.0).into(),
}
}
);
assert_eq!(
parser::parse_command("v2 = 132 + 77").unwrap(),
Command {
variable: Some("v2".into()),
expression: Expression::BinOp {
op: BinOp::Add,
lhs: Expression::Num(132.0).into(),
rhs: Expression::Num(77.0).into(),
}
}
);
assert!(parser::parse_command("132 +!s 77").is_err());
assert_eq!(
parser::parse_command("12 - 34 + 23 ^ 4").unwrap(),
Command {
variable: None,
expression: Expression::BinOp {
op: BinOp::Add,
lhs: Expression::BinOp {
op: BinOp::Subtract,
lhs: Expression::Num(12.0).into(),
rhs: Expression::Num(34.0).into(),
}
.into(),
rhs: Expression::BinOp {
op: BinOp::Power,
lhs: Expression::Num(23.0).into(),
rhs: Expression::Num(4.0).into(),
}
.into(),
},
}
);
}
#[test]
fn test_context_calc_expression() {
let ctx = context::Context::new();
// "(132 + 77) * 3 ^ 8"
assert_eq!(
ctx.calc_expression(&Expression::BinOp {
op: BinOp::Multiply,
lhs: Expression::BinOp {
op: BinOp::Add,
lhs: Expression::Num(132.0).into(),
rhs: Expression::Num(77.0).into(),
}
.into(),
rhs: Expression::BinOp {
op: BinOp::Power,
lhs: Expression::Num(3.0).into(),
rhs: Expression::Num(8.0).into(),
}
.into(),
})
.expect("calculate expression is failed"),
1371249.0
);
// "132 + 77"
assert_eq!(
ctx.calc_expression(&Expression::BinOp {
op: BinOp::Add,
lhs: Expression::Num(132.0).into(),
rhs: Expression::Num(77.0).into(),
})
.expect("calculate expression is failed"),
209.0
);
// "v + 77"
assert!(ctx
.calc_expression(&Expression::BinOp {
op: BinOp::Add,
lhs: Expression::Variable("v".into()).into(),
rhs: Expression::Num(77.0).into(),
})
.is_err());
// "3 / (3 * 4 - 12)"
assert!(ctx
.calc_expression(&Expression::BinOp {
op: BinOp::Divide,
lhs: Expression::Num(3.0).into(),
rhs: Expression::BinOp {
op: BinOp::Subtract,
lhs: Expression::BinOp {
op: BinOp::Multiply,
lhs: Expression::Num(3.0).into(),
rhs: Expression::Num(4.0).into(),
}
.into(),
rhs: Expression::Num(12.0).into(),
}
.into(),
})
.is_err());
// "12 - 34 + 23 ^ 4"
assert_eq!(
ctx.calc_expression(&Expression::BinOp {
op: BinOp::Add,
lhs: Expression::BinOp {
op: BinOp::Subtract,
lhs: Expression::Num(12.0).into(),
rhs: Expression::Num(34.0).into(),
}
.into(),
rhs: Expression::BinOp {
op: BinOp::Power,
lhs: Expression::Num(23.0).into(),
rhs: Expression::Num(4.0).into(),
}
.into(),
},)
.expect("calculate expression is failed"),
279819.0
);
}
#[test]
fn test_context_calc_command() {
let mut ctx = context::Context::new();
// "v1 = 132 + 77"
assert_eq!(
ctx.calc_command(&Command {
variable: Some("v1".into()),
expression: Expression::BinOp {
op: BinOp::Add,
lhs: Expression::Num(132.0).into(),
rhs: Expression::Num(77.0).into(),
}
})
.unwrap(),
("v1".into(), 209.0)
);
// "1 - 3 + 2 ^ 4"
assert_eq!(
ctx.calc_command(&Command {
variable: None,
expression: Expression::BinOp {
op: BinOp::Add,
lhs: Expression::BinOp {
op: BinOp::Subtract,
lhs: Expression::Num(1.0).into(),
rhs: Expression::Num(3.0).into(),
}
.into(),
rhs: Expression::BinOp {
op: BinOp::Power,
lhs: Expression::Num(2.0).into(),
rhs: Expression::Num(4.0).into(),
}
.into(),
},
})
.unwrap(),
("$0".into(), 14.0)
);
// "v2 = v1 * 3 + $0"
assert_eq!(
ctx.calc_command(&Command {
variable: Some("v2".into()),
expression: Expression::BinOp {
op: BinOp::Add,
lhs: Expression::BinOp {
op: BinOp::Multiply,
lhs: Expression::Variable("v1".into()).into(),
rhs: Expression::Num(3.0).into(),
}
.into(),
rhs: Expression::Variable("$0".into()).into()
}
})
.unwrap(),
("v2".into(), 641.0)
);
// "5 / 2"
assert_eq!(
ctx.calc_command(&Command {
variable: None,
expression: Expression::BinOp {
op: BinOp::Divide,
lhs: Expression::Num(5.0).into(),
rhs: Expression::Num(2.0).into(),
},
})
.unwrap(),
("$1".into(), 2.5)
);
// "v2 = v2 ^ 2"
assert_eq!(
ctx.calc_command(&Command {
variable: Some("v2".into()),
expression: Expression::BinOp {
op: BinOp::Power,
lhs: Expression::Variable("v2".into()).into(),
rhs: Expression::Num(2.0).into(),
}
})
.unwrap(),
("v2".into(), 410881.0)
);
// "v2 = v2 - $1"
assert_eq!(
ctx.calc_command(&Command {
variable: Some("v2".into()),
expression: Expression::BinOp {
op: BinOp::Subtract,
lhs: Expression::Variable("v2".into()).into(),
rhs: Expression::Variable("$1".into()).into(),
}
})
.unwrap(),
("v2".into(), 410878.5)
);
// "v2 = v2 / $3"
assert!(ctx
.calc_command(&Command {
variable: Some("v2".into()),
expression: Expression::BinOp {
op: BinOp::Divide,
lhs: Expression::Variable("v2".into()).into(),
rhs: Expression::Variable("$3".into()).into(),
}
})
.is_err());
// "v3 = 3 / (3 * 4 - 12)"
assert!(ctx
.calc_command(&Command {
variable: Some("v3".into()),
expression: Expression::BinOp {
op: BinOp::Divide,
lhs: Expression::Num(3.0).into(),
rhs: Expression::BinOp {
op: BinOp::Subtract,
lhs: Expression::BinOp {
op: BinOp::Multiply,
lhs: Expression::Num(3.0).into(),
rhs: Expression::Num(4.0).into(),
}
.into(),
rhs: Expression::Num(12.0).into(),
}
.into(),
}
})
.is_err());
// v3 = v3 - v2
assert!(ctx
.calc_command(&Command {
variable: Some("v3".into()),
expression: Expression::BinOp {
op: BinOp::Subtract,
lhs: Expression::Variable("v3".into()).into(),
rhs: Expression::Variable("v2".into()).into(),
}
})
.is_err());
}
}

View File

@@ -9,3 +9,5 @@ pub mod assignment02;
mod assignment02_grade;
pub mod assignment03;
mod assignment03_grade;
pub mod assignment04;
mod assignment04_grade;

View File

@@ -3,7 +3,7 @@
// # Tries to deny all lints (`rustc -W help`).
#![deny(absolute_paths_not_starting_with_crate)]
#![deny(anonymous_parameters)]
#![deny(box_pointers)]
// #![deny(box_pointers)]
#![deny(deprecated_in_future)]
#![deny(explicit_outlives_requirements)]
#![deny(keyword_idents)]