This commit is contained in:
PaddiM8 2022-01-07 00:39:29 +01:00
parent b937cef83d
commit 95fd78f3d1
7 changed files with 355 additions and 51 deletions

View File

@ -22,6 +22,7 @@ pub enum Expr {
Literal(f64), Literal(f64),
Piecewise(Vec<ConditionalPiece>), Piecewise(Vec<ConditionalPiece>),
Vector(Vec<Expr>), Vector(Vec<Expr>),
Matrix(Vec<Vec<Expr>>),
Indexer(Box<Expr>, Box<Expr>), Indexer(Box<Expr>, Box<Expr>),
} }

View File

@ -125,6 +125,7 @@ pub(crate) fn eval_expr(
} }
Expr::Piecewise(pieces) => eval_piecewise(context, pieces, unit), Expr::Piecewise(pieces) => eval_piecewise(context, pieces, unit),
Expr::Vector(values) => eval_vector(context, values), Expr::Vector(values) => eval_vector(context, values),
Expr::Matrix(rows) => eval_matrix(context, rows),
Expr::Indexer(var, index) => eval_indexer(context, var, index, unit), Expr::Indexer(var, index) => eval_indexer(context, var, index, unit),
} }
} }
@ -536,6 +537,20 @@ fn eval_vector(context: &mut Context, values: &Vec<Expr>) -> Result<KalkValue, C
Ok(KalkValue::Vector(eval_values)) Ok(KalkValue::Vector(eval_values))
} }
fn eval_matrix(context: &mut Context, rows: &Vec<Vec<Expr>>) -> Result<KalkValue, CalcError> {
let mut eval_rows = Vec::new();
for row in rows {
let mut eval_row = Vec::new();
for value in row {
eval_row.push(eval_expr(context, value, "")?)
}
eval_rows.push(eval_row);
}
Ok(KalkValue::Matrix(eval_rows))
}
fn eval_indexer( fn eval_indexer(
context: &mut Context, context: &mut Context,
var: &Expr, var: &Expr,

View File

@ -88,6 +88,7 @@ fn invert(
Expr::Literal(_) => Ok((target_expr, expr.clone())), Expr::Literal(_) => Ok((target_expr, expr.clone())),
Expr::Piecewise(_) => Err(CalcError::UnableToInvert(String::from("Piecewise"))), Expr::Piecewise(_) => Err(CalcError::UnableToInvert(String::from("Piecewise"))),
Expr::Vector(_) => Err(CalcError::UnableToInvert(String::from("Vector"))), Expr::Vector(_) => Err(CalcError::UnableToInvert(String::from("Vector"))),
Expr::Matrix(_) => Err(CalcError::UnableToInvert(String::from("Matrix"))),
Expr::Indexer(_, _) => Err(CalcError::UnableToInvert(String::from("Inverter"))), Expr::Indexer(_, _) => Err(CalcError::UnableToInvert(String::from("Inverter"))),
} }
} }
@ -391,6 +392,9 @@ pub fn contains_var(symbol_table: &SymbolTable, expr: &Expr, var_name: &str) ->
Expr::Vector(items) => items Expr::Vector(items) => items
.iter() .iter()
.any(|x| contains_var(symbol_table, x, var_name)), .any(|x| contains_var(symbol_table, x, var_name)),
Expr::Matrix(rows) => rows
.iter()
.any(|row| row.iter().any(|x| contains_var(symbol_table, x, var_name))),
Expr::Indexer(_, _) => false, Expr::Indexer(_, _) => false,
} }
} }

View File

