mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-06-25 20:21:44 +02:00
Fixed the broken unit tests in the parser and completed the test_angle_units test in the interpreter.
This commit is contained in:
parent
83668bbb84
commit
c86b4bc2f7
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -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",
|
||||||
|
@ -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"
|
||||||
|
@ -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]
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
|
@ -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> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user