Vector indexing with lowered numbers, eg. v_1

This commit is contained in:
PaddiM8 2022-01-05 22:41:41 +01:00
parent 3b740423f7
commit c0cc4060a4
9 changed files with 104 additions and 18 deletions

View File

@ -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]));
} }
} }

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>),
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 {

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::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::*;

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::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,
} }
} }

View File

@ -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;
} }
end += 1; if is_subscript(&c) {
subscript.push(c);
} else {
value.push(c); value.push(c);
}
end += 1;
self.advance(); self.advance();
} }
@ -323,11 +329,23 @@ 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
}; };
if subscript.len() > 0 {
build(
kind,
&format!(
"{}_{}",
value,
crate::text_utils::subscript_to_normal(subscript.chars())
),
(start, end),
)
} else {
build(kind, &value, (start, end)) build(kind, &value, (start, end))
} }
}
fn peek(&mut self) -> Option<&char> { fn peek(&mut self) -> Option<&char> {
self.chars.peek() self.chars.peek()

View File

@ -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)))

View File

@ -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())
) )
} }
} }

View File

@ -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, ()>,

View File

@ -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,
}); });
} }