Store number literals as f64

This commit is contained in:
PaddiM8 2020-12-13 15:52:22 +01:00
parent 4ddb2bf33f
commit 56145c2f45
7 changed files with 148 additions and 122 deletions

View File

@ -19,5 +19,5 @@ pub enum Expr {
Var(String),
Group(Box<Expr>),
FnCall(String, Vec<Expr>),
Literal(String),
Literal(f64),
}

View File

@ -32,13 +32,13 @@ impl<'a> Context<'a> {
String::from("ans"),
Box::new(Expr::Unit(
num.unit.clone(),
Box::new(Expr::Literal(num.value.clone().to_string())),
Box::new(Expr::Literal(num.value.to_f64())),
)),
)
} else {
Stmt::VarDecl(
String::from("ans"),
Box::new(Expr::Literal(num.value.clone().to_string())),
Box::new(Expr::Literal(num.value.to_f64())),
)
});
@ -85,7 +85,7 @@ fn eval_expr(context: &mut Context, expr: &Expr, unit: &str) -> Result<KalkNum,
Expr::Unary(op, expr) => eval_unary_expr(context, op, expr, unit),
Expr::Unit(identifier, expr) => eval_unit_expr(context, identifier, expr),
Expr::Var(identifier) => eval_var_expr(context, identifier, unit),
Expr::Literal(value) => eval_literal_expr(context, value, unit),
Expr::Literal(value) => eval_literal_expr(context, *value, unit),
Expr::Group(expr) => eval_group_expr(context, &expr, unit),
Expr::FnCall(identifier, expressions) => {
eval_fn_call_expr(context, identifier, expressions, unit)
@ -200,7 +200,7 @@ fn eval_var_expr(
) -> Result<KalkNum, CalcError> {
// If there is a constant with this name, return a literal expression with its value
if let Some(value) = prelude::CONSTANTS.get(identifier) {
return eval_expr(context, &Expr::Literal((*value).to_string()), unit);
return eval_expr(context, &Expr::Literal(*value), unit);
}
// Look for the variable in the symbol table
@ -211,14 +211,11 @@ fn eval_var_expr(
}
}
fn eval_literal_expr(context: &mut Context, value: &str, unit: &str) -> Result<KalkNum, CalcError> {
match Float::parse(value) {
Ok(parsed_value) => Ok(KalkNum::new(
Float::with_val(context.precision, parsed_value),
unit.into(),
)),
Err(_) => Err(CalcError::InvalidNumberLiteral(value.into())),
}
fn eval_literal_expr(context: &mut Context, value: f64, unit: &str) -> Result<KalkNum, CalcError> {
Ok(KalkNum::new(
Float::with_val(context.precision, value),
unit.into(),
))
}
fn eval_group_expr(context: &mut Context, expr: &Expr, unit: &str) -> Result<KalkNum, CalcError> {
@ -269,7 +266,7 @@ fn eval_fn_call_expr(
let mut sum = Float::with_val(context.precision, 0);
for n in start..=end {
let n_expr = Expr::Literal(n.to_string());
let n_expr = Expr::Literal(n as f64);
// Update the variable "n" in the symbol table on every iteration,
// then calculate the expression and add it to the total sum.
@ -328,7 +325,7 @@ mod tests {
binary(
var(crate::parser::DECL_UNIT),
TokenKind::Star,
literal("180"),
literal(180f64),
),
TokenKind::Slash,
var("pi"),
@ -340,7 +337,7 @@ mod tests {
binary(
binary(var(crate::parser::DECL_UNIT), TokenKind::Star, var("pi")),
TokenKind::Slash,
literal("180"),
literal(180f64),
),
);
}
@ -370,18 +367,18 @@ mod tests {
#[test]
fn test_literal() {
let stmt = Stmt::Expr(literal("1"));
let stmt = Stmt::Expr(literal(1f64));
assert_eq!(interpret(stmt).unwrap().unwrap().to_f64(), 1f64);
}
#[test]
fn test_binary() {
let add = Stmt::Expr(binary(literal("2"), Plus, literal("3")));
let sub = Stmt::Expr(binary(literal("2"), Minus, literal("3")));
let mul = Stmt::Expr(binary(literal("2"), Star, literal("3")));
let div = Stmt::Expr(binary(literal("2"), Slash, literal("4")));
let pow = Stmt::Expr(binary(literal("2"), Power, literal("3")));
let add = Stmt::Expr(binary(literal(2f64), Plus, literal(3f64)));
let sub = Stmt::Expr(binary(literal(2f64), Minus, literal(3f64)));
let mul = Stmt::Expr(binary(literal(2f64), Star, literal(3f64)));
let div = Stmt::Expr(binary(literal(2f64), Slash, literal(4f64)));
let pow = Stmt::Expr(binary(literal(2f64), Power, literal(3f64)));
assert_eq!(interpret(add).unwrap().unwrap().to_f64(), 5f64);
assert_eq!(interpret(sub).unwrap().unwrap().to_f64(), -1f64);
@ -393,9 +390,9 @@ mod tests {
#[test]
fn test_percent() {
let stmt = Stmt::Expr(binary(
literal("5"),
literal(5f64),
Percent,
group(binary(literal("3"), Plus, unary(Percent, literal("2")))),
group(binary(literal(3f64), Plus, unary(Percent, literal(2f64)))),
));
assert!(cmp(interpret(stmt).unwrap().unwrap(), 1.94f64));
@ -403,8 +400,8 @@ mod tests {
#[test]
fn test_unary() {
let neg = Stmt::Expr(unary(Minus, literal("1")));
let fact = Stmt::Expr(unary(Exclamation, literal("5")));
let neg = Stmt::Expr(unary(Minus, literal(1f64)));
let fact = Stmt::Expr(unary(Exclamation, literal(5f64)));
assert_eq!(interpret(neg).unwrap().unwrap().to_f64(), -1f64);
assert_eq!(interpret(fact).unwrap().unwrap().to_f64(), 120f64);
@ -412,9 +409,9 @@ mod tests {
#[test]
fn test_angle_units() {
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 implicit = Stmt::Expr(fn_call("sin", vec![*literal("1")]));
let rad_explicit = Stmt::Expr(fn_call("sin", vec![*unit("rad", literal(1f64))]));
let deg_explicit = Stmt::Expr(fn_call("sin", vec![*unit("deg", literal(1f64))]));
let implicit = Stmt::Expr(fn_call("sin", vec![*literal(1f64)]));
assert!(cmp(interpret(rad_explicit).unwrap().unwrap(), 0.84147098));
assert!(cmp(interpret(deg_explicit).unwrap().unwrap(), 0.01745240));
@ -449,7 +446,7 @@ mod tests {
// Prepare by inserting a variable declaration in the symbol table.
let mut symbol_table = SymbolTable::new();
symbol_table.insert(var_decl("x", literal("1")));
symbol_table.insert(var_decl("x", literal(1f64)));
let mut context = Context::new(&mut symbol_table, "rad", PRECISION);
assert_eq!(
@ -470,7 +467,7 @@ mod tests {
#[test]
fn test_var_decl() {
let stmt = var_decl("x", literal("1"));
let stmt = var_decl("x", literal(1f64));
let mut symbol_table = SymbolTable::new();
Context::new(&mut symbol_table, "rad", PRECISION)
.interpret(vec![stmt])
@ -481,14 +478,14 @@ mod tests {
#[test]
fn test_fn() {
let stmt = Stmt::Expr(fn_call("f", vec![*literal("1")]));
let stmt = Stmt::Expr(fn_call("f", vec![*literal(1f64)]));
// Prepare by inserting a variable declaration in the symbol table.
let mut symbol_table = SymbolTable::new();
symbol_table.insert(fn_decl(
"f",
vec![String::from("x")],
binary(var("x"), TokenKind::Plus, literal("2")),
binary(var("x"), TokenKind::Plus, literal(2f64)),
));
let mut context = Context::new(&mut symbol_table, "rad", PRECISION);
@ -500,7 +497,7 @@ mod tests {
#[test]
fn test_undefined_fn() {
let stmt = Stmt::Expr(fn_call("f", vec![*literal("1")]));
let stmt = Stmt::Expr(fn_call("f", vec![*literal(1f64)]));
assert_eq!(
interpret(stmt),
@ -508,15 +505,15 @@ mod tests {
);
}
#[test_case("1", "2", 9f64)]
#[test_case("1.2", "2.3", 9f64)]
fn test_sum_fn(start: &str, to: &str, result: f64) {
#[test_case(1f64, 2f64, 9f64)]
#[test_case(1.2f64, 2.3f64, 9f64)]
fn test_sum_fn(start: f64, to: f64, result: f64) {
let stmt = Stmt::Expr(fn_call(
"sum",
vec![
*literal(start),
*literal(to),
*binary(var("n"), TokenKind::Plus, literal("3")),
*binary(var("n"), TokenKind::Plus, literal(3f64)),
],
));

View File

@ -86,7 +86,7 @@ fn invert_binary(
symbol_table,
left,
&TokenKind::Plus,
&multiply_into(&Expr::Literal(String::from("-1")), inside_group)?,
&multiply_into(&Expr::Literal(-1f64), inside_group)?,
);
}
@ -231,7 +231,7 @@ fn invert_fn_call(
Expr::Binary(
Box::new(target_expr),
TokenKind::Power,
Box::new(Expr::Literal(String::from("2"))),
Box::new(Expr::Literal(2f64)),
),
arguments[0].clone(),
));
@ -361,49 +361,49 @@ mod tests {
#[test]
fn test_binary() {
let ladd = binary(decl_unit(), Plus, literal("1"));
let lsub = binary(decl_unit(), Minus, literal("1"));
let lmul = binary(decl_unit(), Star, literal("1"));
let ldiv = binary(decl_unit(), Slash, literal("1"));
let ladd = binary(decl_unit(), Plus, literal(1f64));
let lsub = binary(decl_unit(), Minus, literal(1f64));
let lmul = binary(decl_unit(), Star, literal(1f64));
let ldiv = binary(decl_unit(), Slash, literal(1f64));
let radd = binary(literal("1"), Plus, decl_unit());
let rsub = binary(literal("1"), Minus, decl_unit());
let rmul = binary(literal("1"), Star, decl_unit());
let rdiv = binary(literal("1"), Slash, decl_unit());
let radd = binary(literal(1f64), Plus, decl_unit());
let rsub = binary(literal(1f64), Minus, decl_unit());
let rmul = binary(literal(1f64), Star, decl_unit());
let rdiv = binary(literal(1f64), Slash, decl_unit());
let mut symbol_table = SymbolTable::new();
assert_eq!(
ladd.invert(&mut symbol_table).unwrap(),
*binary(decl_unit(), Minus, literal("1"))
*binary(decl_unit(), Minus, literal(1f64))
);
assert_eq!(
lsub.invert(&mut symbol_table).unwrap(),
*binary(decl_unit(), Plus, literal("1"))
*binary(decl_unit(), Plus, literal(1f64))
);
assert_eq!(
lmul.invert(&mut symbol_table).unwrap(),
*binary(decl_unit(), Slash, literal("1"))
*binary(decl_unit(), Slash, literal(1f64))
);
assert_eq!(
ldiv.invert(&mut symbol_table).unwrap(),
*binary(decl_unit(), Star, literal("1"))
*binary(decl_unit(), Star, literal(1f64))
);
assert_eq!(
radd.invert(&mut symbol_table).unwrap(),
*binary(decl_unit(), Minus, literal("1"))
*binary(decl_unit(), Minus, literal(1f64))
);
assert_eq!(
rsub.invert(&mut symbol_table).unwrap(),
*unary(Minus, binary(decl_unit(), Plus, literal("1")))
*unary(Minus, binary(decl_unit(), Plus, literal(1f64)))
);
assert_eq!(
rmul.invert(&mut symbol_table).unwrap(),
*binary(decl_unit(), Slash, literal("1"))
*binary(decl_unit(), Slash, literal(1f64))
);
assert_eq!(
rdiv.invert(&mut symbol_table).unwrap(),
*binary(decl_unit(), Star, literal("1"))
*binary(decl_unit(), Star, literal(1f64))
);
}
@ -417,34 +417,34 @@ mod tests {
#[test]
fn test_fn_call() {
let call_with_literal = binary(fn_call("f", vec![*literal("2")]), Plus, decl_unit());
let call_with_literal = binary(fn_call("f", vec![*literal(2f64)]), Plus, decl_unit());
let call_with_decl_unit = fn_call("f", vec![*decl_unit()]);
let call_with_decl_unit_and_literal =
fn_call("f", vec![*binary(decl_unit(), Plus, literal("2"))]);
fn_call("f", vec![*binary(decl_unit(), Plus, literal(2f64))]);
let decl = fn_decl(
"f",
vec![String::from("x")],
binary(var("x"), Plus, literal("1")),
binary(var("x"), Plus, literal(1f64)),
);
let mut symbol_table = SymbolTable::new();
symbol_table.insert(decl);
assert_eq!(
call_with_literal.invert(&mut symbol_table).unwrap(),
*binary(decl_unit(), Minus, fn_call("f", vec![*literal("2")])),
*binary(decl_unit(), Minus, fn_call("f", vec![*literal(2f64)])),
);
assert_eq!(
call_with_decl_unit.invert(&mut symbol_table).unwrap(),
*binary(decl_unit(), Minus, literal("1"))
*binary(decl_unit(), Minus, literal(1f64))
);
assert_eq!(
call_with_decl_unit_and_literal
.invert(&mut symbol_table)
.unwrap(),
*binary(
binary(decl_unit(), Minus, literal("1")),
binary(decl_unit(), Minus, literal(1f64)),
Minus,
literal("2")
literal(2f64)
)
);
}
@ -452,84 +452,100 @@ mod tests {
#[test]
fn test_group() {
let group_x = binary(
group(binary(decl_unit(), Plus, literal("3"))),
group(binary(decl_unit(), Plus, literal(3f64))),
Star,
literal("2"),
literal(2f64),
);
let group_unary_minus = binary(
literal("2"),
literal(2f64),
Minus,
group(binary(decl_unit(), Plus, literal("3"))),
group(binary(decl_unit(), Plus, literal(3f64))),
);
let x_group_add = binary(
literal("2"),
literal(2f64),
Star,
group(binary(decl_unit(), Plus, literal("3"))),
group(binary(decl_unit(), Plus, literal(3f64))),
);
let x_group_sub = binary(
literal("2"),
literal(2f64),
Star,
group(binary(decl_unit(), Minus, literal("3"))),
group(binary(decl_unit(), Minus, literal(3f64))),
);
let x_group_mul = binary(
literal("2"),
literal(2f64),
Star,
group(binary(decl_unit(), Star, literal("3"))),
group(binary(decl_unit(), Star, literal(3f64))),
);
let x_group_div = binary(
literal("2"),
literal(2f64),
Star,
group(binary(decl_unit(), Slash, literal("3"))),
group(binary(decl_unit(), Slash, literal(3f64))),
);
let mut symbol_table = SymbolTable::new();
assert_eq!(
group_x.invert(&mut symbol_table).unwrap(),
*binary(
binary(decl_unit(), Minus, binary(literal("2"), Star, literal("3"))),
binary(
decl_unit(),
Minus,
binary(literal(2f64), Star, literal(3f64))
),
Slash,
literal("2")
literal(2f64)
)
);
assert_eq!(
group_unary_minus.invert(&mut symbol_table).unwrap(),
*binary(
binary(
binary(decl_unit(), Minus, literal("2")),
binary(decl_unit(), Minus, literal(2f64)),
Minus,
binary(literal("-1"), Star, literal("3"))
binary(literal(-1f64), Star, literal(3f64))
),
Slash,
literal("-1")
literal(-1f64)
)
);
assert_eq!(
x_group_add.invert(&mut symbol_table).unwrap(),
*binary(
binary(decl_unit(), Minus, binary(literal("2"), Star, literal("3"))),
binary(
decl_unit(),
Minus,
binary(literal(2f64), Star, literal(3f64))
),
Slash,
literal("2")
literal(2f64)
)
);
assert_eq!(
x_group_sub.invert(&mut symbol_table).unwrap(),
*binary(
binary(decl_unit(), Plus, binary(literal("2"), Star, literal("3"))),
binary(
decl_unit(),
Plus,
binary(literal(2f64), Star, literal(3f64))
),
Slash,
literal("2")
literal(2f64)
)
);
assert_eq!(
x_group_mul.invert(&mut symbol_table).unwrap(),
*binary(
binary(decl_unit(), Slash, literal("3")),
binary(decl_unit(), Slash, literal(3f64)),
Slash,
literal("2")
literal(2f64)
)
);
assert_eq!(
x_group_div.invert(&mut symbol_table).unwrap(),
*binary(binary(decl_unit(), Star, literal("3")), Slash, literal("2"))
*binary(
binary(decl_unit(), Star, literal(3f64)),
Slash,
literal(2f64)
)
);
}

View File

@ -95,7 +95,7 @@ impl KalkNum {
) -> Option<KalkNum> {
let result = crate::interpreter::convert_unit(
context,
&Expr::Literal(self.value.to_string()), // TODO: Store as f64, jeez...
&Expr::Literal(self.value.to_f64()),
&self.unit,
to_unit,
);

View File

@ -339,7 +339,7 @@ fn parse_primary(context: &mut Context) -> Result<Expr, CalcError> {
TokenKind::OpenParenthesis => parse_group(context)?,
TokenKind::Pipe | TokenKind::OpenCeil | TokenKind::OpenFloor => parse_group_fn(context)?,
TokenKind::Identifier => parse_identifier(context)?,
TokenKind::Literal => Expr::Literal(advance(context).value.clone()),
TokenKind::Literal => Expr::Literal(advance(context).value.parse::<f64>().unwrap()),
_ => return Err(CalcError::UnableToParseExpression),
};
@ -375,7 +375,7 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
if match_token(context, TokenKind::Literal) {
// If there is a function with this name, parse it as a function, with the next token as the argument.
if context.symbol_table.contains_fn(&identifier.value) {
let parameter = Expr::Literal(advance(context).value.clone());
let parameter = Expr::Literal(advance(context).value.parse::<f64>().unwrap());
return Ok(Expr::FnCall(identifier.value, vec![parameter]));
}
}
@ -508,15 +508,15 @@ mod tests {
assert_eq!(
parse(tokens).unwrap(),
Stmt::Expr(binary(
literal("1"),
literal(1f64),
Plus,
binary(
literal("2"),
literal(2f64),
Star,
group(binary(
literal("3"),
literal(3f64),
Minus,
binary(literal("4"), Slash, literal("5"))
binary(literal(4f64), Slash, literal(5f64))
))
)
))
@ -542,16 +542,16 @@ mod tests {
parse(tokens).unwrap(),
Stmt::Expr(binary(
binary(
literal("1"),
literal(1f64),
Star,
binary(
literal("2"),
literal(2f64),
Power,
binary(literal("3"), Power, literal("4")),
binary(literal(3f64), Power, literal(4f64)),
),
),
Plus,
literal("5")
literal(5f64)
)),
);
}
@ -571,9 +571,9 @@ mod tests {
assert_eq!(
parse(tokens).unwrap(),
Stmt::Expr(binary(
binary(literal("1"), Percent, literal("1"),),
binary(literal(1f64), Percent, literal(1f64)),
Plus,
unary(Percent, literal("5"))
unary(Percent, literal(5f64))
))
);
}
@ -589,7 +589,7 @@ mod tests {
assert_eq!(
parse_with_context(&mut context, tokens).unwrap(),
Stmt::Expr(unit("a", literal("1")))
Stmt::Expr(unit("a", literal(1f64)))
);
}
@ -606,7 +606,10 @@ mod tests {
assert_eq!(
parse(tokens).unwrap(),
Stmt::VarDecl(String::from("x"), binary(literal("1"), Plus, literal("2")))
Stmt::VarDecl(
String::from("x"),
binary(literal(1f64), Plus, literal(2f64))
)
);
}
@ -629,7 +632,7 @@ mod tests {
Stmt::FnDecl(
String::from("f"),
vec![String::from("x")],
binary(literal("1"), Plus, literal("2"))
binary(literal(1f64), Plus, literal(2f64))
)
);
}
@ -654,7 +657,7 @@ mod tests {
context.symbol_table.set(Stmt::FnDecl(
String::from("f"),
vec![String::from("x")],
literal("1"),
literal(1f64),
));
assert_eq!(
@ -662,10 +665,10 @@ mod tests {
Stmt::Expr(binary(
Box::new(Expr::FnCall(
String::from("f"),
vec![*binary(literal("1"), Plus, literal("2"))]
vec![*binary(literal(1f64), Plus, literal(2f64))]
)),
Plus,
literal("3")
literal(3f64)
))
);
}

View File

@ -9,15 +9,15 @@ use FuncType::*;
pub const INIT: &'static str = "unit deg = (rad*180)/pi";
lazy_static! {
pub static ref CONSTANTS: HashMap<&'static str, &'static str> = {
pub static ref CONSTANTS: HashMap<&'static str, f64> = {
let mut m = HashMap::new();
m.insert("pi", "3.14159265");
m.insert("π", "3.14159265");
m.insert("e", "2.71828182");
m.insert("tau", "6.28318530");
m.insert("τ", "6.28318530");
m.insert("phi", "1.61803398");
m.insert("ϕ", "1.61803398");
m.insert("pi", 3.14159265);
m.insert("π", 3.14159265);
m.insert("e", 2.71828182);
m.insert("tau", 6.28318530);
m.insert("τ", 6.28318530);
m.insert("phi", 1.61803398);
m.insert("ϕ", 1.61803398);
m
};
pub static ref UNARY_FUNCS: HashMap<&'static str, (UnaryFuncInfo, &'static str)> = {
@ -154,9 +154,14 @@ fn to_angle_unit(context: &mut interpreter::Context, x: Float, angle_unit: &str)
match angle_unit {
"rad" => x,
_ => {
interpreter::convert_unit(context, &Expr::Literal(x.to_string()), "rad", angle_unit)
.unwrap()
.value
interpreter::convert_unit(
context,
&Expr::Literal(x.to_f64_round(rug::float::Round::Nearest)),
"rad",
angle_unit,
)
.unwrap()
.value
}
}
}
@ -165,9 +170,14 @@ fn from_angle_unit(context: &mut interpreter::Context, x: Float, angle_unit: &st
match angle_unit {
"rad" => x,
_ => {
interpreter::convert_unit(context, &Expr::Literal(x.to_string()), angle_unit, "rad")
.unwrap()
.value
interpreter::convert_unit(
context,
&Expr::Literal(x.to_f64_round(rug::float::Round::Nearest)),
angle_unit,
"rad",
)
.unwrap()
.value
}
}
}

View File

@ -12,8 +12,8 @@ pub fn token(kind: TokenKind, value: &str) -> Token {
}
}
pub fn literal(value: &str) -> Box<Expr> {
Box::new(Expr::Literal(value.into()))
pub fn literal(value: f64) -> Box<Expr> {
Box::new(Expr::Literal(value))
}
pub fn var(identifier: &str) -> Box<Expr> {