diff --git a/examples/easy.blight b/examples/easy.blight deleted file mode 100644 index 3fd14633a137273fc6ee0ff72f516c67114dbeac..0000000000000000000000000000000000000000 --- a/examples/easy.blight +++ /dev/null @@ -1,7 +0,0 @@ -var x: int = 0; -var y: float = 0; -var z: bigint = 0; - -let a: int = 0; -let b: float = 0; -let c: bigint = 0; diff --git a/examples/operations.blight b/examples/operations.blight new file mode 100644 index 0000000000000000000000000000000000000000..f35a323df01bec88a06c2e9198ab22283c5be8a0 --- /dev/null +++ b/examples/operations.blight @@ -0,0 +1 @@ +let a: int = 10 - 2 / 4 * 15; diff --git a/examples/variables.blight b/examples/variables.blight new file mode 100644 index 0000000000000000000000000000000000000000..3fd14633a137273fc6ee0ff72f516c67114dbeac --- /dev/null +++ b/examples/variables.blight @@ -0,0 +1,7 @@ +var x: int = 0; +var y: float = 0; +var z: bigint = 0; + +let a: int = 0; +let b: float = 0; +let c: bigint = 0; diff --git a/src/main.rs b/src/main.rs index 578110b0fc2363d80b4af6eb0c20a4b69c972460..4d43428128b85fcb49dac0e5834199ade93ac1b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; -use miette::{Result, NamedSource}; use clap::{Parser, Subcommand}; +use miette::Result; mod transpile; @@ -21,7 +21,7 @@ enum Commands { /// Compiles a file into javascript Compile { /// lists test values - file: PathBuf + file: PathBuf, }, } diff --git a/src/transpile/ast.rs b/src/transpile/ast.rs index 681845ae057ba288aee21fc14a16f817fb4aa547..4053d51b8e00e88ac26bbeda0adc941861134c44 100644 --- a/src/transpile/ast.rs +++ b/src/transpile/ast.rs @@ -1,19 +1,21 @@ -use miette::{Result, Diagnostic}; +use miette::{Diagnostic, Result}; use thiserror::Error; use super::tokenizer::Token; -use super::util::PeekableIterator; +use super::util::{Operator, PeekableIterator}; #[derive(Error, Diagnostic, Debug)] #[error("AstError")] struct AstError { #[help] - error: String + error: String, } impl AstError { fn new(error: &'static str) -> AstError { - AstError { error: String::from(error) } + AstError { + error: String::from(error), + } } } @@ -21,7 +23,7 @@ impl AstError { pub enum Type { Int, Float, - Bigint + Bigint, } #[derive(Debug)] @@ -29,107 +31,126 @@ pub enum Ast { ConstantDeclaration { name: String, typ: Type, - value: Box + value: Box, }, VariableDeclaration { name: String, typ: Type, - value: Box + value: Box, + }, + Operation { + operator: Operator, + left: Box, + right: Box, }, Number(String), } -fn parse_expression(tokens: &mut dyn PeekableIterator) -> Result { - match tokens.next() { +fn parse_expression(tokens: &mut dyn PeekableIterator) -> Result { + let left = match tokens.next() { Some(token) => match token { - Token::Number(number) => Token::Number(number.clone()), - _ => return Err(AstError::new("Expected expression, but did not get it").into()) + Token::Number(number) => Ast::Number(number.clone()), + _ => return Err(AstError::new("Expected expression, but did not get it").into()), }, - None => return Err(AstError::new("Expected expression, but got nothing instead").into()) - + None => return Err(AstError::new("Expected expression, but got nothing instead").into()), }; + + match tokens.peek() { + Some(token) => match token { + Token::Operator(operator) => { + tokens.next(); + return Ok(Ast::Operation { + operator: operator.clone(), + left: Box::new(left), + right: Box::new(parse_expression(tokens)?), + }); + } + _ => (), + }, + _ => (), + } + Ok(Ast::Number(String::from("10"))) } /// Precondition: There is at least one token in the iterator -fn parse_statement(tokens: &mut dyn PeekableIterator) -> Result { +fn parse_statement(tokens: &mut dyn PeekableIterator) -> Result { match tokens.next().unwrap() { Token::Let => { let name = match tokens.next() { Some(Token::Identifier(name)) => name, - _ => return Err(AstError::new("Expected identifier after var").into()) + _ => return Err(AstError::new("Expected identifier after var").into()), }; let typ = match tokens.next() { - Some(Token::Colon) => { - match tokens.next() { - Some(Token::Identifier(typ)) => { - match typ.as_str() { - "int" => Type::Int, - "float" => Type::Float, - "bigint" => Type::Bigint, - _ => return Err(AstError::new("Expected type after colon").into()) - } - }, - _ => return Err(AstError::new("Expected type after colon").into()) - } + Some(Token::Colon) => match tokens.next() { + Some(Token::Identifier(typ)) => match typ.as_str() { + "int" => Type::Int, + "float" => Type::Float, + "bigint" => Type::Bigint, + _ => return Err(AstError::new("Expected type after colon").into()), + }, + _ => return Err(AstError::new("Expected type after colon").into()), }, - _ => return Err(AstError::new("Expected colon after identifier").into()) + _ => return Err(AstError::new("Expected colon after identifier").into()), }; let value = match tokens.next() { Some(Token::Equals) => parse_expression(tokens)?, - _ => return Err(AstError::new("Expected equals after identifier").into()) + _ => return Err(AstError::new("Expected equals after identifier").into()), }; match tokens.next() { Some(Token::Semicolon) => (), - _ => return Err(AstError::new("Expected semicolon after variable declaration").into()) + _ => { + return Err( + AstError::new("Expected semicolon after variable declaration").into(), + ) + } }; Ok(Ast::ConstantDeclaration { name: name.clone(), typ, value: Box::new(value), }) - }, + } Token::Var => { let name = match tokens.next() { Some(Token::Identifier(name)) => name, - _ => return Err(AstError::new("Expected identifier after var").into()) + _ => return Err(AstError::new("Expected identifier after var").into()), }; let typ = match tokens.next() { - Some(Token::Colon) => { - match tokens.next() { - Some(Token::Identifier(typ)) => { - match typ.as_str() { - "int" => Type::Int, - "float" => Type::Float, - "bigint" => Type::Bigint, - _ => return Err(AstError::new("Expected type after colon").into()) - } - }, - _ => return Err(AstError::new("Expected type after colon").into()) - } + Some(Token::Colon) => match tokens.next() { + Some(Token::Identifier(typ)) => match typ.as_str() { + "int" => Type::Int, + "float" => Type::Float, + "bigint" => Type::Bigint, + _ => return Err(AstError::new("Expected type after colon").into()), + }, + _ => return Err(AstError::new("Expected type after colon").into()), }, - _ => return Err(AstError::new("Expected colon after identifier").into()) + _ => return Err(AstError::new("Expected colon after identifier").into()), }; let value = match tokens.next() { Some(Token::Equals) => parse_expression(tokens)?, - _ => return Err(AstError::new("Expected equals after identifier").into()) + _ => return Err(AstError::new("Expected equals after identifier").into()), }; match tokens.next() { Some(Token::Semicolon) => (), - _ => return Err(AstError::new("Expected semicolon after variable declaration").into()) + _ => { + return Err( + AstError::new("Expected semicolon after variable declaration").into(), + ) + } }; Ok(Ast::VariableDeclaration { name: name.clone(), typ, value: Box::new(value), }) - }, + } Token::Number(number) => Ok(Ast::Number(number.clone())), - _ => return Err(AstError::new("Unexpected token").into()) + _ => return Err(AstError::new("Unexpected token").into()), } } - pub fn parse(tokens: Vec) -> Result> { let mut iter = tokens.iter().peekable(); let mut ast: Vec = Vec::new(); diff --git a/src/transpile/mod.rs b/src/transpile/mod.rs index cd1b4299d1fa893eed5378d1b4b40c556f101790..88c8b5d7f4fc737a7e2469ea9814ec0832b82af0 100644 --- a/src/transpile/mod.rs +++ b/src/transpile/mod.rs @@ -1,12 +1,12 @@ -use miette::{Result}; +use miette::Result; -mod tokenizer; mod ast; +mod tokenizer; mod walker; mod util; -pub fn transpile(content: String) -> Result { +pub fn transpile(content: String) -> Result { // 1. Tokenize the input let tokens = tokenizer::tokenize(content)?; // 2. Parse the tokens into an AST diff --git a/src/transpile/tokenizer.rs b/src/transpile/tokenizer.rs index 2ffa334d9b5e9edda908207756da5a17387871e5..8e7f5945e77c622c91788ca2b31706d5728ce7a0 100644 --- a/src/transpile/tokenizer.rs +++ b/src/transpile/tokenizer.rs @@ -1,19 +1,13 @@ -use miette::{Result, Diagnostic}; +use miette::{Diagnostic, Result}; use thiserror::Error; +use super::util::Operator; + #[derive(Error, Diagnostic, Debug)] #[error("UnknownCharacter")] struct UnknownCharacter { #[help] - error: String -} - -#[derive(Debug, Clone)] -pub enum Operator { - Add, - Substract, - Multiply, - Divide, + error: String, } #[derive(Debug, Clone)] @@ -28,7 +22,7 @@ pub enum Token { Equals, Semicolon, // Operators - Operator(Operator) + Operator(Operator), } pub fn tokenize(content: String) -> Result> { @@ -37,64 +31,65 @@ pub fn tokenize(content: String) -> Result> { loop { match chars.next() { - Some((i, c)) => { - match c { - 'a'..='z' | 'A'..='Z' | '_' => { - let mut current_token = String::new(); - current_token.push(c); - loop { - let c = chars.peek(); - if c.is_none() { - break; - } - let (_, c) = c.unwrap(); - match c { - 'a'..='z' | 'A'..='Z' | '_' | '0'..='9' => { - current_token.push(*c); - chars.next(); - }, - _ => break, + Some((_, c)) => match c { + 'a'..='z' | 'A'..='Z' | '_' => { + let mut current_token = String::new(); + current_token.push(c); + loop { + let c = chars.peek(); + if c.is_none() { + break; + } + let (_, c) = c.unwrap(); + match c { + 'a'..='z' | 'A'..='Z' | '_' | '0'..='9' => { + current_token.push(*c); + chars.next(); } + _ => break, } + } - match current_token.as_str() { - "var" => tokens.push(Token::Var), - "let" => tokens.push(Token::Let), - _ => tokens.push(Token::Identifier(current_token)), + match current_token.as_str() { + "var" => tokens.push(Token::Var), + "let" => tokens.push(Token::Let), + _ => tokens.push(Token::Identifier(current_token)), + } + } + '0'..='9' => { + let mut current_token = String::new(); + current_token.push(c); + loop { + let c = chars.peek(); + if c.is_none() { + break; } - }, - '0'..='9' => { - let mut current_token = String::new(); - current_token.push(c); - loop { - let c = chars.peek(); - if c.is_none() { - break; - } - let (_, c) = c.unwrap(); - match c { - '0'..='9' => { - current_token.push(*c); - chars.next(); - }, - _ => break, + let (_, c) = c.unwrap(); + match c { + '0'..='9' => { + current_token.push(*c); + chars.next(); } + _ => break, } - tokens.push(Token::Number(current_token)); - }, - ':' => tokens.push(Token::Colon), - '=' => tokens.push(Token::Equals), - ';' => tokens.push(Token::Semicolon), - '+' => tokens.push(Token::Operator(Operator::Add)), - '-' => tokens.push(Token::Operator(Operator::Substract)), - '*' => tokens.push(Token::Operator(Operator::Multiply)), - '/' => tokens.push(Token::Operator(Operator::Divide)), - s if s.is_whitespace() => (), - _ => { - return Err(UnknownCharacter { error: format!("Unsupported character: {}", c)}.into()) } + tokens.push(Token::Number(current_token)); + } + ':' => tokens.push(Token::Colon), + '=' => tokens.push(Token::Equals), + ';' => tokens.push(Token::Semicolon), + '+' => tokens.push(Token::Operator(Operator::Add)), + '-' => tokens.push(Token::Operator(Operator::Substract)), + '*' => tokens.push(Token::Operator(Operator::Multiply)), + '/' => tokens.push(Token::Operator(Operator::Divide)), + s if s.is_whitespace() => (), + _ => { + return Err(UnknownCharacter { + error: format!("Unsupported character: {}", c), + } + .into()) } - } + }, None => break, } } diff --git a/src/transpile/util.rs b/src/transpile/util.rs index 6c14555c0d66cb65392181a00798fd2a90d6e332..162b1e38cfab2ee3f543d767e30eb23bab01dae2 100644 --- a/src/transpile/util.rs +++ b/src/transpile/util.rs @@ -1,6 +1,6 @@ // Rust why do you not have a peekable iterator in the standard library? // Stolen from https://users.rust-lang.org/t/peekable-argument-there-has-to-be-a-simpler-way/6959 -pub trait PeekableIterator : std::iter::Iterator { +pub trait PeekableIterator: std::iter::Iterator { fn peek(&mut self) -> Option<&Self::Item>; } @@ -9,3 +9,22 @@ impl PeekableIterator for std::iter::Peekable { std::iter::Peekable::peek(self) } } + +#[derive(Debug, Clone)] +pub enum Operator { + Add, + Substract, + Multiply, + Divide, +} + +impl Operator { + pub fn to_string(self) -> String { + String::from(match self { + Self::Add => "+", + Self::Substract => "-", + Self::Divide => "/", + Self::Multiply => "*", + }) + } +} diff --git a/src/transpile/walker.rs b/src/transpile/walker.rs index 936288b1a0d2fd7942c21bc4e7ca9be3266602ed..5a323574dbdd939e4da47c7b1ecdfc95603f5ebf 100644 --- a/src/transpile/walker.rs +++ b/src/transpile/walker.rs @@ -3,18 +3,24 @@ use super::ast::{Ast, Type}; fn transpile_helper(tree: Ast) -> String { match tree { Ast::Number(number) => format!("{}", number), - Ast::VariableDeclaration { name, typ, value } => { - match typ { - Type::Bigint => format!("let {} = BigInt({});\n", name, transpile_helper(*value)), - _ => format!("let {} = {};\n", name, transpile_helper(*value)) - } + Ast::Operation { + operator, + left, + right, + } => format!( + "{} {} {}", + transpile_helper(*left), + operator.to_string(), + transpile_helper(*right) + ), + Ast::VariableDeclaration { name, typ, value } => match typ { + Type::Bigint => format!("let {} = BigInt({});\n", name, transpile_helper(*value)), + _ => format!("let {} = {};\n", name, transpile_helper(*value)), + }, + Ast::ConstantDeclaration { name, typ, value } => match typ { + Type::Bigint => format!("const {} = BigInt({});\n", name, transpile_helper(*value)), + _ => format!("const {} = {};\n", name, transpile_helper(*value)), }, - Ast::ConstantDeclaration { name, typ, value } => { - match typ { - Type::Bigint => format!("const {} = BigInt({});\n", name, transpile_helper(*value)), - _ => format!("const {} = {};\n", name, transpile_helper(*value)) - } - } } }