From e22f5647c6a6a99f826b39ce79e5f46574e1f1f9 Mon Sep 17 00:00:00 2001 From: Garrit Franke Date: Sun, 6 Dec 2020 18:12:09 +0100 Subject: [PATCH] Add math operations --- examples/hello_world.sb | 10 +++---- examples_out/out.js | 10 +++---- src/generator/js.rs | 26 +++++++++++++++++ src/lexer/mod.rs | 8 ++--- src/parser/mod.rs | 46 +++++++++++++++++++++++++---- src/parser/node_type.rs | 61 ++++++++++++++++++++++++++++++++++++++ src/parser/tests.rs | 65 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 205 insertions(+), 21 deletions(-) diff --git a/examples/hello_world.sb b/examples/hello_world.sb index fdb0b0e..0503092 100644 --- a/examples/hello_world.sb +++ b/examples/hello_world.sb @@ -1,9 +1,7 @@ -// main.sb fn main() { - return foo(2); -} - -fn foo(x) { - return x; + return 2 * fib(1); } +fn fib(n) { + return 2 + 1; +} \ No newline at end of file diff --git a/examples_out/out.js b/examples_out/out.js index 6b2a7c4..b3e980d 100644 --- a/examples_out/out.js +++ b/examples_out/out.js @@ -1,8 +1,8 @@ -// out.js function main() { - return foo(2); +return 2 * fib(1) + } -function foo(x) { - return x; +function fib(n) { +return 2 + 1 } -console.log(main()); +console.log(main()) \ No newline at end of file diff --git a/src/generator/js.rs b/src/generator/js.rs index 1168622..d8174b1 100644 --- a/src/generator/js.rs +++ b/src/generator/js.rs @@ -55,6 +55,7 @@ fn generate_expression(expr: Expression) -> String { Expression::Char(_) => todo!(), Expression::FunctionCall(name, e) => generate_function_call(name, e), Expression::Assign(_, _) => todo!(), + Expression::BinOp(left, op, right) => generate_bin_op(*left, op, *right), } } @@ -67,6 +68,7 @@ fn generate_function_call(func: String, args: Vec) -> String { Expression::FunctionCall(n, a) => generate_function_call(n, a), Expression::Str(s) | Expression::Variable(s) => s, Expression::Assign(_, _) => todo!(), + Expression::BinOp(left, op, right) => generate_bin_op(*left, op, *right), }) .collect::>() .join(","); @@ -79,3 +81,27 @@ fn generate_return(ret: Option) -> String { None => "return;\n".to_string(), } } + +fn generate_bin_op(left: Expression, op: BinOp, right: Expression) -> String { + let op_str = match op { + BinOp::Addition => "+", + BinOp::And => "&&", + BinOp::Division => "/", + BinOp::Equal => "===", + BinOp::GreaterThan => ">", + BinOp::GreaterThanOrEqual => ">=", + BinOp::LessThan => "<", + BinOp::LessThanOrEqual => "<=", + BinOp::Modulus => "%", + BinOp::Multiplication => "*", + BinOp::NotEqual => "!==", + BinOp::Or => "||", + BinOp::Subtraction => "-", + }; + format!( + "{l} {op} {r}", + l = generate_expression(left), + op = op_str, + r = generate_expression(right) + ) +} diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index fa82527..a971e4a 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -62,9 +62,9 @@ pub enum TokenKind { /// "==" Equals, /// "<" - SmallerThen, + LessThan, /// ">" - LargerThen, + GreaterThan, /// "(" BraceOpen, /// ")" @@ -173,8 +173,8 @@ impl Cursor<'_> { ':' => Colon, ';' => SemiColon, ',' => Comma, - '<' => SmallerThen, - '>' => LargerThen, + '<' => LessThan, + '>' => GreaterThan, '(' => BraceOpen, ')' => BraceClose, '[' => SquareBraceOpen, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 4336998..a661abf 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -3,6 +3,7 @@ use crate::lexer::{Token, TokenKind, Value}; use crate::parser::node_type::Statement; use crate::parser::node_type::*; use crate::util::string_util::highlight_position_in_file; +use std::convert::TryFrom; use std::iter::Peekable; use std::vec::IntoIter; @@ -97,16 +98,17 @@ impl Parser { )), } } - fn match_keyword(&mut self, keyword: Keyword) -> Result<(), String> { let token = self.next_token(); - match &token.kind { TokenKind::Keyword(ref k) if k == &keyword => Ok(()), _ => Err(self.make_error(TokenKind::SemiColon, token)), } } - + fn match_operator(&mut self) -> Result { + let token = self.next_token(); + BinOp::try_from(token.kind) + } fn match_identifier(&mut self) -> Result { match self.next_token().kind { TokenKind::Identifier(n) => Ok(n), @@ -272,18 +274,27 @@ impl Parser { let token = self.next_token(); match token.kind { TokenKind::Literal(Value::Int) => { - let state = Expression::Int(token.raw.parse::().map_err(|e| e.to_string())?); + let state = match BinOp::try_from(self.peek().ok_or("Could not peek token")?.kind) { + Ok(_) => self.parse_bin_op()?, + Err(_) => Expression::Int(token.raw.parse::().map_err(|e| e.to_string())?), + }; Ok(state) } TokenKind::Literal(Value::Str) => { - let state = Expression::Str(token.raw); + let state = match BinOp::try_from(self.peek().ok_or("Could not peek token")?.kind) { + Ok(_) => self.parse_bin_op()?, + Err(_) => Expression::Str(token.raw), + }; Ok(state) } TokenKind::Identifier(val) => { let next = self.peek().ok_or_else(|| "Token expected")?; let state = match &next.kind { TokenKind::BraceOpen => self.parse_function_call()?, - _ => Expression::Variable(val), + _ => match BinOp::try_from(self.peek().ok_or("Could not peek token")?.kind) { + Ok(_) => self.parse_bin_op()?, + Err(_) => Expression::Str(val), + }, }; Ok(state) } @@ -291,6 +302,29 @@ impl Parser { } } + fn parse_bin_op(&mut self) -> Result { + let left = self.prev().ok_or_else(|| "Expected Token")?; + match &left.kind { + TokenKind::Identifier(_) | TokenKind::Literal(_) => { + let op = self.match_operator()?; + let right = self.peek().ok_or_else(|| "Expected token")?; + match &right.kind { + TokenKind::Identifier(_) | TokenKind::Literal(_) => Ok(Expression::BinOp( + Box::from(Expression::try_from(left)?), + op, + Box::from(self.parse_expression()?), + )), + _ => Ok(Expression::BinOp( + Box::from(Expression::try_from(left)?), + op, + Box::from(self.parse_expression()?), + )), + } + } + _ => Err(self.make_error(TokenKind::Unknown, left)), + } + } + fn parse_declare(&mut self) -> Result { match ( self.next_token().kind, diff --git a/src/parser/node_type.rs b/src/parser/node_type.rs index 5e0ce30..c1896e3 100644 --- a/src/parser/node_type.rs +++ b/src/parser/node_type.rs @@ -1,3 +1,6 @@ +use crate::parser::{Token, TokenKind, Value}; +use core::convert::TryFrom; + #[derive(Debug)] pub struct Program { pub func: Vec, @@ -33,4 +36,62 @@ pub enum Expression { FunctionCall(String, Vec), Variable(String), Assign(String, Box), + BinOp(Box, BinOp, Box), +} + +impl TryFrom for Expression { + type Error = String; + + fn try_from(token: Token) -> std::result::Result { + let kind = token.kind; + match kind { + TokenKind::Identifier(val) => Ok(Expression::Variable(val)), + TokenKind::Literal(Value::Int) => Ok(Expression::Int( + token + .raw + .parse() + .map_err(|_| "Int value could not be parsed")?, + )), + TokenKind::Literal(Value::Str) => Ok(Expression::Str(token.raw)), + other => panic!("Value could not be parsed"), + } + } +} + +#[derive(Debug, Eq, PartialEq)] +pub enum BinOp { + Addition, + Subtraction, + Multiplication, + Division, + Modulus, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual, + Equal, + NotEqual, + And, + Or, +} + +impl TryFrom for BinOp { + type Error = String; + fn try_from(token: TokenKind) -> Result { + match token { + TokenKind::Star => Ok(BinOp::Multiplication), + TokenKind::Slash => Ok(BinOp::Division), + TokenKind::Plus => Ok(BinOp::Addition), + TokenKind::Minus => Ok(BinOp::Subtraction), + TokenKind::LessThan => Ok(BinOp::LessThan), + TokenKind::GreaterThan => Ok(BinOp::GreaterThan), + TokenKind::Equals => Ok(BinOp::Equal), + // TokenKind::LessThanOrEqual => BinOp::LessThanOrEqual, + // TokenKind::GreaterThanOrEqual => BinOp::GreaterThanOrEqual, + // TokenKind::NotEquals => BinOp::NotEqual, + // TokenKind::And => BinOp::And, + // TokenKind::Or => BinOp::Or, + other => Err(format!("Token {:?} cannot be converted into a BinOp", other).into()), + } + } } diff --git a/src/parser/tests.rs b/src/parser/tests.rs index 68bde8a..8b565bd 100644 --- a/src/parser/tests.rs +++ b/src/parser/tests.rs @@ -149,3 +149,68 @@ fn test_parse_nexted_function_call() { let tree = parse(tokens, Some(raw.to_string())); assert!(tree.is_ok()) } + +#[test] +fn test_parse_basic_ops() { + let raw = " + fn main() { + return 2 * 5; + } + "; + let tokens = tokenize(raw); + let tree = parse(tokens, Some(raw.to_string())); + assert!(tree.is_ok()) +} + +#[test] +fn test_parse_compound_ops() { + let raw = " + fn main() { + return 2 * 5 / 3; + } + "; + let tokens = tokenize(raw); + let tree = parse(tokens, Some(raw.to_string())); + assert!(tree.is_ok()) +} + +#[test] +#[ignore] +fn test_parse_compound_ops_with_function_call() { + let raw = " + fn main() { + return 2 * fib(1) / 3; + } + + fn fib(n) { + return n + 1; + } + "; + let tokens = tokenize(raw); + let tree = parse(tokens, Some(raw.to_string())); + assert!(tree.is_ok()) +} + +#[test] +fn test_parse_compound_ops_with_strings() { + let raw = " + fn main() { + return 2 * \"Hello\"; + } + "; + let tokens = tokenize(raw); + let tree = parse(tokens, Some(raw.to_string())); + assert!(tree.is_ok()) +} + +#[test] +fn test_parse_compound_ops_with_identifier() { + let raw = " + fn main(n) { + return 2 * n; + } + "; + let tokens = tokenize(raw); + let tree = parse(tokens, Some(raw.to_string())); + assert!(tree.is_ok()) +}