Initial commit

This commit is contained in:
PaddiM8 2020-05-28 23:46:35 +02:00
commit 70d2913cb9
11 changed files with 991 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

7
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": []
}

5
Cargo.lock generated Normal file
View File

@ -0,0 +1,5 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "lek"
version = "0.1.0"

13
Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "lek"
version = "0.1.0"
authors = ["paddi"]
edition = "2018"
[profile.release]
panic = "abort"
#lto = true
#codegen-units = 1
#incremental = false
[dependencies]

158
src/interpreter.rs Normal file
View File

@ -0,0 +1,158 @@
use std::{collections::HashMap, mem};
use crate::lexer::TokenKind;
use crate::parser::{Expr, Stmt, Unit};
use crate::prelude::{self, Prelude};
use crate::visitor::Visitor;
pub struct Interpreter {
pub symbol_table: HashMap<String, Stmt>,
angle_unit: Unit,
prelude: Prelude,
}
impl Interpreter {
pub fn new(angle_unit: Unit) -> Self {
let mut hashmap: HashMap<String, Stmt> = HashMap::new();
for constant in prelude::CONSTANTS {
hashmap.insert(
constant.0.to_string(),
Stmt::VarDecl(
constant.0.to_string(),
Box::new(Expr::Literal(constant.1.to_string())),
),
);
}
Interpreter {
angle_unit,
symbol_table: hashmap,
prelude: Prelude::new(),
}
}
pub fn interpret(&mut self, statements: Vec<Stmt>) -> Option<f64> {
for (i, stmt) in statements.iter().enumerate() {
let value = self.visit_stmt(stmt);
if i == statements.len() - 1 {
if let Stmt::Expr(_) = stmt {
return Some(value);
}
}
}
return None;
}
pub fn set_angle_unit(&mut self, angle_unit: Unit) {
self.prelude.angle_unit = angle_unit.clone();
self.angle_unit = angle_unit;
}
}
impl TokenKind {
fn to_unit(&self) -> Unit {
match self {
TokenKind::Deg => Unit::Degrees,
TokenKind::Rad => Unit::Radians,
_ => panic!("Invalid unit."),
}
}
}
impl Unit {
// TODO: Something more generic
fn compare(&self, second_token: &Unit) -> bool {
mem::discriminant(self) == mem::discriminant(second_token)
}
}
impl Visitor<f64, f64> for Interpreter {
fn visit_stmt(&mut self, stmt: &Stmt) -> f64 {
match stmt {
Stmt::VarDecl(identifier, _) => {
self.symbol_table.insert(identifier.clone(), stmt.clone());
0f64
}
Stmt::FnDecl(identifier, argument, _) => {
self.visit_stmt(&Stmt::VarDecl(
argument.clone(),
Box::new(Expr::Literal(String::from("0"))),
));
self.symbol_table
.insert(format!("{}()", identifier.clone()), stmt.clone());
0f64
}
Stmt::Expr(expr) => self.visit_expr(&expr),
}
}
fn visit_expr(&mut self, expr: &Expr) -> f64 {
match expr {
Expr::Binary(left, op, right) => {
let left = self.visit_expr(&left);
let right = self.visit_expr(&right);
match op {
TokenKind::Plus => left + right,
TokenKind::Minus => left - right,
TokenKind::Star => left * right,
TokenKind::Slash => left / right,
TokenKind::Power => left.powf(right),
_ => 0f64,
}
}
Expr::Unary(_, expr) => self.visit_expr(&expr).clone(),
Expr::Unit(expr, kind) => {
let x = self.visit_expr(&expr);
// Don't do any angle conversions if the defauly angle unit is the same as the unit kind
if (kind.compare(&TokenKind::Deg) || kind.compare(&TokenKind::Rad))
&& self.angle_unit.compare(&kind.to_unit())
{
return x;
}
match kind {
TokenKind::Deg => x.to_radians(),
TokenKind::Rad => x.to_degrees(),
_ => panic!("Invalid unit."),
}
}
Expr::Var(identifier) => {
let value = self
.symbol_table
.get(identifier)
.expect("Undefined variable.")
.clone();
if let Stmt::VarDecl(_, expr) = value {
return self.visit_expr(&expr);
}
panic!("Unknown error.");
}
Expr::Literal(value) => value.parse().unwrap(),
Expr::Group(expr) => self.visit_expr(&expr),
Expr::FnCall(identifier, expr) => {
let x = self.visit_expr(&expr);
if let Some(result) = self.prelude.call_unary_func(identifier, x) {
result
} else {
let stmt = self
.symbol_table
.get(&format!("{}()", identifier))
.expect("Undefined function")
.clone();
match stmt {
Stmt::FnDecl(_, argument, fn_body) => {
self.visit_stmt(&Stmt::VarDecl(argument.clone(), expr.clone()));
self.visit_expr(&*fn_body)
}
_ => panic!(),
}
}
}
}
}
}

