mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-01-31 08:59:15 +01:00
Vector indexing with lowered numbers, eg. v_1
This commit is contained in:
parent
443a1876fb
commit
098bb8fb90
@ -201,7 +201,7 @@ impl Completer for RLHelper {
|
||||
}
|
||||
|
||||
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]));
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ pub enum Expr {
|
||||
Literal(f64),
|
||||
Piecewise(Vec<ConditionalPiece>),
|
||||
Vector(Vec<Expr>),
|
||||
Indexer(Box<Expr>, Box<Expr>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@ -67,6 +68,14 @@ impl Identifier {
|
||||
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 {
|
||||
|
@ -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::Indexer(var, index) => eval_indexer(context, var, index, unit),
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,9 +251,10 @@ fn eval_var_expr(
|
||||
.symbol_table
|
||||
.get_var(identifier.full_name.as_ref() as &str)
|
||||
.cloned();
|
||||
match var_decl {
|
||||
Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr, unit),
|
||||
_ => Err(CalcError::UndefinedVar(identifier.full_name.clone())),
|
||||
if let Some(Stmt::VarDecl(_, expr)) = var_decl {
|
||||
eval_expr(context, &expr, unit)
|
||||
} 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))
|
||||
}
|
||||
|
||||
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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -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::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
|
||||
.iter()
|
||||
.any(|x| contains_var(symbol_table, x, var_name)),
|
||||
Expr::Indexer(_, _) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,7 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
|
||||
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>()
|
||||
.unwrap_or(10);
|
||||
}
|
||||
@ -266,6 +266,7 @@ impl<'a> Lexer<'a> {
|
||||
let start = self.index;
|
||||
let mut end = start;
|
||||
let mut value = String::new();
|
||||
let mut subscript = String::new();
|
||||
|
||||
while is_valid_identifier(self.peek()) {
|
||||
let c = *self.peek().unwrap();
|
||||
@ -292,8 +293,13 @@ impl<'a> Lexer<'a> {
|
||||
break;
|
||||
}
|
||||
|
||||
if is_subscript(&c) {
|
||||
subscript.push(c);
|
||||
} else {
|
||||
value.push(c);
|
||||
}
|
||||
|
||||
end += 1;
|
||||
value.push(c);
|
||||
self.advance();
|
||||
}
|
||||
|
||||
@ -323,10 +329,22 @@ impl<'a> Lexer<'a> {
|
||||
"sech⁻¹" => String::from("asech"),
|
||||
"∛" => String::from("cbrt"),
|
||||
"°" => 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> {
|
||||
|
@ -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.
|
||||
#[wasm_bindgen]
|
||||
#[derive(Clone)]
|
||||
pub struct Context {
|
||||
tokens: Vec<Token>,
|
||||
pos: usize,
|
||||
@ -99,10 +100,12 @@ impl Default for Context {
|
||||
/// Error that occured during parsing or evaluation.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum CalcError {
|
||||
CanOnlyIndexVectors,
|
||||
Expected(String),
|
||||
ExpectedDx,
|
||||
ExpectedIf,
|
||||
IncorrectAmountOfArguments(usize, String, usize),
|
||||
ItemOfIndexDoesNotExist(usize),
|
||||
InvalidNumberLiteral(String),
|
||||
InvalidOperator,
|
||||
InvalidUnit,
|
||||
@ -123,6 +126,7 @@ pub enum CalcError {
|
||||
impl ToString for CalcError {
|
||||
fn to_string(&self) -> String {
|
||||
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::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."),
|
||||
@ -130,6 +134,7 @@ impl ToString for CalcError {
|
||||
"Expected {} arguments for function {}, but got {}.",
|
||||
expected, func, got
|
||||
),
|
||||
CalcError::ItemOfIndexDoesNotExist(index) => format!("Item of index {} does not exist.", index),
|
||||
CalcError::InvalidNumberLiteral(x) => format!("Invalid number literal: '{}'.", x),
|
||||
CalcError::InvalidOperator => format!("Invalid operator."),
|
||||
CalcError::InvalidUnit => format!("Invalid unit."),
|
||||
@ -607,7 +612,7 @@ fn parse_vector(context: &mut Context) -> Result<Expr, CalcError> {
|
||||
|
||||
if peek(context).kind == TokenKind::EOF {
|
||||
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;
|
||||
if identifier.full_name.starts_with("log") {
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -700,6 +705,28 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
||||
// Eg. x
|
||||
if parse_as_var_instead || context.symbol_table.contains_var(&identifier.pure_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 {
|
||||
context.unit_decl_base_unit = Some(identifier.full_name);
|
||||
Ok(Expr::Var(Identifier::from_full_name(DECL_UNIT)))
|
||||
|
@ -65,7 +65,7 @@ pub fn to_radix_pretty(value: f64, radix: u8) -> String {
|
||||
format!(
|
||||
"{}{}",
|
||||
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 std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SymbolTable {
|
||||
pub(crate) hashmap: HashMap<String, Stmt>,
|
||||
pub(crate) unit_types: HashMap<String, ()>,
|
||||
|
@ -9,22 +9,21 @@ pub fn is_superscript(c: &char) -> bool {
|
||||
|
||||
pub fn is_subscript(c: &char) -> bool {
|
||||
match c {
|
||||
'₀' | '₁' | '₂' | '₃' | '₄' | '₅' | '₆' | '₇' | '₈' | '₉' | '₊' | '₋' | '₌' | '₍' | '₎' => {
|
||||
true
|
||||
}
|
||||
'₀' | '₁' | '₂' | '₃' | '₄' | '₅' | '₆' | '₇' | '₈' | '₉' | '₊' | '₋' | '₌' | '₍' | '₎'
|
||||
| 'ₖ' | 'ₗ' | 'ₘ' | 'ₙ' | 'ₓ' => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
} else {
|
||||
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();
|
||||
for c in chars {
|
||||
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,
|
||||
});
|
||||
}
|
||||
@ -50,7 +54,7 @@ pub fn subscript_to_digits(chars: impl Iterator<Item = char>) -> 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();
|
||||
for c in chars {
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user