| author | |
| committer | |
| log | 2900f23394e35089aca94dbd1de195c7666365df |
| tree | 63fc7d53b2754c3f33a86f8813c086511d9bfa2a |
| parent | 8133856c0042befc85ff2725f34dfb71d50b1095 |
21 files changed, 192 insertions(+), 189 deletions(-)
.github/logo.png created| Binary files /dev/null and b/.github/logo.png differ | |||
README.md+2| ... | @@ -1,3 +1,5 @@ | ... | @@ -1,3 +1,5 @@ |
| 1 |  | ||
| 2 | |||
| 1 | # Blight | 3 | # Blight |
| 2 | 4 | ||
| 3 | Blight sure is a programming language. | 5 | Blight sure is a programming language. |
crates/ast/src/lib.rs+1| ... | @@ -3,6 +3,7 @@ use miette::Result; | ... | @@ -3,6 +3,7 @@ use miette::Result; |
| 3 | mod error; | 3 | mod error; |
| 4 | mod parse_block; | 4 | mod parse_block; |
| 5 | mod parse_expression; | 5 | mod parse_expression; |
| 6 | mod parse_function_call; | ||
| 6 | mod parse_statement; | 7 | mod parse_statement; |
| 7 | mod parse_type; | 8 | mod parse_type; |
| 8 | mod types; | 9 | mod types; |
crates/ast/src/parse_expression.rs+19-35| ... | @@ -4,6 +4,7 @@ use tokenizer::Token; | ... | @@ -4,6 +4,7 @@ use tokenizer::Token; |
| 4 | 4 | ||
| 5 | use super::error::AstError; | 5 | use super::error::AstError; |
| 6 | use super::parse_block::parse_block; | 6 | use super::parse_block::parse_block; |
| 7 | use super::parse_function_call::parse_function_call; | ||
| 7 | use super::types::{Ast, Block, ParserContext}; | 8 | use super::types::{Ast, Block, ParserContext}; |
| 8 | 9 | ||
| 9 | pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> { | 10 | pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> { |
| ... | @@ -11,6 +12,16 @@ pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> { | ... | @@ -11,6 +12,16 @@ pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> { |
| 11 | Some(token) => match token { | 12 | Some(token) => match token { |
| 12 | Token::Number(number) => Ast::Number(number), | 13 | Token::Number(number) => Ast::Number(number), |
| 13 | Token::String(string) => Ast::String(string.clone()), | 14 | Token::String(string) => Ast::String(string.clone()), |
| 15 | Token::BuiltIn(name) => { | ||
| 16 | match tokens.peek() { | ||
| 17 | Some(Token::OpenParanthesis) => { | ||
| 18 | // FRICK we consumed the open paranthesis | ||
| 19 | tokens.back(); | ||
| 20 | parse_function_call(tokens)? | ||
| 21 | } | ||
| 22 | _ => Ast::BuiltIn(name.clone()), | ||
| 23 | } | ||
| 24 | } | ||
| 14 | Token::True => Ast::Bool(true), | 25 | Token::True => Ast::Bool(true), |
| 15 | Token::False => Ast::Bool(false), | 26 | Token::False => Ast::Bool(false), |
| 16 | Token::OpenBracket => { | 27 | Token::OpenBracket => { |
| ... | @@ -57,43 +68,16 @@ pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> { | ... | @@ -57,43 +68,16 @@ pub fn parse_expression(tokens: &mut ParserContext) -> Result<Ast> { |
| 57 | else_value, | 68 | else_value, |
| 58 | }); | 69 | }); |
| 59 | } | 70 | } |
| 60 | Token::Identifier(name) => match tokens.peek() { | 71 | Token::Identifier(name) => { |
| 61 | Some(Token::OpenParanthesis) => { | 72 | match tokens.peek() { |
| 62 | tokens.next(); | 73 | Some(Token::OpenParanthesis) => { |
| 63 | 74 | // FRICK we consumed the open paranthesis | |
| 64 | let mut arguments = Vec::new(); | 75 | tokens.back(); |
| 65 | 76 | parse_function_call(tokens)? | |
| 66 | loop { | ||
| 67 | match tokens.peek() { | ||
| 68 | Some(Token::ClosedParanthesis) => { | ||
| 69 | tokens.next(); | ||
| 70 | break; | ||
| 71 | } | ||
| 72 | _ => (), | ||
| 73 | } | ||
| 74 | arguments.push(parse_expression(tokens)?); | ||
| 75 | |||
| 76 | match tokens.peek() { | ||
| 77 | Some(Token::Comma) => { | ||
| 78 | tokens.next(); | ||
| 79 | } | ||
| 80 | Some(Token::ClosedParanthesis) => (), | ||
| 81 | _ => { | ||
| 82 | return Err(AstError::new( | ||
| 83 | "Expected ) or , but found something else instead.", | ||
| 84 | ) | ||
| 85 | .into()) | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | Ast::FunctionCall { | ||
| 91 | name: name.clone(), | ||
| 92 | arguments, | ||
| 93 | } | 77 | } |
| 78 | _ => Ast::Identifier(name.clone()), | ||
| 94 | } | 79 | } |
| 95 | _ => Ast::Identifier(name.clone()), | 80 | } |
| 96 | }, | ||
| 97 | _ => { | 81 | _ => { |
| 98 | println!("token: {:?}", token); | 82 | println!("token: {:?}", token); |
| 99 | return Err(AstError::new("Expected expression, but did not get it").into()); | 83 | return Err(AstError::new("Expected expression, but did not get it").into()); |
crates/ast/src/parse_function_call.rs created+56| ... | @@ -0,0 +1,56 @@ | ||
| 1 | use miette::Result; | ||
| 2 | |||
| 3 | use tokenizer::Token; | ||
| 4 | |||
| 5 | use super::error::AstError; | ||
| 6 | use super::parse_expression::parse_expression; | ||
| 7 | use super::types::{Ast, ParserContext}; | ||
| 8 | |||
| 9 | /// Parses a function call AS AN EXPRESSION | ||
| 10 | /// This does not consume the semicolon at the end of the function call | ||
| 11 | /// if it is a statement. | ||
| 12 | pub fn parse_function_call(tokens: &mut ParserContext) -> Result<Ast> { | ||
| 13 | let identifier = match tokens.next() { | ||
| 14 | Some(Token::Identifier(identifier)) => Ast::Identifier(identifier), | ||
| 15 | Some(Token::BuiltIn(identifier)) => Ast::BuiltIn(identifier), | ||
| 16 | _ => return Err(AstError::new("Expected identifier").into()), | ||
| 17 | }; | ||
| 18 | |||
| 19 | match tokens.peek() { | ||
| 20 | Some(Token::OpenParanthesis) => { | ||
| 21 | tokens.next(); | ||
| 22 | |||
| 23 | let mut arguments = Vec::new(); | ||
| 24 | |||
| 25 | loop { | ||
| 26 | match tokens.peek() { | ||
| 27 | Some(Token::ClosedParanthesis) => { | ||
| 28 | tokens.next(); | ||
| 29 | break; | ||
| 30 | } | ||
| 31 | _ => (), | ||
| 32 | } | ||
| 33 | arguments.push(parse_expression(tokens)?); | ||
| 34 | |||
| 35 | match tokens.peek() { | ||
| 36 | Some(Token::Comma) => { | ||
| 37 | tokens.next(); | ||
| 38 | } | ||
| 39 | Some(Token::ClosedParanthesis) => (), | ||
| 40 | _ => { | ||
| 41 | return Err(AstError::new( | ||
| 42 | "Expected ) or , but found something else instead.", | ||
| 43 | ) | ||
| 44 | .into()) | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | Ok(Ast::FunctionCall { | ||
| 50 | name: Box::new(identifier), | ||
| 51 | arguments, | ||
| 52 | }) | ||
| 53 | } | ||
| 54 | _ => Err(AstError::new("Unexpected identifier").into()), | ||
| 55 | } | ||
| 56 | } | ||
crates/ast/src/parse_statement.rs+23-36| ... | @@ -5,6 +5,7 @@ use tokenizer::Token; | ... | @@ -5,6 +5,7 @@ use tokenizer::Token; |
| 5 | use super::error::AstError; | 5 | use super::error::AstError; |
| 6 | use super::parse_block::parse_block; | 6 | use super::parse_block::parse_block; |
| 7 | use super::parse_expression::parse_expression; | 7 | use super::parse_expression::parse_expression; |
| 8 | use super::parse_function_call::parse_function_call; | ||
| 8 | use super::parse_type::parse_type; | 9 | use super::parse_type::parse_type; |
| 9 | use super::types::{Argument, Ast, Block, ParserContext}; | 10 | use super::types::{Argument, Ast, Block, ParserContext}; |
| 10 | 11 | ||
| ... | @@ -12,6 +13,16 @@ use super::types::{Argument, Ast, Block, ParserContext}; | ... | @@ -12,6 +13,16 @@ use super::types::{Argument, Ast, Block, ParserContext}; |
| 12 | pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { | 13 | pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { |
| 13 | let token = tokens.next().unwrap(); | 14 | let token = tokens.next().unwrap(); |
| 14 | match token { | 15 | match token { |
| 16 | Token::BuiltIn(name) => { | ||
| 17 | match tokens.peek() { | ||
| 18 | Some(Token::OpenParanthesis) => { | ||
| 19 | // Okay, so we messed up. We shouldn't have consumed that token. | ||
| 20 | tokens.back(); | ||
| 21 | parse_function_call(tokens) | ||
| 22 | } | ||
| 23 | _ => Ok(Ast::BuiltIn(name)), | ||
| 24 | } | ||
| 25 | } | ||
| 15 | Token::OpenBracket => { | 26 | Token::OpenBracket => { |
| 16 | // FRICK, we have to backtrack | 27 | // FRICK, we have to backtrack |
| 17 | tokens.back(); | 28 | tokens.back(); |
| ... | @@ -44,7 +55,7 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { | ... | @@ -44,7 +55,7 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { |
| 44 | Some(Token::Semicolon) => (), | 55 | Some(Token::Semicolon) => (), |
| 45 | _ => { | 56 | _ => { |
| 46 | return Err( | 57 | return Err( |
| 47 | AstError::new("Expected semicolon after variable declaration").into(), | 58 | AstError::new("Expected semicolon after constant declaration").into(), |
| 48 | ) | 59 | ) |
| 49 | } | 60 | } |
| 50 | }; | 61 | }; |
| ... | @@ -213,45 +224,21 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { | ... | @@ -213,45 +224,21 @@ pub fn parse_statement(tokens: &mut ParserContext) -> Result<Ast> { |
| 213 | else_value, | 224 | else_value, |
| 214 | }); | 225 | }); |
| 215 | } | 226 | } |
| 216 | Token::Identifier(name) => match tokens.peek() { | 227 | Token::Identifier(_) => match tokens.peek() { |
| 217 | Some(Token::OpenParanthesis) => { | 228 | Some(Token::OpenParanthesis) => { |
| 218 | tokens.next(); | 229 | // We need to give the identifier back to the parser |
| 219 | 230 | tokens.back(); | |
| 220 | let mut arguments = Vec::new(); | 231 | let result = parse_function_call(tokens); |
| 221 | |||
| 222 | loop { | ||
| 223 | match tokens.peek() { | ||
| 224 | Some(Token::ClosedParanthesis) => { | ||
| 225 | tokens.next(); | ||
| 226 | break; | ||
| 227 | } | ||
| 228 | _ => (), | ||
| 229 | } | ||
| 230 | arguments.push(parse_expression(tokens)?); | ||
| 231 | |||
| 232 | match tokens.peek() { | ||
| 233 | Some(Token::Comma) => { | ||
| 234 | tokens.next(); | ||
| 235 | } | ||
| 236 | Some(Token::ClosedParanthesis) => (), | ||
| 237 | _ => { | ||
| 238 | return Err(AstError::new( | ||
| 239 | "Expected ) or , but found something else instead.", | ||
| 240 | ) | ||
| 241 | .into()) | ||
| 242 | } | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | match tokens.next() { | 232 | match tokens.next() { |
| 247 | Some(Token::Semicolon) => (), | 233 | Some(Token::Semicolon) => (), |
| 248 | _ => return Err(AstError::new("Expected semicolon after function call").into()), | 234 | _ => { |
| 235 | return Err(AstError::new( | ||
| 236 | "Expected semicolon after function call statement", | ||
| 237 | ) | ||
| 238 | .into()) | ||
| 239 | } | ||
| 249 | }; | 240 | }; |
| 250 | 241 | result | |
| 251 | Ok(Ast::FunctionCall { | ||
| 252 | name: name.clone(), | ||
| 253 | arguments, | ||
| 254 | }) | ||
| 255 | } | 242 | } |
| 256 | _ => Err(AstError::new("Unexpected identifier").into()), | 243 | _ => Err(AstError::new("Unexpected identifier").into()), |
| 257 | }, | 244 | }, |
crates/ast/src/parse_type.rs+1| ... | @@ -13,6 +13,7 @@ pub fn parse_type(tokens: &mut ParserContext) -> Result<Type> { | ... | @@ -13,6 +13,7 @@ pub fn parse_type(tokens: &mut ParserContext) -> Result<Type> { |
| 13 | "bigint" => Ok(Type::Bigint), | 13 | "bigint" => Ok(Type::Bigint), |
| 14 | "bool" => Ok(Type::Bool), | 14 | "bool" => Ok(Type::Bool), |
| 15 | "string" => Ok(Type::String), | 15 | "string" => Ok(Type::String), |
| 16 | "any" => Ok(Type::Any), | ||
| 16 | _ => return Err(AstError::new("Expected type").into()), | 17 | _ => return Err(AstError::new("Expected type").into()), |
| 17 | }, | 18 | }, |
| 18 | _ => return Err(AstError::new("Expected type").into()), | 19 | _ => return Err(AstError::new("Expected type").into()), |
crates/ast/src/types.rs+8-5| ... | @@ -41,28 +41,29 @@ impl ParserContext { | ... | @@ -41,28 +41,29 @@ impl ParserContext { |
| 41 | } | 41 | } |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | #[derive(Debug)] | 44 | #[derive(Debug, Clone)] |
| 45 | pub enum Type { | 45 | pub enum Type { |
| 46 | Int, | 46 | Int, |
| 47 | Float, | 47 | Float, |
| 48 | Bigint, | 48 | Bigint, |
| 49 | Bool, | 49 | Bool, |
| 50 | String, | 50 | String, |
| 51 | Any, | ||
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | #[derive(Debug)] | 54 | #[derive(Debug, Clone)] |
| 54 | pub struct Block { | 55 | pub struct Block { |
| 55 | pub value: Vec<Ast>, | 56 | pub value: Vec<Ast>, |
| 56 | pub implicit_return: bool, | 57 | pub implicit_return: bool, |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | #[derive(Debug)] | 60 | #[derive(Debug, Clone)] |
| 60 | pub struct Argument { | 61 | pub struct Argument { |
| 61 | pub name: String, | 62 | pub name: String, |
| 62 | pub return_type: Type, | 63 | pub return_type: Type, |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 65 | #[derive(Debug)] | 66 | #[derive(Debug, Clone)] |
| 66 | pub enum Ast { | 67 | pub enum Ast { |
| 67 | ConstantDeclaration { | 68 | ConstantDeclaration { |
| 68 | name: String, | 69 | name: String, |
| ... | @@ -86,7 +87,8 @@ pub enum Ast { | ... | @@ -86,7 +87,8 @@ pub enum Ast { |
| 86 | block: Block, | 87 | block: Block, |
| 87 | }, | 88 | }, |
| 88 | FunctionCall { | 89 | FunctionCall { |
| 89 | name: String, | 90 | /// Identifier or BuiltIn |
| 91 | name: Box<Ast>, | ||
| 90 | arguments: Vec<Ast>, | 92 | arguments: Vec<Ast>, |
| 91 | }, | 93 | }, |
| 92 | If { | 94 | If { |
| ... | @@ -101,6 +103,7 @@ pub enum Ast { | ... | @@ -101,6 +103,7 @@ pub enum Ast { |
| 101 | Bool(bool), | 103 | Bool(bool), |
| 102 | String(String), | 104 | String(String), |
| 103 | Identifier(String), | 105 | Identifier(String), |
| 106 | BuiltIn(String), | ||
| 104 | Comment(String), | 107 | Comment(String), |
| 105 | DocComment(String), | 108 | DocComment(String), |
| 106 | } | 109 | } |
crates/js_ast/src/lib.rs+46-104| ... | @@ -109,10 +109,27 @@ fn get_expression_handle(ast: &Ast, ctx: &mut TranslateContext) -> JsExpression | ... | @@ -109,10 +109,27 @@ fn get_expression_handle(ast: &Ast, ctx: &mut TranslateContext) -> JsExpression |
| 109 | for argument in arguments { | 109 | for argument in arguments { |
| 110 | handles.push(get_expression_handle(argument, ctx)); | 110 | handles.push(get_expression_handle(argument, ctx)); |
| 111 | } | 111 | } |
| 112 | ctx.push_internal_variable(JsExpression::FunctionCall { | 112 | match *name.clone() { |
| 113 | name: ctx.get_identifier(name.clone()), | 113 | Ast::Identifier(name) => ctx.push_internal_variable(JsExpression::FunctionCall { |
| 114 | arguments: handles, | 114 | name: Box::new(JsExpression::Identifier(ctx.get_identifier(name.clone()))), |
| 115 | }) | 115 | arguments: handles, |
| 116 | }), | ||
| 117 | Ast::BuiltIn(name) => match name.as_str() { | ||
| 118 | "@raw" => { | ||
| 119 | if handles.len() != 1 { | ||
| 120 | panic!("@raw expects exactly one argument"); | ||
| 121 | } | ||
| 122 | match handles.get(0).unwrap() { | ||
| 123 | JsExpression::String(value) => { | ||
| 124 | return JsExpression::Raw(value.clone()); | ||
| 125 | } | ||
| 126 | _ => panic!("@raw expects a string argument"), | ||
| 127 | } | ||
| 128 | } | ||
| 129 | _ => panic!("Unknown built-in function {}", name), | ||
| 130 | }, | ||
| 131 | _ => panic!("Function name must be an identifier"), | ||
| 132 | } | ||
| 116 | } | 133 | } |
| 117 | Ast::Operation { | 134 | Ast::Operation { |
| 118 | operator, | 135 | operator, |
| ... | @@ -286,13 +303,32 @@ fn translate_statement(ast: &Ast, ctx: &mut TranslateContext) { | ... | @@ -286,13 +303,32 @@ fn translate_statement(ast: &Ast, ctx: &mut TranslateContext) { |
| 286 | for argument in arguments { | 303 | for argument in arguments { |
| 287 | handles.push(get_expression_handle(argument, ctx)); | 304 | handles.push(get_expression_handle(argument, ctx)); |
| 288 | } | 305 | } |
| 289 | ctx.push(JsStatement::Expression(JsExpression::FunctionCall { | 306 | match *name.clone() { |
| 290 | name: match name.as_str() { | 307 | Ast::Identifier(name) => { |
| 291 | "print" => "console.log".to_string(), | 308 | ctx.push(JsStatement::Expression(JsExpression::FunctionCall { |
| 292 | _ => name.clone(), | 309 | name: Box::new(JsExpression::Identifier(ctx.get_identifier(name.clone()))), |
| 310 | arguments: handles, | ||
| 311 | })); | ||
| 312 | } | ||
| 313 | Ast::BuiltIn(name) => match name.as_str() { | ||
| 314 | "@raw" => { | ||
| 315 | if handles.len() != 1 { | ||
| 316 | panic!("@raw expects exactly one argument"); | ||
| 317 | } | ||
| 318 | match handles.get(0).unwrap() { | ||
| 319 | JsExpression::String(value) => { | ||
| 320 | ctx.push(JsStatement::Expression(JsExpression::FunctionCall { | ||
| 321 | name: Box::new(JsExpression::Raw(value.clone())), | ||
| 322 | arguments: handles, | ||
| 323 | })); | ||
| 324 | } | ||
| 325 | _ => panic!("@raw expects a string argument"), | ||
| 326 | } | ||
| 327 | } | ||
| 328 | _ => panic!("Unknown built-in function {}", name), | ||
| 293 | }, | 329 | }, |
| 294 | arguments: handles, | 330 | _ => panic!("Function name must be an identifier"), |
| 295 | })); | 331 | } |
| 296 | } | 332 | } |
| 297 | Ast::Comment(_) | Ast::DocComment(_) => {} | 333 | Ast::Comment(_) | Ast::DocComment(_) => {} |
| 298 | _ => { | 334 | _ => { |
| ... | @@ -310,97 +346,3 @@ pub fn translate(_ast: Vec<Ast>) -> Result<Vec<JsStatement>> { | ... | @@ -310,97 +346,3 @@ pub fn translate(_ast: Vec<Ast>) -> Result<Vec<JsStatement>> { |
| 310 | 346 | ||
| 311 | Ok(ctx.pop_context()) | 347 | Ok(ctx.pop_context()) |
| 312 | } | 348 | } |
| 313 | |||
| 314 | #[cfg(test)] | ||
| 315 | mod tests { | ||
| 316 | use crate::translate; | ||
| 317 | use crate::{JsExpression, JsStatement}; | ||
| 318 | use ast::{Ast, Type}; | ||
| 319 | use util::Operator; | ||
| 320 | |||
| 321 | #[test] | ||
| 322 | fn test_translate() { | ||
| 323 | let ast = Ast::ConstantDeclaration { | ||
| 324 | name: "hi".to_string(), | ||
| 325 | return_type: Type::Int, | ||
| 326 | value: Box::new(Ast::Operation { | ||
| 327 | operator: Operator::Add, | ||
| 328 | left: Box::new(Ast::Operation { | ||
| 329 | operator: Operator::Add, | ||
| 330 | left: Box::new(Ast::Number(1.0)), | ||
| 331 | right: Box::new(Ast::Operation { | ||
| 332 | operator: Operator::Add, | ||
| 333 | left: Box::new(Ast::Number(2.0)), | ||
| 334 | right: Box::new(Ast::Number(3.0)), | ||
| 335 | }), | ||
| 336 | }), | ||
| 337 | right: Box::new(Ast::Operation { | ||
| 338 | operator: Operator::Add, | ||
| 339 | left: Box::new(Ast::Number(4.0)), | ||
| 340 | right: Box::new(Ast::Number(5.0)), | ||
| 341 | }), | ||
| 342 | }), | ||
| 343 | }; | ||
| 344 | assert_eq!( | ||
| 345 | translate(vec![ast]).unwrap(), | ||
| 346 | vec![ | ||
| 347 | JsStatement::ConstantDeclaration { | ||
| 348 | name: "$0".to_string(), | ||
| 349 | value: JsExpression::Number(1.0) | ||
| 350 | }, | ||
| 351 | JsStatement::ConstantDeclaration { | ||
| 352 | name: "$1".to_string(), | ||
| 353 | value: JsExpression::Number(2.0) | ||
| 354 | }, | ||
| 355 | JsStatement::ConstantDeclaration { | ||
| 356 | name: "$2".to_string(), | ||
| 357 | value: JsExpression::Number(3.0) | ||
| 358 | }, | ||
| 359 | JsStatement::ConstantDeclaration { | ||
| 360 | name: "$3".to_string(), | ||
| 361 | value: JsExpression::Operation { | ||
| 362 | operator: Operator::Add, | ||
| 363 | left: Box::new(JsExpression::Identifier("$1".to_string())), | ||
| 364 | right: Box::new(JsExpression::Identifier("$2".to_string())) | ||
| 365 | } | ||
| 366 | }, | ||
| 367 | JsStatement::ConstantDeclaration { | ||
| 368 | name: "$4".to_string(), | ||
| 369 | value: JsExpression::Operation { | ||
| 370 | operator: Operator::Add, | ||
| 371 | left: Box::new(JsExpression::Identifier("$0".to_string())), | ||
| 372 | right: Box::new(JsExpression::Identifier("$3".to_string())) | ||
| 373 | } | ||
| 374 | }, | ||
| 375 | JsStatement::ConstantDeclaration { | ||
| 376 | name: "$5".to_string(), | ||
| 377 | value: JsExpression::Number(4.0) | ||
| 378 | }, | ||
| 379 | JsStatement::ConstantDeclaration { | ||
| 380 | name: "$6".to_string(), | ||
| 381 | value: JsExpression::Number(5.0) | ||
| 382 | }, | ||
| 383 | JsStatement::ConstantDeclaration { | ||
| 384 | name: "$7".to_string(), | ||
| 385 | value: JsExpression::Operation { | ||
| 386 | operator: Operator::Add, | ||
| 387 | left: Box::new(JsExpression::Identifier("$5".to_string())), | ||
| 388 | right: Box::new(JsExpression::Identifier("$6".to_string())) | ||
| 389 | } | ||
| 390 | }, | ||
| 391 | JsStatement::ConstantDeclaration { | ||
| 392 | name: "$8".to_string(), | ||
| 393 | value: JsExpression::Operation { | ||
| 394 | operator: Operator::Add, | ||
| 395 | left: Box::new(JsExpression::Identifier("$4".to_string())), | ||
| 396 | right: Box::new(JsExpression::Identifier("$7".to_string())) | ||
| 397 | } | ||
| 398 | }, | ||
| 399 | JsStatement::ConstantDeclaration { | ||
| 400 | name: "hi".to_string(), | ||
| 401 | value: JsExpression::Identifier("$8".to_string()) | ||
| 402 | } | ||
| 403 | ] | ||
| 404 | ) | ||
| 405 | } | ||
| 406 | } |
crates/js_ast/src/types.rs+2-1| ... | @@ -10,6 +10,7 @@ pub enum JsExpression { | ... | @@ -10,6 +10,7 @@ pub enum JsExpression { |
| 10 | String(String), | 10 | String(String), |
| 11 | Bool(bool), | 11 | Bool(bool), |
| 12 | Identifier(String), | 12 | Identifier(String), |
| 13 | Raw(String), | ||
| 13 | Array(Vec<JsExpression>), | 14 | Array(Vec<JsExpression>), |
| 14 | Object(HashMap<String, JsExpression>), | 15 | Object(HashMap<String, JsExpression>), |
| 15 | Operation { | 16 | Operation { |
| ... | @@ -22,7 +23,7 @@ pub enum JsExpression { | ... | @@ -22,7 +23,7 @@ pub enum JsExpression { |
| 22 | value: Vec<JsStatement>, | 23 | value: Vec<JsStatement>, |
| 23 | }, | 24 | }, |
| 24 | FunctionCall { | 25 | FunctionCall { |
| 25 | name: String, | 26 | name: Box<JsExpression>, |
| 26 | arguments: Vec<JsExpression>, | 27 | arguments: Vec<JsExpression>, |
| 27 | }, | 28 | }, |
| 28 | } | 29 | } |
crates/js_printer/src/print_expression.rs+9-4| ... | @@ -4,6 +4,7 @@ use crate::print_statement::print_statement; | ... | @@ -4,6 +4,7 @@ use crate::print_statement::print_statement; |
| 4 | 4 | ||
| 5 | pub fn print_expression(statement: &JsExpression) -> String { | 5 | pub fn print_expression(statement: &JsExpression) -> String { |
| 6 | match statement { | 6 | match statement { |
| 7 | JsExpression::Raw(value) => format!("{}", value), | ||
| 7 | JsExpression::Number(value) => format!("{}", value), | 8 | JsExpression::Number(value) => format!("{}", value), |
| 8 | JsExpression::BigInt(value) => format!("{}n", value), | 9 | JsExpression::BigInt(value) => format!("{}n", value), |
| 9 | JsExpression::String(value) => { | 10 | JsExpression::String(value) => { |
| ... | @@ -61,7 +62,11 @@ pub fn print_expression(statement: &JsExpression) -> String { | ... | @@ -61,7 +62,11 @@ pub fn print_expression(statement: &JsExpression) -> String { |
| 61 | ), | 62 | ), |
| 62 | JsExpression::FunctionCall { name, arguments } => format!( | 63 | JsExpression::FunctionCall { name, arguments } => format!( |
| 63 | "{}({})", | 64 | "{}({})", |
| 64 | name, | 65 | match *name.clone() { |
| 66 | JsExpression::Identifier(value) => value.clone(), | ||
| 67 | JsExpression::Raw(value) => value.clone(), | ||
| 68 | _ => panic!("Function call name must be an identifier"), | ||
| 69 | }, | ||
| 65 | arguments | 70 | arguments |
| 66 | .iter() | 71 | .iter() |
| 67 | .map(|x| print_expression(x)) | 72 | .map(|x| print_expression(x)) |
| ... | @@ -238,21 +243,21 @@ mod tests { | ... | @@ -238,21 +243,21 @@ mod tests { |
| 238 | fn function_call() { | 243 | fn function_call() { |
| 239 | assert_eq!( | 244 | assert_eq!( |
| 240 | print_expression(&JsExpression::FunctionCall { | 245 | print_expression(&JsExpression::FunctionCall { |
| 241 | name: "hello".to_string(), | 246 | name: Box::new(JsExpression::Identifier("hello".to_string())), |
| 242 | arguments: vec![], | 247 | arguments: vec![], |
| 243 | }), | 248 | }), |
| 244 | "hello()" | 249 | "hello()" |
| 245 | ); | 250 | ); |
| 246 | assert_eq!( | 251 | assert_eq!( |
| 247 | print_expression(&JsExpression::FunctionCall { | 252 | print_expression(&JsExpression::FunctionCall { |
| 248 | name: "hello".to_string(), | 253 | name: Box::new(JsExpression::Identifier("hello".to_string())), |
| 249 | arguments: vec![JsExpression::Number(1.0)], | 254 | arguments: vec![JsExpression::Number(1.0)], |
| 250 | }), | 255 | }), |
| 251 | "hello(1)" | 256 | "hello(1)" |
| 252 | ); | 257 | ); |
| 253 | assert_eq!( | 258 | assert_eq!( |
| 254 | print_expression(&JsExpression::FunctionCall { | 259 | print_expression(&JsExpression::FunctionCall { |
| 255 | name: "hello".to_string(), | 260 | name: Box::new(JsExpression::Identifier("hello".to_string())), |
| 256 | arguments: vec![JsExpression::Number(1.0), JsExpression::Number(2.0)], | 261 | arguments: vec![JsExpression::Number(1.0), JsExpression::Number(2.0)], |
| 257 | }), | 262 | }), |
| 258 | "hello(1,2)" | 263 | "hello(1,2)" |
crates/tokenizer/src/lib.rs+9-2| ... | @@ -16,6 +16,7 @@ pub enum Token { | ... | @@ -16,6 +16,7 @@ pub enum Token { |
| 16 | Identifier(String), | 16 | Identifier(String), |
| 17 | Number(f64), | 17 | Number(f64), |
| 18 | String(String), | 18 | String(String), |
| 19 | BuiltIn(String), | ||
| 19 | // Comments | 20 | // Comments |
| 20 | Comment(String), | 21 | Comment(String), |
| 21 | DocComment(String), | 22 | DocComment(String), |
| ... | @@ -49,7 +50,7 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> { | ... | @@ -49,7 +50,7 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> { |
| 49 | loop { | 50 | loop { |
| 50 | match chars.next() { | 51 | match chars.next() { |
| 51 | Some((_, c)) => match c { | 52 | Some((_, c)) => match c { |
| 52 | 'a'..='z' | 'A'..='Z' | '_' => { | 53 | 'a'..='z' | 'A'..='Z' | '_' | '@' => { |
| 53 | let mut current_token = String::new(); | 54 | let mut current_token = String::new(); |
| 54 | current_token.push(c); | 55 | current_token.push(c); |
| 55 | loop { | 56 | loop { |
| ... | @@ -77,7 +78,13 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> { | ... | @@ -77,7 +78,13 @@ pub fn tokenize(content: String) -> Result<Vec<Token>> { |
| 77 | "return" => tokens.push(Token::Return), | 78 | "return" => tokens.push(Token::Return), |
| 78 | "true" => tokens.push(Token::True), | 79 | "true" => tokens.push(Token::True), |
| 79 | "false" => tokens.push(Token::False), | 80 | "false" => tokens.push(Token::False), |
| 80 | _ => tokens.push(Token::Identifier(current_token)), | 81 | _ => { |
| 82 | if current_token.starts_with('@') { | ||
| 83 | tokens.push(Token::BuiltIn(current_token)); | ||
| 84 | } else { | ||
| 85 | tokens.push(Token::Identifier(current_token)) | ||
| 86 | } | ||
| 87 | } | ||
| 81 | } | 88 | } |
| 82 | } | 89 | } |
| 83 | '"' => { | 90 | '"' => { |
examples/basics/control.blight+2| ... | @@ -1,3 +1,5 @@ | ... | @@ -1,3 +1,5 @@ |
| 1 | let print: any = @raw("console.log"); | ||
| 2 | |||
| 1 | let x: bool = true; | 3 | let x: bool = true; |
| 2 | 4 | ||
| 3 | if x { | 5 | if x { |
examples/basics/expressions.blight+1| ... | @@ -7,4 +7,5 @@ let x: int = 5 + { | ... | @@ -7,4 +7,5 @@ let x: int = 5 + { |
| 7 | y | 7 | y |
| 8 | }; | 8 | }; |
| 9 | 9 | ||
| 10 | let print: any = @raw("console.log"); | ||
| 10 | print(x); // 10 | 11 | print(x); // 10 |
examples/basics/fib.blight+1| ... | @@ -8,4 +8,5 @@ pub fn fib(n: int) -> int { | ... | @@ -8,4 +8,5 @@ pub fn fib(n: int) -> int { |
| 8 | } | 8 | } |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | let print: any = @raw("console.log"); | ||
| 11 | print(fib(10)); | 12 | print(fib(10)); |
examples/basics/functions.blight+2-1| ... | @@ -4,4 +4,5 @@ fn bingus(x: int, y: int) -> int { | ... | @@ -4,4 +4,5 @@ fn bingus(x: int, y: int) -> int { |
| 4 | 4 | ||
| 5 | let result: int = bingus(10, 25); | 5 | let result: int = bingus(10, 25); |
| 6 | 6 | ||
| 7 | print(result); // built-in function | 7 | let print: any = @raw("console.log"); |
| 8 | print(result); |
examples/basics/operations.blight+3| ... | @@ -1 +1,4 @@ | ... | @@ -1 +1,4 @@ |
| 1 | let a: int = 10 - 2 / 4 * 15; | 1 | let a: int = 10 - 2 / 4 * 15; |
| 2 | |||
| 3 | let print: any = @raw("console.log"); | ||
| 4 | print(a); |
examples/basics/shadowing.blight+2| ... | @@ -1,3 +1,5 @@ | ... | @@ -1,3 +1,5 @@ |
| 1 | let print: any = @raw("console.log"); | ||
| 2 | |||
| 1 | let x: int = 0; | 3 | let x: int = 0; |
| 2 | let x: int = 1; | 4 | let x: int = 1; |
| 3 | print(x); // 1 | 5 | print(x); // 1 |
examples/basics/variables.blight+3| ... | @@ -5,3 +5,6 @@ var z: bigint = 0; | ... | @@ -5,3 +5,6 @@ var z: bigint = 0; |
| 5 | let a: int = 0; | 5 | let a: int = 0; |
| 6 | let b: float = 0; | 6 | let b: float = 0; |
| 7 | let c: bigint = 0; | 7 | let c: bigint = 0; |
| 8 | |||
| 9 | let print: any = @raw("console.log"); | ||
| 10 | print(x); |
examples/basics/weird.blight+1| ... | @@ -9,5 +9,6 @@ fn bingus(y: bool) -> int { | ... | @@ -9,5 +9,6 @@ fn bingus(y: bool) -> int { |
| 9 | x | 9 | x |
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | let print: any = @raw("console.log"); | ||
| 12 | print(bingus(true)); | 13 | print(bingus(true)); |
| 13 | print(bingus(false)); | 14 | print(bingus(false)); |
examples/interop/blight_in_js/main.js+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | import { add, TOP_LEVEL } from "./add.js"; | 1 | import { add, TOP_LEVEL } from "./add.js"; |
| 2 | 2 | ||
| 3 | console.log(add(1, 2)); | 3 | console.log(add(1, 2)); |
| 4 | console.log(TOP_LEVEL) | 4 | console.log(TOP_LEVEL); |