179
src/lexer.rs Normal file
View File

@ -0,0 +1,179 @@
use std::str;
#[derive(Clone, Debug)]
pub enum TokenKind {
Unknown,
Literal,
Identifier,
Plus,
Minus,
Star,
Slash,
Power,
Equals,
Deg,
Rad,
Pipe,
OpenParenthesis,
ClosedParenthesis,
EOF,
}
#[derive(Clone, Debug)]
pub struct Token {
pub kind: TokenKind,
pub value: String,
}
pub struct Lexer<'a> {
source: &'a [u8],
index: usize,
}
impl<'a> Lexer<'a> {
pub fn lex(source: &str) -> Vec<Token> {
let mut lexer = Lexer {
source: source.as_bytes(),
index: 0,
};
let mut tokens = Vec::new();
while !lexer.is_at_end() {
tokens.push(lexer.next());
}
// If there isn't already an EOF token, add it.
if let TokenKind::EOF = tokens.last().unwrap().kind {
} else {
tokens.push(lexer.build(TokenKind::EOF, ""));
}
tokens
}
fn next(&mut self) -> Token {
let mut c = self.peek();
while c == ' ' || c == '\t' || c == '\r' || c == '\n' {
self.advance();
if self.is_at_end() {
return self.build(TokenKind::EOF, "");
} else {
c = self.peek();
}
}
let token = match c {
'+' => {
self.advance();
self.build(TokenKind::Plus, "")
}
'-' => {
self.advance();
self.build(TokenKind::Minus, "")
}
'*' => {
self.advance();
self.build(TokenKind::Star, "")
}
'/' => {
self.advance();
self.build(TokenKind::Slash, "")
}
'^' => {
self.advance();
self.build(TokenKind::Power, "")
}
'(' => {
self.advance();
self.build(TokenKind::OpenParenthesis, "")
}
')' => {
self.advance();
self.build(TokenKind::ClosedParenthesis, "")
}
'|' => {
self.advance();
self.build(TokenKind::Pipe, "")
}
'=' => {
self.advance();
self.build(TokenKind::Equals, "")
}
_ => {
if c.is_digit(10) {
self.next_number_literal()
} else if c.is_alphabetic() {
self.next_identifier()
} else {
self.advance();
self.build(TokenKind::Unknown, "")
}
}
};
token
}
fn next_number_literal(&mut self) -> Token {
let start = self.index;
let mut end = start;
while !self.is_at_end() && (self.peek().is_digit(10) || self.peek() == '.') {
end += 1;
self.advance();
}
if let Ok(value) = str::from_utf8(&self.source[start..end]) {
self.build(TokenKind::Literal, value)
} else {
self.build(TokenKind::Unknown, "")
}
}
fn next_identifier(&mut self) -> Token {
let start = self.index;
let mut end = start;
while !self.is_at_end() && is_valid_identifier(self.peek()) {
end += 1;
self.advance();
}
if let Ok(value) = str::from_utf8(&self.source[start..end]) {
let kind = match value {
"deg" | "°" => TokenKind::Deg,
"rad" => TokenKind::Rad,
_ => TokenKind::Identifier,
};
self.build(kind, value)
} else {
self.build(TokenKind::Unknown, "")
}
}
fn build(&self, kind: TokenKind, value: &str) -> Token {
Token {
kind,
value: value.to_string(),
}
}
fn peek(&self) -> char {
self.source[self.index].into()
}
fn advance(&mut self) {
self.index = self.index + 1;
}
fn is_at_end(&self) -> bool {
self.index >= self.source.len()
}
}
fn is_valid_identifier(c: char) -> bool {
c.is_alphabetic() || c == '°' || c == '√' || c == '\''
}

