Add keywords 'true', 'false' and 'not'

This commit is contained in:
PaddiM8 2022-06-01 00:51:01 +02:00
parent f7d9f6c0d9
commit 0209f18248
10 changed files with 52 additions and 15 deletions

View File

@ -105,7 +105,7 @@ impl Highlighter for LineHighlighter {
let reg = Regex::new( let reg = Regex::new(
r"(?x) r"(?x)
(?P<op>([+\-/*%^!×÷⋅∧∨ᵀ]|if|otherwise|\sand|\sor|\smod|load|exit|clear|help)) | (?P<op>([+\-/*%^!×÷⋅∧∨¬ᵀ]|if|otherwise|\b(and|or|mod|true|false|not)\b|load|exit|clear|help)) |
(?P<radix>0[box][a-zA-Z0-9]+) | (?P<radix>0[box][a-zA-Z0-9]+) |
(?P<identifier>[^!-@\s_|^\[\]\{\}¹²³]+(_\d+)?)", (?P<identifier>[^!-@\s_|^\[\]\{\}¹²³]+(_\d+)?)",
) )
@ -159,6 +159,7 @@ lazy_static! {
m.insert("<=", ""); m.insert("<=", "");
m.insert(" and", ""); m.insert(" and", "");
m.insert(" or", " "); m.insert(" or", " ");
m.insert(" not", " ¬");
m.insert("*", "×"); m.insert("*", "×");
m.insert("/", "÷"); m.insert("/", "÷");
m.insert("^T", ""); m.insert("^T", "");
@ -194,6 +195,14 @@ impl Completer for RLHelper {
return Ok((pos - key.len(), vec![value.to_string()])); return Ok((pos - key.len(), vec![value.to_string()]));
} }
// If the key starts with a space, it should also be expanded
// if it is at the start of the line. To do this, the strings
// are compared with the space removed in these situations.
if key.starts_with(' ') && slice.len() == key.len() - 1 && slice == &key[1..] {
let value = &(*COMPLETION_FUNCS.get(key).unwrap())[1..];
return Ok((pos - (key.len() - 1), vec![value.to_string()]));
}
let mut subscript_digits = String::new(); let mut subscript_digits = String::new();
for c in slice.chars().rev() { for c in slice.chars().rev() {
if c.is_digit(10) { if c.is_digit(10) {

View File

@ -212,7 +212,7 @@ fn analyse_expr(context: &mut Context, expr: Expr) -> Result<Expr, KalkError> {
Expr::Var(identifier) => analyse_var(context, identifier, None, None)?, Expr::Var(identifier) => analyse_var(context, identifier, None, None)?,
Expr::Group(value) => Expr::Group(Box::new(analyse_expr(context, *value)?)), Expr::Group(value) => Expr::Group(Box::new(analyse_expr(context, *value)?)),
Expr::FnCall(identifier, arguments) => analyse_fn(context, identifier, arguments)?, Expr::FnCall(identifier, arguments) => analyse_fn(context, identifier, arguments)?,
Expr::Literal(_) => expr, Expr::Literal(_) | Expr::Boolean(_) => expr,
Expr::Piecewise(pieces) => { Expr::Piecewise(pieces) => {
let mut analysed_pieces = Vec::new(); let mut analysed_pieces = Vec::new();
for piece in pieces { for piece in pieces {

View File

@ -20,6 +20,7 @@ pub enum Expr {
Group(Box<Expr>), Group(Box<Expr>),
FnCall(Identifier, Vec<Expr>), FnCall(Identifier, Vec<Expr>),
Literal(f64), Literal(f64),
Boolean(bool),
Piecewise(Vec<ConditionalPiece>), Piecewise(Vec<ConditionalPiece>),
Vector(Vec<Expr>), Vector(Vec<Expr>),
Matrix(Vec<Vec<Expr>>), Matrix(Vec<Vec<Expr>>),

View File

@ -23,7 +23,7 @@ pub enum KalkError {
VariableReferencesItself, VariableReferencesItself,
PiecewiseConditionsAreFalse, PiecewiseConditionsAreFalse,
EvaluationError(String), EvaluationError(String),
UnexpectedToken(TokenKind, TokenKind), UnexpectedToken(TokenKind, Option<TokenKind>),
UnexpectedType(String, Vec<String>), UnexpectedType(String, Vec<String>),
UndefinedFn(String), UndefinedFn(String),
UndefinedVar(String), UndefinedVar(String),
@ -66,7 +66,11 @@ impl ToString for KalkError {
KalkError::PiecewiseConditionsAreFalse => String::from("All the conditions in the piecewise are false."), KalkError::PiecewiseConditionsAreFalse => String::from("All the conditions in the piecewise are false."),
KalkError::EvaluationError(msg) => format!("Evaluation error: {}", msg), KalkError::EvaluationError(msg) => format!("Evaluation error: {}", msg),
KalkError::UnexpectedToken(got, expected) => { KalkError::UnexpectedToken(got, expected) => {
format!("Unexpected token: '{:?}', expected '{:?}'.", got, expected) if let Some(expected) = expected {
format!("Unexpected token: '{:?}', expected '{:?}'.", got, expected)
} else {
format!("Unexpected token: '{:?}'.", got)
}
} }
KalkError::UnexpectedType(got, expected) => { KalkError::UnexpectedType(got, expected) => {
format!("Unexpected type. Got {:?} but expected: {:?}.", got, expected.join(", ")) format!("Unexpected type. Got {:?} but expected: {:?}.", got, expected.join(", "))

View File

@ -126,6 +126,7 @@ pub(crate) fn eval_expr(
Expr::Unit(identifier, expr) => eval_unit_expr(context, identifier, expr), Expr::Unit(identifier, expr) => eval_unit_expr(context, identifier, expr),
Expr::Var(identifier) => eval_var_expr(context, identifier, unit), 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::Boolean(value) => Ok(KalkValue::Boolean(*value)),
Expr::Group(expr) => eval_group_expr(context, expr, unit), Expr::Group(expr) => eval_group_expr(context, expr, unit),
Expr::FnCall(identifier, expressions) => { Expr::FnCall(identifier, expressions) => {
eval_fn_call_expr(context, identifier, expressions, unit) eval_fn_call_expr(context, identifier, expressions, unit)
@ -209,6 +210,10 @@ fn eval_unary_expr(
match op { match op {
TokenKind::Minus => num.mul(context, KalkValue::from(-1f64)), TokenKind::Minus => num.mul(context, KalkValue::from(-1f64)),
TokenKind::Not => match num {
KalkValue::Boolean(boolean) => Ok(KalkValue::Boolean(!boolean)),
_ => Err(KalkError::InvalidOperator),
},
TokenKind::Percent => num.mul(context, KalkValue::from(0.01f64)), TokenKind::Percent => num.mul(context, KalkValue::from(0.01f64)),
TokenKind::Exclamation => prelude::special_funcs::factorial(num), TokenKind::Exclamation => prelude::special_funcs::factorial(num),
_ => Err(KalkError::InvalidOperator), _ => Err(KalkError::InvalidOperator),

View File

@ -85,7 +85,7 @@ fn invert(
arguments, arguments,
unknown_var, unknown_var,
), ),
Expr::Literal(_) => Ok((target_expr, expr.clone())), Expr::Literal(_) | Expr::Boolean(_) => Ok((target_expr, expr.clone())),
Expr::Piecewise(_) => Err(KalkError::UnableToInvert(String::from("Piecewise"))), Expr::Piecewise(_) => Err(KalkError::UnableToInvert(String::from("Piecewise"))),
Expr::Vector(_) => Err(KalkError::UnableToInvert(String::from("Vector"))), Expr::Vector(_) => Err(KalkError::UnableToInvert(String::from("Vector"))),
Expr::Matrix(_) => Err(KalkError::UnableToInvert(String::from("Matrix"))), Expr::Matrix(_) => Err(KalkError::UnableToInvert(String::from("Matrix"))),
@ -391,7 +391,7 @@ pub fn contains_var(symbol_table: &SymbolTable, expr: &Expr, var_name: &str) ->
false false
} }
Expr::Literal(_) => false, Expr::Literal(_) | Expr::Boolean(_) => false,
Expr::Piecewise(_) => true, // Let it try to invert this. It will just display the error message. Expr::Piecewise(_) => true, // Let it try to invert this. It will just display the error message.
Expr::Vector(items) => items Expr::Vector(items) => items
.iter() .iter()

View File

@ -25,6 +25,9 @@ pub enum TokenKind {
LessOrEquals, LessOrEquals,
And, And,
Or, Or,
Not,
True,
False,
UnitKeyword, UnitKeyword,
ToKeyword, ToKeyword,
@ -153,6 +156,7 @@ impl<'a> Lexer<'a> {
'<' => build(TokenKind::LessThan, "", span), '<' => build(TokenKind::LessThan, "", span),
'∧' => build(TokenKind::And, "", span), '∧' => build(TokenKind::And, "", span),
'' => build(TokenKind::Or, "", span), '' => build(TokenKind::Or, "", span),
'¬' => build(TokenKind::Not, "", span),
',' => build(TokenKind::Comma, "", span), ',' => build(TokenKind::Comma, "", span),
':' => build(TokenKind::Colon, "", span), ':' => build(TokenKind::Colon, "", span),
';' => build(TokenKind::Semicolon, "", span), ';' => build(TokenKind::Semicolon, "", span),
@ -324,6 +328,9 @@ impl<'a> Lexer<'a> {
let kind = match value.as_ref() { let kind = match value.as_ref() {
"and" => TokenKind::And, "and" => TokenKind::And,
"or" => TokenKind::Or, "or" => TokenKind::Or,
"not" => TokenKind::Not,
"true" => TokenKind::True,
"false" => TokenKind::False,
"mod" => TokenKind::Percent, "mod" => TokenKind::Percent,
"unit" => TokenKind::UnitKeyword, "unit" => TokenKind::UnitKeyword,
"to" => TokenKind::ToKeyword, "to" => TokenKind::ToKeyword,
@ -391,7 +398,7 @@ fn is_valid_identifier(c: Option<&char>) -> bool {
match c { match c {
'+' | '-' | '/' | '*' | '%' | '^' | '!' | '(' | ')' | '=' | '.' | ',' | ';' | '|' '+' | '-' | '/' | '*' | '%' | '^' | '!' | '(' | ')' | '=' | '.' | ',' | ';' | '|'
| '⌊' | '⌋' | '⌈' | '⌉' | '[' | ']' | '{' | '}' | 'π' | '√' | 'τ' | 'ϕ' | 'Γ' | '<' | '⌊' | '⌋' | '⌈' | '⌉' | '[' | ']' | '{' | '}' | 'π' | '√' | 'τ' | 'ϕ' | 'Γ' | '<'
| '>' | '≠' | '≥' | '≤' | '×' | '÷' | '⋅' | '⟦' | '⟧' | '∧' | '' | ':' | 'ᵀ' | '>' | '≠' | '≥' | '≤' | '×' | '÷' | '⋅' | '⟦' | '⟧' | '∧' | '' | '¬' | ':' | 'ᵀ'
| '\n' => false, | '\n' => false,
_ => !c.is_digit(10) || is_superscript(c) || is_subscript(c), _ => !c.is_digit(10) || is_superscript(c) || is_subscript(c),
} }

View File

@ -454,6 +454,7 @@ fn parse_exponent(context: &mut Context) -> Result<Expr, KalkError> {
if match_token(context, TokenKind::Power) { if match_token(context, TokenKind::Power) {
let op = advance(context).kind; let op = advance(context).kind;
let right = Box::new(parse_exponent(context)?); let right = Box::new(parse_exponent(context)?);
return Ok(Expr::Binary(Box::new(left), op, right)); return Ok(Expr::Binary(Box::new(left), op, right));
} }
@ -461,9 +462,10 @@ fn parse_exponent(context: &mut Context) -> Result<Expr, KalkError> {
} }
fn parse_unary(context: &mut Context) -> Result<Expr, KalkError> { fn parse_unary(context: &mut Context) -> Result<Expr, KalkError> {
if match_token(context, TokenKind::Minus) { if match_token(context, TokenKind::Minus) || match_token(context, TokenKind::Not) {
let op = advance(context).kind; let op = advance(context).kind;
let expr = Box::new(parse_unary(context)?); let expr = Box::new(parse_unary(context)?);
return Ok(Expr::Unary(op, expr)); return Ok(Expr::Unary(op, expr));
} }
@ -511,7 +513,15 @@ fn parse_primary(context: &mut Context) -> Result<Expr, KalkError> {
TokenKind::Pipe | TokenKind::OpenCeil | TokenKind::OpenFloor => parse_group_fn(context)?, TokenKind::Pipe | TokenKind::OpenCeil | TokenKind::OpenFloor => 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(KalkError::UnableToParseExpression), TokenKind::True => {
advance(context);
Expr::Boolean(true)
}
TokenKind::False => {
advance(context);
Expr::Boolean(false)
}
_ => return Err(KalkError::UnexpectedToken(peek(context).kind, None)),
}; };
Ok(expr) Ok(expr)
@ -713,7 +723,7 @@ fn consume(context: &mut Context, kind: TokenKind) -> Result<&Token, KalkError>
return Ok(advance(context)); return Ok(advance(context));
} }
Err(KalkError::UnexpectedToken(peek(context).kind, kind)) Err(KalkError::UnexpectedToken(peek(context).kind, Some(kind)))
} }
fn is_at_end(context: &Context) -> bool { fn is_at_end(context: &Context) -> bool {
@ -728,7 +738,7 @@ fn skip_newlines(context: &mut Context) {
fn string_to_num(value: &str) -> Result<f64, KalkError> { fn string_to_num(value: &str) -> Result<f64, KalkError> {
let base = get_base(value)?; let base = get_base(value)?;
if let Some(result) = crate::radix::parse_float_radix(&value.replace(" ", ""), base) { if let Some(result) = crate::radix::parse_float_radix(&value.replace(' ', ""), base) {
Ok(result) Ok(result)
} else { } else {
Err(KalkError::InvalidNumberLiteral(value.into())) Err(KalkError::InvalidNumberLiteral(value.into()))

View File

@ -48,8 +48,8 @@
"svelte-preprocess": "^4.6.1", "svelte-preprocess": "^4.6.1",
"terser-webpack-plugin": "^4.2.3", "terser-webpack-plugin": "^4.2.3",
"ts-loader": "^8.0.2", "ts-loader": "^8.0.2",
"ts-node": "^9.0.0", "ts-node": "^10.8.0",
"typescript": "^4.1.2", "typescript": "^4.7.2",
"webpack": "^4.44.1", "webpack": "^4.44.1",
"webpack-cli": "^3.3.12", "webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0" "webpack-dev-server": "^3.11.0"

View File

@ -310,7 +310,7 @@
let result = input; let result = input;
let offset = 0; let offset = 0;
result = result.replace( result = result.replace(
/(?<power>\^[0-9T])|(?<brackets>\[\[)|(?<radix>0[box][a-zA-Z0-9]+)|(?<comparison>(!=|[<>]=?))|(?<html>[<>&]|(\n\s*\}?|\s+))|(?<op>([+\-/*%^!≈×÷⋅∧∨ᵀ]|if|otherwise|and|or|mod)|(?<identifier>[^!-@\s_|^⌊⌋⌈⌉≈\[\]\{\}⟦⟧≠≥≤⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎×÷⋅∧∨ᵀ]+(_\d+)?)\(?)/g, /(?<power>\^[0-9T])|(?<brackets>\[\[)|(?<radix>0[box][a-zA-Z0-9]+)|(?<comparison>(!=|[<>]=?))|(?<html>[<>&]|(\n\s*\}?|\s+))|(?<op>([+\-/*%^!≈×÷⋅∧∨¬ᵀ]|if|otherwise|and|or|mod|true|false|not)|(?<identifier>[^!-@\s_|^⌊⌋⌈⌉≈\[\]\{\}⟦⟧≠≥≤⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎×÷⋅∧∨ᵀ]+(_\d+)?)\(?)/g,
( (
substring, substring,
power, power,
@ -395,6 +395,7 @@
if (substring == "/") substring = "÷"; if (substring == "/") substring = "÷";
if (substring == "and") substring = "∧"; if (substring == "and") substring = "∧";
if (substring == "or") substring = ""; if (substring == "or") substring = "";
if (substring == "not") substring = "¬";
} }
if (identifier) { if (identifier) {