Browse Source

WIP: Add for loops

github-actions
Garrit Franke 3 years ago
parent
commit
031e378538
  1. 8
      examples/playground.sb
  2. 1
      src/generator/c.rs
  3. 24
      src/generator/js.rs
  4. 4
      src/lexer/mod.rs
  5. 1
      src/parser/node_type.rs
  6. 12
      src/parser/rules.rs
  7. 34
      src/parser/tests.rs

8
examples/playground.sb

@ -1,6 +1,8 @@
fn main() {
let x: string
let x = ["Foo", "Bar"]
x = "Hello World"
_printf(x)
for i in x {
_printf(x[i])
_printf("\n")
}
}

1
src/generator/c.rs

@ -114,6 +114,7 @@ fn generate_statement(statement: Statement) -> String {
Statement::Assign(name, state) => generate_assign(*name, *state),
Statement::Block(_) => generate_block(statement),
Statement::While(expr, body) => generate_while_loop(expr, *body),
Statement::For(ident, expr, body) => todo!(),
};
format!("{}\n", state)

24
src/generator/js.rs

@ -49,14 +49,19 @@ fn generate_function(func: Function) -> String {
.join(", ");
let mut raw = format!("function {N}({A})", N = func.name, A = arguments);
raw += &generate_block(func.body);
raw += &generate_block(func.body, None);
raw
}
fn generate_block(block: Statement) -> String {
/// prepend is used to pass optional statements, that will be put in front of the regular block
/// Currently used in for statements, to declare local variables
fn generate_block(block: Statement, prepend: Option<Statement>) -> String {
let mut generated = String::from("{\n");
// TODO: Prepend statements
let statements = match block {
Statement::Block(blk) => blk,
_ => panic!("Block body should be of type Statement::Block"),
@ -80,8 +85,9 @@ fn generate_statement(statement: Statement) -> String {
generate_conditional(expr, *if_state, else_state.map(|x| *x))
}
Statement::Assign(name, state) => generate_assign(*name, *state),
Statement::Block(_) => generate_block(statement),
Statement::Block(_) => generate_block(statement, None),
Statement::While(expr, body) => generate_while_loop(expr, *body),
Statement::For(ident, expr, body) => generate_for_loop(ident, expr, *body),
};
format!("{};\n", state)
@ -104,7 +110,17 @@ fn generate_while_loop(expr: Expression, body: Statement) -> String {
out_str += &generate_expression(expr);
out_str += ") ";
out_str += &generate_block(body);
out_str += &generate_block(body, None);
out_str
}
fn generate_for_loop(ident: Variable, expr: Expression, body: Statement) -> String {
let expr_string = generate_expression(expr);
let mut out_str = format!("for (let {I} = 0; {I} < {E}.length; {I}++)", I = ident.name, E = expr_string);
// Currently, the index of the current iteration is used, instead of the value itself.
// TODO: generate local variable
out_str += &generate_block(body, None);
out_str
}

4
src/lexer/mod.rs

@ -127,6 +127,8 @@ pub enum Keyword {
Else,
Return,
While,
For,
In,
Break,
Continue,
Function,
@ -331,6 +333,8 @@ impl Cursor<'_> {
c if c == "let" => Keyword::Let,
c if c == "return" => Keyword::Return,
c if c == "while" => Keyword::While,
c if c == "for" => Keyword::For,
c if c == "in" => Keyword::In,
_ => Keyword::Unknown,
}
}

1
src/parser/node_type.rs

@ -71,6 +71,7 @@ pub enum Statement {
Return(Option<Expression>),
If(Expression, Box<Statement>, Option<Box<Statement>>),
While(Expression, Box<Statement>),
For(Variable, Expression, Box<Statement>),
Exp(Expression),
}

12
src/parser/rules.rs

@ -122,6 +122,7 @@ impl Parser {
TokenKind::Keyword(Keyword::Return) => self.parse_return(),
TokenKind::Keyword(Keyword::If) => self.parse_conditional_statement(),
TokenKind::Keyword(Keyword::While) => self.parse_while_loop(),
TokenKind::Keyword(Keyword::For) => self.parse_for_loop(),
TokenKind::Identifier(_) => {
let ident = self.match_identifier()?;
@ -299,6 +300,17 @@ impl Parser {
Ok(Statement::While(expr, Box::new(body)))
}
fn parse_for_loop(&mut self) -> Result<Statement, String> {
self.match_keyword(Keyword::For)?;
let ident = self.match_identifier()?;
self.match_keyword(Keyword::In)?;
let expr = self.parse_expression()?;
let body = self.parse_block()?;
Ok(Statement::For(Variable {name: ident, ty: None}, expr, Box::new(body)))
}
fn parse_conditional_statement(&mut self) -> Result<Statement, String> {
self.match_keyword(Keyword::If)?;
let condition = self.parse_expression()?;

34
src/parser/tests.rs

@ -641,3 +641,37 @@ fn test_late_initializing_variable() {
let tree = parse(tokens, Some(raw.to_string()));
assert!(tree.is_ok());
}
#[test]
fn test_simple_for_loop() {
let raw = "
fn main() {
let x = [1, 2, 3]
for i in x {
_printf(i)
}
}
";
let tokens = tokenize(raw);
let tree = parse(tokens, Some(raw.to_string()));
assert!(tree.is_ok());
}
#[test]
fn test_nested_for_loop() {
let raw = "
fn main() {
let x = [1, 2, 3]
for i in x {
for j in x {
_printf(j)
}
}
}
";
let tokens = tokenize(raw);
let tree = parse(tokens, Some(raw.to_string()));
assert!(tree.is_ok());
}
Loading…
Cancel
Save