From 266f616b552eb86530ed664e44aea026cb5820db Mon Sep 17 00:00:00 2001 From: Lino Le Van <11367844+lino-levan@users.noreply.github.com> Date: Wed, 10 Jan 2024 08:31:10 -0800 Subject: [PATCH] feat: add function calling and print built-in --- cli/src/main.rs | 55 ++++++++++- compiler/src/ast/mod.rs | 164 ++++++++++++++++++++++--------- compiler/src/ast/types.rs | 15 ++- compiler/src/tokenizer.rs | 32 +++--- compiler/src/walker.rs | 28 +++++- examples/basics/functions.blight | 2 + 6 files changed, 218 insertions(+), 78 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 80a9fc9d1d699a0bb84feab83bd8415d72d104f6..3657c674a0bc53881ad85225d4359d058ccc29f0 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -20,8 +20,16 @@ struct Cli { enum Commands { /// Compiles a file into javascript Compile { - /// lists test values file: PathBuf, + // Whether to output to stdout instead of a file + #[arg(long, default_value_t = false)] + stdout: bool, + }, + Run { + file: PathBuf, + /// What javascript runtime to use + #[arg(short, long, default_value = "deno")] + runtime: String, }, } @@ -29,10 +37,51 @@ fn main() -> Result<()> { let cli = Cli::parse(); match cli.command { - Commands::Compile { file } => { + Commands::Compile { file, stdout } => { let contents = std::fs::read_to_string(file.clone()).unwrap(); let result = transpile(contents.clone())?; - std::fs::write(file.with_extension("js"), result).unwrap(); + + if stdout { + println!("{}", result); + } else { + std::fs::write(file.with_extension("js"), result).unwrap(); + } + } + Commands::Run { file, runtime } => { + let contents = std::fs::read_to_string(file.clone()).unwrap(); + let result = transpile(contents.clone())?; + let mut temp_file = std::env::temp_dir(); + temp_file.push("temp.js"); + std::fs::write(temp_file.clone(), result).unwrap(); + match runtime.as_str() { + "deno" => { + let output = std::process::Command::new("deno") + .arg("run") + .arg("-A") + .arg(temp_file) + .output() + .expect("Failed to execute process"); + println!("{}", String::from_utf8_lossy(&output.stdout)); + } + "node" => { + let output = std::process::Command::new("node") + .arg(temp_file) + .output() + .expect("Failed to execute process"); + println!("{}", String::from_utf8_lossy(&output.stdout)); + } + "bun" => { + let output = std::process::Command::new("bun") + .arg("run") + .arg(temp_file) + .output() + .expect("Failed to execute process"); + println!("{}", String::from_utf8_lossy(&output.stdout)); + } + _ => { + println!("Unknown runtime: {}", runtime); + } + } } } diff --git a/compiler/src/ast/mod.rs b/compiler/src/ast/mod.rs index 3e65d33f4aa166d54f61b290c690090ee4947993..af0367c7ddb2497d40cd6145f9bbbf25c3a8298d 100644 --- a/compiler/src/ast/mod.rs +++ b/compiler/src/ast/mod.rs @@ -6,42 +6,50 @@ mod types; use super::tokenizer::Token; use error::AstError; -use types::{ParserContext, Argument}; +use types::{Argument, ParserContext}; pub use types::{Ast, Type}; fn parse_expression(tokens: &mut ParserContext) -> Result { - let left = match tokens.next() { Some(token) => match token { Token::Number(number) => Ast::Number(number.clone()), - Token::Identifier(name) => { - match tokens.peek() { - Some(Token::OpenParanthesis) => { - tokens.next(); + Token::Identifier(name) => match tokens.peek() { + Some(Token::OpenParanthesis) => { + tokens.next(); - let mut arguments = Vec::new(); + let mut arguments = Vec::new(); - loop { - match tokens.peek() { - Some(Token::ClosedParanthesis) => break, - _ => () + loop { + match tokens.peek() { + Some(Token::ClosedParanthesis) => { + tokens.next(); + break; } - arguments.push(parse_expression(tokens)?); + _ => (), + } + arguments.push(parse_expression(tokens)?); - match tokens.peek() { - Some(Token::Comma) => { - tokens.next(); - } - Some(Token::ClosedParanthesis) => (), - _ => return Err(AstError::new("Expected ) or , but found something else instead.").into()), + match tokens.peek() { + Some(Token::Comma) => { + tokens.next(); + } + Some(Token::ClosedParanthesis) => (), + _ => { + return Err(AstError::new( + "Expected ) or , but found something else instead.", + ) + .into()) } } + } - Ast::FunctionCall { name: name.clone(), arguments } + Ast::FunctionCall { + name: name.clone(), + arguments, } - _ => Ast::Identifier(name.clone()) } + _ => Ast::Identifier(name.clone()), }, _ => return Err(AstError::new("Expected expression, but did not get it").into()), }, @@ -90,7 +98,9 @@ fn parse_statement(tokens: &mut ParserContext) -> Result { let inner = parse_statement(tokens)?; - return Ok(Ast::Public { value: Box::new(inner) }) + return Ok(Ast::Public { + value: Box::new(inner), + }); } Token::Let => { let name = match tokens.next() { @@ -151,14 +161,12 @@ fn parse_statement(tokens: &mut ParserContext) -> Result { match tokens.next() { Some(Token::Semicolon) => (), - _ => { - return Err( - AstError::new("Expected semicolon after return statement").into(), - ) - } + _ => return Err(AstError::new("Expected semicolon after return statement").into()), }; - Ok(Ast::Return { return_value: Box::new(expression) }) + Ok(Ast::Return { + return_value: Box::new(expression), + }) } Token::Function => { let name = match tokens.next() { @@ -168,7 +176,12 @@ fn parse_statement(tokens: &mut ParserContext) -> Result { match tokens.next() { Some(Token::OpenParanthesis) => (), - _ => return Err(AstError::new("Expected open paranthesis after identifier in function").into()), + _ => { + return Err(AstError::new( + "Expected open paranthesis after identifier in function", + ) + .into()) + } }; let mut arguments = Vec::new(); @@ -178,13 +191,18 @@ fn parse_statement(tokens: &mut ParserContext) -> Result { Some(Token::ClosedParanthesis) => { tokens.next(); break; - }, - _ => () + } + _ => (), } let argument_name = match tokens.next() { Some(Token::Identifier(name)) => name, - _ => return Err(AstError::new("Expected identifier for argument name in function").into()), + _ => { + return Err(AstError::new( + "Expected identifier for argument name in function", + ) + .into()) + } }; let return_type = match tokens.next() { @@ -195,21 +213,34 @@ fn parse_statement(tokens: &mut ParserContext) -> Result { match tokens.peek() { Some(Token::Comma) => { tokens.next(); - }, - _ => () + } + _ => (), } - arguments.push(Argument { name: argument_name.clone(), return_type}) + arguments.push(Argument { + name: argument_name.clone(), + return_type, + }) } let return_type = match tokens.next() { Some(Token::RightArrow) => parse_type(tokens)?, - _ => return Err(AstError::new("Expected right arrow after argument list in function").into()), + _ => { + return Err(AstError::new( + "Expected right arrow after argument list in function", + ) + .into()) + } }; match tokens.next() { Some(Token::OpenBracket) => (), - _ => return Err(AstError::new("Expected open bracket after type in function declaration").into()), + _ => { + return Err(AstError::new( + "Expected open bracket after type in function declaration", + ) + .into()) + } }; let mut value = Vec::new(); @@ -219,15 +250,62 @@ fn parse_statement(tokens: &mut ParserContext) -> Result { Some(Token::ClosedBracket) => { tokens.next(); break; - }, - _ => () + } + _ => (), } value.push(parse_statement(tokens)?); } - return Ok(Ast::Function { name, arguments, return_type, value }) + return Ok(Ast::Function { + name, + arguments, + return_type, + value, + }); } + Token::Identifier(name) => match tokens.peek() { + Some(Token::OpenParanthesis) => { + tokens.next(); + + let mut arguments = Vec::new(); + + loop { + match tokens.peek() { + Some(Token::ClosedParanthesis) => { + tokens.next(); + break; + } + _ => (), + } + arguments.push(parse_expression(tokens)?); + + match tokens.peek() { + Some(Token::Comma) => { + tokens.next(); + } + Some(Token::ClosedParanthesis) => (), + _ => { + return Err(AstError::new( + "Expected ) or , but found something else instead.", + ) + .into()) + } + } + } + + match tokens.next() { + Some(Token::Semicolon) => (), + _ => return Err(AstError::new("Expected semicolon after function call").into()), + }; + + Ok(Ast::FunctionCall { + name: name.clone(), + arguments, + }) + } + _ => Err(AstError::new("Unexpected identifier").into()), + }, Token::Comment(comment) => Ok(Ast::Comment(comment.clone())), Token::DocComment(comment) => { let mut concatted_comments = comment.clone(); @@ -236,15 +314,13 @@ fn parse_statement(tokens: &mut ParserContext) -> Result { Some(Token::DocComment(comment)) => { concatted_comments.push_str(format!("\n{}", comment).as_str()); tokens.next(); - }, + } _ => break, } } Ok(Ast::DocComment(concatted_comments)) - }, - _ => { - return Err(AstError::new("Unexpected token").into()) - }, + } + _ => return Err(AstError::new("Unexpected token").into()), } } diff --git a/compiler/src/ast/types.rs b/compiler/src/ast/types.rs index 415ae2edd2d690fd9e10ce80948fa44314a033c4..9017263626910df62029a21156356946f50e2430 100644 --- a/compiler/src/ast/types.rs +++ b/compiler/src/ast/types.rs @@ -1,10 +1,10 @@ -use crate::util::Operator; use crate::tokenizer::Token; +use crate::util::Operator; #[derive(Debug)] pub struct ParserContext { pub tokens: Vec, - pub cursor: usize + pub cursor: usize, } impl ParserContext { @@ -35,7 +35,6 @@ impl ParserContext { } } - #[derive(Debug)] pub enum Type { Int, @@ -46,13 +45,13 @@ pub enum Type { #[derive(Debug)] pub struct Argument { pub name: String, - pub return_type: Type + pub return_type: Type, } #[derive(Debug)] pub enum Ast { Public { - value: Box + value: Box, }, ConstantDeclaration { name: String, @@ -70,13 +69,13 @@ pub enum Ast { right: Box, }, Return { - return_value: Box + return_value: Box, }, Function { name: String, arguments: Vec, return_type: Type, - value: Vec + value: Vec, }, FunctionCall { name: String, @@ -85,5 +84,5 @@ pub enum Ast { Number(String), Identifier(String), Comment(String), - DocComment(String) + DocComment(String), } diff --git a/compiler/src/tokenizer.rs b/compiler/src/tokenizer.rs index 7a630ce7c2183f492021da4afd67e2156d7d15fe..cca3003f9559005512d084e033206d71367c83c6 100644 --- a/compiler/src/tokenizer.rs +++ b/compiler/src/tokenizer.rs @@ -94,23 +94,19 @@ pub fn tokenize(content: String) -> Result> { '=' => tokens.push(Token::Equals), ';' => tokens.push(Token::Semicolon), '+' => tokens.push(Token::Operator(Operator::Add)), - '-' => { - match chars.peek() { - Some((_, '>')) => { - chars.next(); - tokens.push(Token::RightArrow); - } - _ => tokens.push(Token::Operator(Operator::Substract)) + '-' => match chars.peek() { + Some((_, '>')) => { + chars.next(); + tokens.push(Token::RightArrow); } + _ => tokens.push(Token::Operator(Operator::Substract)), }, - '*' => { - match chars.peek() { - Some((_, '*')) => { - chars.next(); - tokens.push(Token::Operator(Operator::Power)); - } - _ => tokens.push(Token::Operator(Operator::Multiply)) + '*' => match chars.peek() { + Some((_, '*')) => { + chars.next(); + tokens.push(Token::Operator(Operator::Power)); } + _ => tokens.push(Token::Operator(Operator::Multiply)), }, '/' => { match chars.peek() { @@ -121,7 +117,7 @@ pub fn tokenize(content: String) -> Result> { Some((_, '/')) => { chars.next(); // Consume the third slash true - }, + } _ => false, }; @@ -136,7 +132,7 @@ pub fn tokenize(content: String) -> Result> { '\n' => break, _ => { comment.push(c.clone()); - }, + } } } @@ -148,9 +144,9 @@ pub fn tokenize(content: String) -> Result> { tokens.push(Token::Comment(comment)); } } - _ => tokens.push(Token::Operator(Operator::Divide)) + _ => tokens.push(Token::Operator(Operator::Divide)), } - }, + } ',' => tokens.push(Token::Comma), '(' => tokens.push(Token::OpenParanthesis), ')' => tokens.push(Token::ClosedParanthesis), diff --git a/compiler/src/walker.rs b/compiler/src/walker.rs index b0184c933ed62dda09bf6cd1084fa44e6da2b7be..7f43bce3ba9aeb7236b07d80d60bd9c5c38da2fe 100644 --- a/compiler/src/walker.rs +++ b/compiler/src/walker.rs @@ -19,16 +19,29 @@ fn transpile_helper(tree: &Ast) -> Result { transpile_helper(right)? ), Ast::Public { value } => format!("export {}", transpile_helper(value)?), - Ast::VariableDeclaration { name, return_type, value } => match return_type { + Ast::VariableDeclaration { + name, + return_type, + value, + } => match return_type { Type::Bigint => format!("let {} = BigInt({});\n", name, transpile_helper(value)?), _ => format!("let {} = {};\n", name, transpile_helper(value)?), }, - Ast::ConstantDeclaration { name, return_type, value } => match return_type { + Ast::ConstantDeclaration { + name, + return_type, + value, + } => match return_type { Type::Bigint => format!("const {} = BigInt({});\n", name, transpile_helper(value)?), _ => format!("const {} = {};\n", name, transpile_helper(value)?), }, Ast::Return { return_value } => format!("return {};\n", transpile_helper(return_value)?), - Ast::Function { name, arguments, return_type: _, value } => { + Ast::Function { + name, + arguments, + return_type: _, + value, + } => { let mut result = format!("function {}(", name); for (i, argument) in arguments.iter().enumerate() { @@ -48,9 +61,12 @@ fn transpile_helper(tree: &Ast) -> Result { result += "}\n"; result - }, + } Ast::FunctionCall { name, arguments } => { - let mut result = format!("{}(", name); + let mut result = match name.as_str() { + "print" => String::from("console.log("), + _ => format!("{}(", name), + }; for (i, argument) in arguments.iter().enumerate() { if i == 0 { @@ -60,6 +76,8 @@ fn transpile_helper(tree: &Ast) -> Result { } } + result += ")"; + result } }) diff --git a/examples/basics/functions.blight b/examples/basics/functions.blight index 45495ec2575c29e29748d2e63626c21ceaedebd1..f209f19cc05683a8698d1b0ada617c9135918c42 100644 --- a/examples/basics/functions.blight +++ b/examples/basics/functions.blight @@ -3,3 +3,5 @@ fn bingus(x: int, y: int) -> int { } let result: int = bingus(10, 25); + +print(result); // built-in function -- 2.54.0