mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-06-24 11:41:28 +02:00
added percentage unit and modulo
This commit is contained in:
parent
4f655033b9
commit
dd1b4d723f
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -92,7 +92,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kalk"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"phf",
|
||||
@ -103,7 +103,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kalk_cli"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"kalk",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "kalk"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
authors = ["PaddiM8"]
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
|
@ -113,7 +113,7 @@ fn eval_binary_expr(
|
||||
}
|
||||
|
||||
let (left, left_unit) = eval_expr(context, left_expr, "")?;
|
||||
let (right, _) = if left_unit.len() > 0 {
|
||||
let (mut right, _) = if left_unit.len() > 0 {
|
||||
let (_, right_unit) = eval_expr(context, right_expr, "")?; // TODO: Avoid evaluating this twice.
|
||||
|
||||
if right_unit.len() > 0 {
|
||||
@ -131,12 +131,17 @@ fn eval_binary_expr(
|
||||
unit.into()
|
||||
};
|
||||
|
||||
if let Expr::Unary(TokenKind::Percent, _) = right_expr {
|
||||
right *= left.clone();
|
||||
}
|
||||
|
||||
Ok((
|
||||
match op {
|
||||
TokenKind::Plus => left + right,
|
||||
TokenKind::Minus => left - right,
|
||||
TokenKind::Star => left * right,
|
||||
TokenKind::Slash => left / right,
|
||||
TokenKind::Percent => left % right,
|
||||
TokenKind::Power => left.pow(right),
|
||||
_ => Float::with_val(1, 1),
|
||||
},
|
||||
@ -154,6 +159,7 @@ fn eval_unary_expr(
|
||||
|
||||
match op {
|
||||
TokenKind::Minus => Ok((-expr_value, unit)),
|
||||
TokenKind::Percent => Ok((expr_value * 0.01, unit)),
|
||||
TokenKind::Exclamation => Ok((
|
||||
Float::with_val(
|
||||
context.precision,
|
||||
@ -409,6 +415,17 @@ mod tests {
|
||||
assert_eq!(interpret(pow).unwrap().unwrap(), 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_percent() {
|
||||
let stmt = Stmt::Expr(binary(
|
||||
literal("5"),
|
||||
Percent,
|
||||
group(binary(literal("3"), Star, unary(Percent, literal("2")))),
|
||||
));
|
||||
|
||||
assert!(cmp(interpret(stmt).unwrap().unwrap(), 0.14));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unary() {
|
||||
let neg = Stmt::Expr(unary(Minus, literal("1")));
|
||||
|
@ -15,6 +15,7 @@ pub enum TokenKind {
|
||||
Power,
|
||||
Equals,
|
||||
Exclamation,
|
||||
Percent,
|
||||
|
||||
UnitKeyword,
|
||||
ToKeyword,
|
||||
@ -112,6 +113,7 @@ impl<'a> Lexer<'a> {
|
||||
'!' => build(TokenKind::Exclamation, "", span),
|
||||
',' => build(TokenKind::Comma, "", span),
|
||||
';' => build(TokenKind::Semicolon, "", span),
|
||||
'%' => build(TokenKind::Percent, "", span),
|
||||
_ => build(TokenKind::Unknown, "", span),
|
||||
};
|
||||
|
||||
@ -207,7 +209,7 @@ fn build(kind: TokenKind, value: &str, span: (usize, usize)) -> Token {
|
||||
|
||||
fn is_valid_identifier(c: Option<&char>) -> bool {
|
||||
if let Some(c) = c {
|
||||
regex::Regex::new(r"[^\s\n\r0-9\+-/\*\^!\(\)=\.,;|⌊⌋⌈⌉]")
|
||||
regex::Regex::new(r"[^\s\n\r0-9\+-/%\*\^!\(\)=\.,;|⌊⌋⌈⌉]")
|
||||
.unwrap()
|
||||
.is_match(&c.to_string())
|
||||
} else {
|
||||
@ -230,12 +232,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_token_kinds() {
|
||||
let tokens = Lexer::lex("+-*/^()|=!,");
|
||||
let tokens = Lexer::lex("+-*/%^()|=!,");
|
||||
let expected = vec![
|
||||
TokenKind::Plus,
|
||||
TokenKind::Minus,
|
||||
TokenKind::Star,
|
||||
TokenKind::Slash,
|
||||
TokenKind::Percent,
|
||||
TokenKind::Power,
|
||||
TokenKind::OpenParenthesis,
|
||||
TokenKind::ClosedParenthesis,
|
||||
|
@ -72,6 +72,7 @@ pub enum CalcError {
|
||||
UndefinedFn(String),
|
||||
UndefinedVar(String),
|
||||
UnableToInvert(String),
|
||||
UnableToParseExpression,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
@ -240,18 +241,41 @@ fn parse_sum(context: &mut Context) -> Result<Expr, CalcError> {
|
||||
fn parse_factor(context: &mut Context) -> Result<Expr, CalcError> {
|
||||
let mut left = parse_unit(context)?;
|
||||
|
||||
if let Expr::Unary(TokenKind::Percent, percent_left) = left.clone() {
|
||||
let try_parse = parse_factor(context);
|
||||
if !try_parse.is_err() {
|
||||
left = Expr::Binary(
|
||||
percent_left,
|
||||
TokenKind::Percent,
|
||||
Box::new(try_parse.unwrap()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
while match_token(context, TokenKind::Star)
|
||||
|| match_token(context, TokenKind::Slash)
|
||||
|| match_token(context, TokenKind::Percent)
|
||||
|| match_token(context, TokenKind::Identifier)
|
||||
|| match_token(context, TokenKind::Literal)
|
||||
{
|
||||
// If the next token is an identifier, assume it's multiplication. Eg. 3y
|
||||
// If the token is an identifier, assume it's multiplication. Eg. 3y
|
||||
let op = match peek(context).kind {
|
||||
TokenKind::Identifier | TokenKind::Literal => TokenKind::Star,
|
||||
_ => advance(context).kind.clone(),
|
||||
};
|
||||
|
||||
let right = parse_unit(context)?;
|
||||
let parse_next = parse_unit(context);
|
||||
let right = if let Ok(right) = parse_next {
|
||||
right
|
||||
/*} else if let Err(CalcError::UnableToParseExpression) = parse_next {
|
||||
// If it failed to parse further,
|
||||
// try to parse it as something else.
|
||||
// Eg. percent unary
|
||||
break;*/
|
||||
} else {
|
||||
return parse_next;
|
||||
};
|
||||
|
||||
left = Expr::Binary(Box::new(left), op, Box::new(right));
|
||||
}
|
||||
|
||||
@ -279,7 +303,12 @@ fn parse_unary(context: &mut Context) -> Result<Expr, CalcError> {
|
||||
return Ok(Expr::Unary(op, expr));
|
||||
}
|
||||
|
||||
Ok(parse_exponent(context)?)
|
||||
let expr = parse_exponent(context)?;
|
||||
if match_token(context, TokenKind::Percent) {
|
||||
Ok(Expr::Unary(advance(context).kind.clone(), Box::new(expr)))
|
||||
} else {
|
||||
Ok(expr)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_exponent(context: &mut Context) -> Result<Expr, CalcError> {
|
||||
@ -310,7 +339,8 @@ 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)?,
|
||||
_ => Expr::Literal(advance(context).value.clone()),
|
||||
TokenKind::Literal => Expr::Literal(advance(context).value.clone()),
|
||||
_ => return Err(CalcError::UnableToParseExpression),
|
||||
};
|
||||
|
||||
Ok(expr)
|
||||
@ -526,6 +556,28 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_percent() {
|
||||
let tokens = vec![
|
||||
token(Literal, "1"),
|
||||
token(Percent, ""),
|
||||
token(Literal, "1"),
|
||||
token(Plus, ""),
|
||||
token(Literal, "5"),
|
||||
token(Percent, ""),
|
||||
token(EOF, ""),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
parse(tokens).unwrap(),
|
||||
Stmt::Expr(binary(
|
||||
binary(literal("1"), Percent, literal("1"),),
|
||||
Plus,
|
||||
unary(Percent, literal("5"))
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unit() {
|
||||
let tokens = vec![token(Literal, "1"), token(Identifier, "a")];
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "kalk_cli"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
authors = ["PaddiM8"]
|
||||
edition = "2018"
|
||||
readme = "../README.md"
|
||||
@ -15,7 +15,7 @@ path = "src/main.rs"
|
||||
name = "kalk"
|
||||
|
||||
[dependencies]
|
||||
kalk = { path = "../kalk", version = "^0.2.2" }
|
||||
kalk = { path = "../kalk", version = "^0.2.3" }
|
||||
rustyline = "6.1.2"
|
||||
ansi_term = "0.12"
|
||||
regex = "1"
|
||||
|
@ -61,6 +61,7 @@ fn print_calc_err(err: CalcError) {
|
||||
UnableToInvert(msg) => format!("Unable to invert: {}", msg),
|
||||
UndefinedFn(name) => format!("Undefined function: '{}'.", name),
|
||||
UndefinedVar(name) => format!("Undefined variable: '{}'.", name),
|
||||
UnableToParseExpression => format!("Unable to parse expression."),
|
||||
Unknown => format!("Unknown error."),
|
||||
});
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ impl Highlighter for LineHighlighter {
|
||||
let reg = Regex::new(
|
||||
r"(?x)
|
||||
(?P<identifier>[^!-@\s_|^⌊⌋⌈⌉]+(_\d+)?) |
|
||||
(?P<op>[+\-/*^!])",
|
||||
(?P<op>[+\-/*%^!])",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user