mirror of
https://github.com/PaddiM8/kalker.git
synced 2024-12-12 17:40:52 +01:00
Moved variable declaration parsing to analysis.rs
This commit is contained in:
parent
91ade0d4a5
commit
841317b09b
@ -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 ¶meter_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(
|
||||
|
@ -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")));
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user