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) {
return message;
fn main() {
foo(2);
}
fn fib() {}
fn foo(x) {
return x;
}

7
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())

22
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::<Vec<String>>()
.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<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 {
match ret {
Some(expr) => format!("return {}\n", generate_expression(expr)),

3
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
}

2
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");

66
src/parser/mod.rs

@ -13,6 +13,8 @@ mod tests;
pub struct Parser {
tokens: Peekable<IntoIter<Token>>,
peeked: Vec<Token>,
current: Option<Token>,
prev: Option<Token>,
raw: Option<String>,
}
@ -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<Token> {
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<Token> {
@ -116,6 +124,10 @@ impl Parser {
None => format!("Token {:?} not found, found {:?}", token_kind, other),
}
}
fn prev(&mut self) -> Option<Token> {
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<Statement, String> {
let token = self.next_token();
dbg!(&token);
match &token.kind {
TokenKind::Keyword(Keyword::Let) => {
let state = self.parse_declare();
@ -195,8 +206,53 @@ 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<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> {

62
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())
}

Loading…
Cancel
Save