51
src/main.rs Normal file
View File

@ -0,0 +1,51 @@
use std::env;
use std::io;
mod interpreter;
mod lexer;
mod math_parser;
mod parser;
mod prelude;
mod visitor;
use math_parser::MathParser;
use parser::Unit;
#[allow(unused_assignments)] // The compiler gives a warning that is not valid.
fn main() {
let angle_unit = if let Ok(angle_unit_var) = env::var("ANGLE_UNIT") {
match angle_unit_var.as_ref() {
"radians" => Unit::Radians,
"degrees" => Unit::Degrees,
_ => {
println!("Unexpected angle unit: {}.", angle_unit_var);
return;
}
}
} else {
Unit::Radians
};
let mut math_parser = MathParser::new();
math_parser.set_angle_unit(angle_unit);
if let Some(expr) = env::args().skip(1).next() {
if let Some(result) = math_parser.parse(&expr) {
println!("{}", result);
}
} else {
let mut history: Vec<String> = Vec::new();
let mut input = String::new();
loop {
eprint!(">>> ");
input = String::new();
io::stdin().read_line(&mut input).unwrap();
history.push(input.clone());
if let Some(result) = math_parser.parse(&input) {
println!("{}", result);
}
}
}
}

30
src/math_parser.rs Normal file
View File

@ -0,0 +1,30 @@
use crate::interpreter::Interpreter;
use crate::lexer::Lexer;
use crate::parser::{Parser, Unit};
pub const DEFAULT_ANGLE_UNIT: Unit = Unit::Radians;
pub struct MathParser {
parser: Parser,
interpreter: Interpreter,
}
impl MathParser {
pub fn new() -> Self {
MathParser {
parser: Parser::new(),
interpreter: Interpreter::new(DEFAULT_ANGLE_UNIT),
}
}
pub fn parse(&mut self, source: &str) -> Option<f64> {
let tokens = Lexer::lex(source);
let statements = self.parser.parse(tokens);
self.interpreter.interpret(statements)
}
pub fn set_angle_unit(&mut self, unit: Unit) {
self.interpreter.set_angle_unit(unit);
}
}

255
src/parser.rs Normal file
View File

