diff --git a/examples/hello_world.sb b/examples/hello_world.sb index 22dd2aa..4101960 100644 --- a/examples/hello_world.sb +++ b/examples/hello_world.sb @@ -1,5 +1,7 @@ -fn main(message) { - return message; +fn main() { + foo(2); } -fn fib() {} \ No newline at end of file +fn foo(x) { + return x; +} \ No newline at end of file diff --git a/examples_out/out.js b/examples_out/out.js index e221fee..de93608 100644 --- a/examples_out/out.js +++ b/examples_out/out.js @@ -1,6 +1,7 @@ -function main(message,) { -return message +function main() { +foo(2) } -function fib() { +function foo(x) { +return x } console.log(main()) \ No newline at end of file diff --git a/src/generator/js.rs b/src/generator/js.rs index 2d6e32f..f3139ab 100644 --- a/src/generator/js.rs +++ b/src/generator/js.rs @@ -22,8 +22,9 @@ fn generate_function(func: Function) -> String { let arguments: String = func .arguments .into_iter() - .map(|arg: Variable| format!("{},", arg.name)) - .collect(); + .map(|var| var.name) + .collect::>() + .join(", "); let mut raw = format!("function {N}({A})", N = func.name, A = arguments); raw += " {\n"; @@ -53,11 +54,26 @@ fn generate_expression(expr: Expression) -> String { Expression::Int(val) => val.to_string(), Expression::Variable(val) | Expression::Str(val) => val, Expression::Char(_) => todo!(), - Expression::FunctionCall(_, _) => todo!(), + Expression::FunctionCall(name, e) => generate_function_call(name, e), Expression::Assign(_, _) => todo!(), } } +fn generate_function_call(func: String, args: Vec) -> String { + let formatted_args = args + .into_iter() + .map(|arg| match arg { + Expression::Char(c) => c.to_string(), + Expression::Int(i) => i.to_string(), + Expression::FunctionCall(n, a) => generate_function_call(n, a), + Expression::Str(s) | Expression::Variable(s) => s, + Expression::Assign(_, _) => todo!(), + }) + .collect::>() + .join(","); + format!("{N}({A})\n", N = func, A = formatted_args) +} + fn generate_return(ret: Option) -> String { match ret { Some(expr) => format!("return {}\n", generate_expression(expr)), diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index a693519..42deb64 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -55,6 +55,8 @@ pub enum TokenKind { Colon, /// ";" SemiColon, + /// "," + Comma, /// "=" Assign, /// "==" @@ -200,7 +202,6 @@ impl Cursor<'_> { let position = self.pos(); let token = Token::new(token_kind, len, raw, position); - dbg!(&token); token } diff --git a/src/main.rs b/src/main.rs index 28f8987..7c9c77f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,8 @@ fn main() -> Result<(), String> { let program = parser::parse(tokens, Some(contents))?; + dbg!(":#?", &program); + let output = generator::js::JsGenerator::generate(program); let mut file = std::fs::File::create("examples_out/out.js").expect("create failed"); file.write_all(output.as_bytes()).expect("write failed"); diff --git a/src/parser/mod.rs b/src/parser/mod.rs index c73674a..02075f8 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -13,6 +13,8 @@ mod tests; pub struct Parser { tokens: Peekable>, peeked: Vec, + current: Option, + prev: Option, raw: Option, } @@ -26,6 +28,8 @@ impl Parser { Parser { tokens: tokens_without_whitespace.into_iter().peekable(), peeked: vec![], + current: None, + prev: None, raw: raw, } } @@ -35,11 +39,15 @@ impl Parser { } fn next(&mut self) -> Option { - if self.peeked.is_empty() { + self.prev = self.current.to_owned(); + let item = if self.peeked.is_empty() { self.tokens.next() } else { self.peeked.pop() - } + }; + + self.current = item.to_owned(); + item } fn peek(&mut self) -> Option { @@ -116,6 +124,10 @@ impl Parser { None => format!("Token {:?} not found, found {:?}", token_kind, other), } } + + fn prev(&mut self) -> Option { + self.prev.clone() + } } impl Parser { @@ -172,7 +184,7 @@ impl Parser { let next = self.next().ok_or_else(|| "Expected identifier")?; match next.kind { TokenKind::Identifier(name) => args.push(Variable { name: name }), - _ => return Err(self.make_error(TokenKind::Identifier("".into()), next)), + _ => return Err(self.make_error(TokenKind::Identifier("Argument".into()), next)), } } @@ -181,7 +193,6 @@ impl Parser { fn parse_statement(&mut self) -> Result { let token = self.next_token(); - dbg!(&token); match &token.kind { TokenKind::Keyword(Keyword::Let) => { let state = self.parse_declare(); @@ -195,10 +206,55 @@ impl Parser { Ok(state) } - _ => Err(self.make_error(TokenKind::Unknown, token)), + TokenKind::Identifier(name) => { + if let Ok(_) = self.peek_token(TokenKind::BraceOpen) { + let state = self.parse_function_call()?; + self.match_token(TokenKind::SemiColon)?; + Ok(state) + } else { + let state = Statement::Exp(Expression::Variable(name.into())); + Ok(state) + } + } + + _ => return Err(self.make_error(TokenKind::Unknown, token)), } } + /// Parses a function call from tokens. + /// The name of the function needs to be passed here, because we have already passed it with our cursor. + fn parse_function_call(&mut self) -> Result { + let ident_token = self.prev().ok_or_else(|| "Expected function identifier")?; + let name = match &ident_token.kind { + TokenKind::Identifier(name) => name, + _ => { + panic!(self.make_error(TokenKind::Identifier("Function name".into()), ident_token)) + } + } + .to_string(); + + self.match_token(TokenKind::BraceOpen)?; + + let mut args = Vec::new(); + + loop { + let next = self.peek().ok_or_else(|| "Can not peek token")?; + match &next.kind { + TokenKind::BraceClose => break, + TokenKind::Comma => continue, + TokenKind::Identifier(_) | TokenKind::Literal(_) => { + args.push(self.parse_expression()?) + } + _ => { + return Err(self.make_error(TokenKind::BraceClose, next)); + } + }; + } + + self.match_token(TokenKind::BraceClose)?; + Ok(Statement::Exp(Expression::FunctionCall(name, args))) + } + fn parse_return(&mut self) -> Result { // TODO: Replace unwrap with make_error let peeked = self.peek().unwrap(); diff --git a/src/parser/tests.rs b/src/parser/tests.rs index 17d4402..00b1570 100644 --- a/src/parser/tests.rs +++ b/src/parser/tests.rs @@ -73,3 +73,65 @@ fn test_parse_variable_declaration() { let tree = parse(tokens, Some(raw.to_string())); assert!(tree.is_ok()) } + +#[test] +fn test_parse_function_with_args() { + let raw = " + fn main(foo) { + return foo; + } + "; + let tokens = tokenize(raw); + let tree = parse(tokens, Some(raw.to_string())); + assert!(tree.is_ok()) +} + +#[test] +fn test_parse_function_call() { + let raw = " + fn main(foo) { + foo(); + } + + fn foo() { + foo(2); + } + "; + let tokens = tokenize(raw); + let tree = parse(tokens, Some(raw.to_string())); + assert!(tree.is_ok()) +} + +#[test] +#[ignore] +fn test_parse_return_function_call() { + let raw = " + fn main() { + return fib(); + } + + fn fib() { + return fib(2); + } + "; + let tokens = tokenize(raw); + let tree = parse(tokens, Some(raw.to_string())); + assert!(tree.is_ok()) +} + +#[test] +#[ignore] +fn test_parse_function_call_multiple_arguments() { + let raw = " + fn main() { + fib(1, 2, 3); + } + + fn fib() { + return 2; + } + "; + let tokens = tokenize(raw); + let tree = parse(tokens, Some(raw.to_string())); + assert!(tree.is_ok()) +}