Comparison operators and Iverson brackets

This commit is contained in:
bakk 2021-05-31 13:46:06 +02:00
parent 2402d444f5
commit fe50789cbd
8 changed files with 225 additions and 14 deletions

View File

@ -156,6 +156,12 @@ fn eval_binary_expr(
TokenKind::Slash => left.div(context, right), TokenKind::Slash => left.div(context, right),
TokenKind::Percent => left.rem(context, right), TokenKind::Percent => left.rem(context, right),
TokenKind::Power => left.pow(context, right), TokenKind::Power => left.pow(context, right),
TokenKind::Equals => left.eq(context, right),
TokenKind::NotEquals => left.not_eq(context, right),
TokenKind::GreaterThan => left.greater_than(context, right),
TokenKind::LessThan => left.less_than(context, right),
TokenKind::GreaterOrEquals => left.greater_or_equals(context, right),
TokenKind::LessOrEquals => left.less_or_equals(context, right),
_ => KalkNum::from(1), _ => KalkNum::from(1),
}; };
@ -468,12 +474,54 @@ mod tests {
let mul = Stmt::Expr(binary(literal(2f64), Star, literal(3f64))); let mul = Stmt::Expr(binary(literal(2f64), Star, literal(3f64)));
let div = Stmt::Expr(binary(literal(2f64), Slash, literal(4f64))); let div = Stmt::Expr(binary(literal(2f64), Slash, literal(4f64)));
let pow = Stmt::Expr(binary(literal(2f64), Power, literal(3f64))); let pow = Stmt::Expr(binary(literal(2f64), Power, literal(3f64)));
let equals = Stmt::Expr(binary(literal(2f64), Equals, literal(3f64)));
let not_equals = Stmt::Expr(binary(literal(2f64), NotEquals, literal(3f64)));
let greater_than = Stmt::Expr(binary(literal(2f64), GreaterThan, literal(3f64)));
let less_than = Stmt::Expr(binary(literal(2f64), LessThan, literal(3f64)));
let greater_or_equals = Stmt::Expr(binary(literal(2f64), GreaterOrEquals, literal(3f64)));
let less_or_equals = Stmt::Expr(binary(literal(2f64), LessOrEquals, literal(3f64)));
assert_eq!(interpret(add).unwrap().unwrap().to_f64(), 5f64); assert_eq!(interpret(add).unwrap().unwrap().to_f64(), 5f64);
assert_eq!(interpret(sub).unwrap().unwrap().to_f64(), -1f64); assert_eq!(interpret(sub).unwrap().unwrap().to_f64(), -1f64);
assert_eq!(interpret(mul).unwrap().unwrap().to_f64(), 6f64); assert_eq!(interpret(mul).unwrap().unwrap().to_f64(), 6f64);
assert_eq!(interpret(div).unwrap().unwrap().to_f64(), 0.5f64); assert_eq!(interpret(div).unwrap().unwrap().to_f64(), 0.5f64);
assert_eq!(interpret(pow).unwrap().unwrap().to_f64(), 8f64); assert_eq!(interpret(pow).unwrap().unwrap().to_f64(), 8f64);
let result = interpret(equals).unwrap().unwrap();
assert_eq!(
(result.to_f64(), result.boolean_value.unwrap()),
(3f64, false)
);
let result = interpret(not_equals).unwrap().unwrap();
assert_eq!(
(result.to_f64(), result.boolean_value.unwrap()),
(3f64, true)
);
let result = interpret(greater_than).unwrap().unwrap();
assert_eq!(
(result.to_f64(), result.boolean_value.unwrap()),
(3f64, false)
);
let result = interpret(less_than).unwrap().unwrap();
assert_eq!(
(result.to_f64(), result.boolean_value.unwrap()),
(3f64, true)
);
let result = interpret(greater_or_equals).unwrap().unwrap();
assert_eq!(
(result.to_f64(), result.boolean_value.unwrap()),
(3f64, false)
);
let result = interpret(less_or_equals).unwrap().unwrap();
assert_eq!(
(result.to_f64(), result.boolean_value.unwrap()),
(3f64, true)
);
} }
#[test] #[test]

