Moved variable declaration parsing to analysis.rs

This commit is contained in:
PaddiM8 2022-01-14 19:49:04 +01:00
parent 91ade0d4a5
commit 841317b09b
3 changed files with 147 additions and 98 deletions

View File

@ -70,66 +70,126 @@ pub(crate) fn analyse_stmt(
}
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!(),
Ok(
if let Expr::Binary(left, TokenKind::Equals, right) = value {
match *left {
Expr::Binary(identifier_expr, TokenKind::Star, parameter_expr) => {
build_fn_decl(context, *identifier_expr, *parameter_expr, *right)?
}
Expr::Var(identifier) if !context.in_conditional => {
if inverter::contains_var(
&mut context.symbol_table,
&right,
&identifier.full_name,
) {
return Err(CalcError::VariableReferencesItself);
}
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;
if prelude::is_constant(&identifier.full_name) {
return Err(CalcError::UnableToOverrideConstant(
identifier.pure_name.into(),
));
}
return Ok(fn_decl);
let result =
Stmt::VarDecl(identifier, Box::new(analyse_expr(context, *right)?));
context.symbol_table.insert(result.clone());
result
}
_ => Expr::Binary(
Box::new(Expr::Binary(
identifier_expr,
TokenKind::Star,
parameter_expr,
)),
_ => Stmt::Expr(Box::new(Expr::Binary(
Box::new(analyse_expr(context, *left)?),
TokenKind::Equals,
right,
),
))),
}
} else {
Expr::Binary(left, TokenKind::Equals, right)
}
} else {
value
};
Stmt::Expr(Box::new(analyse_expr(context, value)?))
},
)
}
Ok(Stmt::Expr(Box::new(analyse_expr(context, value)?)))
fn build_fn_decl(
context: &mut Context,
identifier_expr: Expr,
parameter_expr: Expr,
right: Expr,
) -> Result<Stmt, CalcError> {
Ok(match identifier_expr {
Expr::Var(identifier) if !prelude::is_prelude_func(&identifier.full_name) => {
// Check if all the expressions in the parameter_expr are
// variables. If not, it can't be turned into a function declaration.
let all_are_vars = match &parameter_expr {
Expr::Vector(exprs) => {
exprs
.iter()
.any(|x| if let Expr::Var(_) = x { true } else { false })
}
Expr::Group(expr) => {
if let Expr::Var(_) = &**expr {
true
} else {
false
}
}
_ => false,
};
if !all_are_vars {
// Analyse it as a function call instead
return Ok(Stmt::Expr(Box::new(analyse_expr(
context,
Expr::Binary(
Box::new(Expr::Binary(
Box::new(Expr::Var(identifier)),
TokenKind::Star,
Box::new(parameter_expr),
)),
TokenKind::Equals,
Box::new(right),
),
)?)));
}
let exprs = match parameter_expr {
Expr::Vector(exprs) => exprs,
Expr::Group(expr) => vec![*expr],
_ => unreachable!(),
};
let mut parameters = Vec::new();
for expr in exprs {
if let Expr::Var(argument_identifier) = expr {
parameters.push(format!(
"{}-{}",
identifier.pure_name, argument_identifier.pure_name
));
}
}
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;
fn_decl
}
_ => Stmt::Expr(Box::new(Expr::Binary(
Box::new(Expr::Binary(
Box::new(identifier_expr),
TokenKind::Star,
Box::new(parameter_expr),
)),
TokenKind::Equals,
Box::new(right),
))),
})
}
fn analyse_expr(context: &mut Context, expr: Expr) -> Result<Expr, CalcError> {
@ -201,8 +261,13 @@ fn analyse_binary<'a>(
op: TokenKind,
right: Expr,
) -> Result<Expr, CalcError> {
let previous_in_conditional = context.in_conditional;
if op == TokenKind::And || op == TokenKind::Or {
context.in_conditional = true;
}
let right = analyse_expr(context, right)?;
match (&left, &op) {
let result = match (&left, &op) {
(_, TokenKind::Equals) if !context.in_conditional => {
// Equation
context.in_equation = true;
@ -212,7 +277,10 @@ fn analyse_binary<'a>(
// abort and analyse as a comparison instead.
if context.in_equation == false {
context.in_conditional = true;
return analyse_binary(context, left, op, right);
let result = analyse_binary(context, left, op, right);
context.in_conditional = previous_in_conditional;
return result;
}
context.in_equation = false;
@ -221,7 +289,10 @@ fn analyse_binary<'a>(
var_name
} else {
context.in_conditional = true;
return analyse_binary(context, left, op, right);
let result = analyse_binary(context, left, op, right);
context.in_conditional = previous_in_conditional;
return result;
};
let inverted = if inverter::contains_var(&mut context.symbol_table, &left, var_name) {
@ -263,7 +334,11 @@ fn analyse_binary<'a>(
op,
Box::new(right),
)),
}
};
context.in_conditional = previous_in_conditional;
result
}
fn analyse_var(

View File

@ -32,17 +32,26 @@ mod tests {
.get_value()
}
fn is_true(x: KalkValue) -> bool {
if let KalkValue::Boolean(boolean) = x {
boolean
} else {
false
}
}
#[test]
fn test_basics() {
let result = eval_file("basics");
assert_eq!(result.to_string_real(10), "3400");
assert!(!result.has_imaginary());
assert!(is_true(eval_file("basics")));
}
#[test]
fn test_radix() {
let result = eval_file("radix");
assert_eq!(result.to_string_real(10), "62");
assert_eq!(result.to_string_imaginary(10, false), "11.3125");
assert!(is_true(eval_file("radix")));
}
#[test]
fn test_variables() {
assert!(is_true(eval_file("variables")));
}
}

