Fixed the broken unit tests in the parser and completed the test_angle_units test in the interpreter.

This commit is contained in:
PaddiM8 2020-06-15 21:27:47 +02:00
parent 83668bbb84
commit c86b4bc2f7
6 changed files with 89 additions and 31 deletions

1
Cargo.lock generated
View File

@ -140,6 +140,7 @@ dependencies = [
name = "kalk" name = "kalk"
version = "0.1.10" version = "0.1.10"
dependencies = [ dependencies = [
"lazy_static",
"phf", "phf",
"regex", "regex",
"rug", "rug",

View File

@ -15,3 +15,4 @@ phf = { version = "0.8", features = ["macros"] }
rug = "1.9.0" rug = "1.9.0"
test-case = "1.0.0" test-case = "1.0.0"
regex = "1" regex = "1"
lazy_static = "1.4.0"

View File

@ -265,10 +265,38 @@ mod tests {
const PRECISION: u32 = 53; const PRECISION: u32 = 53;
lazy_static::lazy_static! {
static ref DEG_RAD_UNIT: Stmt = unit_decl(
"deg",
"rad",
binary(
binary(
var(crate::parser::DECL_UNIT),
TokenKind::Star,
literal("180"),
),
TokenKind::Slash,
var("pi"),
),
);
static ref RAD_DEG_UNIT: Stmt = unit_decl(
"rad",
"deg",
binary(
binary(var(crate::parser::DECL_UNIT), TokenKind::Star, var("pi")),
TokenKind::Slash,
literal("180"),
),
);
}
fn interpret(stmt: Stmt) -> Result<Option<Float>, CalcError> { fn interpret(stmt: Stmt) -> Result<Option<Float>, CalcError> {
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
let mut context = Context::new(&mut symbol_table, "rad", PRECISION); symbol_table
.insert(DEG_RAD_UNIT.clone())
.insert(RAD_DEG_UNIT.clone());
let mut context = Context::new(&mut symbol_table, "rad", PRECISION);
context.interpret(vec![stmt]) context.interpret(vec![stmt])
} }
@ -315,14 +343,19 @@ mod tests {
fn test_angle_units() { fn test_angle_units() {
let rad_explicit = Stmt::Expr(fn_call("sin", vec![*unit("rad", literal("1"))])); let rad_explicit = Stmt::Expr(fn_call("sin", vec![*unit("rad", literal("1"))]));
let deg_explicit = Stmt::Expr(fn_call("sin", vec![*unit("deg", literal("1"))])); let deg_explicit = Stmt::Expr(fn_call("sin", vec![*unit("deg", literal("1"))]));
//let implicit = Stmt::Expr(fn_call("sin", vec![*literal("1")])); let implicit = Stmt::Expr(fn_call("sin", vec![*literal("1")]));
assert!(cmp(interpret(rad_explicit).unwrap().unwrap(), 0.84147098)); assert!(cmp(interpret(rad_explicit).unwrap().unwrap(), 0.84147098));
assert!(cmp(interpret(deg_explicit).unwrap().unwrap(), 0.01745240)); assert!(cmp(interpret(deg_explicit).unwrap().unwrap(), 0.01745240));
// TODO: Get this to work. let mut rad_symbol_table = SymbolTable::new();
/*let mut rad_symbol_table = SymbolTable::new(); rad_symbol_table
.insert(DEG_RAD_UNIT.clone())
.insert(RAD_DEG_UNIT.clone());
let mut deg_symbol_table = SymbolTable::new(); let mut deg_symbol_table = SymbolTable::new();
deg_symbol_table
.insert(DEG_RAD_UNIT.clone())
.insert(RAD_DEG_UNIT.clone());
let mut rad_context = Context::new(&mut rad_symbol_table, "rad", PRECISION); let mut rad_context = Context::new(&mut rad_symbol_table, "rad", PRECISION);
let mut deg_context = Context::new(&mut deg_symbol_table, "deg", PRECISION); let mut deg_context = Context::new(&mut deg_symbol_table, "deg", PRECISION);
@ -336,7 +369,7 @@ mod tests {
assert!(cmp( assert!(cmp(
deg_context.interpret(vec![implicit]).unwrap().unwrap(), deg_context.interpret(vec![implicit]).unwrap().unwrap(),
0.01745240 0.01745240
));*/ ));
} }
#[test] #[test]

View File

@ -51,7 +51,7 @@ fn invert_binary(
symbol_table, symbol_table,
left, left,
op, op,
&multiply_in(&Expr::Literal(String::from("-1")), inside_group)?, &multiply_into(&Expr::Literal(String::from("-1")), inside_group)?,
); );
} }
@ -64,13 +64,17 @@ fn invert_binary(
return invert( return invert(
target_expr, target_expr,
symbol_table, symbol_table,
&multiply_in(right, inside_group)?, &multiply_into(right, inside_group)?,
); );
} }
// Same as above but left/right switched. // Same as above but left/right switched.
if let Expr::Group(inside_group) = right { if let Expr::Group(inside_group) = right {
return invert(target_expr, symbol_table, &multiply_in(left, inside_group)?); return invert(
target_expr,
symbol_table,
&multiply_into(left, inside_group)?,
);
} }
TokenKind::Slash TokenKind::Slash
@ -130,14 +134,15 @@ fn invert_binary(
fn invert_unary(target_expr: Expr, op: &TokenKind, expr: &Expr) -> Result<(Expr, Expr), CalcError> { fn invert_unary(target_expr: Expr, op: &TokenKind, expr: &Expr) -> Result<(Expr, Expr), CalcError> {
match op { match op {
TokenKind::Minus => Ok(( TokenKind::Minus => Ok((
// Make the target expression negative
Expr::Unary(TokenKind::Minus, Box::new(target_expr)), Expr::Unary(TokenKind::Minus, Box::new(target_expr)),
expr.clone(), expr.clone(), // And then continue inverting the inner-expression.
)), )),
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
// Not necessary yet // TODO: Implement
fn invert_unit( fn invert_unit(
_target_expr: Expr, _target_expr: Expr,
_identifier: &str, _identifier: &str,
@ -152,6 +157,7 @@ fn invert_fn_call(
identifier: &str, identifier: &str,
arguments: &Vec<Expr>, arguments: &Vec<Expr>,
) -> Result<(Expr, Expr), CalcError> { ) -> Result<(Expr, Expr), CalcError> {
// Get the function definition from the symbol table.
let (parameters, body) = let (parameters, body) =
if let Some(Stmt::FnDecl(_, parameters, body)) = symbol_table.get_fn(identifier).cloned() { if let Some(Stmt::FnDecl(_, parameters, body)) = symbol_table.get_fn(identifier).cloned() {
(parameters, body) (parameters, body)
@ -159,6 +165,7 @@ fn invert_fn_call(
return Err(CalcError::UndefinedFn(identifier.into())); return Err(CalcError::UndefinedFn(identifier.into()));
}; };
// Make sure the input-expression is valid.
if parameters.len() != arguments.len() { if parameters.len() != arguments.len() {
return Err(CalcError::IncorrectAmountOfArguments( return Err(CalcError::IncorrectAmountOfArguments(
parameters.len(), parameters.len(),
@ -167,6 +174,7 @@ fn invert_fn_call(
)); ));
} }
// Make the parameters usable as variables inside the function.
let mut parameters_iter = parameters.iter(); let mut parameters_iter = parameters.iter();
for argument in arguments { for argument in arguments {
symbol_table.insert(Stmt::VarDecl( symbol_table.insert(Stmt::VarDecl(
@ -175,10 +183,12 @@ fn invert_fn_call(
)); ));
} }
// Invert everything in the function body.
invert(target_expr, symbol_table, &body) invert(target_expr, symbol_table, &body)
} }
fn contains_the_unit(expr: &Expr) -> bool { fn contains_the_unit(expr: &Expr) -> bool {
// Recursively scan the expression for the unit.
match expr { match expr {
Expr::Binary(left, _, right) => contains_the_unit(left) || contains_the_unit(right), Expr::Binary(left, _, right) => contains_the_unit(left) || contains_the_unit(right),
Expr::Unary(_, expr) => contains_the_unit(expr), Expr::Unary(_, expr) => contains_the_unit(expr),
@ -198,21 +208,25 @@ fn contains_the_unit(expr: &Expr) -> bool {
} }
} }
fn multiply_in(expr: &Expr, base_expr: &Expr) -> Result<Expr, CalcError> { /// Multiply an expression into a group.
fn multiply_into(expr: &Expr, base_expr: &Expr) -> Result<Expr, CalcError> {
match base_expr { match base_expr {
Expr::Binary(left, op, right) => match op { Expr::Binary(left, op, right) => match op {
// If + or -, multiply the expression with each term.
TokenKind::Plus | TokenKind::Minus => Ok(Expr::Binary( TokenKind::Plus | TokenKind::Minus => Ok(Expr::Binary(
Box::new(multiply_in(expr, &left)?), Box::new(multiply_into(expr, &left)?),
op.clone(), op.clone(),
Box::new(multiply_in(expr, &right)?), Box::new(multiply_into(expr, &right)?),
)), )),
// If * or /, only multiply with the first factor.
TokenKind::Star | TokenKind::Slash => Ok(Expr::Binary( TokenKind::Star | TokenKind::Slash => Ok(Expr::Binary(
Box::new(multiply_in(expr, &left)?), Box::new(multiply_into(expr, &left)?),
op.clone(), op.clone(),
right.clone(), right.clone(),
)), )),
_ => unimplemented!(), _ => unimplemented!(),
}, },
// If it's a literal, just multiply them together.
Expr::Literal(_) | Expr::Var(_) => Ok(Expr::Binary( Expr::Literal(_) | Expr::Var(_) => Ok(Expr::Binary(
Box::new(expr.clone()), Box::new(expr.clone()),
TokenKind::Star, TokenKind::Star,

View File

@ -414,6 +414,7 @@ mod tests {
fn parse_with_context(context: &mut Context, tokens: Vec<Token>) -> Result<Stmt, CalcError> { fn parse_with_context(context: &mut Context, tokens: Vec<Token>) -> Result<Stmt, CalcError> {
context.tokens = tokens; context.tokens = tokens;
context.pos = 0;
parse_stmt(context) parse_stmt(context)
} }
@ -421,6 +422,7 @@ mod tests {
fn parse(tokens: Vec<Token>) -> Result<Stmt, CalcError> { fn parse(tokens: Vec<Token>) -> Result<Stmt, CalcError> {
let mut context = Context::new(); let mut context = Context::new();
context.tokens = tokens; context.tokens = tokens;
context.pos = 0;
parse_stmt(&mut context) parse_stmt(&mut context)
} }
@ -448,6 +450,7 @@ mod tests {
token(Slash, ""), token(Slash, ""),
token(Literal, "5"), token(Literal, "5"),
token(ClosedParenthesis, ""), token(ClosedParenthesis, ""),
token(EOF, ""),
]; ];
assert_eq!( assert_eq!(
@ -480,6 +483,7 @@ mod tests {
token(Literal, "4"), token(Literal, "4"),
token(Plus, ""), token(Plus, ""),
token(Literal, "5"), token(Literal, "5"),
token(EOF, ""),
]; ];
assert_eq!( assert_eq!(
@ -500,20 +504,20 @@ mod tests {
); );
} }
/*#[test_case(Deg)] #[test]
#[test_case(Rad)] fn test_unit() {
fn test_unary(angle_unit: TokenKind) { let tokens = vec![token(Literal, "1"), token(Identifier, "a")];
let tokens = vec![
token(Minus, ""), let mut context = Context::new();
token(Literal, "1"), context
token(angle_unit.clone(), ""), .symbol_table
]; .insert(unit_decl("a", "b", var(super::DECL_UNIT)));
assert_eq!( assert_eq!(
parse(tokens).unwrap(), parse_with_context(&mut context, tokens).unwrap(),
Stmt::Expr(unary(Minus, Box::new(Expr::Unit(literal("1"), angle_unit)))) Stmt::Expr(unit("a", literal("1")))
); );
}*/ }
#[test] #[test]
fn test_var_decl() { fn test_var_decl() {
@ -523,6 +527,7 @@ mod tests {
token(Literal, "1"), token(Literal, "1"),
token(Plus, ""), token(Plus, ""),
token(Literal, "2"), token(Literal, "2"),
token(EOF, ""),
]; ];
assert_eq!( assert_eq!(
@ -542,6 +547,7 @@ mod tests {
token(Literal, "1"), token(Literal, "1"),
token(Plus, ""), token(Plus, ""),
token(Literal, "2"), token(Literal, "2"),
token(EOF, ""),
]; ];
assert_eq!( assert_eq!(
@ -565,6 +571,7 @@ mod tests {
token(ClosedParenthesis, ""), token(ClosedParenthesis, ""),
token(Plus, ""), token(Plus, ""),
token(Literal, "3"), token(Literal, "3"),
token(EOF, ""),
]; ];
let mut context = Context::new(); let mut context = Context::new();

View File

@ -3,8 +3,8 @@ use std::collections::HashMap;
#[derive(Debug)] #[derive(Debug)]
pub struct SymbolTable { pub struct SymbolTable {
hashmap: HashMap<String, Stmt>, pub(crate) hashmap: HashMap<String, Stmt>,
unit_types: HashMap<String, ()>, pub(crate) unit_types: HashMap<String, ()>,
} }
impl SymbolTable { impl SymbolTable {
@ -15,22 +15,24 @@ impl SymbolTable {
} }
} }
pub fn insert(&mut self, value: Stmt) -> Option<Stmt> { pub fn insert(&mut self, value: Stmt) -> &mut Self {
match &value { match &value {
Stmt::VarDecl(identifier, _) => { Stmt::VarDecl(identifier, _) => {
self.hashmap.insert(format!("var.{}", identifier), value) self.hashmap.insert(format!("var.{}", identifier), value);
} }
Stmt::UnitDecl(identifier, to_unit, _) => { Stmt::UnitDecl(identifier, to_unit, _) => {
self.unit_types.insert(identifier.to_string(), ()); self.unit_types.insert(identifier.to_string(), ());
self.unit_types.insert(to_unit.to_string(), ()); self.unit_types.insert(to_unit.to_string(), ());
self.hashmap self.hashmap
.insert(format!("unit.{}.{}", identifier, to_unit), value) .insert(format!("unit.{}.{}", identifier, to_unit), value);
} }
Stmt::FnDecl(identifier, _, _) => { Stmt::FnDecl(identifier, _, _) => {
self.hashmap.insert(format!("fn.{}", identifier), value) self.hashmap.insert(format!("fn.{}", identifier), value);
} }
_ => panic!("Can only insert VarDecl, UnitDecl and FnDecl into symbol table."), _ => panic!("Can only insert VarDecl, UnitDecl and FnDecl into symbol table."),
} }
self
} }
pub fn get_var(&self, key: &str) -> Option<&Stmt> { pub fn get_var(&self, key: &str) -> Option<&Stmt> {