Created 'Identifier' struct that contains prime count and name without primes (ticks ')

This commit is contained in:
bakk 2021-05-17 20:17:34 +02:00
parent 374a449eab
commit 9acdd71589
6 changed files with 166 additions and 81 deletions

View File

@ -3,8 +3,8 @@ use crate::lexer::TokenKind;
/// A tree structure of a statement.
#[derive(Debug, Clone, PartialEq)]
pub enum Stmt {
VarDecl(String, Box<Expr>),
FnDecl(String, Vec<String>, Box<Expr>),
VarDecl(Identifier, Box<Expr>),
FnDecl(Identifier, Vec<String>, Box<Expr>),
UnitDecl(String, String, Box<Expr>),
/// For simplicity, expressions can be put into statements. This is the form in which expressions are passed to the interpreter.
Expr(Box<Expr>),
@ -16,8 +16,41 @@ pub enum Expr {
Binary(Box<Expr>, TokenKind, Box<Expr>),
Unary(TokenKind, Box<Expr>),
Unit(String, Box<Expr>),
Var(String),
Var(Identifier),
Group(Box<Expr>),
FnCall(String, Vec<Expr>),
FnCall(Identifier, Vec<Expr>),
Literal(f64),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Identifier {
pub full_name: String,
pub pure_name: String,
pub prime_count: i32,
}
impl Identifier {
pub fn from_full_name(full_name: &str) -> Self {
let (pure_name, prime_count) = separate_identifier_and_prime(full_name);
Identifier {
full_name: full_name.to_string(),
pure_name,
prime_count,
}
}
}
fn separate_identifier_and_prime(identifier: &str) -> (String, i32) {
let mut prim_count = 0;
let mut pure_identifier = identifier.to_string();
loop {
if pure_identifier.ends_with("'") {
pure_identifier.pop();
prim_count += 1;
} else {
return (pure_identifier, prim_count);
}
}
}

View File

@ -1,3 +1,4 @@
use crate::ast::Identifier;
use crate::ast::{Expr, Stmt};
use crate::calculus;
use crate::kalk_num::KalkNum;
@ -46,14 +47,17 @@ impl<'a> Context<'a> {
// Insert the last value into the `ans` variable.
self.symbol_table.set(if (&num.unit).len() > 0 {
Stmt::VarDecl(
String::from("ans"),
Identifier::from_full_name("ans"),
Box::new(Expr::Unit(
num.unit.clone(),
Box::new(Expr::Literal(num.to_f64())),
)),
)
} else {
Stmt::VarDecl(String::from("ans"), Box::new(Expr::Literal(num.to_f64())))
Stmt::VarDecl(
Identifier::from_full_name("ans"),
Box::new(Expr::Literal(num.to_f64())),
)
});
if i == statements.len() - 1 {
@ -130,7 +134,8 @@ fn eval_binary_expr(
// move this to the match statement further down.
if let Expr::Var(right_unit) = right_expr {
let left_unit = eval_expr(context, left_expr, "")?.unit;
return convert_unit(context, left_expr, &left_unit, &right_unit); // TODO: Avoid evaluating this twice.
return convert_unit(context, left_expr, &left_unit, &right_unit.full_name);
// TODO: Avoid evaluating this twice.
}
}
@ -202,9 +207,10 @@ pub fn convert_unit(
if let Some(Stmt::UnitDecl(_, _, unit_def)) =
context.symbol_table.get_unit(to_unit, from_unit).cloned()
{
context
.symbol_table
.insert(Stmt::VarDecl(DECL_UNIT.into(), Box::new(expr.clone())));
context.symbol_table.insert(Stmt::VarDecl(
Identifier::from_full_name(DECL_UNIT),
Box::new(expr.clone()),
));
Ok(KalkNum::new(
eval_expr(context, &unit_def, "")?.value,
@ -217,25 +223,28 @@ pub fn convert_unit(
fn eval_var_expr(
context: &mut Context,
identifier: &str,
identifier: &Identifier,
unit: &str,
) -> Result<KalkNum, CalcError> {
// If there is a constant with this name, return a literal expression with its value
if let Some(value) = prelude::CONSTANTS.get(identifier) {
if let Some(value) = prelude::CONSTANTS.get(identifier.full_name.as_ref() as &str) {
return eval_expr(context, &Expr::Literal(*value), unit);
}
if identifier == "n" {
if identifier.full_name == "n" {
if let Some(value) = context.sum_n_value {
return Ok(KalkNum::from(value));
}
}
// Look for the variable in the symbol table
let var_decl = context.symbol_table.get_var(identifier).cloned();
let var_decl = context
.symbol_table
.get_var(identifier.full_name.as_ref() as &str)
.cloned();
match var_decl {
Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr, unit),
_ => Err(CalcError::UndefinedVar(identifier.into())),
_ => Err(CalcError::UndefinedVar(identifier.full_name)),
}
}
@ -254,9 +263,9 @@ fn eval_group_expr(context: &mut Context, expr: &Expr, unit: &str) -> Result<Kal
eval_expr(context, expr, unit)
}
fn eval_fn_call_expr(
pub(crate) fn eval_fn_call_expr(
context: &mut Context,
identifier: &str,
identifier: &Identifier,
expressions: &[Expr],
unit: &str,
) -> Result<KalkNum, CalcError> {
@ -282,7 +291,7 @@ fn eval_fn_call_expr(
}
// Special functions
match identifier {
match identifier.full_name.as_ref() {
"sum" | "Σ" => {
// Make sure exactly 3 arguments were supplied.
if expressions.len() != 3 {
@ -317,20 +326,20 @@ fn eval_fn_call_expr(
));
}
return calculus::integrate(context, expressions);
return calculus::integrate(context, &expressions[0], &expressions[1], &expressions[2]);
}
_ => (),
}
// Symbol Table
let stmt_definition = context.symbol_table.get_fn(identifier).cloned();
let stmt_definition = context.symbol_table.get_fn(&identifier.full_name).cloned();
match stmt_definition {
Some(Stmt::FnDecl(_, arguments, fn_body)) => {
if arguments.len() != expressions.len() {
return Err(CalcError::IncorrectAmountOfArguments(
arguments.len(),
identifier.into(),
identifier.full_name,
expressions.len(),
));
}
@ -339,13 +348,16 @@ fn eval_fn_call_expr(
for (i, argument) in arguments.iter().enumerate() {
eval_stmt(
context,
&Stmt::VarDecl(argument.clone(), Box::new(expressions[i].clone())),
&Stmt::VarDecl(
Identifier::from_full_name(argument),
Box::new(expressions[i].clone()),
),
)?;
}
eval_expr(context, &fn_body, unit)
}
_ => Err(CalcError::UndefinedFn(identifier.into())),
_ => Err(CalcError::UndefinedFn(identifier.full_name)),
}
}

View File

@ -1,3 +1,4 @@
use crate::ast::Identifier;
use crate::ast::{Expr, Stmt};
use crate::lexer::TokenKind;
use crate::parser::CalcError;
@ -44,7 +45,7 @@ impl Expr {
symbol_table: &mut SymbolTable,
unknown_var: &str,
) -> Result<Self, CalcError> {
let target_expr = Expr::Var(unknown_var.into());
let target_expr = Expr::Var(Identifier::from_full_name(unknown_var));
let result = invert(target_expr, symbol_table, self, unknown_var);
Ok(result?.0)
@ -80,7 +81,7 @@ fn invert(
Expr::FnCall(identifier, arguments) => invert_fn_call(
target_expr,
symbol_table,
&identifier,
identifier,
arguments,
unknown_var,
),
@ -166,14 +167,20 @@ fn invert_binary(
TokenKind::Power => {
return if contains_var(symbol_table, left, unknown_var) {
invert(
Expr::FnCall("root".into(), vec![target_expr, right.clone()]),
Expr::FnCall(
Identifier::from_full_name("root"),
vec![target_expr, right.clone()],
),
symbol_table,
right,
unknown_var,
)
} else {
invert(
Expr::FnCall("log".into(), vec![target_expr, left.clone()]),
Expr::FnCall(
Identifier::from_full_name("log"),
vec![target_expr, left.clone()],
),
symbol_table,
right,
unknown_var,
@ -240,7 +247,7 @@ fn invert_unit(
let x = Expr::Binary(
Box::new(target_expr),
TokenKind::ToKeyword,
Box::new(Expr::Var(identifier.into())),
Box::new(Expr::Var(Identifier::from_full_name(identifier))),
);
invert(x, symbol_table, expr, unknown_var)
}
@ -248,38 +255,41 @@ fn invert_unit(
fn invert_var(
target_expr: Expr,
symbol_table: &mut SymbolTable,
identifier: &str,
identifier: &Identifier,
unknown_var: &str,
) -> Result<(Expr, Expr), CalcError> {
if identifier == unknown_var {
Ok((target_expr, Expr::Var(identifier.into())))
} else if let Some(Stmt::VarDecl(_, var_expr)) = symbol_table.get_var(identifier).cloned() {
if identifier.full_name == unknown_var {
Ok((target_expr, Expr::Var(*identifier)))
} else if let Some(Stmt::VarDecl(_, var_expr)) =
symbol_table.get_var(&identifier.full_name).cloned()
{
invert(target_expr, symbol_table, &var_expr, unknown_var)
} else {
Ok((target_expr, Expr::Var(identifier.into())))
Ok((target_expr, Expr::Var(*identifier)))
}
}
fn invert_fn_call(
target_expr: Expr,
symbol_table: &mut SymbolTable,
identifier: &str,
identifier: &Identifier,
arguments: &Vec<Expr>,
unknown_var: &str,
) -> Result<(Expr, Expr), CalcError> {
// If prelude function
match arguments.len() {
1 => {
if prelude::UNARY_FUNCS.contains_key(identifier) {
if let Some(fn_inv) = INVERSE_UNARY_FUNCS.get(identifier) {
if prelude::UNARY_FUNCS.contains_key(identifier.full_name.as_ref() as &str) {
if let Some(fn_inv) = INVERSE_UNARY_FUNCS.get(identifier.full_name.as_ref() as &str)
{
return invert(
Expr::FnCall(fn_inv.to_string(), vec![target_expr]),
Expr::FnCall(Identifier::from_full_name(fn_inv), vec![target_expr]),
symbol_table,
&arguments[0],
unknown_var,
);
} else {
match identifier {
match identifier.full_name.as_ref() {
"sqrt" => {
return invert(
Expr::Binary(
@ -295,7 +305,7 @@ fn invert_fn_call(
_ => {
return Err(CalcError::UnableToInvert(format!(
"Function '{}'",
identifier
identifier.full_name
)));
}
}
@ -303,10 +313,10 @@ fn invert_fn_call(
}
}
2 => {
if prelude::BINARY_FUNCS.contains_key(identifier) {
if prelude::BINARY_FUNCS.contains_key(identifier.full_name.as_ref() as &str) {
return Err(CalcError::UnableToInvert(format!(
"Function '{}'",
identifier
identifier.full_name
)));
}
}
@ -314,18 +324,19 @@ fn invert_fn_call(
}
// Get the function definition from the symbol table.
let (parameters, body) =
if let Some(Stmt::FnDecl(_, parameters, body)) = symbol_table.get_fn(identifier).cloned() {
(parameters, body)
} else {
return Err(CalcError::UndefinedFn(identifier.into()));
};
let (parameters, body) = if let Some(Stmt::FnDecl(_, parameters, body)) =
symbol_table.get_fn(&identifier.full_name).cloned()
{
(parameters, body)
} else {
return Err(CalcError::UndefinedFn(identifier.full_name));
};
// Make sure the input is valid.
if parameters.len() != arguments.len() {
return Err(CalcError::IncorrectAmountOfArguments(
parameters.len(),
identifier.into(),
identifier.full_name,
arguments.len(),
));
}
@ -334,7 +345,7 @@ fn invert_fn_call(
let mut parameters_iter = parameters.iter();
for argument in arguments {
symbol_table.insert(Stmt::VarDecl(
parameters_iter.next().unwrap().to_string(),
Identifier::from_full_name(&parameters_iter.next().unwrap().to_string()),
Box::new(argument.clone()),
));
}
@ -353,8 +364,10 @@ pub fn contains_var(symbol_table: &SymbolTable, expr: &Expr, var_name: &str) ->
Expr::Unary(_, expr) => contains_var(symbol_table, expr, var_name),
Expr::Unit(_, expr) => contains_var(symbol_table, expr, var_name),
Expr::Var(identifier) => {
identifier == var_name
|| if let Some(Stmt::VarDecl(_, var_expr)) = symbol_table.get_var(identifier) {
identifier.full_name == var_name
|| if let Some(Stmt::VarDecl(_, var_expr)) =
symbol_table.get_var(&identifier.full_name)
{
contains_var(symbol_table, var_expr, var_name)
} else {
false
@ -409,6 +422,7 @@ fn multiply_into(expr: &Expr, base_expr: &Expr) -> Result<Expr, CalcError> {
#[cfg(test)]
mod tests {
use crate::ast::Expr;
use crate::ast::Identifier;
use crate::lexer::TokenKind::*;
use crate::parser::DECL_UNIT;
use crate::symbol_table::SymbolTable;
@ -416,7 +430,9 @@ mod tests {
use wasm_bindgen_test::*;
fn decl_unit() -> Box<Expr> {
Box::new(Expr::Var(crate::parser::DECL_UNIT.into()))
Box::new(Expr::Var(Identifier::from_full_name(
crate::parser::DECL_UNIT,
)))
}
#[test]

View File

@ -1,3 +1,4 @@
use crate::ast::Identifier;
use crate::kalk_num::KalkNum;
use crate::{
ast::{Expr, Stmt},
@ -205,7 +206,7 @@ fn parse_identifier_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
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) {
if !prelude::is_prelude_func(&identifier.full_name) {
advance(context);
let expr = parse_expr(context)?;
let mut parameter_identifiers = Vec::new();
@ -215,7 +216,7 @@ fn parse_identifier_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
// Extract these.
for parameter in parameters {
if let Expr::Var(parameter_identifier) = parameter {
parameter_identifiers.push(parameter_identifier);
parameter_identifiers.push(parameter_identifier.full_name);
}
}
@ -245,7 +246,10 @@ fn parse_var_decl_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
return Err(CalcError::VariableReferencesItself);
}
Ok(Stmt::VarDecl(identifier.value, Box::new(expr)))
Ok(Stmt::VarDecl(
Identifier::from_full_name(&identifier.value),
Box::new(expr),
))
}
fn parse_unit_decl_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
@ -309,9 +313,10 @@ fn parse_equation(context: &mut Context) -> Result<Expr, CalcError> {
return Err(CalcError::UnableToSolveEquation);
}
context
.symbol_table
.insert(Stmt::VarDecl(var_name.into(), Box::new(inverted.clone())));
context.symbol_table.insert(Stmt::VarDecl(
Identifier::from_full_name(var_name),
Box::new(inverted.clone()),
));
return Ok(inverted);
}
@ -323,7 +328,7 @@ fn parse_to(context: &mut Context) -> Result<Expr, CalcError> {
if match_token(context, TokenKind::ToKeyword) {
advance(context);
let right = Expr::Var(advance(context).value.clone()); // Parse this as a variable for now.
let right = Expr::Var(Identifier::from_full_name(&advance(context).value)); // Parse this as a variable for now.
return Ok(Expr::Binary(
Box::new(left),
@ -469,7 +474,7 @@ fn parse_group_fn(context: &mut Context) -> Result<Expr, CalcError> {
let expr = parse_expr(context)?;
advance(context);
Ok(Expr::FnCall(name.to_string(), vec![expr]))
Ok(Expr::FnCall(Identifier::from_full_name(name), vec![expr]))
}
fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
@ -480,7 +485,10 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
// If there is a function with this name, parse it as a function, with the next token as the argument.
if context.symbol_table.contains_fn(&identifier.value) {
let parameter = Expr::Literal(string_to_num(&advance(context).value));
return Ok(Expr::FnCall(identifier.value, vec![parameter]));
return Ok(Expr::FnCall(
Identifier::from_full_name(&identifier.value),
vec![parameter],
));
}
}
@ -514,32 +522,37 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
context.is_in_integral = false;
}
return Ok(Expr::FnCall(identifier.value, parameters));
return Ok(Expr::FnCall(
Identifier::from_full_name(&identifier.value),
parameters,
));
}
// Eg. dx inside an integral, should be parsed as *one* identifier
if context.is_in_integral && identifier.value.starts_with("d") {
return Ok(Expr::Var(identifier.value));
return Ok(Expr::Var(Identifier::from_full_name(&identifier.value)));
}
// Eg. x
if parse_as_var_instead || context.symbol_table.contains_var(&identifier.value) {
Ok(Expr::Var(identifier.value))
Ok(Expr::Var(Identifier::from_full_name(&identifier.value)))
} else if context.parsing_unit_decl {
context.unit_decl_base_unit = Some(identifier.value);
Ok(Expr::Var(DECL_UNIT.into()))
Ok(Expr::Var(Identifier::from_full_name(DECL_UNIT)))
} else {
if let Some(equation_var) = &context.equation_variable {
if &identifier.value == equation_var {
return Ok(Expr::Var(identifier.value));
return Ok(Expr::Var(Identifier::from_full_name(&identifier.value)));
}
} else if context.contains_equal_sign {
context.equation_variable = Some(identifier.value.clone());
return Ok(Expr::Var(identifier.value));
return Ok(Expr::Var(Identifier::from_full_name(&identifier.value)));
}
let mut chars = identifier.value.chars();
let mut left = Expr::Var(chars.next().unwrap().to_string());
let mut left = Expr::Var(Identifier::from_full_name(
&chars.next().unwrap().to_string(),
));
// Turn each individual character into its own variable reference.
// This parses eg `xy` as `x*y` instead of *one* variable.
@ -556,7 +569,7 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
parse_exponent(context)?
} else {
Expr::Var(c.to_string())
Expr::Var(Identifier::from_full_name(&c.to_string()))
};
left = Expr::Binary(Box::new(left), TokenKind::Star, Box::new(right));
@ -610,6 +623,7 @@ fn string_to_num(value: &str) -> f64 {
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::Identifier;
use crate::lexer::{Token, TokenKind::*};
use crate::test_helpers::*;
use wasm_bindgen_test::*;
@ -763,7 +777,7 @@ mod tests {
assert_eq!(
parse(tokens).unwrap(),
Stmt::VarDecl(
String::from("x"),
Identifier::from_full_name("x"),
binary(literal(1f64), Plus, literal(2f64))
)
);
@ -787,7 +801,7 @@ mod tests {
assert_eq!(
parse(tokens).unwrap(),
Stmt::FnDecl(
String::from("f"),
Identifier::from_full_name("f"),
vec![String::from("x")],
binary(literal(1f64), Plus, literal(2f64))
)
@ -813,7 +827,7 @@ mod tests {
// Add the function to the symbol table first, in order to prevent errors.
context.symbol_table.set(Stmt::FnDecl(
String::from("f"),
Identifier::from_full_name("f"),
vec![String::from("x")],
literal(1f64),
));
@ -822,7 +836,7 @@ mod tests {
parse_with_context(&mut context, tokens).unwrap(),
Stmt::Expr(binary(
Box::new(Expr::FnCall(
String::from("f"),
Identifier::from_full_name("f"),
vec![*binary(literal(1f64), Plus, literal(2f64))]
)),
Plus,

View File

@ -18,7 +18,8 @@ impl SymbolTable {
pub fn insert(&mut self, value: Stmt) -> &mut Self {
match &value {
Stmt::VarDecl(identifier, _) => {
self.hashmap.insert(format!("var.{}", identifier), value);
self.hashmap
.insert(format!("var.{}", identifier.full_name), value);
}
Stmt::UnitDecl(identifier, to_unit, _) => {
self.unit_types.insert(identifier.to_string(), ());
@ -27,7 +28,8 @@ impl SymbolTable {
.insert(format!("unit.{}.{}", identifier, to_unit), value);
}
Stmt::FnDecl(identifier, _, _) => {
self.hashmap.insert(format!("fn.{}", identifier), value);
self.hashmap
.insert(format!("fn.{}", identifier.full_name), value);
}
_ => panic!("Can only insert VarDecl, UnitDecl and FnDecl into symbol table."),
}
@ -49,11 +51,15 @@ impl SymbolTable {
pub fn set(&mut self, value: Stmt) {
let existing_item = match &value {
Stmt::VarDecl(identifier, _) => self.hashmap.get_mut(&format!("var.{}", identifier)),
Stmt::VarDecl(identifier, _) => self
.hashmap
.get_mut(&format!("var.{}", identifier.full_name)),
Stmt::UnitDecl(identifier, to_unit, _) => self
.hashmap
.get_mut(&format!("unit.{}.{}", identifier, to_unit)),
Stmt::FnDecl(identifier, _, _) => self.hashmap.get_mut(&format!("fn.{}", identifier)),
Stmt::FnDecl(identifier, _, _) => self
.hashmap
.get_mut(&format!("fn.{}", identifier.full_name)),
_ => panic!("Can only set VarDecl, UnitDecl and FnDecl in symbol table."),
};

View File

@ -1,5 +1,6 @@
#![allow(dead_code)]
use crate::ast::Expr;
use crate::ast::Identifier;
use crate::ast::Stmt;
use crate::lexer::Token;
use crate::lexer::TokenKind;
@ -17,11 +18,14 @@ pub fn literal(value: f64) -> Box<Expr> {
}
pub fn var(identifier: &str) -> Box<Expr> {
Box::new(Expr::Var(identifier.into()))
Box::new(Expr::Var(Identifier::from_full_name(identifier)))
}
pub fn fn_call(identifier: &str, arguments: Vec<Expr>) -> Box<Expr> {
Box::new(Expr::FnCall(identifier.into(), arguments))
Box::new(Expr::FnCall(
Identifier::from_full_name(identifier),
arguments,
))
}
pub fn binary(left: Box<Expr>, op: TokenKind, right: Box<Expr>) -> Box<Expr> {
@ -41,11 +45,11 @@ pub fn unit(identifier: &str, expr: Box<Expr>) -> Box<Expr> {
}
pub fn var_decl(identifier: &str, value: Box<Expr>) -> Stmt {
Stmt::VarDecl(identifier.into(), value)
Stmt::VarDecl(Identifier::from_full_name(identifier), value)
}
pub fn fn_decl(identifier: &str, parameters: Vec<String>, value: Box<Expr>) -> Stmt {
Stmt::FnDecl(identifier.into(), parameters, value)
Stmt::FnDecl(Identifier::from_full_name(identifier), parameters, value)
}
pub fn unit_decl(unit: &str, base_unit: &str, expr: Box<Expr>) -> Stmt {