@ -173,6 +173,7 @@ pub enum KalkValue {
Number(Float, Float, String), Number(Float, Float, String),
Boolean(bool), Boolean(bool),
Vector(Vec<KalkValue>), Vector(Vec<KalkValue>),
Matrix(Vec<Vec<KalkValue>>),
} }
impl KalkValue { impl KalkValue {
@ -192,7 +193,7 @@ impl KalkValue {
if &as_str == "0" { if &as_str == "0" {
imaginary_as_str imaginary_as_str
} else { } else {
format!("{} {} {}i", as_str, sign, imaginary_as_str) format!("{} {} {}i", as_str, sign, imaginary_as_str)
} }
} else { } else {
as_str as_str
@ -206,18 +207,43 @@ impl KalkValue {
} }
} }
KalkValue::Vector(values) => { KalkValue::Vector(values) => {
format!(
"({})",
values
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(", ")
)
}
KalkValue::Matrix(rows) => {
let mut value_strings = Vec::new();
let mut longest = 0;
for row in rows {
for value in row {
let value_str = value.to_string();
longest = longest.max(value_str.len());
value_strings.push(format!("{},", value_str));
}
value_strings.last_mut().unwrap().pop(); // Trailing comma
value_strings.push(String::from("\n"));
}
let mut result = String::from("["); let mut result = String::from("[");
for value in values { for value_str in value_strings {
result.push_str(&value.to_string()); if value_str == "\n" {
result.push_str(", "); result.push_str("\n ");
} else {
result.push_str(&format!("{:width$} ", value_str, width = longest + 1));
}
} }
if values.len() > 0 { result.pop(); // Trailing new-line
result.pop(); result.pop(); // Trailing space
result.pop(); result.pop(); // Trailing space
} result.pop(); // Trailing comma
result.push(']');
result.push_str("]");
result result
} }
@ -656,6 +682,9 @@ impl KalkValue {
KalkValue::Number(real, imaginary, _), KalkValue::Number(real, imaginary, _),
KalkValue::Number(real_rhs, imaginary_rhs, unit), KalkValue::Number(real_rhs, imaginary_rhs, unit),
) => KalkValue::Number(real + real_rhs, imaginary + imaginary_rhs, unit.to_string()), ) => KalkValue::Number(real + real_rhs, imaginary + imaginary_rhs, unit.to_string()),
(KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => {
calculate_matrix(self, rhs, &KalkValue::add_without_unit)
}
(KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => { (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
calculate_vector(self, rhs, &KalkValue::add_without_unit) calculate_vector(self, rhs, &KalkValue::add_without_unit)
} }
@ -669,6 +698,9 @@ impl KalkValue {
KalkValue::Number(real, imaginary, _), KalkValue::Number(real, imaginary, _),
KalkValue::Number(real_rhs, imaginary_rhs, unit), KalkValue::Number(real_rhs, imaginary_rhs, unit),
) => KalkValue::Number(real - real_rhs, imaginary - imaginary_rhs, unit.to_string()), ) => KalkValue::Number(real - real_rhs, imaginary - imaginary_rhs, unit.to_string()),
(KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => {
calculate_matrix(self, rhs, &KalkValue::sub_without_unit)
}
(KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => { (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
calculate_vector(self, rhs, &KalkValue::sub_without_unit) calculate_vector(self, rhs, &KalkValue::sub_without_unit)
} }
@ -677,26 +709,83 @@ impl KalkValue {
} }
pub(crate) fn mul_without_unit(self, rhs: &KalkValue) -> KalkValue { pub(crate) fn mul_without_unit(self, rhs: &KalkValue) -> KalkValue {
// (a + bi)(c + di) = ac + adi + bci + bdi² // Make sure matrix is always first to avoid having to match
match (self.clone(), rhs) { // different orders in the next match expression.
let (lhs, rhs) = match (&self, rhs) {
(KalkValue::Matrix(_), KalkValue::Matrix(_)) => (&self, rhs),
(_, KalkValue::Matrix(_)) => (rhs, &self),
_ => (&self, rhs),
};
match (lhs, rhs) {
( (
KalkValue::Number(real, imaginary, _), KalkValue::Number(real, imaginary, _),
KalkValue::Number(real_rhs, imaginary_rhs, unit), KalkValue::Number(real_rhs, imaginary_rhs, unit),
) => KalkValue::Number( ) => KalkValue::Number(
real.clone() * real_rhs.clone() - imaginary.clone() * imaginary_rhs.clone(), // (a + bi)(c + di) = ac + adi + bci + bdi²
real * imaginary_rhs + imaginary * real_rhs, real.clone() * real_rhs - imaginary.clone() * imaginary_rhs,
real.clone() * imaginary_rhs + imaginary * real_rhs,
unit.to_string(), unit.to_string(),
), ),
(KalkValue::Matrix(rows), KalkValue::Number(_, _, _)) => KalkValue::Matrix(
rows.iter()
.map(|row| {
row.iter()
.map(|x| x.clone().mul_without_unit(rhs))
.collect()
})
.collect(),
),
(KalkValue::Matrix(rows), KalkValue::Vector(values_rhs)) => {
if rows.first().unwrap().len() != values_rhs.len() {
return KalkValue::nan();
}
let mut new_values: Vec<KalkValue> = Vec::new();
for row in rows {
new_values.push(
row.iter()
.zip(values_rhs)
.map(|(x, y)| x.clone().mul_without_unit(y))
.sum(),
)
}
KalkValue::Vector(new_values)
}
(KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => {
if rows.first().unwrap().len() != rows_rhs.len() {
return KalkValue::nan();
}
let mut result = Vec::new();
// For every row in lhs
for i in 0..rows.len() {
let mut dot_products = Vec::new();
// For every column in rhs
for j in 0..rows.len() {
let mut dot_product = KalkValue::from(0f64);
// For every value in the current lhs row
for (k, value) in rows[i].iter().enumerate() {
let value_rhs = &rows_rhs[k][j];
dot_product = dot_product
.add_without_unit(&value.clone().mul_without_unit(value_rhs));
}
dot_products.push(dot_product);
}
result.push(dot_products);
}
KalkValue::Matrix(result)
}
(KalkValue::Vector(values), KalkValue::Number(_, _, _)) => KalkValue::Vector( (KalkValue::Vector(values), KalkValue::Number(_, _, _)) => KalkValue::Vector(
values values
.iter() .iter()
.map(|x| x.clone().mul_without_unit(&self)) .map(|x| x.clone().mul_without_unit(lhs))
.collect(),
),
(KalkValue::Number(_, _, _), KalkValue::Vector(values_rhs)) => KalkValue::Vector(
values_rhs
.iter()
.map(|x| self.clone().mul_without_unit(x))
.collect(), .collect(),
), ),
(KalkValue::Vector(values), KalkValue::Vector(values_rhs)) => { (KalkValue::Vector(values), KalkValue::Vector(values_rhs)) => {
@ -738,6 +827,9 @@ impl KalkValue {
) )
} }
} }
(KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => {
calculate_matrix(self, rhs, &KalkValue::div_without_unit)
}
(KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => { (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
calculate_vector(self, rhs, &KalkValue::div_without_unit) calculate_vector(self, rhs, &KalkValue::div_without_unit)
} }
@ -772,6 +864,35 @@ impl KalkValue {
KalkValue::Number(pow(real, real_rhs.clone()), float!(0), unit.to_string()) KalkValue::Number(pow(real, real_rhs.clone()), float!(0), unit.to_string())
} }
} }
(KalkValue::Matrix(rows), KalkValue::Number(real, imaginary, _)) => {
if real < &0f64
|| real.clone().fract() > 0.000001f64
|| !(imaginary == &0f64 || imaginary == &-0f64)
|| rows.len() != rows.first().unwrap().len()
{
return KalkValue::nan();
}
if real == &0f64 {
return KalkValue::from(1f64);
}
let mut result = KalkValue::from(1f64);
for _ in 0..primitive!(real) as i32 {
result = result.mul_without_unit(&self);
}
result
}
(KalkValue::Number(_, _, _), KalkValue::Matrix(rows)) => KalkValue::Matrix(
rows.iter()
.map(|row| {
row.iter()
.map(|x| self.clone().pow_without_unit(x))
.collect()
})
.collect(),
),
(KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => { (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
calculate_vector(self, rhs, &KalkValue::pow_without_unit) calculate_vector(self, rhs, &KalkValue::pow_without_unit)
} }
@ -898,6 +1019,66 @@ fn calculate_vector(
} }
} }
fn calculate_matrix(
x: KalkValue,
y: &KalkValue,
action: &dyn Fn(KalkValue, &KalkValue) -> KalkValue,
) -> KalkValue {
// Make sure matrix is always first to avoid having to match
// different orders in the next match expression.
let (x, y) = match (&x, y) {
(_, KalkValue::Matrix(_)) => (y, &x),
_ => (&x, y),
};
match (x, y) {
(KalkValue::Matrix(rows), KalkValue::Number(_, _, _)) => KalkValue::Matrix(
rows.iter()
.map(|row| row.iter().map(|x| action(x.clone(), y)).collect())
.collect(),
),
(KalkValue::Matrix(rows), KalkValue::Vector(values_rhs)) => {
if rows.len() != values_rhs.len() {
return KalkValue::nan();
}
let mut new_rows = Vec::new();
for (i, row) in rows.iter().enumerate() {
new_rows.push(Vec::new());
for value in row {
new_rows
.last_mut()
.unwrap()
.push(action(value.clone(), &values_rhs[i]))
}
}
KalkValue::Matrix(new_rows)
}
(KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => {
if rows.len() != rows_rhs.len()
|| rows.first().unwrap().len() != rows_rhs.first().unwrap().len()
{
return KalkValue::nan();
}
let mut new_rows = Vec::new();
for (i, row) in rows.iter().enumerate() {
new_rows.push(Vec::new());
for (j, value) in row.iter().enumerate() {
new_rows
.last_mut()
.unwrap()
.push(action(value.clone(), &rows_rhs[i][j]))
}
}
KalkValue::Matrix(new_rows)
}
_ => KalkValue::nan(),
}
}
fn calculate_unit( fn calculate_unit(
context: &mut crate::interpreter::Context, context: &mut crate::interpreter::Context,
left: &KalkValue, left: &KalkValue,
@ -937,6 +1118,20 @@ impl Into<String> for ScientificNotation {
} }
} }
impl std::iter::Sum<KalkValue> for KalkValue {
fn sum<I>(iter: I) -> KalkValue
where
I: std::iter::Iterator<Item = KalkValue>,
{
let mut sum = KalkValue::from(0f64);
for x in iter {
sum = sum.add_without_unit(&x);
}
sum
}
}
impl Into<String> for KalkValue { impl Into<String> for KalkValue {
fn into(self) -> String { fn into(self) -> String {
self.to_string() self.to_string()

View File

@ -105,6 +105,7 @@ pub enum CalcError {
ExpectedIf, ExpectedIf,
IncorrectAmountOfArguments(usize, String, usize), IncorrectAmountOfArguments(usize, String, usize),
ItemOfIndexDoesNotExist(usize), ItemOfIndexDoesNotExist(usize),
InconsistentColumnWidths,
InvalidNumberLiteral(String), InvalidNumberLiteral(String),
InvalidOperator, InvalidOperator,
InvalidUnit, InvalidUnit,
@ -134,6 +135,7 @@ impl ToString for CalcError {
expected, func, got expected, func, got
), ),
CalcError::ItemOfIndexDoesNotExist(index) => format!("Item of index {} does not exist.", index), CalcError::ItemOfIndexDoesNotExist(index) => format!("Item of index {} does not exist.", index),
CalcError::InconsistentColumnWidths => format!("Inconsistent column widths. Matrix columns must be the same size."),
CalcError::InvalidNumberLiteral(x) => format!("Invalid number literal: '{}'.", x), CalcError::InvalidNumberLiteral(x) => format!("Invalid number literal: '{}'.", x),
CalcError::InvalidOperator => format!("Invalid operator."), CalcError::InvalidOperator => format!("Invalid operator."),
CalcError::InvalidUnit => format!("Invalid unit."), CalcError::InvalidUnit => format!("Invalid unit."),
@ -614,12 +616,39 @@ fn parse_group_fn(context: &mut Context) -> Result<Expr, CalcError> {
} }
fn parse_vector(context: &mut Context) -> Result<Expr, CalcError> { fn parse_vector(context: &mut Context) -> Result<Expr, CalcError> {
advance(context); let kind = advance(context).kind;
if kind == TokenKind::OpenBracket {
skip_newlines(context);
}
let mut rows = vec![vec![parse_expr(context)?]];
let mut column_count = None;
let mut items_in_row = 1;
while match_token(context, TokenKind::Comma)
|| match_token(context, TokenKind::Semicolon)
|| (match_token(context, TokenKind::Newline)
&& peek_next(context).kind != TokenKind::ClosedBracket)
{
if kind == TokenKind::OpenBracket
&& (match_token(context, TokenKind::Newline)
|| match_token(context, TokenKind::Semicolon))
{
if let Some(columns) = column_count {
if columns != items_in_row {
return Err(CalcError::InconsistentColumnWidths);
}
} else {
column_count = Some(items_in_row);
}
rows.push(Vec::new());
items_in_row = 0;
}
let mut values = vec![parse_expr(context)?];
while match_token(context, TokenKind::Comma) {
advance(context); advance(context);
values.push(parse_expr(context)?); rows.last_mut().unwrap().push(parse_expr(context)?);
items_in_row += 1;
} }
if peek(context).kind == TokenKind::EOF { if peek(context).kind == TokenKind::EOF {
@ -628,12 +657,21 @@ fn parse_vector(context: &mut Context) -> Result<Expr, CalcError> {
))); )));
} }
if kind == TokenKind::OpenBracket {
skip_newlines(context);
}
advance(context); advance(context);
if values.len() == 1 { if rows.len() == 1 {
Ok(Expr::Group(Box::new(values.pop().unwrap()))) let mut values = rows.pop().unwrap();
if values.len() == 1 {
Ok(Expr::Group(Box::new(values.pop().unwrap())))
} else {
Ok(Expr::Vector(values))
}
} else { } else {
Ok(Expr::Vector(values)) Ok(Expr::Matrix(rows))
} }
} }

