| author | |
| committer | |
| log | 266f616b552eb86530ed664e44aea026cb5820db |
| tree | ac537bd975a86f79730ca87c12b09cf957b062dc |
| parent | bf7dedc48580545fcbcbb877d6f84087b96b4c62 |
6 files changed, 219 insertions(+), 79 deletions(-)
cli/src/main.rs+52-3| ... | ... | @@ -20,8 +20,16 @@ struct Cli { |
| 20 | 20 | enum Commands { |
| 21 | 21 | /// Compiles a file into javascript |
| 22 | 22 | Compile { |
| 23 | /// lists test values | |
| 24 | 23 | file: PathBuf, |
| 24 | // Whether to output to stdout instead of a file | |
| 25 | #[arg(long, default_value_t = false)] | |
| 26 | stdout: bool, | |
| 27 | }, | |
| 28 | Run { | |
| 29 | file: PathBuf, | |
| 30 | /// What javascript runtime to use | |
| 31 | #[arg(short, long, default_value = "deno")] | |
| 32 | runtime: String, | |
| 25 | 33 | }, |
| 26 | 34 | } |
| 27 | 35 | |
| ... | ... | @@ -29,10 +37,51 @@ fn main() -> Result<()> { |
| 29 | 37 | let cli = Cli::parse(); |
| 30 | 38 | |
| 31 | 39 | match cli.command { |
| 32 | Commands::Compile { file } => { | |
| 40 | Commands::Compile { file, stdout } => { | |
| 41 | let contents = std::fs::read_to_string(file.clone()).unwrap(); | |
| 42 | let result = transpile(contents.clone())?; | |
| 43 | ||
| 44 | if stdout { | |
| 45 | println!("{}", result); | |
| 46 | } else { | |
| 47 | std::fs::write(file.with_extension("js"), result).unwrap(); | |
| 48 | } | |
| 49 | } | |
| 50 | Commands::Run { file, runtime } => { | |
| 33 | 51 | let contents = std::fs::read_to_string(file.clone()).unwrap(); |
| 34 | 52 | let result = transpile(contents.clone())?; |
| 35 | std::fs::write(file.with_extension("js"), result).unwrap(); | |
| 53 | let mut temp_file = std::env::temp_dir(); | |
| 54 | temp_file.push("temp.js"); | |
| 55 | std::fs::write(temp_file.clone(), result).unwrap(); | |
| 56 | match runtime.as_str() { | |
| 57 | "deno" => { | |
| 58 | let output = std::process::Command::new("deno") | |
| 59 | .arg("run") | |
| 60 | .arg("-A") | |
| 61 | .arg(temp_file) | |
| 62 | .output() | |
| 63 | .expect("Failed to execute process"); | |
| 64 | println!("{}", String::from_utf8_lossy(&output.stdout)); | |
| 65 | } | |
| 66 | "node" => { | |
| 67 | let output = std::process::Command::new("node") | |
| 68 | .arg(temp_file) | |
| 69 | .output() | |
| 70 | .expect("Failed to execute process"); | |
| 71 | println!("{}", String::from_utf8_lossy(&output.stdout)); | |
| 72 | } | |
| 73 | "bun" => { | |
| 74 | let output = std::process::Command::new("bun") | |
| 75 | .arg("run") | |
| 76 | .arg(temp_file) | |
| 77 | .output() | |
| 78 | .expect("Failed to execute process"); | |
| 79 | println!("{}", String::from_utf8_lossy(&output.stdout)); | |
| 80 | } | |
| 81 | _ => { | |
| 82 | println!("Unknown runtime: {}", runtime); | |
| 83 | } | |
| 84 | } | |
| 36 | 85 | } |
| 37 | 86 | } |
| 38 | 87 |
compiler/src/ast/mod.rs+121-45| ... | ... | @@ -6,42 +6,50 @@ mod types; |
| 6 | 6 | use super::tokenizer::Token; |
| 7 | 7 | |
| 8 | 8 | use error::AstError; |
| 9 | use types::{ParserContext, Argument}; | |
| 9 | use types::{Argument, ParserContext}; | |
| 10 | 10 | |
| 11 | 11 | pub use types::{Ast, Type}; |
| 12 | 12 | |
| 13 | 13 | fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> { |
| 14 | ||
| 15 | 14 | let left = match tokens.next() { |
| 16 | 15 | Some(token) => match token { |
| 17 | 16 | Token::Number(number) => Ast::Number(number.clone()), |
| 18 | Token::Identifier(name) => { | |
| 19 | match tokens.peek() { | |
| 20 | Some(Token::OpenParanthesis) => { | |
| 21 | tokens.next(); | |
| 17 | Token::Identifier(name) => match tokens.peek() { | |
| 18 | Some(Token::OpenParanthesis) => { | |
| 19 | tokens.next(); | |
| 22 | 20 | |
| 23 | let mut arguments = Vec::new(); | |
| 21 | let mut arguments = Vec::new(); | |
| 22 | ||
| 23 | loop { | |
| 24 | match tokens.peek() { | |
| 25 | Some(Token::ClosedParanthesis) => { | |
| 26 | tokens.next(); | |
| 27 | break; | |
| 28 | } | |
| 29 | _ => (), | |
| 30 | } | |
| 31 | arguments.push(parse_expression(tokens)?); | |
| 24 | 32 | |
| 25 | loop { | |
| 26 | match tokens.peek() { | |
| 27 | Some(Token::ClosedParanthesis) => break, | |
| 28 | _ => () | |
| 33 | match tokens.peek() { | |
| 34 | Some(Token::Comma) => { | |
| 35 | tokens.next(); | |
| 29 | 36 | } |
| 30 | arguments.push(parse_expression(tokens)?); | |
| 31 | ||
| 32 | match tokens.peek() { | |
| 33 | Some(Token::Comma) => { | |
| 34 | tokens.next(); | |
| 35 | } | |
| 36 | Some(Token::ClosedParanthesis) => (), | |
| 37 | _ => return Err(AstError::new("Expected ) or , but found something else instead.").into()), | |
| 37 | Some(Token::ClosedParanthesis) => (), | |
| 38 | _ => { | |
| 39 | return Err(AstError::new( | |
| 40 | "Expected ) or , but found something else instead.", | |
| 41 | ) | |
| 42 | .into()) | |
| 38 | 43 | } |
| 39 | 44 | } |
| 45 | } | |
| 40 | 46 | |
| 41 | Ast::FunctionCall { name: name.clone(), arguments } | |
| 47 | Ast::FunctionCall { | |
| 48 | name: name.clone(), | |
| 49 | arguments, | |
| 42 | 50 | } |
| 43 | _ => Ast::Identifier(name.clone()) | |
| 44 | 51 | } |
| 52 | _ => Ast::Identifier(name.clone()), | |
| 45 | 53 | }, |
| 46 | 54 | _ => return Err(AstError::new("Expected expression, but did not get it").into()), |
| 47 | 55 | }, |
| ... | ... | @@ -90,7 +98,9 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { |
| 90 | 98 | |
| 91 | 99 | let inner = parse_statement(tokens)?; |
| 92 | 100 | |
| 93 | return Ok(Ast::Public { value: Box::new(inner) }) | |
| 101 | return Ok(Ast::Public { | |
| 102 | value: Box::new(inner), | |
| 103 | }); | |
| 94 | 104 | } |
| 95 | 105 | Token::Let => { |
| 96 | 106 | let name = match tokens.next() { |
| ... | ... | @@ -151,14 +161,12 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { |
| 151 | 161 | |
| 152 | 162 | match tokens.next() { |
| 153 | 163 | Some(Token::Semicolon) => (), |
| 154 | _ => { | |
| 155 | return Err( | |
| 156 | AstError::new("Expected semicolon after return statement").into(), | |
| 157 | ) | |
| 158 | } | |
| 164 | _ => return Err(AstError::new("Expected semicolon after return statement").into()), | |
| 159 | 165 | }; |
| 160 | 166 | |
| 161 | Ok(Ast::Return { return_value: Box::new(expression) }) | |
| 167 | Ok(Ast::Return { | |
| 168 | return_value: Box::new(expression), | |
| 169 | }) | |
| 162 | 170 | } |
| 163 | 171 | Token::Function => { |
| 164 | 172 | let name = match tokens.next() { |
| ... | ... | @@ -168,7 +176,12 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { |
| 168 | 176 | |
| 169 | 177 | match tokens.next() { |
| 170 | 178 | Some(Token::OpenParanthesis) => (), |
| 171 | _ => return Err(AstError::new("Expected open paranthesis after identifier in function").into()), | |
| 179 | _ => { | |
| 180 | return Err(AstError::new( | |
| 181 | "Expected open paranthesis after identifier in function", | |
| 182 | ) | |
| 183 | .into()) | |
| 184 | } | |
| 172 | 185 | }; |
| 173 | 186 | |
| 174 | 187 | let mut arguments = Vec::new(); |
| ... | ... | @@ -178,13 +191,18 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { |
| 178 | 191 | Some(Token::ClosedParanthesis) => { |
| 179 | 192 | tokens.next(); |
| 180 | 193 | break; |
| 181 | }, | |
| 182 | _ => () | |
| 194 | } | |
| 195 | _ => (), | |
| 183 | 196 | } |
| 184 | 197 | |
| 185 | 198 | let argument_name = match tokens.next() { |
| 186 | 199 | Some(Token::Identifier(name)) => name, |
| 187 | _ => return Err(AstError::new("Expected identifier for argument name in function").into()), | |
| 200 | _ => { | |
| 201 | return Err(AstError::new( | |
| 202 | "Expected identifier for argument name in function", | |
| 203 | ) | |
| 204 | .into()) | |
| 205 | } | |
| 188 | 206 | }; |
| 189 | 207 | |
| 190 | 208 | let return_type = match tokens.next() { |
| ... | ... | @@ -195,21 +213,34 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { |
| 195 | 213 | match tokens.peek() { |
| 196 | 214 | Some(Token::Comma) => { |
| 197 | 215 | tokens.next(); |
| 198 | }, | |
| 199 | _ => () | |
| 216 | } | |
| 217 | _ => (), | |
| 200 | 218 | } |
| 201 | 219 | |
| 202 | arguments.push(Argument { name: argument_name.clone(), return_type}) | |
| 220 | arguments.push(Argument { | |
| 221 | name: argument_name.clone(), | |
| 222 | return_type, | |
| 223 | }) | |
| 203 | 224 | } |
| 204 | 225 | |
| 205 | 226 | let return_type = match tokens.next() { |
| 206 | 227 | Some(Token::RightArrow) => parse_type(tokens)?, |
| 207 | _ => return Err(AstError::new("Expected right arrow after argument list in function").into()), | |
| 228 | _ => { | |
| 229 | return Err(AstError::new( | |
| 230 | "Expected right arrow after argument list in function", | |
| 231 | ) | |
| 232 | .into()) | |
| 233 | } | |
| 208 | 234 | }; |
| 209 | 235 | |
| 210 | 236 | match tokens.next() { |
| 211 | 237 | Some(Token::OpenBracket) => (), |
| 212 | _ => return Err(AstError::new("Expected open bracket after type in function declaration").into()), | |
| 238 | _ => { | |
| 239 | return Err(AstError::new( | |
| 240 | "Expected open bracket after type in function declaration", | |
| 241 | ) | |
| 242 | .into()) | |
| 243 | } | |
| 213 | 244 | }; |
| 214 | 245 | |
| 215 | 246 | let mut value = Vec::new(); |
| ... | ... | @@ -219,15 +250,62 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { |
| 219 | 250 | Some(Token::ClosedBracket) => { |
| 220 | 251 | tokens.next(); |
| 221 | 252 | break; |
| 222 | }, | |
| 223 | _ => () | |
| 253 | } | |
| 254 | _ => (), | |
| 224 | 255 | } |
| 225 | 256 | |
| 226 | 257 | value.push(parse_statement(tokens)?); |
| 227 | 258 | } |
| 228 | 259 | |
| 229 | return Ok(Ast::Function { name, arguments, return_type, value }) | |
| 260 | return Ok(Ast::Function { | |
| 261 | name, | |
| 262 | arguments, | |
| 263 | return_type, | |
| 264 | value, | |
| 265 | }); | |
| 230 | 266 | } |
| 267 | Token::Identifier(name) => match tokens.peek() { | |
| 268 | Some(Token::OpenParanthesis) => { | |
| 269 | tokens.next(); | |
| 270 | ||
| 271 | let mut arguments = Vec::new(); | |
| 272 | ||
| 273 | loop { | |
| 274 | match tokens.peek() { | |
| 275 | Some(Token::ClosedParanthesis) => { | |
| 276 | tokens.next(); | |
| 277 | break; | |
| 278 | } | |
| 279 | _ => (), | |
| 280 | } | |
| 281 | arguments.push(parse_expression(tokens)?); | |
| 282 | ||
| 283 | match tokens.peek() { | |
| 284 | Some(Token::Comma) => { | |
| 285 | tokens.next(); | |
| 286 | } | |
| 287 | Some(Token::ClosedParanthesis) => (), | |
| 288 | _ => { | |
| 289 | return Err(AstError::new( | |
| 290 | "Expected ) or , but found something else instead.", | |
| 291 | ) | |
| 292 | .into()) | |
| 293 | } | |
| 294 | } | |
| 295 | } | |
| 296 | ||
| 297 | match tokens.next() { | |
| 298 | Some(Token::Semicolon) => (), | |
| 299 | _ => return Err(AstError::new("Expected semicolon after function call").into()), | |
| 300 | }; | |
| 301 | ||
| 302 | Ok(Ast::FunctionCall { | |
| 303 | name: name.clone(), | |
| 304 | arguments, | |
| 305 | }) | |
| 306 | } | |
| 307 | _ => Err(AstError::new("Unexpected identifier").into()), | |
| 308 | }, | |
| 231 | 309 | Token::Comment(comment) => Ok(Ast::Comment(comment.clone())), |
| 232 | 310 | Token::DocComment(comment) => { |
| 233 | 311 | let mut concatted_comments = comment.clone(); |
| ... | ... | @@ -236,15 +314,13 @@ fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { |
| 236 | 314 | Some(Token::DocComment(comment)) => { |
| 237 | 315 | concatted_comments.push_str(format!("\n{}", comment).as_str()); |
| 238 | 316 | tokens.next(); |
| 239 | }, | |
| 317 | } | |
| 240 | 318 | _ => break, |
| 241 | 319 | } |
| 242 | 320 | } |
| 243 | 321 | Ok(Ast::DocComment(concatted_comments)) |
| 244 | }, | |
| 245 | _ => { | |
| 246 | return Err(AstError::new("Unexpected token").into()) | |
| 247 | }, | |
| 322 | } | |
| 323 | _ => return Err(AstError::new("Unexpected token").into()), | |
| 248 | 324 | } |
| 249 | 325 | } |
| 250 | 326 |
compiler/src/ast/types.rs+7-8| ... | ... | @@ -1,10 +1,10 @@ |
| 1 | use crate::util::Operator; | |
| 2 | 1 | use crate::tokenizer::Token; |
| 2 | use crate::util::Operator; | |
| 3 | 3 | |
| 4 | 4 | #[derive(Debug)] |
| 5 | 5 | pub struct ParserContext { |
| 6 | 6 | pub tokens: Vec<Token>, |
| 7 | pub cursor: usize | |
| 7 | pub cursor: usize, | |
| 8 | 8 | } |
| 9 | 9 | |
| 10 | 10 | impl ParserContext { |
| ... | ... | @@ -35,7 +35,6 @@ impl ParserContext { |
| 35 | 35 | } |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | ||
| 39 | 38 | #[derive(Debug)] |
| 40 | 39 | pub enum Type { |
| 41 | 40 | Int, |
| ... | ... | @@ -46,13 +45,13 @@ pub enum Type { |
| 46 | 45 | #[derive(Debug)] |
| 47 | 46 | pub struct Argument { |
| 48 | 47 | pub name: String, |
| 49 | pub return_type: Type | |
| 48 | pub return_type: Type, | |
| 50 | 49 | } |
| 51 | 50 | |
| 52 | 51 | #[derive(Debug)] |
| 53 | 52 | pub enum Ast { |
| 54 | 53 | Public { |
| 55 | value: Box<Ast> | |
| 54 | value: Box<Ast>, | |
| 56 | 55 | }, |
| 57 | 56 | ConstantDeclaration { |
| 58 | 57 | name: String, |
| ... | ... | @@ -70,13 +69,13 @@ pub enum Ast { |
| 70 | 69 | right: Box<Ast>, |
| 71 | 70 | }, |
| 72 | 71 | Return { |
| 73 | return_value: Box<Ast> | |
| 72 | return_value: Box<Ast>, | |
| 74 | 73 | }, |
| 75 | 74 | Function { |
| 76 | 75 | name: String, |
| 77 | 76 | arguments: Vec<Argument>, |
| 78 | 77 | return_type: Type, |
| 79 | value: Vec<Ast> | |
| 78 | value: Vec<Ast>, | |
| 80 | 79 | }, |
| 81 | 80 | FunctionCall { |
| 82 | 81 | name: String, |
| ... | ... | @@ -85,5 +84,5 @@ pub enum Ast { |
| 85 | 84 | Number(String), |
| 86 | 85 | Identifier(String), |
| 87 | 86 | Comment(String), |
| 88 | DocComment(String) | |
| 87 | DocComment(String), | |
| 89 | 88 | } |
compiler/src/tokenizer.rs+14-18| ... | ... | @@ -94,23 +94,19 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> { |
| 94 | 94 | '=' => tokens.push(Token::Equals), |
| 95 | 95 | ';' => tokens.push(Token::Semicolon), |
| 96 | 96 | '+' => tokens.push(Token::Operator(Operator::Add)), |
| 97 | '-' => { | |
| 98 | match chars.peek() { | |
| 99 | Some((_, '>')) => { | |
| 100 | chars.next(); | |
| 101 | tokens.push(Token::RightArrow); | |
| 102 | } | |
| 103 | _ => tokens.push(Token::Operator(Operator::Substract)) | |
| 97 | '-' => match chars.peek() { | |
| 98 | Some((_, '>')) => { | |
| 99 | chars.next(); | |
| 100 | tokens.push(Token::RightArrow); | |
| 104 | 101 | } |
| 102 | _ => tokens.push(Token::Operator(Operator::Substract)), | |
| 105 | 103 | }, |
| 106 | '*' => { | |
| 107 | match chars.peek() { | |
| 108 | Some((_, '*')) => { | |
| 109 | chars.next(); | |
| 110 | tokens.push(Token::Operator(Operator::Power)); | |
| 111 | } | |
| 112 | _ => tokens.push(Token::Operator(Operator::Multiply)) | |
| 104 | '*' => match chars.peek() { | |
| 105 | Some((_, '*')) => { | |
| 106 | chars.next(); | |
| 107 | tokens.push(Token::Operator(Operator::Power)); | |
| 113 | 108 | } |
| 109 | _ => tokens.push(Token::Operator(Operator::Multiply)), | |
| 114 | 110 | }, |
| 115 | 111 | '/' => { |
| 116 | 112 | match chars.peek() { |
| ... | ... | @@ -121,7 +117,7 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> { |
| 121 | 117 | Some((_, '/')) => { |
| 122 | 118 | chars.next(); // Consume the third slash |
| 123 | 119 | true |
| 124 | }, | |
| 120 | } | |
| 125 | 121 | _ => false, |
| 126 | 122 | }; |
| 127 | 123 | |
| ... | ... | @@ -136,7 +132,7 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> { |
| 136 | 132 | '\n' => break, |
| 137 | 133 | _ => { |
| 138 | 134 | comment.push(c.clone()); |
| 139 | }, | |
| 135 | } | |
| 140 | 136 | } |
| 141 | 137 | } |
| 142 | 138 | |
| ... | ... | @@ -148,9 +144,9 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> { |
| 148 | 144 | tokens.push(Token::Comment(comment)); |
| 149 | 145 | } |
| 150 | 146 | } |
| 151 | _ => tokens.push(Token::Operator(Operator::Divide)) | |
| 147 | _ => tokens.push(Token::Operator(Operator::Divide)), | |
| 152 | 148 | } |
| 153 | }, | |
| 149 | } | |
| 154 | 150 | ',' => tokens.push(Token::Comma), |
| 155 | 151 | '(' => tokens.push(Token::OpenParanthesis), |
| 156 | 152 | ')' => tokens.push(Token::ClosedParanthesis), |
compiler/src/walker.rs+23-5| ... | ... | @@ -19,16 +19,29 @@ fn transpile_helper(tree: &Ast) -> Result<String> { |
| 19 | 19 | transpile_helper(right)? |
| 20 | 20 | ), |
| 21 | 21 | Ast::Public { value } => format!("export {}", transpile_helper(value)?), |
| 22 | Ast::VariableDeclaration { name, return_type, value } => match return_type { | |
| 22 | Ast::VariableDeclaration { | |
| 23 | name, | |
| 24 | return_type, | |
| 25 | value, | |
| 26 | } => match return_type { | |
| 23 | 27 | Type::Bigint => format!("let {} = BigInt({});\n", name, transpile_helper(value)?), |
| 24 | 28 | _ => format!("let {} = {};\n", name, transpile_helper(value)?), |
| 25 | 29 | }, |
| 26 | Ast::ConstantDeclaration { name, return_type, value } => match return_type { | |
| 30 | Ast::ConstantDeclaration { | |
| 31 | name, | |
| 32 | return_type, | |
| 33 | value, | |
| 34 | } => match return_type { | |
| 27 | 35 | Type::Bigint => format!("const {} = BigInt({});\n", name, transpile_helper(value)?), |
| 28 | 36 | _ => format!("const {} = {};\n", name, transpile_helper(value)?), |
| 29 | 37 | }, |
| 30 | 38 | Ast::Return { return_value } => format!("return {};\n", transpile_helper(return_value)?), |
| 31 | Ast::Function { name, arguments, return_type: _, value } => { | |
| 39 | Ast::Function { | |
| 40 | name, | |
| 41 | arguments, | |
| 42 | return_type: _, | |
| 43 | value, | |
| 44 | } => { | |
| 32 | 45 | let mut result = format!("function {}(", name); |
| 33 | 46 | |
| 34 | 47 | for (i, argument) in arguments.iter().enumerate() { |
| ... | ... | @@ -48,9 +61,12 @@ fn transpile_helper(tree: &Ast) -> Result<String> { |
| 48 | 61 | result += "}\n"; |
| 49 | 62 | |
| 50 | 63 | result |
| 51 | }, | |
| 64 | } | |
| 52 | 65 | Ast::FunctionCall { name, arguments } => { |
| 53 | let mut result = format!("{}(", name); | |
| 66 | let mut result = match name.as_str() { | |
| 67 | "print" => String::from("console.log("), | |
| 68 | _ => format!("{}(", name), | |
| 69 | }; | |
| 54 | 70 | |
| 55 | 71 | for (i, argument) in arguments.iter().enumerate() { |
| 56 | 72 | if i == 0 { |
| ... | ... | @@ -60,6 +76,8 @@ fn transpile_helper(tree: &Ast) -> Result<String> { |
| 60 | 76 | } |
| 61 | 77 | } |
| 62 | 78 | |
| 79 | result += ")"; | |
| 80 | ||
| 63 | 81 | result |
| 64 | 82 | } |
| 65 | 83 | }) |
examples/basics/functions.blight+2| ... | ... | @@ -3,3 +3,5 @@ fn bingus(x: int, y: int) -> int { |
| 3 | 3 | } |
| 4 | 4 | |
| 5 | 5 | let result: int = bingus(10, 25); |
| 6 | ||
| 7 | print(result); // built-in function |