@ -0,0 +1,255 @@
use std::mem;
use crate::lexer::{Token, TokenKind};
#[derive(Debug, Clone)]
pub enum Stmt {
VarDecl(String, Box<Expr>),
FnDecl(String, String, Box<Expr>),
Expr(Box<Expr>),
}
#[derive(Debug, Clone)]
pub enum Expr {
Binary(Box<Expr>, TokenKind, Box<Expr>),
Unary(TokenKind, Box<Expr>),
Unit(Box<Expr>, TokenKind),
Var(String),
Group(Box<Expr>),
FnCall(String, Box<Expr>),
Literal(String),
}
#[derive(Debug, Clone)]
pub enum Unit {
Radians,
Degrees,
}
pub struct Parser {
tokens: Vec<Token>,
pos: usize,
}
impl TokenKind {
pub fn is_unit(&self) -> bool {
match self {
TokenKind::Deg | TokenKind::Rad => true,
_ => false,
}
}
pub fn compare(&self, second_token: &TokenKind) -> bool {
mem::discriminant(self) == mem::discriminant(second_token)
}
}
impl Parser {
pub fn new() -> Parser {
Parser {
tokens: Vec::new(),
pos: 0,
}
}
pub fn parse(&mut self, tokens: Vec<Token>) -> Vec<Stmt> {
self.tokens = tokens;
self.pos = 0;
let mut statements: Vec<Stmt> = Vec::new();
while !self.is_at_end() {
statements.push(self.parse_stmt());
}
statements
}
fn parse_stmt(&mut self) -> Stmt {
if self.match_token(TokenKind::Identifier) {
return match self.peek_next().kind {
TokenKind::Equals => self.parse_var_decl_stmt(),
TokenKind::Identifier => self.parse_identifier_stmt(),
_ => Stmt::Expr(Box::new(self.parse_expr())),
};
}
Stmt::Expr(Box::new(self.parse_expr()))
}
fn parse_identifier_stmt(&mut self) -> Stmt {
let began_at = self.pos;
let primary = self.parse_primary(); // Since function declarations and function calls look the same at first, simply parse a "function call", and re-use the data.
// If `primary` is followed by an equal sign, it is a function declaration.
if self.peek().kind.compare(&TokenKind::Equals) {
self.advance();
let expr = self.parse_expr();
match primary {
Expr::FnCall(identifier, argument) => match *argument {
Expr::Var(argument_identifier) => {
Stmt::FnDecl(identifier, argument_identifier, Box::new(expr))
}
_ => panic!("Unexpected error."),
},
_ => panic!("Unexpected error."),
}
} else {
// It is a function call, not a function declaration.
// Redo the parsing for this specific part.
self.pos = began_at;
Stmt::Expr(Box::new(self.parse_expr()))
}
}
fn parse_var_decl_stmt(&mut self) -> Stmt {
let identifier = self.advance().clone();
self.advance(); // Equal sign
let expr = self.parse_expr();
Stmt::VarDecl(identifier.value, Box::new(expr))
}
fn parse_expr(&mut self) -> Expr {
self.parse_sum()
}
fn parse_sum(&mut self) -> Expr {
let mut left = self.parse_factor();
while self.match_token(TokenKind::Plus) || self.match_token(TokenKind::Minus) {
let op = self.peek().kind.clone();
self.advance();
let right = self.parse_factor();
left = Expr::Binary(Box::new(left), op, Box::new(right));
}
left
}
fn parse_factor(&mut self) -> Expr {
let mut left = self.parse_unary();
while self.match_token(TokenKind::Star)
|| self.match_token(TokenKind::Slash)
|| self.match_token(TokenKind::Identifier)
{
let mut op = self.peek().kind.clone();
// If the next token is an identifier, assume it's multiplication. Eg. 3y
if let TokenKind::Identifier = op {
op = TokenKind::Star;
} else {
self.advance();
}
let right = self.parse_unary();
left = Expr::Binary(Box::new(left), op, Box::new(right));
}
left
}
fn parse_unary(&mut self) -> Expr {
if self.match_token(TokenKind::Minus) {
let op = self.advance().kind.clone();
return Expr::Unary(op, Box::new(self.parse_unary()));
}
self.parse_exponent()
}
fn parse_exponent(&mut self) -> Expr {
let left = self.parse_primary();
if self.match_token(TokenKind::Power) {
let op = self.advance().kind.clone();
return Expr::Binary(Box::new(left), op, Box::new(self.parse_exponent()));
}
left
}
fn parse_primary(&mut self) -> Expr {
let expr = match self.peek().kind {
TokenKind::OpenParenthesis => self.parse_group(),
TokenKind::Pipe => self.parse_abs(),
TokenKind::Identifier => self.parse_identifier(),
_ => Expr::Literal(self.consume(TokenKind::Literal).value.clone()),
};
if !self.is_at_end() && self.peek().kind.is_unit() {
Expr::Unit(Box::new(expr), self.advance().kind.clone())
} else {
expr
}
}
fn parse_group(&mut self) -> Expr {
self.advance();
let group_expr = Expr::Group(Box::new(self.parse_expr()));
self.consume(TokenKind::ClosedParenthesis);
group_expr
}
fn parse_abs(&mut self) -> Expr {
self.advance();
let group_expr = Expr::Group(Box::new(self.parse_expr()));
self.consume(TokenKind::Pipe);
Expr::FnCall(String::from("abs"), Box::new(group_expr))
}
fn parse_identifier(&mut self) -> Expr {
let identifier = self.advance().clone();
if self.match_token(TokenKind::OpenParenthesis) {
self.advance();
let parameter = self.parse_expr();
self.consume(TokenKind::ClosedParenthesis);
Expr::FnCall(identifier.value, Box::new(parameter))
} else {
Expr::Var(identifier.value)
}
}
fn peek(&self) -> &Token {
&self.tokens[self.pos]
}
fn peek_next(&self) -> &Token {
&self.tokens[self.pos + 1]
}
fn previous(&self) -> &Token {
&self.tokens[self.pos - 1]
}
fn match_token(&self, kind: TokenKind) -> bool {
if self.is_at_end() {
return false;
}
self.peek().kind.compare(&kind)
}
fn advance(&mut self) -> &Token {
self.pos += 1;
self.previous()
}
fn consume(&mut self, kind: TokenKind) -> &Token {
if self.match_token(kind) {
return self.advance();
}
panic!("Unexpected token.");
}
fn is_at_end(&self) -> bool {
self.pos >= self.tokens.len() || self.peek().kind.compare(&TokenKind::EOF)
}
}

