mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-06-25 20:21:44 +02:00
Integrated the angle unit system with then new dynamic unit system.
This commit is contained in:
parent
1d19f40e9f
commit
83668bbb84
@ -1,7 +1,6 @@
|
||||
use crate::ast::{Expr, Stmt};
|
||||
use crate::lexer::TokenKind;
|
||||
use crate::parser::CalcError;
|
||||
use crate::parser::Unit;
|
||||
use crate::parser::DECL_UNIT;
|
||||
use crate::prelude;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
@ -10,14 +9,14 @@ use rug::Float;
|
||||
|
||||
pub struct Context<'a> {
|
||||
symbol_table: &'a mut SymbolTable,
|
||||
angle_unit: Unit,
|
||||
angle_unit: String,
|
||||
precision: u32,
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
pub fn new(symbol_table: &'a mut SymbolTable, angle_unit: &Unit, precision: u32) -> Self {
|
||||
pub fn new(symbol_table: &'a mut SymbolTable, angle_unit: &str, precision: u32) -> Self {
|
||||
Context {
|
||||
angle_unit: angle_unit.clone(),
|
||||
angle_unit: angle_unit.into(),
|
||||
symbol_table,
|
||||
precision,
|
||||
}
|
||||
@ -68,7 +67,7 @@ fn eval_expr(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
|
||||
match expr {
|
||||
Expr::Binary(left, op, right) => eval_binary_expr(context, &left, op, &right),
|
||||
Expr::Unary(op, expr) => eval_unary_expr(context, op, expr),
|
||||
Expr::Unit(_, expr) => eval_unit_expr(context, expr),
|
||||
Expr::Unit(identifier, expr) => eval_unit_expr(context, identifier, expr),
|
||||
Expr::Var(identifier) => eval_var_expr(context, identifier),
|
||||
Expr::Literal(value) => eval_literal_expr(context, value),
|
||||
Expr::Group(expr) => eval_group_expr(context, &expr),
|
||||
@ -118,11 +117,20 @@ fn eval_unary_expr(context: &mut Context, op: &TokenKind, expr: &Expr) -> Result
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_unit_expr(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
|
||||
fn eval_unit_expr(
|
||||
context: &mut Context,
|
||||
identifier: &str,
|
||||
expr: &Expr,
|
||||
) -> Result<Float, CalcError> {
|
||||
let angle_unit = &context.angle_unit.clone();
|
||||
if (identifier == "rad" || identifier == "deg") && angle_unit != identifier {
|
||||
return convert_unit(context, expr, identifier, angle_unit);
|
||||
}
|
||||
|
||||
eval_expr(context, expr)
|
||||
}
|
||||
|
||||
fn convert_unit(
|
||||
pub fn convert_unit(
|
||||
context: &mut Context,
|
||||
expr: &Expr,
|
||||
from_unit: &str,
|
||||
@ -175,12 +183,12 @@ fn eval_fn_call_expr(
|
||||
let prelude_func = match expressions.len() {
|
||||
1 => {
|
||||
let x = eval_expr(context, &expressions[0])?;
|
||||
prelude::call_unary_func(identifier, x, &context.angle_unit)
|
||||
prelude::call_unary_func(context, identifier, x, &context.angle_unit.clone())
|
||||
}
|
||||
2 => {
|
||||
let x = eval_expr(context, &expressions[0])?;
|
||||
let y = eval_expr(context, &expressions[1])?;
|
||||
prelude::call_binary_func(identifier, x, y, &context.angle_unit)
|
||||
prelude::call_binary_func(context, identifier, x, y, &context.angle_unit.clone())
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
@ -259,10 +267,16 @@ mod tests {
|
||||
|
||||
fn interpret(stmt: Stmt) -> Result<Option<Float>, CalcError> {
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION);
|
||||
let mut context = Context::new(&mut symbol_table, "rad", PRECISION);
|
||||
|
||||
context.interpret(vec![stmt])
|
||||
}
|
||||
|
||||
fn cmp(x: Float, y: f64) -> bool {
|
||||
println!("{} = {}", x.to_f64(), y);
|
||||
(x.to_f64() - y).abs() < 0.0001
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_literal() {
|
||||
let stmt = Stmt::Expr(literal("1"));
|
||||
@ -292,26 +306,38 @@ mod tests {
|
||||
fn test_unary() {
|
||||
let neg = Stmt::Expr(unary(Minus, literal("1")));
|
||||
let fact = Stmt::Expr(unary(Exclamation, literal("5")));
|
||||
let fact_dec = Stmt::Expr(unary(Exclamation, literal("5.2")));
|
||||
|
||||
assert_eq!(interpret(neg).unwrap().unwrap(), -1);
|
||||
assert_eq!(interpret(fact).unwrap().unwrap(), 120);
|
||||
|
||||
let fact_dec_result = interpret(fact_dec).unwrap().unwrap();
|
||||
assert!(fact_dec_result > 169.406 && fact_dec_result < 169.407);
|
||||
}
|
||||
|
||||
/*#[test]
|
||||
fn test_unit() {
|
||||
let rad = Stmt::Expr(Box::new(Expr::Unit(literal("1"), Rad)));
|
||||
let deg = Stmt::Expr(Box::new(Expr::Unit(literal("1"), Deg)));
|
||||
#[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")]));
|
||||
|
||||
assert_eq!(interpret(rad).unwrap().unwrap(), 1);
|
||||
assert!(
|
||||
(interpret(deg).unwrap().unwrap() - Float::with_val(PRECISION, 0.017456)).abs()
|
||||
< Float::with_val(PRECISION, 0.0001)
|
||||
);
|
||||
}*/
|
||||
assert!(cmp(interpret(rad_explicit).unwrap().unwrap(), 0.84147098));
|
||||
assert!(cmp(interpret(deg_explicit).unwrap().unwrap(), 0.01745240));
|
||||
|
||||
// TODO: Get this to work.
|
||||
/*let mut rad_symbol_table = SymbolTable::new();
|
||||
let mut deg_symbol_table = SymbolTable::new();
|
||||
let mut rad_context = Context::new(&mut rad_symbol_table, "rad", PRECISION);
|
||||
let mut deg_context = Context::new(&mut deg_symbol_table, "deg", PRECISION);
|
||||
|
||||
assert!(cmp(
|
||||
rad_context
|
||||
.interpret(vec![implicit.clone()])
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
0.84147098
|
||||
));
|
||||
assert!(cmp(
|
||||
deg_context.interpret(vec![implicit]).unwrap().unwrap(),
|
||||
0.01745240
|
||||
));*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_var() {
|
||||
@ -321,7 +347,7 @@ mod tests {
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
symbol_table.insert(var_decl("x", literal("1")));
|
||||
|
||||
let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION);
|
||||
let mut context = Context::new(&mut symbol_table, "rad", PRECISION);
|
||||
assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 1);
|
||||
}
|
||||
|
||||
@ -339,7 +365,7 @@ mod tests {
|
||||
fn test_var_decl() {
|
||||
let stmt = var_decl("x", literal("1"));
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
Context::new(&mut symbol_table, &Unit::Radians, PRECISION)
|
||||
Context::new(&mut symbol_table, "rad", PRECISION)
|
||||
.interpret(vec![stmt])
|
||||
.unwrap();
|
||||
|
||||
@ -358,7 +384,7 @@ mod tests {
|
||||
binary(var("x"), TokenKind::Plus, literal("2")),
|
||||
));
|
||||
|
||||
let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION);
|
||||
let mut context = Context::new(&mut symbol_table, "rad", PRECISION);
|
||||
assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 3);
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,7 @@ pub enum TokenKind {
|
||||
Exclamation,
|
||||
|
||||
UnitKeyword,
|
||||
To,
|
||||
Deg,
|
||||
Rad,
|
||||
ToKeyword,
|
||||
|
||||
Pipe,
|
||||
OpenCeil,
|
||||
@ -170,10 +168,8 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
|
||||
let kind = match value.as_ref() {
|
||||
"deg" | "°" => TokenKind::Deg,
|
||||
"rad" => TokenKind::Rad,
|
||||
"unit" => TokenKind::UnitKeyword,
|
||||
"to" => TokenKind::To,
|
||||
"to" => TokenKind::ToKeyword,
|
||||
_ => TokenKind::Identifier,
|
||||
};
|
||||
|
||||
|
@ -7,6 +7,7 @@ use crate::{
|
||||
use rug::Float;
|
||||
|
||||
pub const DECL_UNIT: &'static str = ".u";
|
||||
pub const DEFAULT_ANGLE_UNIT: &'static str = "rad";
|
||||
|
||||
/// Struct containing the current state of the parser. It stores user-defined functions and variables.
|
||||
/// # Examples
|
||||
@ -20,7 +21,7 @@ pub struct Context {
|
||||
tokens: Vec<Token>,
|
||||
pos: usize,
|
||||
symbol_table: SymbolTable,
|
||||
angle_unit: Unit,
|
||||
angle_unit: String,
|
||||
/// This is true whenever the parser is currently parsing a unit declaration.
|
||||
/// It is necessary to keep track of this in order to know when to find (figure out) units that haven't been defined yet.
|
||||
/// Unit names are instead treated as variables.
|
||||
@ -32,18 +33,22 @@ pub struct Context {
|
||||
|
||||
impl Context {
|
||||
pub fn new() -> Self {
|
||||
Context {
|
||||
let mut context = Self {
|
||||
tokens: Vec::new(),
|
||||
pos: 0,
|
||||
symbol_table: SymbolTable::new(),
|
||||
angle_unit: Unit::Radians,
|
||||
angle_unit: DEFAULT_ANGLE_UNIT.into(),
|
||||
parsing_unit_decl: false,
|
||||
unit_decl_base_unit: None,
|
||||
}
|
||||
};
|
||||
|
||||
parse(&mut context, crate::prelude::INIT).unwrap();
|
||||
|
||||
context
|
||||
}
|
||||
|
||||
pub fn set_angle_unit(mut self, unit: Unit) -> Self {
|
||||
self.angle_unit = unit;
|
||||
pub fn set_angle_unit(mut self, unit: &str) -> Self {
|
||||
self.angle_unit = unit.into();
|
||||
|
||||
self
|
||||
}
|
||||
@ -55,13 +60,6 @@ impl Default for Context {
|
||||
}
|
||||
}
|
||||
|
||||
/// Mathematical unit used in calculations.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Unit {
|
||||
Radians,
|
||||
Degrees,
|
||||
}
|
||||
|
||||
/// Error that occured during parsing or evaluation.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum CalcError {
|
||||
|
@ -1,6 +1,10 @@
|
||||
use crate::ast::Expr;
|
||||
use crate::interpreter;
|
||||
use rug::Float;
|
||||
use FuncType::*;
|
||||
|
||||
pub const INIT: &'static str = "unit deg = (rad*180)/pi";
|
||||
|
||||
pub const CONSTANTS: phf::Map<&'static str, &'static str> = phf::phf_map! {
|
||||
"pi" => "3.14159265",
|
||||
"π" => "3.14159265",
|
||||
@ -11,7 +15,6 @@ pub const CONSTANTS: phf::Map<&'static str, &'static str> = phf::phf_map! {
|
||||
"ϕ" => "1.61803398",
|
||||
};
|
||||
|
||||
use crate::parser::Unit;
|
||||
use funcs::*;
|
||||
pub const UNARY_FUNCS: phf::Map<&'static str, UnaryFuncInfo> = phf::phf_map! {
|
||||
"cos" => UnaryFuncInfo(cos, Trig),
|
||||
@ -75,57 +78,76 @@ pub struct UnaryFuncInfo(fn(Float) -> Float, FuncType);
|
||||
pub struct BinaryFuncInfo(fn(Float, Float) -> Float, FuncType);
|
||||
|
||||
impl UnaryFuncInfo {
|
||||
fn call(&self, x: Float, angle_unit: &Unit) -> Float {
|
||||
fn call(&self, context: &mut interpreter::Context, x: Float, angle_unit: &str) -> Float {
|
||||
let func = self.0;
|
||||
match self.1 {
|
||||
FuncType::Trig => func(from_angle_unit(x, angle_unit)),
|
||||
FuncType::InverseTrig => to_angle_unit(func(x), angle_unit),
|
||||
FuncType::Trig => func(from_angle_unit(context, x, angle_unit)),
|
||||
FuncType::InverseTrig => to_angle_unit(context, func(x), angle_unit),
|
||||
FuncType::Other => func(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BinaryFuncInfo {
|
||||
fn call(&self, x: Float, y: Float, angle_unit: &Unit) -> Float {
|
||||
fn call(
|
||||
&self,
|
||||
context: &mut interpreter::Context,
|
||||
x: Float,
|
||||
y: Float,
|
||||
angle_unit: &str,
|
||||
) -> Float {
|
||||
let func = self.0;
|
||||
match self.1 {
|
||||
FuncType::Trig => func(
|
||||
from_angle_unit(x, angle_unit),
|
||||
from_angle_unit(y, angle_unit),
|
||||
from_angle_unit(context, x, angle_unit),
|
||||
from_angle_unit(context, y, angle_unit),
|
||||
),
|
||||
FuncType::InverseTrig => to_angle_unit(func(x, y), angle_unit),
|
||||
FuncType::InverseTrig => to_angle_unit(context, func(x, y), angle_unit),
|
||||
FuncType::Other => func(x, y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_unary_func(name: &str, x: Float, angle_unit: &Unit) -> Option<Float> {
|
||||
pub fn call_unary_func(
|
||||
context: &mut interpreter::Context,
|
||||
name: &str,
|
||||
x: Float,
|
||||
angle_unit: &str,
|
||||
) -> Option<Float> {
|
||||
if let Some(func_info) = UNARY_FUNCS.get(name) {
|
||||
Some(func_info.call(x, &angle_unit))
|
||||
Some(func_info.call(context, x, &angle_unit))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_binary_func(name: &str, x: Float, y: Float, angle_unit: &Unit) -> Option<Float> {
|
||||
pub fn call_binary_func(
|
||||
context: &mut interpreter::Context,
|
||||
name: &str,
|
||||
x: Float,
|
||||
y: Float,
|
||||
angle_unit: &str,
|
||||
) -> Option<Float> {
|
||||
if let Some(func_info) = BINARY_FUNCS.get(name) {
|
||||
Some(func_info.call(x, y, angle_unit))
|
||||
Some(func_info.call(context, x, y, angle_unit))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn to_angle_unit(x: Float, angle_unit: &Unit) -> Float {
|
||||
fn to_angle_unit(context: &mut interpreter::Context, x: Float, angle_unit: &str) -> Float {
|
||||
match angle_unit {
|
||||
Unit::Radians => x,
|
||||
Unit::Degrees => special_funcs::to_degrees(x),
|
||||
"rad" => x,
|
||||
_ => interpreter::convert_unit(context, &Expr::Literal(x.to_string()), "rad", angle_unit)
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_angle_unit(x: Float, angle_unit: &Unit) -> Float {
|
||||
fn from_angle_unit(context: &mut interpreter::Context, x: Float, angle_unit: &str) -> Float {
|
||||
match angle_unit {
|
||||
Unit::Radians => x,
|
||||
Unit::Degrees => special_funcs::to_radians(x),
|
||||
"rad" => x,
|
||||
_ => interpreter::convert_unit(context, &Expr::Literal(x.to_string()), angle_unit, "rad")
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,14 +157,6 @@ pub mod special_funcs {
|
||||
pub fn factorial(x: Float) -> Float {
|
||||
((x + 1) as Float).gamma()
|
||||
}
|
||||
|
||||
pub fn to_degrees(x: Float) -> Float {
|
||||
Float::with_val(53, x.to_f64().to_degrees())
|
||||
}
|
||||
|
||||
pub fn to_radians(x: Float) -> Float {
|
||||
Float::with_val(53, x.to_f64().to_radians())
|
||||
}
|
||||
}
|
||||
|
||||
mod funcs {
|
||||
|
@ -36,6 +36,10 @@ pub fn group(expr: Box<Expr>) -> Box<Expr> {
|
||||
Box::new(Expr::Group(expr))
|
||||
}
|
||||
|
||||
pub fn unit(identifier: &str, expr: Box<Expr>) -> Box<Expr> {
|
||||
Box::new(Expr::Unit(identifier.into(), expr))
|
||||
}
|
||||
|
||||
pub fn var_decl(identifier: &str, value: Box<Expr>) -> Stmt {
|
||||
Stmt::VarDecl(identifier.into(), value)
|
||||
}
|
||||
@ -43,3 +47,7 @@ pub fn var_decl(identifier: &str, value: Box<Expr>) -> Stmt {
|
||||
pub fn fn_decl(identifier: &str, parameters: Vec<String>, value: Box<Expr>) -> Stmt {
|
||||
Stmt::FnDecl(identifier.into(), parameters, value)
|
||||
}
|
||||
|
||||
pub fn unit_decl(unit: &str, base_unit: &str, expr: Box<Expr>) -> Stmt {
|
||||
Stmt::UnitDecl(unit.into(), base_unit.into(), expr)
|
||||
}
|
||||
|
@ -2,13 +2,12 @@ mod output;
|
||||
mod repl;
|
||||
|
||||
use kalk::parser;
|
||||
use kalk::parser::Unit;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
fn main() {
|
||||
let mut parser_context = parser::Context::new().set_angle_unit(get_angle_unit());
|
||||
let mut parser_context = parser::Context::new().set_angle_unit(&get_angle_unit());
|
||||
|
||||
// Command line argument input, execute it and exit.
|
||||
let mut args = env::args().skip(1);
|
||||
@ -64,16 +63,10 @@ kalk [OPTIONS] [INPUT]
|
||||
}
|
||||
}
|
||||
|
||||
fn get_angle_unit() -> Unit {
|
||||
fn get_angle_unit() -> String {
|
||||
if let Ok(angle_unit_var) = env::var("ANGLE_UNIT") {
|
||||
match angle_unit_var.as_ref() {
|
||||
"radians" => Unit::Radians,
|
||||
"degrees" => Unit::Degrees,
|
||||
_ => {
|
||||
panic!("Unexpected angle unit: {}.", angle_unit_var);
|
||||
}
|
||||
}
|
||||
angle_unit_var
|
||||
} else {
|
||||
Unit::Radians
|
||||
String::from("rad")
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user