Made it possible for the parser to parse combined variables, eg. xy, so that the user does not have to write the multiplication sign.

This commit is contained in:
PaddiM8 2020-05-30 17:56:16 +02:00
parent 01aa30c2c1
commit 0dac28bb42
4 changed files with 46 additions and 31 deletions

View File

@ -10,16 +10,6 @@ pub struct Context<'a> {
impl<'a> Context<'a> { impl<'a> Context<'a> {
pub fn new(angle_unit: Unit, symbol_table: &'a mut SymbolTable) -> Self { pub fn new(angle_unit: Unit, symbol_table: &'a mut SymbolTable) -> Self {
for constant in prelude::CONSTANTS {
symbol_table.insert(
constant.0,
Stmt::VarDecl(
constant.0.to_string(),
Box::new(Expr::Literal(constant.1.to_string())),
),
);
}
Context { Context {
angle_unit: angle_unit.clone(), angle_unit: angle_unit.clone(),
symbol_table, symbol_table,
@ -119,6 +109,12 @@ fn eval_unit_expr(context: &mut Context, expr: &Expr, kind: &TokenKind) -> Resul
} }
fn eval_var_expr(context: &mut Context, identifier: &str) -> Result<f64, String> { fn eval_var_expr(context: &mut Context, identifier: &str) -> Result<f64, String> {
// If there is a constant with this name, return a literal expression with its value
if let Some(value) = prelude::CONSTANTS.get(identifier) {
return eval_expr(context, &Expr::Literal(value.to_string()));
}
// Look for the variable in the symbol table
let var_decl = context.symbol_table.get(identifier).cloned(); let var_decl = context.symbol_table.get(identifier).cloned();
match var_decl { match var_decl {
Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr), Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr),

View File

@ -121,15 +121,13 @@ fn parse_factor(context: &mut Context) -> Result<Expr, String> {
while match_token(context, TokenKind::Star) while match_token(context, TokenKind::Star)
|| match_token(context, TokenKind::Slash) || match_token(context, TokenKind::Slash)
|| match_token(context, TokenKind::Identifier) || match_token(context, TokenKind::Identifier)
|| match_token(context, TokenKind::Literal)
{ {
let mut op = peek(context).kind.clone();
// If the next token is an identifier, assume it's multiplication. Eg. 3y // If the next token is an identifier, assume it's multiplication. Eg. 3y
if let TokenKind::Identifier = op { let op = match peek(context).kind {
op = TokenKind::Star; TokenKind::Identifier | TokenKind::Literal => TokenKind::Star,
} else { _ => advance(context).kind.clone(),
advance(context); };
}
let right = parse_unary(context)?; let right = parse_unary(context)?;
left = Expr::Binary(Box::new(left), op, Box::new(right)); left = Expr::Binary(Box::new(left), op, Box::new(right));
@ -221,7 +219,24 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, String> {
} }
// Eg. x // Eg. x
Ok(Expr::Var(identifier.value)) if context.symbol_table.contains_var(&identifier.value) {
return Ok(Expr::Var(identifier.value));
} else {
let mut chars = identifier.value.chars();
let mut left = Expr::Var(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.
for c in chars {
left = Expr::Binary(
Box::new(left),
TokenKind::Star,
Box::new(Expr::Var(c.to_string())),
);
}
return Ok(left);
}
} }
fn peek<'a>(context: &'a mut Context) -> &'a Token { fn peek<'a>(context: &'a mut Context) -> &'a Token {

View File

@ -1,14 +1,14 @@
use FuncType::*; use FuncType::*;
pub const CONSTANTS: &[(&str, &str)] = &[ pub const CONSTANTS: phf::Map<&'static str, &'static str> = phf::phf_map! {
("pi", "3.14159265"), "pi" => "3.14159265",
("π", "3.14159265"), "π" => "3.14159265",
("e", "2.71828182"), "e" => "2.71828182",
("tau", "6.28318530"), "tau" => "6.28318530",
("τ", "6.28318530"), "τ" => "6.28318530",
("phi", "1.61803398"), "phi" => "1.61803398",
("ϕ", "1.61803398"), "ϕ" => "1.61803398",
]; };
use crate::ast::Unit; use crate::ast::Unit;
use funcs::*; use funcs::*;

View File

@ -20,9 +20,13 @@ impl SymbolTable {
self.hashmap.get(key) self.hashmap.get(key)
} }
pub fn contains_func(&self, key: &str) -> bool { pub fn contains_var(&self, identifier: &str) -> bool {
prelude::UNARY_FUNCS.contains_key(key) prelude::CONSTANTS.contains_key(identifier) || self.hashmap.contains_key(identifier)
|| prelude::UNARY_FUNCS.contains_key(key) }
|| self.hashmap.contains_key(&format!("{}()", key))
pub fn contains_func(&self, identifier: &str) -> bool {
prelude::UNARY_FUNCS.contains_key(identifier)
|| prelude::UNARY_FUNCS.contains_key(identifier)
|| self.hashmap.contains_key(&format!("{}()", identifier))
} }
} }