286
src/prelude.rs Normal file
View File

@ -0,0 +1,286 @@
use crate::{math_parser, parser::Unit};
use std::collections::HashMap;
pub const CONSTANTS: &[(&str, &str)] = &[
("pi", "3.14159265"),
("π", "3.14159265"),
("e", "2.71828182"),
("tau", "6.28318530"),
("τ", "6.28318530"),
("phi", "1.61803398"),
("ϕ", "1.61803398"),
];
enum FuncType {
Trig,
InverseTrig,
Other,
}
struct UnaryFuncInfo {
func: Box<fn(f64) -> f64>,
func_type: FuncType,
}
impl UnaryFuncInfo {
fn call(&self, x: f64, angle_unit: &Unit) -> f64 {
let func = *self.func;
match self.func_type {
FuncType::Trig => func(self.from_angle_unit(x, angle_unit)),
FuncType::InverseTrig => self.to_angle_unit(func(x), angle_unit),
FuncType::Other => func(x),
}
}
fn to_angle_unit(&self, x: f64, angle_unit: &Unit) -> f64 {
match angle_unit {
Unit::Radians => x,
Unit::Degrees => x.to_degrees(),
}
}
fn from_angle_unit(&self, x: f64, angle_unit: &Unit) -> f64 {
match angle_unit {
Unit::Radians => x,
Unit::Degrees => x.to_radians(),
}
}
}
pub struct Prelude {
pub angle_unit: Unit,
unary: HashMap<String, UnaryFuncInfo>,
}
impl Prelude {
pub fn new() -> Self {
Prelude {
unary: HashMap::new(),
angle_unit: math_parser::DEFAULT_ANGLE_UNIT,
}
}
pub fn call_unary_func(&mut self, name: &str, x: f64) -> Option<f64> {
if let Some(func_info) = self.unary.get(name) {
Some(func_info.call(x, &self.angle_unit))
} else {
let trig_func: Option<fn(f64) -> f64> = match name {
"cos" => Some(funcs::cos),
"cosec" => Some(funcs::cosec),
"cosech" => Some(funcs::cosech),
"cosh" => Some(funcs::cosh),
"cot" => Some(funcs::cot),
"coth" => Some(funcs::coth),
"sec" => Some(funcs::sec),
"sech" => Some(funcs::sech),
"sin" => Some(funcs::sin),
"sinh" => Some(funcs::sinh),
"tan" => Some(funcs::tan),
"tanh" => Some(funcs::tanh),
_ => None,
};
if let Some(func) = trig_func {
let func_info = UnaryFuncInfo {
func: Box::new(func),
func_type: FuncType::Trig,
};
let value = func_info.call(x, &self.angle_unit);
self.unary.insert(name.to_string(), func_info);
return Some(value);
}
let inv_trig_func: Option<fn(f64) -> f64> = match name {
"acos" => Some(funcs::acos),
"acosh" => Some(funcs::acosh),
"acot" => Some(funcs::acot),
"acoth" => Some(funcs::acoth),
"acosec" => Some(funcs::acosec),
"asec" => Some(funcs::asec),
"asech" => Some(funcs::asech),
"asin" => Some(funcs::asin),
"asinh" => Some(funcs::asinh),
"atan" => Some(funcs::atan),
"atanh" => Some(funcs::atanh),
_ => None,
};
if let Some(func) = inv_trig_func {
let func_info = UnaryFuncInfo {
func: Box::new(func),
func_type: FuncType::InverseTrig,
};
let value = func_info.call(x, &self.angle_unit);
self.unary.insert(name.to_string(), func_info);
return Some(value);
}
let misc_func: Option<fn(f64) -> f64> = match name {
"abs" => Some(funcs::abs),
"cbrt" => Some(funcs::cbrt),
"ceil" => Some(funcs::ceil),
"exp" => Some(funcs::exp),
"floor" => Some(funcs::floor),
"frac" => Some(funcs::frac),
"log" => Some(funcs::log),
"ln" => Some(funcs::ln),
"round" => Some(funcs::round),
"sqrt" => Some(funcs::sqrt),
"trunc" => Some(funcs::trunc),
_ => None,
};
if let Some(func) = misc_func {
let func_info = UnaryFuncInfo {
func: Box::new(func),
func_type: FuncType::Other,
};
let value = func_info.call(x, &self.angle_unit);
self.unary.insert(name.to_string(), func_info);
return Some(value);
} else {
None
}
}
}
}
mod funcs {
pub fn abs(x: f64) -> f64 {
x.abs()
}
pub fn acos(x: f64) -> f64 {
x.acos()
}
pub fn acosh(x: f64) -> f64 {
x.acosh()
}
pub fn acot(x: f64) -> f64 {
(1f64 / x).atan()
}
pub fn acoth(x: f64) -> f64 {
(1f64 / x).atanh()
}
pub fn acosec(x: f64) -> f64 {
(1f64 / x).sinh()
}
pub fn asec(x: f64) -> f64 {
(1f64 / x).acos()
}
pub fn asech(x: f64) -> f64 {
(1f64 / x).acosh()
}
pub fn asin(x: f64) -> f64 {
x.asin()
}
pub fn asinh(x: f64) -> f64 {
x.asinh()
}
pub fn atan(x: f64) -> f64 {
x.atan()
}
pub fn atanh(x: f64) -> f64 {
x.atanh()
}
pub fn cbrt(x: f64) -> f64 {
x.cbrt()
}
pub fn ceil(x: f64) -> f64 {
x.ceil()
}
pub fn cos(x: f64) -> f64 {
x.cos()
}
pub fn cosh(x: f64) -> f64 {
x.cos()
}
pub fn cosec(x: f64) -> f64 {
1f64 / x.sin()
}
pub fn cosech(x: f64) -> f64 {
1f64 / x.sinh()
}
pub fn cot(x: f64) -> f64 {
x.cos() / x.sin()
}
pub fn coth(x: f64) -> f64 {
x.cosh() / x.sinh()
}
pub fn exp(x: f64) -> f64 {
x.exp()
}
pub fn floor(x: f64) -> f64 {
x.floor()
}
pub fn frac(x: f64) -> f64 {
x.fract()
}
pub fn log(x: f64) -> f64 {
x.log(10f64)
}
pub fn ln(x: f64) -> f64 {
x.ln()
}
pub fn round(x: f64) -> f64 {
x.round()
}
pub fn sec(x: f64) -> f64 {
1f64 / x.cos()
}
pub fn sech(x: f64) -> f64 {
1f64 / x.cosh()
}
pub fn sin(x: f64) -> f64 {
x.sin()
}
pub fn sinh(x: f64) -> f64 {
x.sinh()
}
pub fn sqrt(x: f64) -> f64 {
x.sqrt()
}
pub fn tan(x: f64) -> f64 {
x.tan()
}
pub fn tanh(x: f64) -> f64 {
x.tanh()
}
pub fn trunc(x: f64) -> f64 {
x.trunc()
}
}

6
src/visitor.rs Normal file
View File

@ -0,0 +1,6 @@
use crate::parser::{Expr, Stmt};
pub trait Visitor<T, U> {
fn visit_stmt(&mut self, stmt: &Stmt) -> T;
fn visit_expr(&mut self, expr: &Expr) -> U;
}