Added the unit statement (very basic and experimental).

This commit is contained in:
PaddiM8 2020-06-13 16:19:32 +02:00
parent c0628855ae
commit 45adb1b526
5 changed files with 165 additions and 78 deletions

View File

@ -7,6 +7,7 @@ use crate::parser::Unit;
pub enum Stmt { pub enum Stmt {
VarDecl(String, Box<Expr>), VarDecl(String, Box<Expr>),
FnDecl(String, Vec<String>, Box<Expr>), FnDecl(String, 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. /// For simplicity, expressions can be put into statements. This is the form in which expressions are passed to the interpreter.
Expr(Box<Expr>), Expr(Box<Expr>),
} }
@ -16,7 +17,7 @@ pub enum Stmt {
pub enum Expr { pub enum Expr {
Binary(Box<Expr>, TokenKind, Box<Expr>), Binary(Box<Expr>, TokenKind, Box<Expr>),
Unary(TokenKind, Box<Expr>), Unary(TokenKind, Box<Expr>),
Unit(Box<Expr>, TokenKind), Unit(String, Box<Expr>),
Var(String), Var(String),
Group(Box<Expr>), Group(Box<Expr>),
FnCall(String, Vec<Expr>), FnCall(String, Vec<Expr>),

View File

@ -39,18 +39,15 @@ impl<'a> Context<'a> {
fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<Float, CalcError> { fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<Float, CalcError> {
match stmt { match stmt {
Stmt::VarDecl(identifier, _) => eval_var_decl_stmt(context, stmt, identifier), Stmt::VarDecl(_, _) => eval_var_decl_stmt(context, stmt),
Stmt::FnDecl(_, _, _) => eval_fn_decl_stmt(context), Stmt::FnDecl(_, _, _) => eval_fn_decl_stmt(context),
Stmt::UnitDecl(_, _, _) => eval_unit_decl_stmt(context),
Stmt::Expr(expr) => eval_expr_stmt(context, &expr), Stmt::Expr(expr) => eval_expr_stmt(context, &expr),
} }
} }
fn eval_var_decl_stmt( fn eval_var_decl_stmt(context: &mut Context, stmt: &Stmt) -> Result<Float, CalcError> {
context: &mut Context, context.symbol_table.insert(stmt.clone());
stmt: &Stmt,
identifier: &str,
) -> Result<Float, CalcError> {
context.symbol_table.insert(&identifier, stmt.clone());
Ok(Float::with_val(context.precision, 1)) Ok(Float::with_val(context.precision, 1))
} }
@ -58,6 +55,10 @@ fn eval_fn_decl_stmt(context: &mut Context) -> Result<Float, CalcError> {
Ok(Float::with_val(context.precision, 1)) // Nothing needs to happen here, since the parser will already have added the FnDecl's to the symbol table. Ok(Float::with_val(context.precision, 1)) // Nothing needs to happen here, since the parser will already have added the FnDecl's to the symbol table.
} }
fn eval_unit_decl_stmt(context: &mut Context) -> Result<Float, CalcError> {
Ok(Float::with_val(context.precision, 1))
}
fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> { fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
eval_expr(context, &expr) eval_expr(context, &expr)
} }
@ -66,7 +67,7 @@ fn eval_expr(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
match expr { match expr {
Expr::Binary(left, op, right) => eval_binary_expr(context, &left, op, &right), Expr::Binary(left, op, right) => eval_binary_expr(context, &left, op, &right),
Expr::Unary(op, expr) => eval_unary_expr(context, op, expr), Expr::Unary(op, expr) => eval_unary_expr(context, op, expr),
Expr::Unit(expr, kind) => eval_unit_expr(context, expr, kind), Expr::Unit(_, expr) => eval_unit_expr(context, expr),
Expr::Var(identifier) => eval_var_expr(context, identifier), Expr::Var(identifier) => eval_var_expr(context, identifier),
Expr::Literal(value) => eval_literal_expr(context, value), Expr::Literal(value) => eval_literal_expr(context, value),
Expr::Group(expr) => eval_group_expr(context, &expr), Expr::Group(expr) => eval_group_expr(context, &expr),
@ -78,12 +79,20 @@ fn eval_expr(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
fn eval_binary_expr( fn eval_binary_expr(
context: &mut Context, context: &mut Context,
left: &Expr, left_expr: &Expr,
op: &TokenKind, op: &TokenKind,
right: &Expr, right_expr: &Expr,
) -> Result<Float, CalcError> { ) -> Result<Float, CalcError> {
let left = eval_expr(context, &left)?; let left = eval_expr(context, left_expr)?;
let right = eval_expr(context, &right)?; let right = if let Expr::Unit(left_unit, _) = left_expr {
if let Expr::Unit(right_unit, right_unit_expr) = right_expr {
convert_unit(context, right_unit_expr, right_unit, &left_unit)?
} else {
eval_expr(context, right_expr)?
}
} else {
eval_expr(context, right_expr)?
};
Ok(match op { Ok(match op {
TokenKind::Plus => left + right, TokenKind::Plus => left + right,
@ -108,26 +117,26 @@ fn eval_unary_expr(context: &mut Context, op: &TokenKind, expr: &Expr) -> Result
} }
} }
fn eval_unit_expr( fn eval_unit_expr(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
eval_expr(context, expr)
}
fn convert_unit(
context: &mut Context, context: &mut Context,
expr: &Expr, expr: &Expr,
kind: &TokenKind, from_unit: &str,
to_unit: &str,
) -> Result<Float, CalcError> { ) -> Result<Float, CalcError> {
let x = eval_expr(context, &expr); if let Some(Stmt::UnitDecl(_, _, unit_def)) =
let unit = kind.to_unit()?; context.symbol_table.get_unit(from_unit, to_unit).cloned()
{
context
.symbol_table
.insert(Stmt::VarDecl(String::from("u"), Box::new(expr.clone())));
// Don't do any angle conversions if the defauly angle unit is the same as the unit kind eval_expr(context, &unit_def)
match unit { } else {
Unit::Degrees | Unit::Radians => { Err(CalcError::InvalidUnit)
if context.angle_unit == unit {
return x;
}
}
}
match unit {
Unit::Degrees => Ok(prelude::special_funcs::to_radians(x?)),
Unit::Radians => Ok(prelude::special_funcs::to_degrees(x?)),
} }
} }
@ -138,7 +147,7 @@ fn eval_var_expr(context: &mut Context, identifier: &str) -> Result<Float, CalcE
} }
// Look for the variable in the symbol table // Look for the variable in the symbol table
let var_decl = context.symbol_table.get(identifier).cloned(); let var_decl = context.symbol_table.get_var(identifier).cloned();
match var_decl { match var_decl {
Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr), Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr),
_ => Err(CalcError::UndefinedVar(identifier.into())), _ => Err(CalcError::UndefinedVar(identifier.into())),
@ -202,7 +211,7 @@ fn eval_fn_call_expr(
// then calculate the expression and add it to the total sum. // then calculate the expression and add it to the total sum.
context context
.symbol_table .symbol_table
.set("n", Stmt::VarDecl(String::from("n"), Box::new(n_expr))); .set(Stmt::VarDecl(String::from("n"), Box::new(n_expr)));
sum += eval_expr(context, &expressions[2])?; sum += eval_expr(context, &expressions[2])?;
} }
@ -212,10 +221,7 @@ fn eval_fn_call_expr(
} }
// Symbol Table // Symbol Table
let stmt_definition = context let stmt_definition = context.symbol_table.get_fn(identifier).cloned();
.symbol_table
.get(&format!("{}()", identifier))
.cloned();
match stmt_definition { match stmt_definition {
Some(Stmt::FnDecl(_, arguments, fn_body)) => { Some(Stmt::FnDecl(_, arguments, fn_body)) => {
@ -294,7 +300,7 @@ mod tests {
assert!(fact_dec_result > 169.406 && fact_dec_result < 169.407); assert!(fact_dec_result > 169.406 && fact_dec_result < 169.407);
} }
#[test] /*#[test]
fn test_unit() { fn test_unit() {
let rad = Stmt::Expr(Box::new(Expr::Unit(literal("1"), Rad))); let rad = Stmt::Expr(Box::new(Expr::Unit(literal("1"), Rad)));
let deg = Stmt::Expr(Box::new(Expr::Unit(literal("1"), Deg))); let deg = Stmt::Expr(Box::new(Expr::Unit(literal("1"), Deg)));
@ -304,7 +310,7 @@ mod tests {
(interpret(deg).unwrap().unwrap() - Float::with_val(PRECISION, 0.017456)).abs() (interpret(deg).unwrap().unwrap() - Float::with_val(PRECISION, 0.017456)).abs()
< Float::with_val(PRECISION, 0.0001) < Float::with_val(PRECISION, 0.0001)
); );
} }*/
#[test] #[test]
fn test_var() { fn test_var() {
@ -312,7 +318,7 @@ mod tests {
// Prepare by inserting a variable declaration in the symbol table. // Prepare by inserting a variable declaration in the symbol table.
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
symbol_table.insert("x", var_decl("x", literal("1"))); symbol_table.insert(var_decl("x", literal("1")));
let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION); let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION);
assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 1); assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 1);
@ -345,14 +351,11 @@ mod tests {
// Prepare by inserting a variable declaration in the symbol table. // Prepare by inserting a variable declaration in the symbol table.
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
symbol_table.insert( symbol_table.insert(fn_decl(
"f()", "f",
fn_decl( vec![String::from("x")],
"f", binary(var("x"), TokenKind::Plus, literal("2")),
vec![String::from("x")], ));
binary(var("x"), TokenKind::Plus, literal("2")),
),
);
let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION); let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION);
assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 3); assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 3);

View File

@ -16,6 +16,8 @@ pub enum TokenKind {
Equals, Equals,
Exclamation, Exclamation,
UnitKeyword,
To,
Deg, Deg,
Rad, Rad,
@ -170,6 +172,8 @@ impl<'a> Lexer<'a> {
let kind = match value.as_ref() { let kind = match value.as_ref() {
"deg" | "°" => TokenKind::Deg, "deg" | "°" => TokenKind::Deg,
"rad" => TokenKind::Rad, "rad" => TokenKind::Rad,
"unit" => TokenKind::UnitKeyword,
"to" => TokenKind::To,
_ => TokenKind::Identifier, _ => TokenKind::Identifier,
}; };

View File

@ -19,6 +19,8 @@ pub struct Context {
pos: usize, pos: usize,
symbol_table: SymbolTable, symbol_table: SymbolTable,
angle_unit: Unit, angle_unit: Unit,
parsing_unit_decl: bool,
unit_decl_base_unit: Option<String>,
} }
impl Context { impl Context {
@ -28,6 +30,8 @@ impl Context {
pos: 0, pos: 0,
symbol_table: SymbolTable::new(), symbol_table: SymbolTable::new(),
angle_unit: Unit::Radians, angle_unit: Unit::Radians,
parsing_unit_decl: false,
unit_decl_base_unit: None,
} }
} }
@ -101,6 +105,8 @@ fn parse_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
TokenKind::OpenParenthesis => parse_identifier_stmt(context)?, TokenKind::OpenParenthesis => parse_identifier_stmt(context)?,
_ => Stmt::Expr(Box::new(parse_expr(context)?)), _ => Stmt::Expr(Box::new(parse_expr(context)?)),
}); });
} else if match_token(context, TokenKind::UnitKeyword) {
return parse_unit_decl_stmt(context);
} }
Ok(Stmt::Expr(Box::new(parse_expr(context)?))) Ok(Stmt::Expr(Box::new(parse_expr(context)?)))
@ -132,9 +138,7 @@ fn parse_identifier_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
// Insert the function declaration into the symbol table during parsing // Insert the function declaration into the symbol table during parsing
// so that the parser can find out if particular functions exist. // so that the parser can find out if particular functions exist.
context context.symbol_table.insert(fn_decl.clone());
.symbol_table
.insert(&format!("{}()", identifier), fn_decl.clone());
return Ok(fn_decl); return Ok(fn_decl);
} }
@ -156,6 +160,29 @@ fn parse_var_decl_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
Ok(Stmt::VarDecl(identifier.value, Box::new(expr))) Ok(Stmt::VarDecl(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();
consume(context, TokenKind::Equals)?;
// Parse the definition
context.parsing_unit_decl = true;
let def = parse_expr(context)?;
context.parsing_unit_decl = false;
let base_unit = if let Some(base_unit) = &context.unit_decl_base_unit {
base_unit.clone()
} else {
return Err(CalcError::InvalidUnit);
};
let stmt = Stmt::UnitDecl(identifier.value, base_unit, Box::new(def));
context.symbol_table.insert(stmt.clone());
Ok(stmt)
}
fn parse_expr(context: &mut Context) -> Result<Expr, CalcError> { fn parse_expr(context: &mut Context) -> Result<Expr, CalcError> {
Ok(parse_sum(context)?) Ok(parse_sum(context)?)
} }
@ -175,7 +202,7 @@ fn parse_sum(context: &mut Context) -> Result<Expr, CalcError> {
} }
fn parse_factor(context: &mut Context) -> Result<Expr, CalcError> { fn parse_factor(context: &mut Context) -> Result<Expr, CalcError> {
let mut left = parse_unary(context)?; let mut left = parse_unit(context)?;
while match_token(context, TokenKind::Star) while match_token(context, TokenKind::Star)
|| match_token(context, TokenKind::Slash) || match_token(context, TokenKind::Slash)
@ -188,13 +215,27 @@ fn parse_factor(context: &mut Context) -> Result<Expr, CalcError> {
_ => advance(context).kind.clone(), _ => advance(context).kind.clone(),
}; };
let right = parse_unary(context)?; let right = parse_unit(context)?;
left = Expr::Binary(Box::new(left), op, Box::new(right)); left = Expr::Binary(Box::new(left), op, Box::new(right));
} }
Ok(left) Ok(left)
} }
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),
));
}
Ok(expr)
}
fn parse_unary(context: &mut Context) -> Result<Expr, CalcError> { fn parse_unary(context: &mut Context) -> Result<Expr, CalcError> {
if match_token(context, TokenKind::Minus) { if match_token(context, TokenKind::Minus) {
let op = advance(context).kind.clone(); let op = advance(context).kind.clone();
@ -236,11 +277,7 @@ fn parse_primary(context: &mut Context) -> Result<Expr, CalcError> {
_ => Expr::Literal(advance(context).value.clone()), _ => Expr::Literal(advance(context).value.clone()),
}; };
if !is_at_end(context) && peek(context).kind.is_unit() { Ok(expr)
Ok(Expr::Unit(Box::new(expr), advance(context).kind.clone()))
} else {
Ok(expr)
}
} }
fn parse_group(context: &mut Context) -> Result<Expr, CalcError> { fn parse_group(context: &mut Context) -> Result<Expr, CalcError> {
@ -295,7 +332,10 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
} }
// Eg. x // Eg. x
if context.symbol_table.contains_var(&identifier.value) { if context.parsing_unit_decl {
context.unit_decl_base_unit = Some(identifier.value);
Ok(Expr::Var(String::from("u")))
} else if context.symbol_table.contains_var(&identifier.value) {
Ok(Expr::Var(identifier.value)) Ok(Expr::Var(identifier.value))
} else { } else {
let mut chars = identifier.value.chars(); let mut chars = identifier.value.chars();
@ -315,19 +355,19 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
} }
} }
fn peek(context: &mut Context) -> &Token { fn peek(context: &Context) -> &Token {
&context.tokens[context.pos] &context.tokens[context.pos]
} }
fn peek_next(context: &mut Context) -> &Token { fn peek_next(context: &Context) -> &Token {
&context.tokens[context.pos + 1] &context.tokens[context.pos + 1]
} }
fn previous(context: &mut Context) -> &Token { fn previous(context: &Context) -> &Token {
&context.tokens[context.pos - 1] &context.tokens[context.pos - 1]
} }
fn match_token(context: &mut Context, kind: TokenKind) -> bool { fn match_token(context: &Context, kind: TokenKind) -> bool {
if is_at_end(context) { if is_at_end(context) {
return false; return false;
} }
@ -348,7 +388,7 @@ fn consume(context: &mut Context, kind: TokenKind) -> Result<&Token, CalcError>
Err(CalcError::UnexpectedToken(kind)) Err(CalcError::UnexpectedToken(kind))
} }
fn is_at_end(context: &mut Context) -> bool { fn is_at_end(context: &Context) -> bool {
context.pos >= context.tokens.len() || peek(context).kind == TokenKind::EOF context.pos >= context.tokens.len() || peek(context).kind == TokenKind::EOF
} }
@ -357,7 +397,6 @@ mod tests {
use super::*; use super::*;
use crate::lexer::{Token, TokenKind::*}; use crate::lexer::{Token, TokenKind::*};
use crate::test_helpers::*; use crate::test_helpers::*;
use test_case::test_case;
fn parse_with_context(context: &mut Context, tokens: Vec<Token>) -> Result<Stmt, CalcError> { fn parse_with_context(context: &mut Context, tokens: Vec<Token>) -> Result<Stmt, CalcError> {
context.tokens = tokens; context.tokens = tokens;
@ -447,7 +486,7 @@ mod tests {
); );
} }
#[test_case(Deg)] /*#[test_case(Deg)]
#[test_case(Rad)] #[test_case(Rad)]
fn test_unary(angle_unit: TokenKind) { fn test_unary(angle_unit: TokenKind) {
let tokens = vec![ let tokens = vec![
@ -460,7 +499,7 @@ mod tests {
parse(tokens).unwrap(), parse(tokens).unwrap(),
Stmt::Expr(unary(Minus, Box::new(Expr::Unit(literal("1"), angle_unit)))) Stmt::Expr(unary(Minus, Box::new(Expr::Unit(literal("1"), angle_unit))))
); );
} }*/
#[test] #[test]
fn test_var_decl() { fn test_var_decl() {
@ -517,10 +556,11 @@ mod tests {
let mut context = Context::new(); let mut context = Context::new();
// Add the function to the symbol table first, in order to prevent errors. // Add the function to the symbol table first, in order to prevent errors.
context.symbol_table.set( context.symbol_table.set(Stmt::FnDecl(
"f()", String::from("f"),
Stmt::FnDecl(String::from("f"), vec![String::from("x")], literal("1")), vec![String::from("x")],
); literal("1"),
));
assert_eq!( assert_eq!(
parse_with_context(&mut context, tokens).unwrap(), parse_with_context(&mut context, tokens).unwrap(),

View File

@ -1,40 +1,79 @@
use crate::{ast::Stmt, prelude}; use crate::{ast::Stmt, prelude};
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Debug)]
pub struct SymbolTable { pub struct SymbolTable {
hashmap: HashMap<String, Stmt>, hashmap: HashMap<String, Stmt>,
unit_types: HashMap<String, ()>,
} }
impl SymbolTable { impl SymbolTable {
pub fn new() -> Self { pub fn new() -> Self {
SymbolTable { SymbolTable {
hashmap: HashMap::new(), hashmap: HashMap::new(),
unit_types: HashMap::new(),
} }
} }
pub fn insert(&mut self, key: &str, value: Stmt) { pub fn insert(&mut self, value: Stmt) -> Option<Stmt> {
self.hashmap.insert(key.into(), value); match &value {
Stmt::VarDecl(identifier, _) => {
self.hashmap.insert(format!("var.{}", identifier), value)
}
Stmt::UnitDecl(identifier, to_unit, _) => {
self.unit_types.insert(identifier.to_string(), ());
self.unit_types.insert(to_unit.to_string(), ());
self.hashmap
.insert(format!("unit.{}.{}", identifier, to_unit), value)
}
Stmt::FnDecl(identifier, _, _) => {
self.hashmap.insert(format!("fn.{}", identifier), value)
}
_ => panic!("Can only insert VarDecl, UnitDecl and FnDecl into symbol table."),
}
} }
pub fn get(&self, key: &str) -> Option<&Stmt> { pub fn get_var(&self, key: &str) -> Option<&Stmt> {
self.hashmap.get(key) self.hashmap.get(&format!("var.{}", key))
} }
pub fn set(&mut self, key: &str, value: Stmt) { pub fn get_unit(&self, key: &str, to_unit: &str) -> Option<&Stmt> {
if let Some(stmt) = self.hashmap.get_mut(key) { self.hashmap.get(&format!("unit.{}.{}", key, to_unit))
}
pub fn get_fn(&self, key: &str) -> Option<&Stmt> {
self.hashmap.get(&format!("fn.{}", key))
}
pub fn set(&mut self, value: Stmt) {
let existing_item = match &value {
Stmt::VarDecl(identifier, _) => self.hashmap.get_mut(&format!("var.{}", identifier)),
Stmt::UnitDecl(identifier, to_unit, _) => self
.hashmap
.get_mut(&format!("unit.{}.{}", identifier, to_unit)),
Stmt::FnDecl(identifier, _, _) => self.hashmap.get_mut(&format!("fn.{}", identifier)),
_ => panic!("Can only set VarDecl, UnitDecl and FnDecl in symbol table."),
};
if let Some(stmt) = existing_item {
*stmt = value; *stmt = value;
} else { } else {
self.insert(key, value); self.insert(value);
} }
} }
pub fn contains_var(&self, identifier: &str) -> bool { pub fn contains_var(&self, identifier: &str) -> bool {
prelude::CONSTANTS.contains_key(identifier) || self.hashmap.contains_key(identifier) prelude::CONSTANTS.contains_key(identifier)
|| self.hashmap.contains_key(&format!("var.{}", identifier))
}
pub fn contains_unit(&self, identifier: &str) -> bool {
self.unit_types.contains_key(identifier)
} }
pub fn contains_fn(&self, identifier: &str) -> bool { pub fn contains_fn(&self, identifier: &str) -> bool {
prelude::UNARY_FUNCS.contains_key(identifier) prelude::UNARY_FUNCS.contains_key(identifier)
|| prelude::UNARY_FUNCS.contains_key(identifier) || prelude::UNARY_FUNCS.contains_key(identifier)
|| self.hashmap.contains_key(&format!("{}()", identifier)) || self.hashmap.contains_key(&format!("fn.{}", identifier))
} }
} }