mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-02-12 14:39:15 +01:00
Special symbols for *, /, arc functions, and subscript
This commit is contained in:
parent
09fafcb573
commit
75460502ef
@ -4,8 +4,8 @@ use kalk::parser;
|
|||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use regex::Captures;
|
use regex::Captures;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rustyline::config::Configurer;
|
|
||||||
use rustyline::completion::Completer;
|
use rustyline::completion::Completer;
|
||||||
|
use rustyline::config::Configurer;
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use rustyline::highlight::Highlighter;
|
use rustyline::highlight::Highlighter;
|
||||||
use rustyline::hint::Hinter;
|
use rustyline::hint::Hinter;
|
||||||
@ -17,8 +17,8 @@ use rustyline::{Editor, Helper};
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::borrow::Cow::Owned;
|
use std::borrow::Cow::Owned;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::process;
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::process;
|
||||||
|
|
||||||
pub fn start(mut parser: &mut parser::Context, precision: u32) {
|
pub fn start(mut parser: &mut parser::Context, precision: u32) {
|
||||||
let mut editor = Editor::<RLHelper>::new();
|
let mut editor = Editor::<RLHelper>::new();
|
||||||
@ -96,8 +96,8 @@ impl Highlighter for LineHighlighter {
|
|||||||
|
|
||||||
let reg = Regex::new(
|
let reg = Regex::new(
|
||||||
r"(?x)
|
r"(?x)
|
||||||
(?P<op>([+\-/*%^!]|if|otherwise)) |
|
(?P<op>([+\-/*%^!×÷]|if|otherwise)) |
|
||||||
(?P<identifier>[^!-@\s_|^⌊⌋⌈⌉\[\]\{\}≠≥≤]+(_\d+)?)",
|
(?P<identifier>[^!-@\s_|^⌊⌋⌈⌉\[\]\{\}≠≥≤⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎]+(_\d+)?)",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -146,6 +146,21 @@ lazy_static! {
|
|||||||
m.insert("!=", "≠");
|
m.insert("!=", "≠");
|
||||||
m.insert(">=", "≥");
|
m.insert(">=", "≥");
|
||||||
m.insert("<=", "≤");
|
m.insert("<=", "≤");
|
||||||
|
m.insert("*", "×");
|
||||||
|
m.insert("/", "÷");
|
||||||
|
m.insert("asin", "sin⁻¹()");
|
||||||
|
m.insert("acos", "cos⁻¹()");
|
||||||
|
m.insert("atan", "tan⁻¹()");
|
||||||
|
m.insert("acot", "cot⁻¹()");
|
||||||
|
m.insert("acosec", "cosec⁻¹()");
|
||||||
|
m.insert("asec", "sec⁻¹()");
|
||||||
|
m.insert("asinh", "sinh⁻¹()");
|
||||||
|
m.insert("acosh", "cosh⁻¹()");
|
||||||
|
m.insert("atanh", "tanh⁻¹()");
|
||||||
|
m.insert("acoth", "coth⁻¹()");
|
||||||
|
m.insert("acosech", "cosech⁻¹()");
|
||||||
|
m.insert("asech", "sech⁻¹()");
|
||||||
|
m.insert("cbrt", "∛");
|
||||||
m
|
m
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -159,10 +174,25 @@ impl Completer for RLHelper {
|
|||||||
_ctx: &rustyline::Context<'_>,
|
_ctx: &rustyline::Context<'_>,
|
||||||
) -> Result<(usize, Vec<Self::Candidate>), ReadlineError> {
|
) -> Result<(usize, Vec<Self::Candidate>), ReadlineError> {
|
||||||
for key in COMPLETION_FUNCS.keys() {
|
for key in COMPLETION_FUNCS.keys() {
|
||||||
if line[..pos].ends_with(key) {
|
let slice = &line[..pos];
|
||||||
|
if slice.ends_with(key) {
|
||||||
let value = *COMPLETION_FUNCS.get(key).unwrap();
|
let value = *COMPLETION_FUNCS.get(key).unwrap();
|
||||||
return Ok((pos - key.len(), vec![value.to_string()]));
|
return Ok((pos - key.len(), vec![value.to_string()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut subscript_digits = String::new();
|
||||||
|
for c in slice.chars().rev() {
|
||||||
|
if c.is_digit(10) {
|
||||||
|
subscript_digits.insert(0, c);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if subscript_digits.len() > 0 {
|
||||||
|
let value = kalk::text_utils::digits_to_subscript(subscript_digits.chars());
|
||||||
|
return Ok((pos - subscript_digits.chars().count() - 1, vec![value]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((0, vec![line.to_string()]))
|
Ok((0, vec![line.to_string()]))
|
||||||
@ -171,11 +201,10 @@ impl Completer for RLHelper {
|
|||||||
fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) {
|
fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) {
|
||||||
line.backspace(line.pos() - start);
|
line.backspace(line.pos() - start);
|
||||||
line.insert_str(line.pos(), elected);
|
line.insert_str(line.pos(), elected);
|
||||||
line.move_forward(match elected {
|
line.move_forward(if elected.ends_with(")") {
|
||||||
"Σ()" => 2,
|
elected.chars().count() - 1
|
||||||
"∏()" => 2,
|
} else {
|
||||||
"∫()" => 2,
|
elected.chars().count()
|
||||||
_ => 1,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::text_utils::{is_subscript, is_superscript};
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::str::Chars;
|
use std::str::Chars;
|
||||||
@ -111,8 +112,8 @@ impl<'a> Lexer<'a> {
|
|||||||
let token = match c {
|
let token = match c {
|
||||||
'+' => build(TokenKind::Plus, "", span),
|
'+' => build(TokenKind::Plus, "", span),
|
||||||
'-' => build(TokenKind::Minus, "", span),
|
'-' => build(TokenKind::Minus, "", span),
|
||||||
'*' => build(TokenKind::Star, "", span),
|
'*' | '×' => build(TokenKind::Star, "", span),
|
||||||
'/' => build(TokenKind::Slash, "", span),
|
'/' | '÷' => build(TokenKind::Slash, "", span),
|
||||||
'^' => build(TokenKind::Power, "", span),
|
'^' => build(TokenKind::Power, "", span),
|
||||||
'|' => build(TokenKind::Pipe, "", span),
|
'|' => build(TokenKind::Pipe, "", span),
|
||||||
'⌈' => build(TokenKind::OpenCeil, "", span),
|
'⌈' => build(TokenKind::OpenCeil, "", span),
|
||||||
@ -217,7 +218,13 @@ impl<'a> Lexer<'a> {
|
|||||||
|
|
||||||
// Only allow identifiers with a special character to have *one* character. No more.
|
// Only allow identifiers with a special character to have *one* character. No more.
|
||||||
// Break the loop if it isn't the first run and the current character is a special character.
|
// Break the loop if it isn't the first run and the current character is a special character.
|
||||||
if end - start > 0 && !(c.is_ascii_alphabetic() || c == '\'' || c == '_') {
|
if end - start > 0
|
||||||
|
&& !(c.is_ascii_alphabetic()
|
||||||
|
|| c == '\''
|
||||||
|
|| c == '_'
|
||||||
|
|| is_superscript(&c)
|
||||||
|
|| is_subscript(&c))
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,9 +244,22 @@ impl<'a> Lexer<'a> {
|
|||||||
let value = match value.as_ref() {
|
let value = match value.as_ref() {
|
||||||
"Σ" | "∑" => String::from("sum"),
|
"Σ" | "∑" => String::from("sum"),
|
||||||
"∏" => String::from("prod"),
|
"∏" => String::from("prod"),
|
||||||
"∫" => String::from("integrate"),
|
"∫" | "integral" => String::from("integrate"),
|
||||||
|
"sin⁻¹" => String::from("asin"),
|
||||||
|
"cos⁻¹" => String::from("acos"),
|
||||||
|
"tan⁻¹" => String::from("atan"),
|
||||||
|
"cot⁻¹" => String::from("acot"),
|
||||||
|
"cosec⁻¹" => String::from("acosec"),
|
||||||
|
"sec⁻¹" => String::from("asec"),
|
||||||
|
"sinh⁻¹" => String::from("asinh"),
|
||||||
|
"cosh⁻¹" => String::from("acosh"),
|
||||||
|
"tanh⁻¹" => String::from("atanh"),
|
||||||
|
"coth⁻¹" => String::from("acoth"),
|
||||||
|
"cosech⁻¹" => String::from("acosech"),
|
||||||
|
"sech⁻¹" => String::from("asech"),
|
||||||
|
"∛" => String::from("cbrt"),
|
||||||
"°" => String::from("deg"),
|
"°" => String::from("deg"),
|
||||||
_ => value,
|
_ => value, // things like log₂ are handled in the parser
|
||||||
};
|
};
|
||||||
|
|
||||||
build(kind, &value, (start, end))
|
build(kind, &value, (start, end))
|
||||||
@ -268,8 +288,8 @@ fn is_valid_identifier(c: Option<&char>) -> bool {
|
|||||||
match c {
|
match c {
|
||||||
'+' | '-' | '/' | '*' | '%' | '^' | '!' | '(' | ')' | '=' | '.' | ',' | ';' | '|'
|
'+' | '-' | '/' | '*' | '%' | '^' | '!' | '(' | ')' | '=' | '.' | ',' | ';' | '|'
|
||||||
| '⌊' | '⌋' | '⌈' | '⌉' | '[' | ']' | '{' | '}' | 'π' | '√' | 'τ' | 'ϕ' | 'Γ' | '<'
|
| '⌊' | '⌋' | '⌈' | '⌉' | '[' | ']' | '{' | '}' | 'π' | '√' | 'τ' | 'ϕ' | 'Γ' | '<'
|
||||||
| '>' | '≠' | '≥' | '≤' => false,
|
| '>' | '≠' | '≥' | '≤' | '×' | '÷' => false,
|
||||||
_ => !c.is_digit(10),
|
_ => !c.is_digit(10) || is_superscript(c) || is_subscript(c),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -8,3 +8,4 @@ pub mod parser;
|
|||||||
mod prelude;
|
mod prelude;
|
||||||
mod symbol_table;
|
mod symbol_table;
|
||||||
mod test_helpers;
|
mod test_helpers;
|
||||||
|
pub mod text_utils;
|
||||||
|
@ -114,6 +114,7 @@ pub enum CalcError {
|
|||||||
UnableToSolveEquation,
|
UnableToSolveEquation,
|
||||||
UnableToOverrideConstant(String),
|
UnableToOverrideConstant(String),
|
||||||
UnableToParseExpression,
|
UnableToParseExpression,
|
||||||
|
UnrecognizedLogBase,
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +143,7 @@ impl ToString for CalcError {
|
|||||||
CalcError::UnableToParseExpression => format!("Unable to parse expression."),
|
CalcError::UnableToParseExpression => format!("Unable to parse expression."),
|
||||||
CalcError::UnableToSolveEquation => format!("Unable to solve equation."),
|
CalcError::UnableToSolveEquation => format!("Unable to solve equation."),
|
||||||
CalcError::UnableToOverrideConstant(name) => format!("Unable to override constant: '{}'.", name),
|
CalcError::UnableToOverrideConstant(name) => format!("Unable to override constant: '{}'.", name),
|
||||||
|
CalcError::UnrecognizedLogBase => format!("Unrecognized log base."),
|
||||||
CalcError::Unknown => format!("Unknown error."),
|
CalcError::Unknown => format!("Unknown error."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -589,8 +591,19 @@ fn parse_group_fn(context: &mut Context) -> Result<Expr, CalcError> {
|
|||||||
|
|
||||||
fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
||||||
let identifier = Identifier::from_full_name(&advance(context).value);
|
let identifier = Identifier::from_full_name(&advance(context).value);
|
||||||
|
|
||||||
|
let mut log_base = None;
|
||||||
|
if identifier.full_name.starts_with("log") {
|
||||||
|
if let Some(c) = identifier.full_name.chars().nth(3) {
|
||||||
|
if crate::text_utils::is_subscript(&c) {
|
||||||
|
log_base = Some(parse_log_base(&identifier)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let exists_as_fn = context.symbol_table.contains_fn(&identifier.pure_name)
|
let exists_as_fn = context.symbol_table.contains_fn(&identifier.pure_name)
|
||||||
|| context.current_function.as_ref() == Some(&identifier.pure_name);
|
|| context.current_function.as_ref() == Some(&identifier.pure_name)
|
||||||
|
|| log_base.is_some();
|
||||||
|
|
||||||
// Eg. sqrt64
|
// Eg. sqrt64
|
||||||
if exists_as_fn
|
if exists_as_fn
|
||||||
@ -602,6 +615,14 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
|||||||
} else {
|
} else {
|
||||||
parse_factor(context)?
|
parse_factor(context)?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(log_base) = log_base {
|
||||||
|
return Ok(Expr::FnCall(
|
||||||
|
Identifier::from_full_name("log"),
|
||||||
|
vec![parameter, log_base],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(Expr::FnCall(identifier, vec![parameter]));
|
return Ok(Expr::FnCall(identifier, vec![parameter]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,6 +658,11 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
|||||||
context.is_in_integral = false;
|
context.is_in_integral = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(log_base) = log_base {
|
||||||
|
parameters.push(log_base);
|
||||||
|
return Ok(Expr::FnCall(Identifier::from_full_name("log"), parameters));
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(Expr::FnCall(identifier, parameters));
|
return Ok(Expr::FnCall(identifier, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,6 +739,15 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_log_base(identifier: &Identifier) -> Result<Expr, CalcError> {
|
||||||
|
let subscript = identifier.full_name.chars().skip(3);
|
||||||
|
if let Some(base) = crate::text_utils::parse_subscript(subscript) {
|
||||||
|
Ok(Expr::Literal(base))
|
||||||
|
} else {
|
||||||
|
Err(CalcError::UnrecognizedLogBase)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn split_into_variables(context: &mut Context, identifier: &Identifier) -> Result<Expr, CalcError> {
|
fn split_into_variables(context: &mut Context, identifier: &Identifier) -> Result<Expr, CalcError> {
|
||||||
let mut chars: Vec<char> = identifier.pure_name.chars().collect();
|
let mut chars: Vec<char> = identifier.pure_name.chars().collect();
|
||||||
let mut left = Expr::Var(Identifier::from_full_name(&chars[0].to_string()));
|
let mut left = Expr::Var(Identifier::from_full_name(&chars[0].to_string()));
|
||||||
|
77
kalk/src/text_utils.rs
Normal file
77
kalk/src/text_utils.rs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
pub fn is_superscript(c: &char) -> bool {
|
||||||
|
match c {
|
||||||
|
'⁰' | '¹' | '²' | '³' | '⁴' | '⁵' | '⁶' | '⁷' | '⁸' | '⁹' | '⁺' | '⁻' | '⁼' | '⁽' | '⁾' => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_subscript(c: &char) -> bool {
|
||||||
|
match c {
|
||||||
|
'₀' | '₁' | '₂' | '₃' | '₄' | '₅' | '₆' | '₇' | '₈' | '₉' | '₊' | '₋' | '₌' | '₍' | '₎' => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_subscript(chars: impl Iterator<Item = char>) -> Option<f64> {
|
||||||
|
if let Ok(result) = subscript_to_digits(chars).parse::<f64>() {
|
||||||
|
Some(result)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscript_to_digits(chars: impl Iterator<Item = char>) -> String {
|
||||||
|
let mut regular = String::new();
|
||||||
|
for c in chars {
|
||||||
|
regular.push(match c {
|
||||||
|
'₀' => '0',
|
||||||
|
'₁' => '1',
|
||||||
|
'₂' => '2',
|
||||||
|
'₃' => '3',
|
||||||
|
'₄' => '4',
|
||||||
|
'₅' => '5',
|
||||||
|
'₆' => '6',
|
||||||
|
'₇' => '7',
|
||||||
|
'₈' => '8',
|
||||||
|
'₉' => '9',
|
||||||
|
'₊' => '+',
|
||||||
|
'₋' => '-',
|
||||||
|
'₌' => '=',
|
||||||
|
'₍' => '(',
|
||||||
|
'₎' => ')',
|
||||||
|
_ => c,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return regular;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn digits_to_subscript(chars: impl Iterator<Item = char>) -> String {
|
||||||
|
let mut subscript = String::new();
|
||||||
|
for c in chars {
|
||||||
|
subscript.push(match c {
|
||||||
|
'0' => '₀',
|
||||||
|
'1' => '₁',
|
||||||
|
'2' => '₂',
|
||||||
|
'3' => '₃',
|
||||||
|
'4' => '₄',
|
||||||
|
'5' => '₅',
|
||||||
|
'6' => '₆',
|
||||||
|
'7' => '₇',
|
||||||
|
'8' => '₈',
|
||||||
|
'9' => '₉',
|
||||||
|
'+' => '₊',
|
||||||
|
'-' => '₋',
|
||||||
|
'=' => '₌',
|
||||||
|
'(' => '₍',
|
||||||
|
')' => '₎',
|
||||||
|
_ => c,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return subscript;
|
||||||
|
}
|
@ -45,7 +45,6 @@
|
|||||||
let calculatorElement: HTMLElement;
|
let calculatorElement: HTMLElement;
|
||||||
let inputElement: HTMLTextAreaElement;
|
let inputElement: HTMLTextAreaElement;
|
||||||
let highlightedTextElement: HTMLElement;
|
let highlightedTextElement: HTMLElement;
|
||||||
let hasBeenInteractedWith = false;
|
|
||||||
let ignoreNextInput = false;
|
let ignoreNextInput = false;
|
||||||
|
|
||||||
function setText(text: string) {
|
function setText(text: string) {
|
||||||
@ -103,7 +102,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyDown(event: KeyboardEvent, kalk: Kalk) {
|
function handleKeyDown(event: KeyboardEvent, kalk: Kalk) {
|
||||||
hasBeenInteractedWith = true;
|
|
||||||
if (event.key == "Enter") {
|
if (event.key == "Enter") {
|
||||||
if (
|
if (
|
||||||
hasUnevenAmountOfBraces(
|
hasUnevenAmountOfBraces(
|
||||||
@ -119,7 +117,7 @@
|
|||||||
|
|
||||||
if (input.trim() == "help") {
|
if (input.trim() == "help") {
|
||||||
output = `<a style="color: ${linkcolor}"
|
output = `<a style="color: ${linkcolor}"
|
||||||
href="https://kalker.strct.net/#usage"
|
href="https://kalker.xyz/#usage"
|
||||||
target="blank">Link to usage guide</a>`;
|
target="blank">Link to usage guide</a>`;
|
||||||
} else if (input.trim() == "clear") {
|
} else if (input.trim() == "clear") {
|
||||||
outputLines = [];
|
outputLines = [];
|
||||||
@ -133,6 +131,10 @@
|
|||||||
: `<span style="color: ${errorcolor}">${result}</span>`;
|
: `<span style="color: ${errorcolor}">${result}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Highlight
|
||||||
|
const target = event.target as HTMLInputElement;
|
||||||
|
setText(target.value);
|
||||||
|
|
||||||
outputLines = output
|
outputLines = output
|
||||||
? [...outputLines, [getHtml(), true], [output, false]]
|
? [...outputLines, [getHtml(), true], [output, false]]
|
||||||
: [...outputLines, [getHtml(), true]];
|
: [...outputLines, [getHtml(), true]];
|
||||||
@ -277,7 +279,7 @@
|
|||||||
let result = input;
|
let result = input;
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
result = result.replace(
|
result = result.replace(
|
||||||
/(?<comparison>(!=|[<>]=?))|(?<html>[<>&]|(\n\s*\}?|\s+))|(?<op>([+\-/*%^!≈]|if|otherwise)|(?<identifier>[^!-@\s_|^⌊⌋⌈⌉≈\[\]\{\}≠≥≤]+(_\d+)?)\(?)/g,
|
/(?<comparison>(!=|[<>]=?))|(?<html>[<>&]|(\n\s*\}?|\s+))|(?<op>([+\-/*%^!≈×÷]|if|otherwise)|(?<identifier>[^!-@\s_|^⌊⌋⌈⌉≈\[\]\{\}≠≥≤⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎]+(_\d+)?)\(?)/g,
|
||||||
(substring, _, comparison, _2, html, _3, op, identifier) => {
|
(substring, _, comparison, _2, html, _3, op, identifier) => {
|
||||||
if (comparison) {
|
if (comparison) {
|
||||||
if (substring == "<=") return "≤";
|
if (substring == "<=") return "≤";
|
||||||
@ -302,6 +304,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (op) {
|
||||||
|
if (substring == "*") return "×";
|
||||||
|
if (substring == "/") return "÷";
|
||||||
|
}
|
||||||
|
|
||||||
if (identifier) {
|
if (identifier) {
|
||||||
let substringWithoutParen = substring.endsWith("(")
|
let substringWithoutParen = substring.endsWith("(")
|
||||||
? substring.slice(0, -1)
|
? substring.slice(0, -1)
|
||||||
@ -353,6 +360,80 @@
|
|||||||
newSubstring = "⌈⌉";
|
newSubstring = "⌈⌉";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "asin": {
|
||||||
|
newSubstring = "sin⁻¹";
|
||||||
|
addParen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "acos": {
|
||||||
|
newSubstring = "cos⁻¹";
|
||||||
|
addParen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "atan": {
|
||||||
|
newSubstring = "tan⁻¹";
|
||||||
|
addParen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "acot": {
|
||||||
|
newSubstring = "cot⁻¹";
|
||||||
|
addParen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "acosec": {
|
||||||
|
newSubstring = "cosec⁻¹";
|
||||||
|
addParen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "asec": {
|
||||||
|
newSubstring = "sec⁻¹";
|
||||||
|
addParen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "asinh": {
|
||||||
|
newSubstring = "sinh⁻¹";
|
||||||
|
addParen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "acosh": {
|
||||||
|
newSubstring = "cosh⁻¹";
|
||||||
|
addParen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "acoth": {
|
||||||
|
newSubstring = "coth⁻¹";
|
||||||
|
addParen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "acosech": {
|
||||||
|
newSubstring = "cosech⁻¹";
|
||||||
|
addParen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "asech": {
|
||||||
|
newSubstring = "sech⁻¹";
|
||||||
|
addParen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "cbrt": {
|
||||||
|
newSubstring = "∛";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let underscoreIndex = newSubstring.lastIndexOf("_");
|
||||||
|
if (underscoreIndex != -1) {
|
||||||
|
let subscript = "";
|
||||||
|
for (
|
||||||
|
let i = underscoreIndex + 1;
|
||||||
|
i < newSubstring.length;
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
subscript += digitToSubscript(newSubstring[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
newSubstring =
|
||||||
|
newSubstring.slice(0, underscoreIndex) + subscript;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset -= substring.length - newSubstring.length;
|
offset -= substring.length - newSubstring.length;
|
||||||
@ -376,8 +457,54 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
result = result.replace(/([_₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎]\d+)/g, (substring) => {
|
||||||
|
let newSubstring = substring
|
||||||
|
.replace("_", "")
|
||||||
|
.replace(/\d/, digitToSubscript);
|
||||||
|
offset -= substring.length - newSubstring.length;
|
||||||
|
|
||||||
|
return newSubstring;
|
||||||
|
});
|
||||||
|
|
||||||
return [result, offset];
|
return [result, offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function digitToSubscript(input: string): string {
|
||||||
|
switch (input) {
|
||||||
|
case "0":
|
||||||
|
return "₀";
|
||||||
|
case "1":
|
||||||
|
return "₁";
|
||||||
|
case "2":
|
||||||
|
return "₂";
|
||||||
|
case "3":
|
||||||
|
return "₃";
|
||||||
|
case "4":
|
||||||
|
return "₄";
|
||||||
|
case "5":
|
||||||
|
return "₅";
|
||||||
|
case "6":
|
||||||
|
return "₆";
|
||||||
|
case "7":
|
||||||
|
return "₇";
|
||||||
|
case "8":
|
||||||
|
return "₈";
|
||||||
|
case "9":
|
||||||
|
return "₉";
|
||||||
|
case "+":
|
||||||
|
return "₊";
|
||||||
|
case "-":
|
||||||
|
return "₋";
|
||||||
|
case "=":
|
||||||
|
return "₌";
|
||||||
|
case "(":
|
||||||
|
return "₍";
|
||||||
|
case ")":
|
||||||
|
return "₎";
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="calculator" bind:this={calculatorElement}>
|
<div class="calculator" bind:this={calculatorElement}>
|
||||||
|
Loading…
Reference in New Issue
Block a user