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.
This commit is contained in:
PaddiM8 2022-01-14 15:08:19 +01:00
parent f3d8802438
commit 7b0435d3ec
4 changed files with 561 additions and 328 deletions

494
kalk/src/analysis.rs Normal file
View File

@ -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<String>,
current_function_parameters: Option<Vec<String>>,
equation_variable: Option<String>,
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<Stmt, CalcError> {
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<Stmt, CalcError> {
// 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<Expr, CalcError> {
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<Expr, CalcError> {
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<Expr>,
adjacent_exponent: Option<Expr>,
) -> Result<Expr, CalcError> {
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::<f64>() {
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<char> = 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<Expr>,
exponent: Option<Expr>,
) -> Result<Expr, CalcError> {
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<Expr>,
) -> Result<Expr, CalcError> {
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<Expr, CalcError> {
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::<f64>().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<Expr, CalcError> {
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<Expr>,
) -> Result<Expr, CalcError> {
let mut chars: Vec<char> = 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));
}

View File

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

View File

@ -1,3 +1,4 @@
mod analysis;
pub mod ast;
pub mod calculation_result;
mod calculus;

View File

@ -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<Token>,
pos: usize,
symbol_table: SymbolTable,
symbol_table: Cell<SymbolTable>,
angle_unit: String,
timeout: Option<u32>,
/// 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<String>,
parsing_identifier_stmt: bool,
equation_variable: Option<String>,
contains_equation_equal_sign: bool,
is_in_integral: bool,
current_function: Option<String>,
current_function_parameters: Option<Vec<String>>,
other_radix: Option<u8>,
}
@ -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<Vec<Stmt>, CalcError>
let mut statements: Vec<Stmt> = 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<Stmt, CalcError> {
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<Stmt, CalcError> {
Ok(Stmt::Expr(Box::new(parse_expr(context)?)))
}
fn parse_identifier_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
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, &parameter_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<Expr, CalcError> {
advance(context);
skip_newlines(context);
@ -349,7 +290,11 @@ fn parse_var_decl_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
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<Stmt, CalcError> {
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<Expr, CalcError> {
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<Expr, CalcError> {
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<Expr, CalcError> {
{
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<Expr, CalcError> {
fn parse_unit(context: &mut Context) -> Result<Expr, CalcError> {
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<Expr, CalcError> {
fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
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::<f64>().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<char> = 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<Expr, CalcError> {
let mut chars: Vec<char> = 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<Token>) -> Result<Stmt, CalcError> {
@ -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),