View File

@ -5,9 +5,8 @@ use crate::ast::Identifier;
use crate::calculation_result::CalculationResult;
use crate::{
ast::{Expr, Stmt},
interpreter, inverter,
interpreter,
lexer::{Lexer, Token, TokenKind},
prelude,
symbol_table::SymbolTable,
};
use wasm_bindgen::prelude::*;
@ -30,8 +29,6 @@ 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>,
equation_variable: Option<String>,
contains_equation_equal_sign: bool,
other_radix: Option<u8>,
}
@ -47,8 +44,6 @@ impl Context {
timeout: None,
parsing_unit_decl: false,
unit_decl_base_unit: None,
equation_variable: None,
contains_equation_equal_sign: false,
other_radix: None,
};
@ -165,12 +160,6 @@ pub fn eval(
input: &str,
#[cfg(feature = "rug")] precision: u32,
) -> Result<Option<CalculationResult>, CalcError> {
// Variable and function declaration parsers will set this to false
// if the equal sign is for one of those instead.
// It also should not contain an iverson bracket, since equal signs in there
// mean something else. This is not super reliable, and should probably be improved in the future.
context.contains_equation_equal_sign =
input.contains("=") && !input.contains("[") && !input.contains("{");
let statements = parse(context, input)?;
let mut symbol_table = context.symbol_table.get_mut();
@ -225,7 +214,7 @@ pub fn parse(context: &mut Context, input: &str) -> Result<Vec<Stmt>, CalcError>
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::Equals => parse_var_decl_stmt(context)?,
_ => Stmt::Expr(Box::new(parse_expr(context)?)),
});
} else if match_token(context, TokenKind::UnitKeyword) {
@ -284,30 +273,6 @@ fn parse_piecewise(context: &mut Context) -> Result<Expr, CalcError> {
Ok(Expr::Piecewise(pieces))
}
fn parse_var_decl_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
let identifier = advance(context).clone();
advance(context); // Equal sign
context.contains_equation_equal_sign = false;
context.equation_variable = None;
let expr = parse_expr(context)?;
if inverter::contains_var(
&mut context.symbol_table.get_mut(),
&expr,
&identifier.value,
) {
return Err(CalcError::VariableReferencesItself);
}
if prelude::is_constant(&identifier.value) {
return Err(CalcError::UnableToOverrideConstant(identifier.value.into()));
}
Ok(Stmt::VarDecl(
Identifier::from_full_name(&identifier.value),
Box::new(expr),
))
}
fn parse_unit_decl_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
advance(context); // Unit keyword
let identifier = advance(context).clone();