View File

@ -186,6 +186,10 @@ impl KalkNum {
} }
pub fn to_string_pretty(&self) -> String { pub fn to_string_pretty(&self) -> String {
if let Some(boolean_value) = self.boolean_value {
return boolean_value.to_string();
}
let real_f64 = self.to_f64(); let real_f64 = self.to_f64();
let imaginary_f64 = self.imaginary_to_f64(); let imaginary_f64 = self.imaginary_to_f64();
if real_f64.is_nan() || imaginary_f64.is_nan() { if real_f64.is_nan() || imaginary_f64.is_nan() {
@ -314,6 +318,58 @@ impl KalkNum {
KalkNum::new(self.value % right.value, &right.unit) KalkNum::new(self.value % right.value, &right.unit)
} }
pub(crate) fn eq(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
let mut right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
right.boolean_value = Some(self.value == right.value);
right
}
pub(crate) fn not_eq(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
let mut right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
right.boolean_value = Some(self.value != right.value);
right
}
pub(crate) fn greater_than(
self,
context: &mut crate::interpreter::Context,
rhs: KalkNum,
) -> KalkNum {
let mut right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
right.boolean_value = Some(self.value > right.value);
right
}
pub(crate) fn less_than(
self,
context: &mut crate::interpreter::Context,
rhs: KalkNum,
) -> KalkNum {
let mut right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
right.boolean_value = Some(self.value < right.value);
right
}
pub(crate) fn greater_or_equals(
self,
context: &mut crate::interpreter::Context,
rhs: KalkNum,
) -> KalkNum {
let mut right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
right.boolean_value = Some(self.value >= right.value);
right
}
pub(crate) fn less_or_equals(
self,
context: &mut crate::interpreter::Context,
rhs: KalkNum,
) -> KalkNum {
let mut right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
right.boolean_value = Some(self.value <= right.value);
right
}
pub(crate) fn add_without_unit(self, rhs: KalkNum) -> KalkNum { pub(crate) fn add_without_unit(self, rhs: KalkNum) -> KalkNum {
KalkNum::new_with_imaginary( KalkNum::new_with_imaginary(
self.value + rhs.value, self.value + rhs.value,

View File

@ -7,6 +7,7 @@ pub struct KalkNum {
pub(crate) value: f64, pub(crate) value: f64,
pub(crate) unit: String, pub(crate) unit: String,
pub(crate) imaginary_value: f64, pub(crate) imaginary_value: f64,
pub(crate) boolean_value: Option<bool>,
} }
#[wasm_bindgen] #[wasm_bindgen]
@ -16,6 +17,7 @@ impl KalkNum {
value, value,
unit: unit.to_string(), unit: unit.to_string(),
imaginary_value: 0f64, imaginary_value: 0f64,
boolean_value: None,
} }
} }
@ -28,6 +30,7 @@ impl KalkNum {
} else { } else {
imaginary_value imaginary_value
}, },
boolean_value: None,
} }
} }
@ -36,6 +39,16 @@ impl KalkNum {
value: 0f64, value: 0f64,
unit: String::new(), unit: String::new(),
imaginary_value: value, imaginary_value: value,
boolean_value: None,
}
}
pub fn from_bool(value: bool) -> Self {
Self {
value: 0f64,
unit: String::new(),
imaginary_value: 0f64,
boolean_value: Some(value),
} }
} }

View File

@ -11,6 +11,7 @@ pub struct KalkNum {
pub(crate) value: Float, pub(crate) value: Float,
pub(crate) unit: String, pub(crate) unit: String,
pub(crate) imaginary_value: Float, pub(crate) imaginary_value: Float,
pub(crate) boolean_value: Option<bool>,
} }
impl KalkNum { impl KalkNum {
@ -19,6 +20,7 @@ impl KalkNum {
value, value,
unit: unit.to_string(), unit: unit.to_string(),
imaginary_value: Float::with_val(63, 0), imaginary_value: Float::with_val(63, 0),
boolean_value: None,
} }
} }
@ -31,6 +33,7 @@ impl KalkNum {
} else { } else {
imaginary_value imaginary_value
}, },
boolean_value: None,
} }
} }
@ -39,6 +42,16 @@ impl KalkNum {
value: Float::with_val(63, 0), value: Float::with_val(63, 0),
unit: String::new(), unit: String::new(),
imaginary_value: value, imaginary_value: value,
boolean_value: None,
}
}
pub fn from_bool(value: bool) -> Self {
Self {
value: Float::with_val(63, 0),
unit: String::new(),
imaginary_value: Float::with_val(63, 0),
boolean_value: Some(value),
} }
} }

