mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-06-25 04:01:51 +02: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> {
|
fn analyse_stmt_expr(context: &mut Context, value: Expr) -> Result<Stmt, CalcError> {
|
||||||
// Apparently you can't pattern match boxed types in the stable compiler.
|
Ok(
|
||||||
// This is a mess...
|
if let Expr::Binary(left, TokenKind::Equals, right) = value {
|
||||||
let value = if let Expr::Binary(left, TokenKind::Equals, right) = value {
|
match *left {
|
||||||
if let Expr::Binary(identifier_expr, TokenKind::Star, parameter_expr) = *left {
|
Expr::Binary(identifier_expr, TokenKind::Star, parameter_expr) => {
|
||||||
match *identifier_expr {
|
build_fn_decl(context, *identifier_expr, *parameter_expr, *right)?
|
||||||
Expr::Var(identifier) if !prelude::is_prelude_func(&identifier.full_name) => {
|
}
|
||||||
let mut parameters = Vec::new();
|
Expr::Var(identifier) if !context.in_conditional => {
|
||||||
match *parameter_expr {
|
if inverter::contains_var(
|
||||||
Expr::Vector(exprs) => {
|
&mut context.symbol_table,
|
||||||
for expr in exprs {
|
&right,
|
||||||
if let Expr::Var(argument_identifier) = expr {
|
&identifier.full_name,
|
||||||
parameters.push(format!(
|
) {
|
||||||
"{}-{}",
|
return Err(CalcError::VariableReferencesItself);
|
||||||
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());
|
if prelude::is_constant(&identifier.full_name) {
|
||||||
context.current_function_parameters = Some(parameters.clone());
|
return Err(CalcError::UnableToOverrideConstant(
|
||||||
let fn_decl = Stmt::FnDecl(
|
identifier.pure_name.into(),
|
||||||
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);
|
let result =
|
||||||
|
Stmt::VarDecl(identifier, Box::new(analyse_expr(context, *right)?));
|
||||||
|
context.symbol_table.insert(result.clone());
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
_ => Expr::Binary(
|
_ => Stmt::Expr(Box::new(Expr::Binary(
|
||||||
Box::new(Expr::Binary(
|
Box::new(analyse_expr(context, *left)?),
|
||||||
identifier_expr,
|
|
||||||
TokenKind::Star,
|
|
||||||
parameter_expr,
|
|
||||||
)),
|
|
||||||
TokenKind::Equals,
|
TokenKind::Equals,
|
||||||
right,
|
right,
|
||||||
),
|
))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Expr::Binary(left, TokenKind::Equals, right)
|
Stmt::Expr(Box::new(analyse_expr(context, value)?))
|
||||||
}
|
},
|
||||||
} else {
|
)
|
||||||
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> {
|
fn analyse_expr(context: &mut Context, expr: Expr) -> Result<Expr, CalcError> {
|
||||||
@ -201,8 +261,13 @@ fn analyse_binary<'a>(
|
|||||||
op: TokenKind,
|
op: TokenKind,
|
||||||
right: Expr,
|
right: Expr,
|
||||||
) -> Result<Expr, CalcError> {
|
) -> 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)?;
|
let right = analyse_expr(context, right)?;
|
||||||
match (&left, &op) {
|
let result = match (&left, &op) {
|
||||||
(_, TokenKind::Equals) if !context.in_conditional => {
|
(_, TokenKind::Equals) if !context.in_conditional => {
|
||||||
// Equation
|
// Equation
|
||||||
context.in_equation = true;
|
context.in_equation = true;
|
||||||
@ -212,7 +277,10 @@ fn analyse_binary<'a>(
|
|||||||
// abort and analyse as a comparison instead.
|
// abort and analyse as a comparison instead.
|
||||||
if context.in_equation == false {
|
if context.in_equation == false {
|
||||||
context.in_conditional = true;
|
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;
|
context.in_equation = false;
|
||||||
@ -221,7 +289,10 @@ fn analyse_binary<'a>(
|
|||||||
var_name
|
var_name
|
||||||
} else {
|
} else {
|
||||||
context.in_conditional = true;
|
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) {
|
let inverted = if inverter::contains_var(&mut context.symbol_table, &left, var_name) {
|
||||||
@ -263,7 +334,11 @@ fn analyse_binary<'a>(
|
|||||||
op,
|
op,
|
||||||
Box::new(right),
|
Box::new(right),
|
||||||
)),
|
)),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
context.in_conditional = previous_in_conditional;
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyse_var(
|
fn analyse_var(
|
||||||
|
@ -32,17 +32,26 @@ mod tests {
|
|||||||
.get_value()
|
.get_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_true(x: KalkValue) -> bool {
|
||||||
|
if let KalkValue::Boolean(boolean) = x {
|
||||||
|
boolean
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_basics() {
|
fn test_basics() {
|
||||||
let result = eval_file("basics");
|
assert!(is_true(eval_file("basics")));
|
||||||
assert_eq!(result.to_string_real(10), "3400");
|
|
||||||
assert!(!result.has_imaginary());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_radix() {
|
fn test_radix() {
|
||||||
let result = eval_file("radix");
|
assert!(is_true(eval_file("radix")));
|
||||||
assert_eq!(result.to_string_real(10), "62");
|
}
|
||||||
assert_eq!(result.to_string_imaginary(10, false), "11.3125");
|
|
||||||
|
#[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::calculation_result::CalculationResult;
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Expr, Stmt},
|
ast::{Expr, Stmt},
|
||||||
interpreter, inverter,
|
interpreter,
|
||||||
lexer::{Lexer, Token, TokenKind},
|
lexer::{Lexer, Token, TokenKind},
|
||||||
prelude,
|
|
||||||
symbol_table::SymbolTable,
|
symbol_table::SymbolTable,
|
||||||
};
|
};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
@ -30,8 +29,6 @@ pub struct Context {
|
|||||||
/// When a unit declaration is being parsed, this value will be set
|
/// 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")
|
/// 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>,
|
unit_decl_base_unit: Option<String>,
|
||||||
equation_variable: Option<String>,
|
|
||||||
contains_equation_equal_sign: bool,
|
|
||||||
other_radix: Option<u8>,
|
other_radix: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,8 +44,6 @@ impl Context {
|
|||||||
timeout: None,
|
timeout: None,
|
||||||
parsing_unit_decl: false,
|
parsing_unit_decl: false,
|
||||||
unit_decl_base_unit: None,
|
unit_decl_base_unit: None,
|
||||||
equation_variable: None,
|
|
||||||
contains_equation_equal_sign: false,
|
|
||||||
other_radix: None,
|
other_radix: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -165,12 +160,6 @@ pub fn eval(
|
|||||||
input: &str,
|
input: &str,
|
||||||
#[cfg(feature = "rug")] precision: u32,
|
#[cfg(feature = "rug")] precision: u32,
|
||||||
) -> Result<Option<CalculationResult>, CalcError> {
|
) -> 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 statements = parse(context, input)?;
|
||||||
|
|
||||||
let mut symbol_table = context.symbol_table.get_mut();
|
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> {
|
fn parse_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
|
||||||
if match_token(context, TokenKind::Identifier) {
|
if match_token(context, TokenKind::Identifier) {
|
||||||
return Ok(match peek_next(context).kind {
|
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)?)),
|
_ => Stmt::Expr(Box::new(parse_expr(context)?)),
|
||||||
});
|
});
|
||||||
} else if match_token(context, TokenKind::UnitKeyword) {
|
} else if match_token(context, TokenKind::UnitKeyword) {
|
||||||
@ -284,30 +273,6 @@ fn parse_piecewise(context: &mut Context) -> Result<Expr, CalcError> {
|
|||||||
Ok(Expr::Piecewise(pieces))
|
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> {
|
fn parse_unit_decl_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
|
||||||
advance(context); // Unit keyword
|
advance(context); // Unit keyword
|
||||||
let identifier = advance(context).clone();
|
let identifier = advance(context).clone();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user