From 7b0435d3ecdd720a45a63757147acacb62d05050 Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Fri, 14 Jan 2022 15:08:19 +0100 Subject: [PATCH] Separated parsing into two phases The first phase (parser.rs) is generally context-free, and builds a naive AST where for example function calls are parsed as `f*(x)` where `f` is a variable and `(x)`is a group. The second phase (analysis.rs) then creates a new AST, where these are turned into a function call expression instead. --- kalk/src/analysis.rs | 494 +++++++++++++++++++++++++++++++++++++++++++ kalk/src/ast.rs | 8 + kalk/src/lib.rs | 1 + kalk/src/parser.rs | 386 +++++---------------------------- 4 files changed, 561 insertions(+), 328 deletions(-) create mode 100644 kalk/src/analysis.rs diff --git a/kalk/src/analysis.rs b/kalk/src/analysis.rs new file mode 100644 index 0000000..c2a005a --- /dev/null +++ b/kalk/src/analysis.rs @@ -0,0 +1,494 @@ +use crate::{ + ast::{ConditionalPiece, Expr, Identifier, Stmt}, + inverter, + lexer::TokenKind, + parser::{self, CalcError}, + prelude, + symbol_table::SymbolTable, +}; + +pub(crate) struct Context<'a> { + pub(crate) symbol_table: &'a mut SymbolTable, + current_function_name: Option, + current_function_parameters: Option>, + equation_variable: Option, + in_integral: bool, + in_unit_decl: bool, + in_conditional: bool, + in_equation: bool, +} + +pub(crate) fn analyse_stmt( + symbol_table: &mut SymbolTable, + statement: Stmt, +) -> Result { + let mut context = Context { + symbol_table, + current_function_name: None, + current_function_parameters: None, + equation_variable: None, + in_integral: false, + in_unit_decl: false, + in_conditional: false, + in_equation: false, + }; + + Ok(match statement { + Stmt::VarDecl(identifier, value) => { + let var_decl = Stmt::VarDecl(identifier, Box::new(analyse_expr(&mut context, *value)?)); + context.symbol_table.insert(var_decl.clone()); + + var_decl + } + Stmt::FnDecl(identifier, parameters, body) => { + context.current_function_name = Some(identifier.pure_name.clone()); + context.current_function_parameters = Some(parameters.clone()); + let fn_decl = Stmt::FnDecl( + identifier, + parameters, + Box::new(analyse_expr(&mut context, *body)?), + ); + context.symbol_table.insert(fn_decl.clone()); + context.current_function_name = None; + context.current_function_parameters = None; + + fn_decl + } + Stmt::UnitDecl(identifier, unit, value) => { + context.in_unit_decl = true; + let result = Stmt::UnitDecl( + identifier, + unit, + Box::new(analyse_expr(&mut context, *value)?), + ); + context.in_unit_decl = false; + + result + } + Stmt::Expr(value) => analyse_stmt_expr(&mut context, *value)?, + }) +} + +fn analyse_stmt_expr(context: &mut Context, value: Expr) -> Result { + // Apparently you can't pattern match boxed types in the stable compiler. + // This is a mess... + let value = if let Expr::Binary(left, TokenKind::Equals, right) = value { + if let Expr::Binary(identifier_expr, TokenKind::Star, parameter_expr) = *left { + match *identifier_expr { + Expr::Var(identifier) if !prelude::is_prelude_func(&identifier.full_name) => { + let mut parameters = Vec::new(); + match *parameter_expr { + Expr::Vector(exprs) => { + for expr in exprs { + if let Expr::Var(argument_identifier) = expr { + parameters.push(format!( + "{}-{}", + identifier.pure_name, argument_identifier.pure_name + )); + } + } + } + Expr::Group(expr) => { + if let Expr::Var(argument_identifier) = *expr { + parameters.push(format!( + "{}-{}", + identifier.pure_name, argument_identifier.pure_name + )); + } + } + _ => unreachable!(), + } + + context.current_function_name = Some(identifier.pure_name.clone()); + context.current_function_parameters = Some(parameters.clone()); + let fn_decl = Stmt::FnDecl( + identifier, + parameters, + Box::new(analyse_expr(context, *right)?), + ); + context.symbol_table.insert(fn_decl.clone()); + context.current_function_name = None; + context.current_function_parameters = None; + + return Ok(fn_decl); + } + _ => Expr::Binary( + Box::new(Expr::Binary( + identifier_expr, + TokenKind::Star, + parameter_expr, + )), + TokenKind::Equals, + right, + ), + } + } else { + Expr::Binary(left, TokenKind::Equals, right) + } + } else { + value + }; + + Ok(Stmt::Expr(Box::new(analyse_expr(context, value)?))) +} + +fn analyse_expr(context: &mut Context, expr: Expr) -> Result { + Ok(match expr { + Expr::Binary(left, op, right) => analyse_binary(context, *left, op, *right)?, + Expr::Unary(op, value) => Expr::Unary(op, Box::new(analyse_expr(context, *value)?)), + Expr::Unit(name, value) => Expr::Unit(name, Box::new(analyse_expr(context, *value)?)), + Expr::Var(identifier) => analyse_var(context, identifier, None, None)?, + Expr::Group(value) => Expr::Group(Box::new(analyse_expr(context, *value)?)), + Expr::FnCall(identifier, arguments) => { + let mut analysed_arguments = Vec::new(); + for argument in arguments { + analysed_arguments.push(analyse_expr(context, argument)?); + } + + Expr::FnCall(identifier, analysed_arguments) + } + Expr::Literal(_) => expr, + Expr::Piecewise(pieces) => { + let mut analysed_pieces = Vec::new(); + for piece in pieces { + context.in_conditional = true; + let condition = analyse_expr(context, piece.condition)?; + context.in_conditional = false; + + analysed_pieces.push(ConditionalPiece { + condition, + expr: analyse_expr(context, piece.expr)?, + }); + } + + Expr::Piecewise(analysed_pieces) + } + Expr::Vector(values) => { + let mut analysed_values = Vec::new(); + for value in values { + analysed_values.push(analyse_expr(context, value)?); + } + + Expr::Vector(analysed_values) + } + Expr::Matrix(rows) => { + let mut analysed_rows = Vec::new(); + for row in rows { + let mut analysed_values = Vec::new(); + for value in row { + analysed_values.push(analyse_expr(context, value)?); + } + + analysed_rows.push(analysed_values); + } + + Expr::Matrix(analysed_rows) + } + Expr::Indexer(value, indexes) => { + let mut analysed_indexes = Vec::new(); + for index in indexes { + analysed_indexes.push(analyse_expr(context, index)?); + } + + Expr::Indexer(Box::new(analyse_expr(context, *value)?), analysed_indexes) + } + }) +} + +fn analyse_binary<'a>( + context: &'a mut Context, + left: Expr, + op: TokenKind, + right: Expr, +) -> Result { + let right = analyse_expr(context, right)?; + match (&left, &op) { + (_, TokenKind::Equals) if !context.in_conditional => { + // Equation + context.in_equation = true; + let left = analyse_expr(context, left)?; + context.in_equation = false; + + let var_name = if let Some(var_name) = &context.equation_variable { + var_name + } else { + return Err(CalcError::UnableToSolveEquation); + }; + + let inverted = if inverter::contains_var(&mut context.symbol_table, &left, var_name) { + left.invert_to_target(context.symbol_table, right, var_name)? + } else { + right.invert_to_target(context.symbol_table, left, var_name)? + }; + + // If the inverted expression still contains the variable, + // the equation solving failed. + if inverter::contains_var(context.symbol_table, &inverted, var_name) { + return Err(CalcError::UnableToSolveEquation); + } + + context.symbol_table.insert(Stmt::VarDecl( + Identifier::from_full_name(var_name), + Box::new(inverted.clone()), + )); + + return Ok(inverted); + } + (Expr::Var(_), TokenKind::Star) => { + if let Expr::Var(identifier) = left { + analyse_var(context, identifier, Some(right), None) + } else { + unreachable!() + } + } + (Expr::Var(_), TokenKind::Power) => { + if let Expr::Var(identifier) = left { + analyse_var(context, identifier, None, Some(right)) + } else { + unreachable!() + } + } + _ => Ok(Expr::Binary( + Box::new(analyse_expr(context, left)?), + op, + Box::new(right), + )), + } +} + +fn analyse_var( + context: &mut Context, + identifier: Identifier, + adjacent_factor: Option, + adjacent_exponent: Option, +) -> Result { + let mut log_base = None; + if identifier.full_name.starts_with("log") { + if let Some(lowered) = identifier.get_lowered_part() { + if let Ok(lowered_float) = lowered.parse::() { + log_base = Some(Expr::Literal(lowered_float)); + } + } + } + + // Eg. f(1, 2, 3), f(3) or f3 + let exists_as_fn = context.symbol_table.contains_fn(&identifier.pure_name) + || context.current_function_name.as_ref() == Some(&identifier.pure_name) + || log_base.is_some(); + if exists_as_fn { + if let Some(adjacent_expr) = adjacent_factor { + return with_adjacent( + build_fn_call(context, identifier, adjacent_expr, log_base)?, + None, + adjacent_exponent, + ); + } + } + + if context.symbol_table.contains_var(&identifier.pure_name) + || (identifier.pure_name.len() == 1 && !context.in_equation) + { + with_adjacent( + build_var(context, &identifier.full_name), + adjacent_factor, + adjacent_exponent, + ) + } else if context + .symbol_table + .contains_var(&identifier.get_name_without_lowered()) + { + with_adjacent( + build_indexed_var(context, identifier)?, + adjacent_factor, + adjacent_exponent, + ) + } else if context.in_unit_decl { + with_adjacent( + build_var(context, parser::DECL_UNIT), + adjacent_factor, + adjacent_exponent, + ) + } else { + if let Some(equation_var) = &context.equation_variable { + if &identifier.full_name == equation_var { + return with_adjacent( + build_var(context, &identifier.full_name), + adjacent_factor, + adjacent_exponent, + ); + } + } + + if context.in_equation { + context.equation_variable = Some(identifier.full_name.clone()); + return with_adjacent( + build_var(context, &identifier.full_name), + adjacent_factor, + adjacent_exponent, + ); + } + + let mut identifier_without_dx: Vec = identifier.full_name.chars().collect(); + let last_char = identifier_without_dx.pop().unwrap_or_default(); + let second_last_char = identifier_without_dx.pop().unwrap_or_default(); + + if context.in_integral && second_last_char == 'd' && identifier.pure_name.len() > 2 { + let new_identifier: String = identifier_without_dx.iter().collect(); + with_adjacent( + build_dx(context, &new_identifier, last_char)?, + adjacent_factor, + adjacent_exponent, + ) + } else { + build_split_up_vars(context, identifier, adjacent_exponent) + } + } +} + +fn with_adjacent( + expr: Expr, + factor: Option, + exponent: Option, +) -> Result { + if let Some(factor) = factor { + Ok(Expr::Binary( + Box::new(expr), + TokenKind::Star, + Box::new(factor), + )) + } else if let Some(exponent) = exponent { + Ok(Expr::Binary( + Box::new(expr), + TokenKind::Power, + Box::new(exponent), + )) + } else { + Ok(expr) + } +} + +fn build_fn_call( + context: &mut Context, + identifier: Identifier, + adjacent_expr: Expr, + log_base: Option, +) -> Result { + let is_integral = identifier.full_name == "integrate"; + if is_integral { + context.in_integral = true; + } + + let arguments = match adjacent_expr { + Expr::Vector(arguments) => { + let mut new_arguments = Vec::new(); + for argument in arguments { + new_arguments.push(analyse_expr(context, argument)?); + } + + new_arguments + } + Expr::Group(argument) => { + if let Some(log_base) = log_base { + return Ok(Expr::FnCall( + Identifier::from_full_name("log"), + vec![analyse_expr(context, *argument)?, log_base], + )); + } else { + vec![analyse_expr(context, *argument)?] + } + } + _ => unreachable!(), + }; + + if is_integral { + context.in_integral = false; + } + + return Ok(Expr::FnCall(identifier, arguments)); +} + +fn build_indexed_var(context: &mut Context, identifier: Identifier) -> Result { + 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..]; + let lowered_expr = if lowered.len() > 0 && lowered.chars().nth(0).unwrap_or('\0').is_digit(10) { + Expr::Literal(lowered.parse::().unwrap_or(f64::NAN)) + } else { + build_var(context, lowered) + }; + + Ok(Expr::Indexer( + Box::new(build_var(context, &var_name)), + vec![lowered_expr], + )) +} + +fn build_dx( + context: &mut Context, + name_without_dx: &str, + char_after_d: char, +) -> Result { + Ok(Expr::Binary( + Box::new(build_var(context, name_without_dx)), + TokenKind::Star, + Box::new(build_var(context, &char_after_d.to_string())), + )) +} + +fn build_split_up_vars( + context: &mut Context, + identifier: Identifier, + adjacent_exponent: Option, +) -> Result { + let mut chars: Vec = identifier.pure_name.chars().collect(); + let last_char = chars.pop().unwrap_or_default(); + let identifier_without_last: String = chars.iter().collect(); + + // Temporarily remove the last character and check if a function + // without that character exists. If it does, + // create a function call expression, where that last character + // is the argument. + if context.symbol_table.contains_fn(&identifier_without_last) { + return Ok(Expr::FnCall( + Identifier::from_full_name(&identifier_without_last), + vec![build_var(context, &last_char.to_string())], + )); + } else { + // Otherwise, re-add the character. + chars.push(last_char); + } + + // Turn each individual character into its own variable reference. + // This parses eg `xy` as `x*y` instead of *one* variable. + let mut left = build_var(context, &chars.first().unwrap().to_string()); + let mut chars_iter = chars.iter().skip(1).peekable(); + let mut adjacent_exponent = adjacent_exponent; + while let Some(c) = chars_iter.next() { + let mut right = build_var(context, &c.to_string()); + + // If last iteration + if chars_iter.peek().is_none() { + if let Some(exponent) = adjacent_exponent { + right = Expr::Binary(Box::new(right), TokenKind::Power, Box::new(exponent)); + adjacent_exponent = None; + } + } + + left = Expr::Binary(Box::new(left), TokenKind::Star, Box::new(right)) + } + + Ok(left) +} + +fn build_var(context: &Context, name: &str) -> Expr { + if let (Some(function_name), Some(params)) = ( + context.current_function_name.as_ref(), + context.current_function_parameters.as_ref(), + ) { + let identifier = Identifier::parameter_from_name(name, &function_name); + if params.contains(&identifier.full_name) { + return Expr::Var(identifier); + } + } + return Expr::Var(Identifier::from_full_name(name)); +} diff --git a/kalk/src/ast.rs b/kalk/src/ast.rs index cf8d34e..ce64401 100644 --- a/kalk/src/ast.rs +++ b/kalk/src/ast.rs @@ -77,6 +77,14 @@ impl Identifier { &self.pure_name } } + + pub fn get_lowered_part(&self) -> Option<&str> { + if let Some(underscore_pos) = self.pure_name.find('_') { + Some(&self.pure_name[underscore_pos + 1..]) + } else { + None + } + } } pub fn build_literal_ast(kalk_value: &crate::kalk_value::KalkValue) -> Expr { diff --git a/kalk/src/lib.rs b/kalk/src/lib.rs index a9dfbc3..c1663d3 100644 --- a/kalk/src/lib.rs +++ b/kalk/src/lib.rs @@ -1,3 +1,4 @@ +mod analysis; pub mod ast; pub mod calculation_result; mod calculus; diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 0b6f931..5c1fa94 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -1,3 +1,6 @@ +use std::cell::Cell; + +use crate::analysis; use crate::ast::Identifier; use crate::calculation_result::CalculationResult; use crate::{ @@ -17,7 +20,7 @@ pub const DEFAULT_ANGLE_UNIT: &'static str = "rad"; pub struct Context { tokens: Vec, pos: usize, - symbol_table: SymbolTable, + symbol_table: Cell, angle_unit: String, timeout: Option, /// This is true whenever the parser is currently parsing a unit declaration. @@ -27,12 +30,8 @@ pub struct Context { /// When a unit declaration is being parsed, this value will be set /// whenever a unit in the expression is found. Eg. unit a = 3b, it will be set to Some("b") unit_decl_base_unit: Option, - parsing_identifier_stmt: bool, equation_variable: Option, contains_equation_equal_sign: bool, - is_in_integral: bool, - current_function: Option, - current_function_parameters: Option>, other_radix: Option, } @@ -43,17 +42,13 @@ impl Context { let mut context = Self { tokens: Vec::new(), pos: 0, - symbol_table: SymbolTable::new(), + symbol_table: Cell::from(SymbolTable::new()), angle_unit: DEFAULT_ANGLE_UNIT.into(), timeout: None, parsing_unit_decl: false, unit_decl_base_unit: None, - parsing_identifier_stmt: false, equation_variable: None, contains_equation_equal_sign: false, - is_in_integral: false, - current_function: None, - current_function_parameters: None, other_radix: None, }; @@ -178,8 +173,9 @@ pub fn eval( input.contains("=") && !input.contains("[") && !input.contains("{"); let statements = parse(context, input)?; + let mut symbol_table = context.symbol_table.get_mut(); let mut interpreter = interpreter::Context::new( - &mut context.symbol_table, + &mut symbol_table, &context.angle_unit, #[cfg(feature = "rug")] precision, @@ -211,7 +207,10 @@ pub fn parse(context: &mut Context, input: &str) -> Result, CalcError> let mut statements: Vec = Vec::new(); while !is_at_end(context) { - statements.push(parse_stmt(context)?); + let parsed = parse_stmt(context)?; + let mut symbol_table = context.symbol_table.get_mut(); + let analysed = analysis::analyse_stmt(&mut symbol_table, parsed)?; + statements.push(analysed); if match_token(context, TokenKind::Semicolon) { advance(context); @@ -227,7 +226,6 @@ fn parse_stmt(context: &mut Context) -> Result { if match_token(context, TokenKind::Identifier) { return Ok(match peek_next(context).kind { TokenKind::Equals => parse_var_decl_stmt(context)?, - TokenKind::OpenParenthesis => parse_identifier_stmt(context)?, _ => Stmt::Expr(Box::new(parse_expr(context)?)), }); } else if match_token(context, TokenKind::UnitKeyword) { @@ -237,63 +235,6 @@ fn parse_stmt(context: &mut Context) -> Result { Ok(Stmt::Expr(Box::new(parse_expr(context)?))) } -fn parse_identifier_stmt(context: &mut Context) -> Result { - let began_at = context.pos; - context.parsing_identifier_stmt = true; - let primary = parse_primary(context)?; // Since function declarations and function calls look the same at first, simply parse a "function call", and re-use the data. - context.parsing_identifier_stmt = false; - - // If `primary` is followed by an equal sign - // treat it as a function declaration - if let TokenKind::Equals = peek(context).kind { - // Use the "function call" expression that was parsed, and put its values into a function declaration statement instead. - if let Expr::FnCall(identifier, parameters) = primary { - if !prelude::is_prelude_func(&identifier.full_name) { - advance(context); - - // All the "arguments" are expected to be parsed as variables, - // since parameter definitions look the same as variable references. - // Extract these. - let mut parameter_identifiers = Vec::new(); - for parameter in parameters { - if let Expr::Var(parameter_identifier) = parameter { - parameter_identifiers.push(format!( - "{}-{}", - &identifier.pure_name, ¶meter_identifier.full_name - )); - } - } - - context.current_function = Some(identifier.pure_name.clone()); - context.current_function_parameters = Some(parameter_identifiers.clone()); - context.contains_equation_equal_sign = false; - context.equation_variable = None; - - // Piecewise - let expr = if match_token(context, TokenKind::OpenBrace) { - parse_piecewise(context)? - } else { - parse_expr(context)? - }; - let fn_decl = Stmt::FnDecl(identifier, parameter_identifiers, Box::new(expr)); - context.current_function = None; - context.current_function_parameters = None; - - // Insert the function declaration into the symbol table during parsing - // so that the parser can find out if particular functions exist. - context.symbol_table.insert(fn_decl.clone()); - - return Ok(fn_decl); - } - } - } - - // It is a function call or eg. x(x + 3), not a function declaration. - // Redo the parsing for this specific part. - context.pos = began_at; - Ok(Stmt::Expr(Box::new(parse_expr(context)?))) -} - fn parse_piecewise(context: &mut Context) -> Result { advance(context); skip_newlines(context); @@ -349,7 +290,11 @@ fn parse_var_decl_stmt(context: &mut Context) -> Result { context.contains_equation_equal_sign = false; context.equation_variable = None; let expr = parse_expr(context)?; - if inverter::contains_var(&context.symbol_table, &expr, &identifier.value) { + if inverter::contains_var( + &mut context.symbol_table.get_mut(), + &expr, + &identifier.value, + ) { return Err(CalcError::VariableReferencesItself); } @@ -386,12 +331,12 @@ fn parse_unit_decl_stmt(context: &mut Context) -> Result { let stmt_inv = Stmt::UnitDecl( base_unit.clone(), identifier.value.clone(), - Box::new(def.invert(&mut context.symbol_table, DECL_UNIT)?), + Box::new(def.invert(&mut context.symbol_table.get_mut(), DECL_UNIT)?), ); let stmt = Stmt::UnitDecl(identifier.value, base_unit, Box::new(def)); - context.symbol_table.insert(stmt.clone()); - context.symbol_table.insert(stmt_inv); + context.symbol_table.get_mut().insert(stmt.clone()); + context.symbol_table.get_mut().insert(stmt_inv); Ok(stmt) } @@ -404,7 +349,7 @@ fn parse_equality(context: &mut Context) -> Result { let mut left = parse_to(context)?; // Equation - if match_token(context, TokenKind::Equals) && context.contains_equation_equal_sign { + /*if match_token(context, TokenKind::Equals) && context.contains_equation_equal_sign { advance(context); let right = parse_to(context)?; let var_name = if let Some(var_name) = &context.equation_variable { @@ -413,25 +358,26 @@ fn parse_equality(context: &mut Context) -> Result { return Err(CalcError::UnableToSolveEquation); }; - let inverted = if inverter::contains_var(&mut context.symbol_table, &left, var_name) { - left.invert_to_target(&mut context.symbol_table, right, var_name)? - } else { - right.invert_to_target(&mut context.symbol_table, left, var_name)? - }; + let inverted = + if inverter::contains_var(&mut context.symbol_table.get_mut(), &left, var_name) { + left.invert_to_target(&mut context.symbol_table.get_mut(), right, var_name)? + } else { + right.invert_to_target(&mut context.symbol_table.get_mut(), left, var_name)? + }; // If the inverted expression still contains the variable, // the equation solving failed. - if inverter::contains_var(&mut context.symbol_table, &inverted, var_name) { + if inverter::contains_var(&mut context.symbol_table.get_mut(), &inverted, var_name) { return Err(CalcError::UnableToSolveEquation); } - context.symbol_table.insert(Stmt::VarDecl( + context.symbol_table.get_mut().insert(Stmt::VarDecl( Identifier::from_full_name(var_name), Box::new(inverted.clone()), )); return Ok(inverted); - } + }*/ // Equality check while match_token(context, TokenKind::Equals) @@ -443,7 +389,11 @@ fn parse_equality(context: &mut Context) -> Result { { let op = peek(context).kind; advance(context); - let right = parse_to(context)?; + let right = if op == TokenKind::Equals && match_token(context, TokenKind::OpenBrace) { + parse_piecewise(context)? + } else { + parse_to(context)? + }; left = Expr::Binary(Box::new(left), op, Box::new(right)); } @@ -527,13 +477,15 @@ fn parse_factor(context: &mut Context) -> Result { fn parse_unit(context: &mut Context) -> Result { let expr = parse_unary(context)?; - let peek = &peek(&context).value; - if match_token(context, TokenKind::Identifier) && context.symbol_table.contains_unit(&peek) { - return Ok(Expr::Unit( - advance(context).value.to_string(), - Box::new(expr), - )); + if match_token(context, TokenKind::Identifier) { + let peek = &peek(context).value.clone(); + if context.symbol_table.get_mut().contains_unit(peek) { + return Ok(Expr::Unit( + advance(context).value.to_string(), + Box::new(expr), + )); + } } Ok(expr) @@ -688,246 +640,19 @@ fn parse_vector(context: &mut Context) -> Result { fn parse_identifier(context: &mut Context) -> Result { 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 c == '_' { - log_base = Some(Expr::Literal(get_base(&identifier.full_name)? as f64)); - } - } - } - - let exists_as_fn = context.symbol_table.contains_fn(&identifier.pure_name) - || context.current_function.as_ref() == Some(&identifier.pure_name) - || log_base.is_some(); - - // Eg. sqrt64 - if exists_as_fn - && (match_token(context, TokenKind::Literal) || match_token(context, TokenKind::Identifier)) + if context.parsing_unit_decl + && !context + .symbol_table + .get_mut() + .contains_var(&identifier.full_name) { - // If there is a function with this name, parse it as a function, with the next token as the argument. - let parameter = if identifier.full_name == "√" { - parse_exponent(context)? - } else { - 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])); - } - - let next_is_group = match peek(context).kind { - TokenKind::OpenParenthesis - | TokenKind::OpenBracket - | TokenKind::Pipe - | TokenKind::OpenCeil - | TokenKind::OpenFloor => true, - _ => false, - }; - let parse_as_var_instead = next_is_group && !context.parsing_identifier_stmt && !exists_as_fn; - - // Eg. sqrt(64) - // If the function doesn't exist, parse it as a variable and multiplication instead. - // Although, if the parse_identifier_stmt function called this function, - // parse it as a function anyway, since it might be creating one. - if !parse_as_var_instead && next_is_group { - let is_integral = identifier.full_name == "integrate" - || identifier.full_name == "integral" - || identifier.full_name == "∫"; - if is_integral { - context.is_in_integral = true; - } - - let mut arguments = match parse_vector(context)? { - Expr::Vector(arguments) => arguments, - Expr::Group(argument) => vec![*argument], - _ => unreachable!(), - }; - - if is_integral { - context.is_in_integral = false; - } - - if let Some(log_base) = log_base { - arguments.push(log_base); - return Ok(Expr::FnCall(Identifier::from_full_name("log"), arguments)); - } - - return Ok(Expr::FnCall(identifier, arguments)); - } - - // 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..]; - let lowered_expr = - if lowered.len() > 0 && lowered.chars().nth(0).unwrap_or('\0').is_digit(10) { - Expr::Literal(lowered.parse::().unwrap_or(f64::NAN)) - } else { - Expr::Var(Identifier::from_full_name(lowered)) - }; - - Ok(Expr::Indexer( - Box::new(Expr::Var(Identifier::from_full_name(&var_name))), - vec![lowered_expr], - )) - } else if context.parsing_unit_decl { context.unit_decl_base_unit = Some(identifier.full_name); Ok(Expr::Var(Identifier::from_full_name(DECL_UNIT))) } else { - if let Some(equation_var) = &context.equation_variable { - if &identifier.full_name == equation_var { - return Ok(build_var(context, &identifier.full_name)); - } - } - if context.contains_equation_equal_sign { - context.equation_variable = Some(identifier.full_name.clone()); - return Ok(build_var(context, &identifier.full_name)); - } - - if identifier.pure_name.len() == 1 { - return Ok(build_var(context, &identifier.full_name)); - } - - // Eg. dx inside an integral, should be parsed as *one* identifier - // Reverse the identifier and take two. This gets the last two characters (in reversed order). - // Now reverse this to finally get the last two characters in correct order. - // It's a bit weird, but it should work for more than ASCII. - let mut identifier_without_dx: Vec = identifier.full_name.chars().collect(); - let mut last_two_chars = String::new(); - let last_char = identifier_without_dx.pop().unwrap_or_default(); - let first_char = identifier_without_dx.pop().unwrap_or_default(); - last_two_chars.push(first_char); - last_two_chars.push(last_char); - - if context.is_in_integral && last_two_chars.starts_with("d") { - // If the token contains more than just "dx", - // save the dx/dy/du/etc. in a variable, that can be - // used further down when splitting the identifier into multiple variables. - if identifier.full_name.len() > 2 { - // This variable will be used further down in order to separate dx from the rest. - let pos = context.pos - 1; - context.pos = pos; - context.tokens[pos] = Token { - kind: TokenKind::Identifier, - value: identifier_without_dx.iter().collect(), - span: (0, 0), - }; - - let left_expr = parse_exponent(context)?; - - // Revert back to how it was before. - context.tokens[pos] = Token { - kind: TokenKind::Identifier, - value: identifier.full_name.to_string(), - span: (0, 0), - }; - - return Ok(Expr::Binary( - Box::new(left_expr), - TokenKind::Star, - Box::new(Expr::Var(Identifier::from_full_name(&last_two_chars))), - )); - } else { - return Ok(Expr::Var(Identifier::from_full_name(&last_two_chars))); - } - } - - if identifier.pure_name.len() > 1 { - split_into_variables(context, &identifier) - } else { - Err(CalcError::UndefinedVar(identifier.full_name)) - } + Ok(Expr::Var(identifier)) } } -fn split_into_variables(context: &mut Context, identifier: &Identifier) -> Result { - let mut chars: Vec = identifier.pure_name.chars().collect(); - let mut left = Expr::Var(Identifier::from_full_name(&chars[0].to_string())); - - // Temporarily remove the last character and check if a function - // without that character exists. If it does, - // create a function call expression, where that last character - // is the argument. - let last_char = chars.pop().unwrap_or_default(); - let identifier_without_last: String = chars.iter().collect(); - if context.symbol_table.contains_fn(&identifier_without_last) { - return Ok(Expr::FnCall( - Identifier::from_full_name(&identifier_without_last), - vec![Expr::Var(Identifier::from_full_name( - &last_char.to_string(), - ))], - )); - } else { - // Otherwise, re-add the character. - chars.push(last_char); - } - - // Turn each individual character into its own variable reference. - // This parses eg `xy` as `x*y` instead of *one* variable. - let mut right_chars = chars.iter().skip(1).peekable(); - while let Some(c) = right_chars.next() { - // If last iteration - let right = if right_chars.peek().is_none() { - // Temporarily change the token content, so that - // the parse_exponent step will parse it as its - // new name. It will later be switched back, - // since the parser sometimes rewinds a bit, - // and may get confused by a sudden change. - let pos = context.pos - 1; - context.pos = pos; - context.tokens[pos] = Token { - kind: TokenKind::Identifier, - value: c.to_string(), - span: (0, 0), - }; - - let last_var = parse_exponent(context)?; - - // Revert back to how it was before. - context.tokens[pos] = Token { - kind: TokenKind::Identifier, - value: identifier.full_name.to_string(), - span: (0, 0), - }; - - last_var - } else { - build_var(context, &c.to_string()) - }; - - left = Expr::Binary(Box::new(left), TokenKind::Star, Box::new(right)); - } - - Ok(left) -} - -fn build_var(context: &Context, name: &str) -> Expr { - if let (Some(function_name), Some(params)) = ( - context.current_function.as_ref(), - context.current_function_parameters.as_ref(), - ) { - let identifier = Identifier::parameter_from_name(name, &function_name); - if params.contains(&identifier.full_name) { - return Expr::Var(identifier); - } - } - return Expr::Var(Identifier::from_full_name(name)); -} - fn peek(context: &Context) -> &Token { if context.pos >= context.tokens.len() { &context.tokens.last().unwrap() // EOF @@ -1011,7 +736,9 @@ mod tests { context.tokens = tokens; context.pos = 0; - parse_stmt(context) + let parsed = parse_stmt(context)?; + let mut symbol_table = context.symbol_table.get_mut(); + analysis::analyse_stmt(&mut symbol_table, parsed) } fn parse(tokens: Vec) -> Result { @@ -1019,7 +746,9 @@ mod tests { context.tokens = tokens; context.pos = 0; - parse_stmt(&mut context) + let parsed = parse_stmt(&mut context)?; + let mut symbol_table = context.symbol_table.get_mut(); + analysis::analyse_stmt(&mut symbol_table, parsed) } #[test] @@ -1035,11 +764,11 @@ mod tests { #[wasm_bindgen_test] fn test_var_multiplication() { let mut context = Context::new(); - context.symbol_table.insert(Stmt::VarDecl( + context.symbol_table.get_mut().insert(Stmt::VarDecl( Identifier::from_full_name("x"), literal(1f64), )); - context.symbol_table.insert(Stmt::VarDecl( + context.symbol_table.get_mut().insert(Stmt::VarDecl( Identifier::from_full_name("y"), literal(2f64), )); @@ -1164,6 +893,7 @@ mod tests { let mut context = Context::new(); context .symbol_table + .get_mut() .insert(unit_decl("a", "b", var(super::DECL_UNIT))); assert_eq!( @@ -1236,7 +966,7 @@ mod tests { let mut context = Context::new(); // Add the function to the symbol table first, in order to prevent errors. - context.symbol_table.set(Stmt::FnDecl( + context.symbol_table.get_mut().set(Stmt::FnDecl( Identifier::from_full_name("f"), vec![String::from("x")], literal(1f64),