View File

@ -13,10 +13,15 @@ pub enum TokenKind {
Star, Star,
Slash, Slash,
Power, Power,
Equals,
Exclamation, Exclamation,
Percent, Percent,
Tick, Tick,
GreaterThan,
LessThan,
Equals,
NotEquals,
GreaterOrEquals,
LessOrEquals,
UnitKeyword, UnitKeyword,
ToKeyword, ToKeyword,
@ -28,6 +33,8 @@ pub enum TokenKind {
ClosedFloor, ClosedFloor,
OpenParenthesis, OpenParenthesis,
ClosedParenthesis, ClosedParenthesis,
OpenBracket,
ClosedBracket,
Comma, Comma,
Semicolon, Semicolon,
@ -110,12 +117,19 @@ impl<'a> Lexer<'a> {
'⌋' => build(TokenKind::ClosedFloor, "", span), '⌋' => build(TokenKind::ClosedFloor, "", span),
'(' => build(TokenKind::OpenParenthesis, "", span), '(' => build(TokenKind::OpenParenthesis, "", span),
')' => build(TokenKind::ClosedParenthesis, "", span), ')' => build(TokenKind::ClosedParenthesis, "", span),
'=' => build(TokenKind::Equals, "", span), '[' => build(TokenKind::OpenBracket, "", span),
']' => build(TokenKind::ClosedBracket, "", span),
'!' => build(TokenKind::Exclamation, "", span), '!' => build(TokenKind::Exclamation, "", span),
'=' => build(TokenKind::Equals, "", span),
'>' => build(TokenKind::GreaterThan, "", span),
'<' => build(TokenKind::LessThan, "", span),
',' => build(TokenKind::Comma, "", span), ',' => build(TokenKind::Comma, "", span),
';' => build(TokenKind::Semicolon, "", span), ';' => build(TokenKind::Semicolon, "", span),
'%' => build(TokenKind::Percent, "", span), '%' => build(TokenKind::Percent, "", span),
'\'' => build(TokenKind::Tick, "", span), '\'' => build(TokenKind::Tick, "", span),
'≠' => build(TokenKind::NotEquals, "", span),
'≥' => build(TokenKind::GreaterOrEquals, "", span),
'≤' => build(TokenKind::LessOrEquals, "", span),
// Some of the special symbols will be lexed here, // Some of the special symbols will be lexed here,
// so that they don't merge with other symbols. // so that they don't merge with other symbols.
'π' => build(TokenKind::Identifier, "π", span), 'π' => build(TokenKind::Identifier, "π", span),
@ -128,13 +142,25 @@ impl<'a> Lexer<'a> {
self.advance(); self.advance();
// Handle ** // Handle tokens with two characters
if let (TokenKind::Star, Some(c)) = (token.kind, self.peek()) { match (token.kind, self.peek()) {
if *c == '*' { (TokenKind::Star, Some('*')) => {
self.advance(); self.advance();
return build(TokenKind::Power, "", span); return build(TokenKind::Power, "", span);
} }
(TokenKind::Exclamation, Some('=')) => {
self.advance();
return build(TokenKind::NotEquals, "", span);
}
(TokenKind::GreaterThan, Some('=')) => {
self.advance();
return build(TokenKind::GreaterOrEquals, "", span);
}
(TokenKind::LessThan, Some('=')) => {
self.advance();
return build(TokenKind::LessOrEquals, "", span);
}
_ => (),
} }
token token
@ -228,7 +254,8 @@ fn is_valid_identifier(c: Option<&char>) -> bool {
if let Some(c) = c { if let Some(c) = c {
match c { match c {
'+' | '-' | '/' | '*' | '%' | '^' | '!' | '(' | ')' | '=' | '.' | ',' | ';' | '|' '+' | '-' | '/' | '*' | '%' | '^' | '!' | '(' | ')' | '=' | '.' | ',' | ';' | '|'
| '⌊' | '⌋' | '⌈' | '⌉' | ']' | 'π' | '√' | 'τ' | 'ϕ' | 'Γ' => false, | '⌊' | '⌋' | '⌈' | '⌉' | '[' | ']' | 'π' | '√' | 'τ' | 'ϕ' | 'Γ' | '<' | '>' | '≠'
| '≥' | '≤' => false,
_ => !c.is_digit(10), _ => !c.is_digit(10),
} }
} else { } else {
@ -274,6 +301,22 @@ mod tests {
match_tokens(tokens, expected); match_tokens(tokens, expected);
} }
#[test]
#[wasm_bindgen_test]
fn test_brackets() {
let tokens = Lexer::lex("[1 < 2]");
let expected = vec![
TokenKind::OpenBracket,
TokenKind::Literal,
TokenKind::LessThan,
TokenKind::Literal,
TokenKind::ClosedBracket,
TokenKind::EOF,
];
match_tokens(tokens, expected);
}
#[test] #[test]
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn test_empty() { fn test_empty() {

View File

@ -149,7 +149,9 @@ pub fn eval(
) -> Result<Option<KalkNum>, CalcError> { ) -> Result<Option<KalkNum>, CalcError> {
// Variable and function declaration parsers will set this to false // Variable and function declaration parsers will set this to false
// if the equal sign is for one of those instead. // if the equal sign is for one of those instead.
context.contains_equation_equal_sign = input.contains("="); // It also should not contain an iverson bracket, since equal signs in there
// mean something else. This is not super reliable, and should probably be improved in the future.
context.contains_equation_equal_sign = input.contains("=") && !input.contains("[");
let statements = parse(context, input)?; let statements = parse(context, input)?;
let mut interpreter = interpreter::Context::new( let mut interpreter = interpreter::Context::new(
@ -302,13 +304,14 @@ fn parse_unit_decl_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
} }
fn parse_expr(context: &mut Context) -> Result<Expr, CalcError> { fn parse_expr(context: &mut Context) -> Result<Expr, CalcError> {
Ok(parse_equation(context)?) Ok(parse_equality(context)?)
} }
fn parse_equation(context: &mut Context) -> Result<Expr, CalcError> { fn parse_equality(context: &mut Context) -> Result<Expr, CalcError> {
let left = parse_to(context)?; let mut left = parse_to(context)?;
if match_token(context, TokenKind::Equals) { // Equation
if match_token(context, TokenKind::Equals) && context.contains_equation_equal_sign {
advance(context); advance(context);
let right = parse_to(context)?; let right = parse_to(context)?;
let var_name = if let Some(var_name) = &context.equation_variable { let var_name = if let Some(var_name) = &context.equation_variable {
@ -333,9 +336,25 @@ fn parse_equation(context: &mut Context) -> Result<Expr, CalcError> {
Identifier::from_full_name(var_name), Identifier::from_full_name(var_name),
Box::new(inverted.clone()), Box::new(inverted.clone()),
)); ));
return Ok(inverted); return Ok(inverted);
} }
// Equality check
while match_token(context, TokenKind::Equals)
|| match_token(context, TokenKind::NotEquals)
|| match_token(context, TokenKind::GreaterThan)
|| match_token(context, TokenKind::LessThan)
|| match_token(context, TokenKind::GreaterOrEquals)
|| match_token(context, TokenKind::LessOrEquals)
{
let op = peek(context).kind;
advance(context);
let right = parse_to(context)?;
left = Expr::Binary(Box::new(left), op, Box::new(right));
}
Ok(left) Ok(left)
} }
@ -462,7 +481,9 @@ fn parse_factorial(context: &mut Context) -> Result<Expr, CalcError> {
fn parse_primary(context: &mut Context) -> Result<Expr, CalcError> { fn parse_primary(context: &mut Context) -> Result<Expr, CalcError> {
let expr = match peek(context).kind { let expr = match peek(context).kind {
TokenKind::OpenParenthesis => parse_group(context)?, TokenKind::OpenParenthesis => parse_group(context)?,
TokenKind::Pipe | TokenKind::OpenCeil | TokenKind::OpenFloor => parse_group_fn(context)?, TokenKind::Pipe | TokenKind::OpenCeil | TokenKind::OpenFloor | TokenKind::OpenBracket => {
parse_group_fn(context)?
}
TokenKind::Identifier => parse_identifier(context)?, TokenKind::Identifier => parse_identifier(context)?,
TokenKind::Literal => Expr::Literal(string_to_num(&advance(context).value)?), TokenKind::Literal => Expr::Literal(string_to_num(&advance(context).value)?),
_ => return Err(CalcError::UnableToParseExpression), _ => return Err(CalcError::UnableToParseExpression),
@ -484,6 +505,7 @@ fn parse_group_fn(context: &mut Context) -> Result<Expr, CalcError> {
TokenKind::Pipe => "abs", TokenKind::Pipe => "abs",
TokenKind::OpenCeil => "ceil", TokenKind::OpenCeil => "ceil",
TokenKind::OpenFloor => "floor", TokenKind::OpenFloor => "floor",
TokenKind::OpenBracket => "iverson",
_ => unreachable!(), _ => unreachable!(),
}; };

View File

@ -89,6 +89,7 @@ lazy_static! {
m.insert("abs", (UnaryFuncInfo(abs, Other), "")); m.insert("abs", (UnaryFuncInfo(abs, Other), ""));
m.insert("cbrt", (UnaryFuncInfo(cbrt, Other), "")); m.insert("cbrt", (UnaryFuncInfo(cbrt, Other), ""));
m.insert("ceil", (UnaryFuncInfo(ceil, Other), "")); m.insert("ceil", (UnaryFuncInfo(ceil, Other), ""));
m.insert("iverson", (UnaryFuncInfo(inverson, Other), ""));
m.insert("exp", (UnaryFuncInfo(exp, Other), "")); m.insert("exp", (UnaryFuncInfo(exp, Other), ""));
m.insert("floor", (UnaryFuncInfo(floor, Other), "")); m.insert("floor", (UnaryFuncInfo(floor, Other), ""));
m.insert("frac", (UnaryFuncInfo(frac, Other), "")); m.insert("frac", (UnaryFuncInfo(frac, Other), ""));
@ -506,6 +507,18 @@ pub mod funcs {
KalkNum::new_with_imaginary(x.value, "", KalkNum::default().value) KalkNum::new_with_imaginary(x.value, "", KalkNum::default().value)
} }
pub fn inverson(x: KalkNum) -> KalkNum {
KalkNum::from(if let Some(boolean_value) = x.boolean_value {
if boolean_value {
1
} else {
0
}
} else {
1
})
}
pub fn log(x: KalkNum) -> KalkNum { pub fn log(x: KalkNum) -> KalkNum {
if x.has_imaginary() || x.value < 0f64 { if x.has_imaginary() || x.value < 0f64 {
// ln(z) / ln(10) // ln(z) / ln(10)

View File

@ -76,7 +76,7 @@ impl Highlighter for LineHighlighter {
let reg = Regex::new( let reg = Regex::new(
r"(?x) r"(?x)
(?P<identifier>[^!-@\s_|^]+(_\d+)?) | (?P<identifier>[^!-@\s_|^\[\]]+(_\d+)?) |
(?P<op>[+\-/*%^!])", (?P<op>[+\-/*%^!])",
) )
.unwrap(); .unwrap();
@ -122,6 +122,9 @@ lazy_static! {
m.insert("sqrt", ""); m.insert("sqrt", "");
m.insert("tau", "τ"); m.insert("tau", "τ");
m.insert("(", "()"); m.insert("(", "()");
m.insert("!=", "");
m.insert(">=", "");
m.insert("<=", "");
m m
}; };
} }