Vector indexing with lowered numbers, eg. v_1

This commit is contained in:
PaddiM8 2022-01-05 22:41:41 +01:00
parent 443a1876fb
commit 098bb8fb90
9 changed files with 104 additions and 18 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

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.
#[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)))

View File

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

View File

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

View File

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