mirror of
https://github.com/PaddiM8/kalker.git
synced 2024-12-12 17:40:52 +01:00
Matrices
This commit is contained in:
parent
b937cef83d
commit
95fd78f3d1
@ -22,6 +22,7 @@ pub enum Expr {
|
||||
Literal(f64),
|
||||
Piecewise(Vec<ConditionalPiece>),
|
||||
Vector(Vec<Expr>),
|
||||
Matrix(Vec<Vec<Expr>>),
|
||||
Indexer(Box<Expr>, Box<Expr>),
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,7 @@ pub(crate) fn eval_expr(
|
||||
}
|
||||
Expr::Piecewise(pieces) => eval_piecewise(context, pieces, unit),
|
||||
Expr::Vector(values) => eval_vector(context, values),
|
||||
Expr::Matrix(rows) => eval_matrix(context, rows),
|
||||
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))
|
||||
}
|
||||
|
||||
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(
|
||||
context: &mut Context,
|
||||
var: &Expr,
|
||||
|
@ -88,6 +88,7 @@ fn invert(
|
||||
Expr::Literal(_) => Ok((target_expr, expr.clone())),
|
||||
Expr::Piecewise(_) => Err(CalcError::UnableToInvert(String::from("Piecewise"))),
|
||||
Expr::Vector(_) => Err(CalcError::UnableToInvert(String::from("Vector"))),
|
||||
Expr::Matrix(_) => Err(CalcError::UnableToInvert(String::from("Matrix"))),
|
||||
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
|
||||
.iter()
|
||||
.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,
|
||||
}
|
||||
}
|
||||
|
@ -173,6 +173,7 @@ pub enum KalkValue {
|
||||
Number(Float, Float, String),
|
||||
Boolean(bool),
|
||||
Vector(Vec<KalkValue>),
|
||||
Matrix(Vec<Vec<KalkValue>>),
|
||||
}
|
||||
|
||||
impl KalkValue {
|
||||
@ -192,7 +193,7 @@ impl KalkValue {
|
||||
if &as_str == "0" {
|
||||
imaginary_as_str
|
||||
} else {
|
||||
format!("{} {} {}i", as_str, sign, imaginary_as_str)
|
||||
format!("{} {} {}i", as_str, sign, imaginary_as_str)
|
||||
}
|
||||
} else {
|
||||
as_str
|
||||
@ -206,18 +207,43 @@ impl KalkValue {
|
||||
}
|
||||
}
|
||||
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("[");
|
||||
for value in values {
|
||||
result.push_str(&value.to_string());
|
||||
result.push_str(", ");
|
||||
for value_str in value_strings {
|
||||
if value_str == "\n" {
|
||||
result.push_str("\n ");
|
||||
} else {
|
||||
result.push_str(&format!("{:width$} ", value_str, width = longest + 1));
|
||||
}
|
||||
}
|
||||
|
||||
if values.len() > 0 {
|
||||
result.pop();
|
||||
result.pop();
|
||||
}
|
||||
|
||||
result.push_str("]");
|
||||
result.pop(); // Trailing new-line
|
||||
result.pop(); // Trailing space
|
||||
result.pop(); // Trailing space
|
||||
result.pop(); // Trailing comma
|
||||
result.push(']');
|
||||
|
||||
result
|
||||
}
|
||||
@ -656,6 +682,9 @@ impl KalkValue {
|
||||
KalkValue::Number(real, imaginary, _),
|
||||
KalkValue::Number(real_rhs, imaginary_rhs, unit),
|
||||
) => 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(_)) => {
|
||||
calculate_vector(self, rhs, &KalkValue::add_without_unit)
|
||||
}
|
||||
@ -669,6 +698,9 @@ impl KalkValue {
|
||||
KalkValue::Number(real, imaginary, _),
|
||||
KalkValue::Number(real_rhs, imaginary_rhs, unit),
|
||||
) => 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(_)) => {
|
||||
calculate_vector(self, rhs, &KalkValue::sub_without_unit)
|
||||
}
|
||||
@ -677,26 +709,83 @@ impl KalkValue {
|
||||
}
|
||||
|
||||
pub(crate) fn mul_without_unit(self, rhs: &KalkValue) -> KalkValue {
|
||||
// (a + bi)(c + di) = ac + adi + bci + bdi²
|
||||
match (self.clone(), rhs) {
|
||||
// Make sure matrix is always first to avoid having to match
|
||||
// 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_rhs, imaginary_rhs, unit),
|
||||
) => KalkValue::Number(
|
||||
real.clone() * real_rhs.clone() - imaginary.clone() * imaginary_rhs.clone(),
|
||||
real * imaginary_rhs + imaginary * real_rhs,
|
||||
// (a + bi)(c + di) = ac + adi + bci + bdi²
|
||||
real.clone() * real_rhs - imaginary.clone() * imaginary_rhs,
|
||||
real.clone() * imaginary_rhs + imaginary * real_rhs,
|
||||
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(
|
||||
values
|
||||
.iter()
|
||||
.map(|x| x.clone().mul_without_unit(&self))
|
||||
.collect(),
|
||||
),
|
||||
(KalkValue::Number(_, _, _), KalkValue::Vector(values_rhs)) => KalkValue::Vector(
|
||||
values_rhs
|
||||
.iter()
|
||||
.map(|x| self.clone().mul_without_unit(x))
|
||||
.map(|x| x.clone().mul_without_unit(lhs))
|
||||
.collect(),
|
||||
),
|
||||
(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(_)) => {
|
||||
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::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(_)) => {
|
||||
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(
|
||||
context: &mut crate::interpreter::Context,
|
||||
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 {
|
||||
fn into(self) -> String {
|
||||
self.to_string()
|
||||
|
@ -105,6 +105,7 @@ pub enum CalcError {
|
||||
ExpectedIf,
|
||||
IncorrectAmountOfArguments(usize, String, usize),
|
||||
ItemOfIndexDoesNotExist(usize),
|
||||
InconsistentColumnWidths,
|
||||
InvalidNumberLiteral(String),
|
||||
InvalidOperator,
|
||||
InvalidUnit,
|
||||
@ -134,6 +135,7 @@ impl ToString for CalcError {
|
||||
expected, func, got
|
||||
),
|
||||
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::InvalidOperator => format!("Invalid operator."),
|
||||
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> {
|
||||
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);
|
||||
values.push(parse_expr(context)?);
|
||||
rows.last_mut().unwrap().push(parse_expr(context)?);
|
||||
items_in_row += 1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if values.len() == 1 {
|
||||
Ok(Expr::Group(Box::new(values.pop().unwrap())))
|
||||
if rows.len() == 1 {
|
||||
let mut values = rows.pop().unwrap();
|
||||
if values.len() == 1 {
|
||||
Ok(Expr::Group(Box::new(values.pop().unwrap())))
|
||||
} else {
|
||||
Ok(Expr::Vector(values))
|
||||
}
|
||||
} else {
|
||||
Ok(Expr::Vector(values))
|
||||
Ok(Expr::Matrix(rows))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,9 +283,8 @@ pub mod funcs {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real > 1f64 || real < -1f64 {
|
||||
// -i * ln(i * sqrt(1 - z²) + z)
|
||||
let root = sqrt(
|
||||
KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x.clone())),
|
||||
);
|
||||
let root =
|
||||
sqrt(KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x)));
|
||||
let iroot = multiply_with_i(root.clone());
|
||||
let (ln_real, ln_imaginary, ln_unit) =
|
||||
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());
|
||||
if imaginary != 0f64 || real == 0f64 {
|
||||
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(
|
||||
1f64 + inv_x2_real,
|
||||
inv_x2_imaginary,
|
||||
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))
|
||||
} else {
|
||||
@ -390,7 +389,7 @@ pub mod funcs {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real <= 0f64 || real > 1f64 {
|
||||
// 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());
|
||||
// sqrt(1/z - 1)
|
||||
let sqrt1 = sqrt(KalkValue::Number(
|
||||
@ -416,9 +415,8 @@ pub mod funcs {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real > 1f64 || real < -1f64 {
|
||||
// i * ln(sqrt(1 - z²) - iz)
|
||||
let root = sqrt(
|
||||
KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x.clone())),
|
||||
);
|
||||
let root =
|
||||
sqrt(KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x)));
|
||||
let iz = multiply_with_i(x.clone());
|
||||
let ln = ln(root.sub_without_unit(&iz));
|
||||
multiply_with_i(ln)
|
||||
@ -431,7 +429,7 @@ pub mod funcs {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 {
|
||||
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));
|
||||
|
||||
ln(x.add_without_unit(&sqrt))
|
||||
|
@ -47,8 +47,19 @@
|
||||
let highlightedTextElement: HTMLElement;
|
||||
let ignoreNextInput = false;
|
||||
|
||||
function setText(text: string) {
|
||||
const [highlighted, offset] = highlight(text);
|
||||
enum HighlightType {
|
||||
Output,
|
||||
InputField,
|
||||
History,
|
||||
}
|
||||
|
||||
function setText(text: string, isFinalBeforeSubmit = false) {
|
||||
const [highlighted, offset] = highlight(
|
||||
text,
|
||||
isFinalBeforeSubmit
|
||||
? HighlightType.History
|
||||
: HighlightType.InputField
|
||||
);
|
||||
const prevCursorPos = inputElement.selectionStart;
|
||||
setHtml(highlighted);
|
||||
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 closedCount = 0;
|
||||
for (const char of input) {
|
||||
if (char == "{") openCount++;
|
||||
if (char == "}") closedCount++;
|
||||
if (onlyCheckFirstLine && char == "\n") break;
|
||||
if (char == openChar) openCount++;
|
||||
if (char == closedChar) closedCount++;
|
||||
}
|
||||
|
||||
return openCount > closedCount;
|
||||
@ -104,8 +121,15 @@
|
||||
function handleKeyDown(event: KeyboardEvent, kalk: Kalk) {
|
||||
if (event.key == "Enter") {
|
||||
if (
|
||||
hasUnevenAmountOfBraces(
|
||||
(event.target as HTMLTextAreaElement).value
|
||||
hasUnevenAmountOfBrackets(
|
||||
(event.target as HTMLTextAreaElement).value,
|
||||
"{",
|
||||
"}"
|
||||
) ||
|
||||
hasUnevenAmountOfBrackets(
|
||||
(event.target as HTMLTextAreaElement).value,
|
||||
"[",
|
||||
"]"
|
||||
)
|
||||
) {
|
||||
return;
|
||||
@ -127,13 +151,13 @@
|
||||
const [result, success] = calculate(kalk, input);
|
||||
|
||||
output = success
|
||||
? highlight(result, true)[0]
|
||||
? highlight(result, HighlightType.Output)[0]
|
||||
: `<span style="color: ${errorcolor}">${result}</span>`;
|
||||
}
|
||||
|
||||
// Highlight
|
||||
const target = event.target as HTMLInputElement;
|
||||
setText(target.value);
|
||||
setText(target.value, true);
|
||||
|
||||
outputLines = output
|
||||
? [...outputLines, [getHtml(), true], [output, false]]
|
||||
@ -276,7 +300,7 @@
|
||||
|
||||
function highlight(
|
||||
input: string,
|
||||
isOutput: boolean = false
|
||||
highlightType: HighlightType
|
||||
): [string, number] {
|
||||
if (!input) return ["", 0];
|
||||
let result = input;
|
||||
@ -316,9 +340,38 @@
|
||||
if (substring.startsWith("\n")) {
|
||||
if (substring.endsWith("}")) {
|
||||
return "<br />}";
|
||||
} else if (substring.endsWith("]")) {
|
||||
return "<br />]";
|
||||
} else {
|
||||
if (!substring.match(/\n\s\s/)) offset += 2;
|
||||
return isOutput ? "<br />" : "<br /> ";
|
||||
let spaceCount = 2;
|
||||
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 />" + " ".repeat(spaceCount);
|
||||
} else if (highlightType == HighlightType.History) {
|
||||
// Account for ">> "
|
||||
return (
|
||||
"<br />" + " ".repeat(spaceCount + 3)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (substring.match(/\s+/)) {
|
||||
@ -326,7 +379,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (op && !isOutput) {
|
||||
if (op && highlightType != HighlightType.Output) {
|
||||
if (substring == "*") return "⋅";
|
||||
if (substring == "/") return "÷";
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user