Browse Source

Add function arguments

github-actions
Garrit Franke 3 years ago
parent
commit
8186b0c56e
  1. 8
      examples/hello_world.sb
  2. 7
      examples_out/out.js
  3. 22
      src/generator/js.rs
  4. 3
      src/lexer/mod.rs
  5. 2
      src/main.rs
  6. 66
      src/parser/mod.rs
  7. 62
      src/parser/tests.rs

8
examples/hello_world.sb

@ -1,5 +1,7 @@
fn main(message) { fn main() {
return message; foo(2);
} }
fn fib() {} fn foo(x) {
return x;
}

7
examples_out/out.js

@ -1,6 +1,7 @@
function main(message,) { function main() {
return message foo(2)
} }
function fib() { function foo(x) {
return x
} }
console.log(main()) console.log(main())

22
src/generator/js.rs

@ -22,8 +22,9 @@ fn generate_function(func: Function) -> String {
let arguments: String = func let arguments: String = func
.arguments .arguments
.into_iter() .into_iter()
.map(|arg: Variable| format!("{},", arg.name)) .map(|var| var.name)
.collect(); .collect::<Vec<String>>()
.join(", ");
let mut raw = format!("function {N}({A})", N = func.name, A = arguments); let mut raw = format!("function {N}({A})", N = func.name, A = arguments);
raw += " {\n"; raw += " {\n";
@ -53,11 +54,26 @@ fn generate_expression(expr: Expression) -> String {
Expression::Int(val) => val.to_string(), Expression::Int(val) => val.to_string(),
Expression::Variable(val) | Expression::Str(val) => val, Expression::Variable(val) | Expression::Str(val) => val,
Expression::Char(_) => todo!(), Expression::Char(_) => todo!(),
Expression::FunctionCall(_, _) => todo!(), Expression::FunctionCall(name, e) => generate_function_call(name, e),
Expression::Assign(_, _) => todo!(), Expression::Assign(_, _) => todo!(),
} }
} }
fn generate_function_call(func: String, args: Vec<Expression>) -> 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::<Vec<String>>()
.join(",");
format!("{N}({A})\n", N = func, A = formatted_args)
}
fn generate_return(ret: Option<Expression>) -> String { fn generate_return(ret: Option<Expression>) -> String {
match ret { match ret {
Some(expr) => format!("return {}\n", generate_expression(expr)), Some(expr) => format!("return {}\n", generate_expression(expr)),

3
src/lexer/mod.rs

@ -55,6 +55,8 @@ pub enum TokenKind {
Colon, Colon,
/// ";" /// ";"
SemiColon, SemiColon,
/// ","
Comma,
/// "=" /// "="
Assign, Assign,
/// "==" /// "=="
@ -200,7 +202,6 @@ impl Cursor<'_> {
let position = self.pos(); let position = self.pos();
let token = Token::new(token_kind, len, raw, position); let token = Token::new(token_kind, len, raw, position);
dbg!(&token);
token token
} }

2
src/main.rs

@ -19,6 +19,8 @@ fn main() -> Result<(), String> {
let program = parser::parse(tokens, Some(contents))?; let program = parser::parse(tokens, Some(contents))?;
dbg!(":#?", &program);
let output = generator::js::JsGenerator::generate(program); let output = generator::js::JsGenerator::generate(program);
let mut file = std::fs::File::create("examples_out/out.js").expect("create failed"); let mut file = std::fs::File::create("examples_out/out.js").expect("create failed");
file.write_all(output.as_bytes()).expect("write failed"); file.write_all(output.as_bytes()).expect("write failed");

66
src/parser/mod.rs

@ -13,6 +13,8 @@ mod tests;
pub struct Parser { pub struct Parser {
tokens: Peekable<IntoIter<Token>>, tokens: Peekable<IntoIter<Token>>,
peeked: Vec<Token>, peeked: Vec<Token>,
current: Option<Token>,
prev: Option<Token>,
raw: Option<String>, raw: Option<String>,
} }
@ -26,6 +28,8 @@ impl Parser {
Parser { Parser {
tokens: tokens_without_whitespace.into_iter().peekable(), tokens: tokens_without_whitespace.into_iter().peekable(),
peeked: vec![], peeked: vec![],
current: None,
prev: None,
raw: raw, raw: raw,
} }
} }
@ -35,11 +39,15 @@ impl Parser {
} }
fn next(&mut self) -> Option<Token> { fn next(&mut self) -> Option<Token> {
if self.peeked.is_empty() { self.prev = self.current.to_owned();
let item = if self.peeked.is_empty() {
self.tokens.next() self.tokens.next()
} else { } else {
self.peeked.pop() self.peeked.pop()
} };
self.current = item.to_owned();
item
} }
fn peek(&mut self) -> Option<Token> { fn peek(&mut self) -> Option<Token> {
@ -116,6 +124,10 @@ impl Parser {
None => format!("Token {:?} not found, found {:?}", token_kind, other), None => format!("Token {:?} not found, found {:?}", token_kind, other),
} }
} }
fn prev(&mut self) -> Option<Token> {
self.prev.clone()
}
} }
impl Parser { impl Parser {
@ -172,7 +184,7 @@ impl Parser {
let next = self.next().ok_or_else(|| "Expected identifier")?; let next = self.next().ok_or_else(|| "Expected identifier")?;
match next.kind { match next.kind {
TokenKind::Identifier(name) => args.push(Variable { name: name }), 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<Statement, String> { fn parse_statement(&mut self) -> Result<Statement, String> {
let token = self.next_token(); let token = self.next_token();
dbg!(&token);
match &token.kind { match &token.kind {
TokenKind::Keyword(Keyword::Let) => { TokenKind::Keyword(Keyword::Let) => {
let state = self.parse_declare(); let state = self.parse_declare();
@ -195,10 +206,55 @@ impl Parser {
Ok(state) 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<Statement, String> {
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<Statement, String> { fn parse_return(&mut self) -> Result<Statement, String> {
// TODO: Replace unwrap with make_error // TODO: Replace unwrap with make_error
let peeked = self.peek().unwrap(); let peeked = self.peek().unwrap();

62
src/parser/tests.rs

@ -73,3 +73,65 @@ fn test_parse_variable_declaration() {
let tree = parse(tokens, Some(raw.to_string())); let tree = parse(tokens, Some(raw.to_string()));
assert!(tree.is_ok()) 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())
}

Loading…
Cancel
Save