View File

@ -283,9 +283,8 @@ pub mod funcs {
let (real, imaginary, unit) = as_number_or_return!(x.clone()); let (real, imaginary, unit) = as_number_or_return!(x.clone());
if imaginary != 0f64 || real > 1f64 || real < -1f64 { if imaginary != 0f64 || real > 1f64 || real < -1f64 {
// -i * ln(i * sqrt(1 - z²) + z) // -i * ln(i * sqrt(1 - z²) + z)
let root = sqrt( let root =
KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x.clone())), sqrt(KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x)));
);
let iroot = multiply_with_i(root.clone()); let iroot = multiply_with_i(root.clone());
let (ln_real, ln_imaginary, ln_unit) = let (ln_real, ln_imaginary, ln_unit) =
as_number_or_return!(ln(iroot.add_without_unit(&x))); as_number_or_return!(ln(iroot.add_without_unit(&x)));
@ -361,14 +360,14 @@ pub mod funcs {
let (real, imaginary, unit) = as_number_or_return!(x.clone()); let (real, imaginary, unit) = as_number_or_return!(x.clone());
if imaginary != 0f64 || real == 0f64 { if imaginary != 0f64 || real == 0f64 {
let (inv_x2_real, inv_x2_imaginary, inv_x2_unit) = as_number_or_return!( let (inv_x2_real, inv_x2_imaginary, inv_x2_unit) = as_number_or_return!(
KalkValue::from(1f64).div_without_unit(&x.clone().mul_without_unit(&x.clone())) KalkValue::from(1f64).div_without_unit(&x.clone().mul_without_unit(&x))
); );
let sqrt = sqrt(KalkValue::Number( let sqrt = sqrt(KalkValue::Number(
1f64 + inv_x2_real, 1f64 + inv_x2_real,
inv_x2_imaginary, inv_x2_imaginary,
inv_x2_unit, inv_x2_unit,
)); ));
let inv_x = KalkValue::from(1f64).div_without_unit(&x.clone()); let inv_x = KalkValue::from(1f64).div_without_unit(&x);
ln(sqrt.add_without_unit(&inv_x)) ln(sqrt.add_without_unit(&inv_x))
} else { } else {
@ -390,7 +389,7 @@ pub mod funcs {
let (real, imaginary, unit) = as_number_or_return!(x.clone()); let (real, imaginary, unit) = as_number_or_return!(x.clone());
if imaginary != 0f64 || real <= 0f64 || real > 1f64 { if imaginary != 0f64 || real <= 0f64 || real > 1f64 {
// 1/z // 1/z
let inv = KalkValue::from(1f64).div_without_unit(&x.clone()); let inv = KalkValue::from(1f64).div_without_unit(&x);
let (inv_real, inv_imaginary, inv_unit) = as_number_or_return!(inv.clone()); let (inv_real, inv_imaginary, inv_unit) = as_number_or_return!(inv.clone());
// sqrt(1/z - 1) // sqrt(1/z - 1)
let sqrt1 = sqrt(KalkValue::Number( let sqrt1 = sqrt(KalkValue::Number(
@ -416,9 +415,8 @@ pub mod funcs {
let (real, imaginary, unit) = as_number_or_return!(x.clone()); let (real, imaginary, unit) = as_number_or_return!(x.clone());
if imaginary != 0f64 || real > 1f64 || real < -1f64 { if imaginary != 0f64 || real > 1f64 || real < -1f64 {
// i * ln(sqrt(1 - z²) - iz) // i * ln(sqrt(1 - z²) - iz)
let root = sqrt( let root =
KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x.clone())), sqrt(KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x)));
);
let iz = multiply_with_i(x.clone()); let iz = multiply_with_i(x.clone());
let ln = ln(root.sub_without_unit(&iz)); let ln = ln(root.sub_without_unit(&iz));
multiply_with_i(ln) multiply_with_i(ln)
@ -431,7 +429,7 @@ pub mod funcs {
let (real, imaginary, unit) = as_number_or_return!(x.clone()); let (real, imaginary, unit) = as_number_or_return!(x.clone());
if imaginary != 0f64 { if imaginary != 0f64 {
let (x2_real, x2_imaginary, x2_unit) = let (x2_real, x2_imaginary, x2_unit) =
as_number_or_return!(x.clone().mul_without_unit(&x.clone())); as_number_or_return!(x.clone().mul_without_unit(&x));
let sqrt = sqrt(KalkValue::Number(x2_real + 1f64, x2_imaginary, x2_unit)); let sqrt = sqrt(KalkValue::Number(x2_real + 1f64, x2_imaginary, x2_unit));
ln(x.add_without_unit(&sqrt)) ln(x.add_without_unit(&sqrt))

View File

@ -47,8 +47,19 @@
let highlightedTextElement: HTMLElement; let highlightedTextElement: HTMLElement;
let ignoreNextInput = false; let ignoreNextInput = false;
function setText(text: string) { enum HighlightType {
const [highlighted, offset] = highlight(text); Output,
InputField,
History,
}
function setText(text: string, isFinalBeforeSubmit = false) {
const [highlighted, offset] = highlight(
text,
isFinalBeforeSubmit
? HighlightType.History
: HighlightType.InputField
);
const prevCursorPos = inputElement.selectionStart; const prevCursorPos = inputElement.selectionStart;
setHtml(highlighted); setHtml(highlighted);
setCaret(prevCursorPos + offset); setCaret(prevCursorPos + offset);
@ -90,12 +101,18 @@
} }
} }
function hasUnevenAmountOfBraces(input: string): boolean { function hasUnevenAmountOfBrackets(
input: string,
openChar: string,
closedChar: string,
onlyCheckFirstLine = false
): boolean {
let openCount = 0; let openCount = 0;
let closedCount = 0; let closedCount = 0;
for (const char of input) { for (const char of input) {
if (char == "{") openCount++; if (onlyCheckFirstLine && char == "\n") break;
if (char == "}") closedCount++; if (char == openChar) openCount++;
if (char == closedChar) closedCount++;
} }
return openCount > closedCount; return openCount > closedCount;
@ -104,8 +121,15 @@
function handleKeyDown(event: KeyboardEvent, kalk: Kalk) { function handleKeyDown(event: KeyboardEvent, kalk: Kalk) {
if (event.key == "Enter") { if (event.key == "Enter") {
if ( if (
hasUnevenAmountOfBraces( hasUnevenAmountOfBrackets(
(event.target as HTMLTextAreaElement).value (event.target as HTMLTextAreaElement).value,
"{",
"}"
) ||
hasUnevenAmountOfBrackets(
(event.target as HTMLTextAreaElement).value,
"[",
"]"
) )
) { ) {
return; return;
@ -127,13 +151,13 @@
const [result, success] = calculate(kalk, input); const [result, success] = calculate(kalk, input);
output = success output = success
? highlight(result, true)[0] ? highlight(result, HighlightType.Output)[0]
: `<span style="color: ${errorcolor}">${result}</span>`; : `<span style="color: ${errorcolor}">${result}</span>`;
} }
// Highlight // Highlight
const target = event.target as HTMLInputElement; const target = event.target as HTMLInputElement;
setText(target.value); setText(target.value, true);
outputLines = output outputLines = output
? [...outputLines, [getHtml(), true], [output, false]] ? [...outputLines, [getHtml(), true], [output, false]]
@ -276,7 +300,7 @@
function highlight( function highlight(
input: string, input: string,
isOutput: boolean = false highlightType: HighlightType
): [string, number] { ): [string, number] {
if (!input) return ["", 0]; if (!input) return ["", 0];
let result = input; let result = input;
@ -316,9 +340,38 @@
if (substring.startsWith("\n")) { if (substring.startsWith("\n")) {
if (substring.endsWith("}")) { if (substring.endsWith("}")) {
return "<br />}"; return "<br />}";
} else if (substring.endsWith("]")) {
return "<br />]";
} else { } else {
if (!substring.match(/\n\s\s/)) offset += 2; let spaceCount = 2;
return isOutput ? "<br />" : "<br />&nbsp;&nbsp;"; const unclosedBracketInFirstLine =
hasUnevenAmountOfBrackets(
input,
"[",
"]",
true
);
if (unclosedBracketInFirstLine) {
let bracketIndex = input.indexOf("[");
spaceCount =
bracketIndex == -1
? spaceCount
: bracketIndex + 1;
}
if (!substring.match(/\n\s/)) offset += spaceCount;
if (highlightType == HighlightType.Output) {
return "<br />";
} else if (
highlightType == HighlightType.InputField
) {
return "<br />" + "&nbsp;".repeat(spaceCount);
} else if (highlightType == HighlightType.History) {
// Account for ">> "
return (
"<br />" + "&nbsp;".repeat(spaceCount + 3)
);
}
} }
} }
if (substring.match(/\s+/)) { if (substring.match(/\s+/)) {
@ -326,7 +379,7 @@
} }
} }
if (op && !isOutput) { if (op && highlightType != HighlightType.Output) {
if (substring == "*") return "⋅"; if (substring == "*") return "⋅";
if (substring == "/") return "÷"; if (substring == "/") return "÷";
} }