mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-06-25 04:01:51 +02:00
Vector indexing with lowered numbers, eg. v_1
This commit is contained in:
parent
3b740423f7
commit
c0cc4060a4
@ -201,7 +201,7 @@ impl Completer for RLHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if subscript_digits.len() > 0 {
|
if subscript_digits.len() > 0 {
|
||||||
let value = kalk::text_utils::digits_to_subscript(subscript_digits.chars());
|
let value = kalk::text_utils::normal_to_subscript(subscript_digits.chars());
|
||||||
return Ok((pos - subscript_digits.chars().count() - 1, vec![value]));
|
return Ok((pos - subscript_digits.chars().count() - 1, vec![value]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ pub enum Expr {
|
|||||||
Literal(f64),
|
Literal(f64),
|
||||||
Piecewise(Vec<ConditionalPiece>),
|
Piecewise(Vec<ConditionalPiece>),
|
||||||
Vector(Vec<Expr>),
|
Vector(Vec<Expr>),
|
||||||
|
Indexer(Box<Expr>, Box<Expr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@ -67,6 +68,14 @@ impl Identifier {
|
|||||||
prime_count: 0u32,
|
prime_count: 0u32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_name_without_lowered(&self) -> &str {
|
||||||
|
if let Some(underscore_pos) = self.pure_name.find('_') {
|
||||||
|
&self.pure_name[0..underscore_pos]
|
||||||
|
} else {
|
||||||
|
&self.pure_name
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_literal_ast(kalk_value: &crate::kalk_value::KalkValue) -> Expr {
|
pub fn build_literal_ast(kalk_value: &crate::kalk_value::KalkValue) -> Expr {
|
||||||
|
@ -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::Indexer(var, index) => eval_indexer(context, var, index, unit),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,9 +251,10 @@ fn eval_var_expr(
|
|||||||
.symbol_table
|
.symbol_table
|
||||||
.get_var(identifier.full_name.as_ref() as &str)
|
.get_var(identifier.full_name.as_ref() as &str)
|
||||||
.cloned();
|
.cloned();
|
||||||
match var_decl {
|
if let Some(Stmt::VarDecl(_, expr)) = var_decl {
|
||||||
Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr, unit),
|
eval_expr(context, &expr, unit)
|
||||||
_ => Err(CalcError::UndefinedVar(identifier.full_name.clone())),
|
} else {
|
||||||
|
Err(CalcError::UndefinedVar(identifier.full_name.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,6 +536,25 @@ fn eval_vector(context: &mut Context, values: &Vec<Expr>) -> Result<KalkValue, C
|
|||||||
Ok(KalkValue::Vector(eval_values))
|
Ok(KalkValue::Vector(eval_values))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn eval_indexer(
|
||||||
|
context: &mut Context,
|
||||||
|
var: &Expr,
|
||||||
|
index: &Expr,
|
||||||
|
unit: &str,
|
||||||
|
) -> Result<KalkValue, CalcError> {
|
||||||
|
let var_value = eval_expr(context, var, unit)?;
|
||||||
|
if let KalkValue::Vector(values) = var_value {
|
||||||
|
let index_value = eval_expr(context, index, unit)?.to_f64() as usize;
|
||||||
|
if let Some(value) = values.get(index_value) {
|
||||||
|
Ok(value.clone())
|
||||||
|
} else {
|
||||||
|
Err(CalcError::ItemOfIndexDoesNotExist(index_value))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(CalcError::CanOnlyIndexVectors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -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::Indexer(_, _) => Err(CalcError::UnableToInvert(String::from("Inverter"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,6 +391,7 @@ 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::Indexer(_, _) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ impl<'a> Lexer<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if base_str != "" {
|
if base_str != "" {
|
||||||
base = crate::text_utils::subscript_to_digits(base_str.chars())
|
base = crate::text_utils::subscript_to_normal(base_str.chars())
|
||||||
.parse::<u8>()
|
.parse::<u8>()
|
||||||
.unwrap_or(10);
|
.unwrap_or(10);
|
||||||
}
|
}
|
||||||
@ -266,6 +266,7 @@ impl<'a> Lexer<'a> {
|
|||||||
let start = self.index;
|
let start = self.index;
|
||||||
let mut end = start;
|
let mut end = start;
|
||||||
let mut value = String::new();
|
let mut value = String::new();
|
||||||
|
let mut subscript = String::new();
|
||||||
|
|
||||||
while is_valid_identifier(self.peek()) {
|
while is_valid_identifier(self.peek()) {
|
||||||
let c = *self.peek().unwrap();
|
let c = *self.peek().unwrap();
|
||||||
@ -292,8 +293,13 @@ impl<'a> Lexer<'a> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if is_subscript(&c) {
|
||||||
|
subscript.push(c);
|
||||||
|
} else {
|
||||||
|
value.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
end += 1;
|
end += 1;
|
||||||
value.push(c);
|
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,10 +329,22 @@ impl<'a> Lexer<'a> {
|
|||||||
"sech⁻¹" => String::from("asech"),
|
"sech⁻¹" => String::from("asech"),
|
||||||
"∛" => String::from("cbrt"),
|
"∛" => String::from("cbrt"),
|
||||||
"°" => String::from("deg"),
|
"°" => String::from("deg"),
|
||||||
_ => value, // things like log₂ are handled in the parser
|
_ => value, // things like log_2 are handled in the parser
|
||||||
};
|
};
|
||||||
|
|
||||||
build(kind, &value, (start, end))
|
if subscript.len() > 0 {
|
||||||
|
build(
|
||||||
|
kind,
|
||||||
|
&format!(
|
||||||
|
"{}_{}",
|
||||||
|
value,
|
||||||
|
crate::text_utils::subscript_to_normal(subscript.chars())
|
||||||
|
),
|
||||||
|
(start, end),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
build(kind, &value, (start, end))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&mut self) -> Option<&char> {
|
fn peek(&mut self) -> Option<&char> {
|
||||||
|
@ -14,6 +14,7 @@ pub const DEFAULT_ANGLE_UNIT: &'static str = "rad";
|
|||||||
|
|
||||||
/// Struct containing the current state of the parser. It stores user-defined functions and variables.
|
/// Struct containing the current state of the parser. It stores user-defined functions and variables.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
tokens: Vec<Token>,
|
tokens: Vec<Token>,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
@ -99,10 +100,12 @@ impl Default for Context {
|
|||||||
/// Error that occured during parsing or evaluation.
|
/// Error that occured during parsing or evaluation.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum CalcError {
|
pub enum CalcError {
|
||||||
|
CanOnlyIndexVectors,
|
||||||
Expected(String),
|
Expected(String),
|
||||||
ExpectedDx,
|
ExpectedDx,
|
||||||
ExpectedIf,
|
ExpectedIf,
|
||||||
IncorrectAmountOfArguments(usize, String, usize),
|
IncorrectAmountOfArguments(usize, String, usize),
|
||||||
|
ItemOfIndexDoesNotExist(usize),
|
||||||
InvalidNumberLiteral(String),
|
InvalidNumberLiteral(String),
|
||||||
InvalidOperator,
|
InvalidOperator,
|
||||||
InvalidUnit,
|
InvalidUnit,
|
||||||
@ -123,6 +126,7 @@ pub enum CalcError {
|
|||||||
impl ToString for CalcError {
|
impl ToString for CalcError {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
CalcError::CanOnlyIndexVectors => format!("Indexing (getting an item with a specific index) is only possible on vectors."),
|
||||||
CalcError::Expected(description) => format!("Expected: {}", description),
|
CalcError::Expected(description) => format!("Expected: {}", description),
|
||||||
CalcError::ExpectedDx => format!("Expected eg. dx, to specify for which variable the operation is being done to. Example with integration: ∫(0, 1, x dx) or ∫(0, 1, x, dx). You may need to put parenthesis around the expression before dx/dy/du/etc."),
|
CalcError::ExpectedDx => format!("Expected eg. dx, to specify for which variable the operation is being done to. Example with integration: ∫(0, 1, x dx) or ∫(0, 1, x, dx). You may need to put parenthesis around the expression before dx/dy/du/etc."),
|
||||||
CalcError::ExpectedIf => format!("Expected 'if', with a condition after it."),
|
CalcError::ExpectedIf => format!("Expected 'if', with a condition after it."),
|
||||||
@ -130,6 +134,7 @@ impl ToString for CalcError {
|
|||||||
"Expected {} arguments for function {}, but got {}.",
|
"Expected {} arguments for function {}, but got {}.",
|
||||||
expected, func, got
|
expected, func, got
|
||||||
),
|
),
|
||||||
|
CalcError::ItemOfIndexDoesNotExist(index) => format!("Item of index {} does not exist.", index),
|
||||||
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."),
|
||||||
@ -607,7 +612,7 @@ fn parse_vector(context: &mut Context) -> Result<Expr, CalcError> {
|
|||||||
|
|
||||||
if peek(context).kind == TokenKind::EOF {
|
if peek(context).kind == TokenKind::EOF {
|
||||||
return Err(CalcError::Expected(String::from(
|
return Err(CalcError::Expected(String::from(
|
||||||
"Closing group symbol, eg. ⌋",
|
"Closing group symbol, eg. )",
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -626,7 +631,7 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
|||||||
let mut log_base = None;
|
let mut log_base = None;
|
||||||
if identifier.full_name.starts_with("log") {
|
if identifier.full_name.starts_with("log") {
|
||||||
if let Some(c) = identifier.full_name.chars().nth(3) {
|
if let Some(c) = identifier.full_name.chars().nth(3) {
|
||||||
if crate::text_utils::is_subscript(&c) {
|
if c == '_' {
|
||||||
log_base = Some(Expr::Literal(get_base(&identifier.full_name)? as f64));
|
log_base = Some(Expr::Literal(get_base(&identifier.full_name)? as f64));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -700,6 +705,28 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
|||||||
// Eg. x
|
// Eg. x
|
||||||
if parse_as_var_instead || context.symbol_table.contains_var(&identifier.pure_name) {
|
if parse_as_var_instead || context.symbol_table.contains_var(&identifier.pure_name) {
|
||||||
Ok(build_var(context, &identifier.full_name))
|
Ok(build_var(context, &identifier.full_name))
|
||||||
|
} else if context
|
||||||
|
.symbol_table
|
||||||
|
.contains_var(&identifier.get_name_without_lowered())
|
||||||
|
{
|
||||||
|
let underscore_pos = identifier.pure_name.find('_').unwrap();
|
||||||
|
let var_name = &identifier.pure_name[0..underscore_pos];
|
||||||
|
let lowered = &identifier.pure_name[underscore_pos + 1..];
|
||||||
|
|
||||||
|
// Create a new identical context to avoid changing the current one
|
||||||
|
let mut lowered_context = context.clone();
|
||||||
|
let mut parsed_stmts = parse(&mut lowered_context, &lowered)?;
|
||||||
|
let parsed_lowered = parsed_stmts
|
||||||
|
.pop()
|
||||||
|
.unwrap_or(Stmt::Expr(Box::new(Expr::Literal(0f64))));
|
||||||
|
if let Stmt::Expr(lowered_expr) = parsed_lowered {
|
||||||
|
Ok(Expr::Indexer(
|
||||||
|
Box::new(Expr::Var(Identifier::from_full_name(&var_name))),
|
||||||
|
lowered_expr,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(CalcError::UnableToParseExpression)
|
||||||
|
}
|
||||||
} else if context.parsing_unit_decl {
|
} else if context.parsing_unit_decl {
|
||||||
context.unit_decl_base_unit = Some(identifier.full_name);
|
context.unit_decl_base_unit = Some(identifier.full_name);
|
||||||
Ok(Expr::Var(Identifier::from_full_name(DECL_UNIT)))
|
Ok(Expr::Var(Identifier::from_full_name(DECL_UNIT)))
|
||||||
|
@ -65,7 +65,7 @@ pub fn to_radix_pretty(value: f64, radix: u8) -> String {
|
|||||||
format!(
|
format!(
|
||||||
"{}{}",
|
"{}{}",
|
||||||
float_to_radix(value, radix),
|
float_to_radix(value, radix),
|
||||||
crate::text_utils::digits_to_subscript(radix.to_string().chars())
|
crate::text_utils::normal_to_subscript(radix.to_string().chars())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{ast::Expr, ast::Identifier, ast::Stmt, prelude};
|
use crate::{ast::Expr, ast::Identifier, ast::Stmt, prelude};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SymbolTable {
|
pub struct SymbolTable {
|
||||||
pub(crate) hashmap: HashMap<String, Stmt>,
|
pub(crate) hashmap: HashMap<String, Stmt>,
|
||||||
pub(crate) unit_types: HashMap<String, ()>,
|
pub(crate) unit_types: HashMap<String, ()>,
|
||||||
|
@ -9,22 +9,21 @@ pub fn is_superscript(c: &char) -> bool {
|
|||||||
|
|
||||||
pub fn is_subscript(c: &char) -> bool {
|
pub fn is_subscript(c: &char) -> bool {
|
||||||
match c {
|
match c {
|
||||||
'₀' | '₁' | '₂' | '₃' | '₄' | '₅' | '₆' | '₇' | '₈' | '₉' | '₊' | '₋' | '₌' | '₍' | '₎' => {
|
'₀' | '₁' | '₂' | '₃' | '₄' | '₅' | '₆' | '₇' | '₈' | '₉' | '₊' | '₋' | '₌' | '₍' | '₎'
|
||||||
true
|
| 'ₖ' | 'ₗ' | 'ₘ' | 'ₙ' | 'ₓ' => true,
|
||||||
}
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_subscript(chars: impl Iterator<Item = char>) -> Option<u8> {
|
pub fn parse_subscript(chars: impl Iterator<Item = char>) -> Option<u8> {
|
||||||
if let Ok(result) = subscript_to_digits(chars).parse::<u8>() {
|
if let Ok(result) = subscript_to_normal(chars).parse::<u8>() {
|
||||||
Some(result)
|
Some(result)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscript_to_digits(chars: impl Iterator<Item = char>) -> String {
|
pub fn subscript_to_normal(chars: impl Iterator<Item = char>) -> String {
|
||||||
let mut regular = String::new();
|
let mut regular = String::new();
|
||||||
for c in chars {
|
for c in chars {
|
||||||
regular.push(match c {
|
regular.push(match c {
|
||||||
@ -43,6 +42,11 @@ pub fn subscript_to_digits(chars: impl Iterator<Item = char>) -> String {
|
|||||||
'₌' => '=',
|
'₌' => '=',
|
||||||
'₍' => '(',
|
'₍' => '(',
|
||||||
'₎' => ')',
|
'₎' => ')',
|
||||||
|
'ₖ' => 'k',
|
||||||
|
'ₗ' => 'l',
|
||||||
|
'ₘ' => 'm',
|
||||||
|
'ₙ' => 'n',
|
||||||
|
'ₓ' => 'x',
|
||||||
_ => c,
|
_ => c,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -50,7 +54,7 @@ pub fn subscript_to_digits(chars: impl Iterator<Item = char>) -> String {
|
|||||||
return regular.trim().to_string();
|
return regular.trim().to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn digits_to_subscript(chars: impl Iterator<Item = char>) -> String {
|
pub fn normal_to_subscript(chars: impl Iterator<Item = char>) -> String {
|
||||||
let mut subscript = String::new();
|
let mut subscript = String::new();
|
||||||
for c in chars {
|
for c in chars {
|
||||||
subscript.push(match c {
|
subscript.push(match c {
|
||||||
@ -69,6 +73,11 @@ pub fn digits_to_subscript(chars: impl Iterator<Item = char>) -> String {
|
|||||||
'=' => '₌',
|
'=' => '₌',
|
||||||
'(' => '₍',
|
'(' => '₍',
|
||||||
')' => '₎',
|
')' => '₎',
|
||||||
|
'k' => 'ₖ',
|
||||||
|
'l' => 'ₗ',
|
||||||
|
'm' => 'ₘ',
|
||||||
|
'n' => 'ₙ',
|
||||||
|
'x' => 'ₓ',
|
||||||
_ => c,
|
_ => c,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user