From d9df6df402f3b355af474faccaa13edd6a3be0be Mon Sep 17 00:00:00 2001 From: fef Date: Fri, 29 Jul 2022 05:11:53 +0200 Subject: [PATCH] add loops --- src/ast/mod.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++++-- src/ast/tree.rs | 30 +++++++++++++++++++++++ src/lex/mod.rs | 16 +++++++++++- src/lex/token.rs | 6 +++++ test.gaybuild | 20 +++++++++++++++ 5 files changed, 132 insertions(+), 3 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 2b08790..85e1558 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -16,6 +16,7 @@ enum Scope { DepList, SourceList, Function, + Loop, } struct Parser { @@ -79,19 +80,33 @@ impl Parser { fn parse_stmt(&mut self) -> Result { let token = self.lexer.peek_or_err()?; match token.kind { + token::Kind::BreakKeyword => self.parse_break_stmt(), token::Kind::DependKeyword => self.parse_depend_stmt(), token::Kind::FnKeyword => self.parse_fn(false), + token::Kind::ForKeyword => self.parse_for_stmt(), token::Kind::IfKeyword => self.parse_if_stmt(), token::Kind::ReturnKeyword => self.parse_return_stmt(), token::Kind::SetKeyword => self.parse_set_stmt(), token::Kind::SourceKeyword => self.parse_source_stmt(), token::Kind::TargetKeyword => self.parse_target_stmt(), token::Kind::TypeKeyword => self.parse_type_stmt(), + token::Kind::WhileKeyword => self.parse_while_stmt(), k if k.is_start_of_expr() => self.parse_expr_stmt(), _ => self.syntax_error(format!("Unexpected token {}", token), &token), } } + /// ```notrust + /// BreakStatement + /// : "break" ";" + /// ``` + fn parse_break_stmt(&mut self) -> Result { + self.lexer.expect_kind(token::Kind::BreakKeyword)?; + self.assert_scope(Scope::Loop)?; + self.lexer.expect_kind(token::Kind::Semi)?; + Ok(tree::Node::Break) + } + /// ```notrust /// BlockStatement /// : "{" [ StatementList ] "}" @@ -182,6 +197,50 @@ impl Parser { }) } + /// ```notrust + /// WhileStatement + /// : "while" "(" Expression ")" BlockStatement + /// ``` + fn parse_while_stmt(&mut self) -> Result { + self.lexer.expect_kind(token::Kind::WhileKeyword)?; + self.scope.push(Scope::Loop); + self.lexer.expect_kind(token::Kind::OParen)?; + let condition = self.parse_expr(&[token::Kind::CParen])?; + let block = self.parse_block_stmt()?; + self.scope.pop(); + Ok(tree::Node::WhileStmt { + condition: Box::new(condition), + block: Box::new(block), + }) + } + + /// ```notrust + /// ForStatement + /// : "for" "(" [ Expression ] ";" [ Expression ] ";" [ Expression ] ")" BlockStatement + /// ``` + fn parse_for_stmt(&mut self) -> Result { + self.lexer.expect_kind(token::Kind::ForKeyword)?; + self.scope.push(Scope::Loop); + self.lexer.expect_kind(token::Kind::OParen)?; + let terminators = [token::Kind::Semi, token::Kind::Semi, token::Kind::CParen]; + let mut exprs = Vec::new(); + for i in 0..3 { + exprs.push(if self.lexer.next_if(terminators[i]) { + None + } else { + Some(Box::new(self.parse_expr(&terminators[i..=i])?)) + }); + } + let body = self.parse_block_stmt()?; + self.scope.pop(); + Ok(tree::Node::ForStmt { + exec: exprs.pop().unwrap(), + condition: exprs.pop().unwrap(), + setup: exprs.pop().unwrap(), + body: Box::new(body), + }) + } + /// ```notrust /// SetStatement /// : "set" AssignmentExpression ";" @@ -213,8 +272,8 @@ impl Parser { /// : "type" Expression ";" /// ``` fn parse_type_stmt(&mut self) -> Result { - self.assert_scope(Scope::Target)?; self.lexer.expect_kind(token::Kind::TypeKeyword)?; + self.assert_scope(Scope::Target)?; let expr = self.parse_expr(&[token::Kind::Semi])?; Ok(tree::Node::TypeExpr(Box::new(expr))) } @@ -224,8 +283,8 @@ impl Parser { /// : "source" Expression ";" /// ``` fn parse_source_stmt(&mut self) -> Result { - self.assert_scope(Scope::Target)?; self.lexer.expect_kind(token::Kind::SourceKeyword)?; + self.assert_scope(Scope::Target)?; self.scope.push(Scope::SourceList); let source = self.parse_expr(&[token::Kind::Semi])?; self.scope.pop(); diff --git a/src/ast/tree.rs b/src/ast/tree.rs index 9bf0069..a6d73e1 100644 --- a/src/ast/tree.rs +++ b/src/ast/tree.rs @@ -8,6 +8,7 @@ use crate::lex::token::Token; #[derive(Debug)] pub enum Node { + Break, Ident(String), DepList(Box), SourceList(Box), @@ -37,6 +38,16 @@ pub enum Node { else_block: Option>, }, ReturnStmt(Box), + ForStmt { + setup: Option>, + condition: Option>, + exec: Option>, + body: Box, + }, + WhileStmt { + condition: Box, + block: Box, + }, UnaryExpr { op: Operator, node: Box, @@ -146,6 +157,22 @@ impl Node { eb.visit(cb, depth); } } + Node::ForStmt { setup, condition, exec, body } => { + if let Some(s) = setup { + s.visit(cb, depth); + } + if let Some(c) = condition { + c.visit(cb, depth); + } + if let Some(e) = exec { + e.visit(cb, depth); + } + body.visit(cb, depth); + } + Node::WhileStmt { condition, block } => { + condition.visit(cb, depth); + block.visit(cb, depth); + } Node::ReturnStmt(node) => node.visit(cb, depth), Node::UnaryExpr { op, node } => node.visit(cb, depth), Node::BinaryExpr { op, lhs, rhs } => { @@ -178,6 +205,7 @@ impl fmt::Display for Node { f, "{}", match self { + Node::Break => "", Node::Ident(name) => name.as_str(), Node::Bool(b) => { tmp = format!("{}", b); @@ -201,6 +229,8 @@ impl fmt::Display for Node { then_block, else_block, } => "", + Node::ForStmt { setup, condition, exec, body } => "", + Node::WhileStmt { condition, block } => "", Node::UnaryExpr { op, node } => op.raw(), Node::BinaryExpr { op, lhs, rhs } => op.raw(), Node::TypeExpr(_) => "type", diff --git a/src/lex/mod.rs b/src/lex/mod.rs index fb66974..f4d31cf 100644 --- a/src/lex/mod.rs +++ b/src/lex/mod.rs @@ -35,11 +35,13 @@ const fn kw(raw: &'static str, kind: token::Kind) -> KeywordMap { KeywordMap { raw, kind } } -static KEYWORDS: [KeywordMap; 12] = [ +static KEYWORDS: [KeywordMap; 15] = [ + kw("break", token::Kind::BreakKeyword), kw("depend", token::Kind::DependKeyword), kw("else", token::Kind::ElseKeyword), kw("false", token::Kind::FalseKeyword), kw("fn", token::Kind::FnKeyword), + kw("for", token::Kind::ForKeyword), kw("if", token::Kind::IfKeyword), kw("include", token::Kind::IncludeKeyword), kw("return", token::Kind::ReturnKeyword), @@ -48,6 +50,7 @@ static KEYWORDS: [KeywordMap; 12] = [ kw("target", token::Kind::TargetKeyword), kw("true", token::Kind::TrueKeyword), kw("type", token::Kind::TypeKeyword), + kw("while", token::Kind::WhileKeyword), ]; impl Iterator for Lexer { @@ -206,6 +209,17 @@ impl Lexer { } } + pub fn next_if(&mut self, kind: token::Kind) -> bool { + if let Some(Ok(token)) = self.peek() { + if token.kind == kind { + self.next(); + return true; + } + } + + false + } + pub fn require_next(&mut self) -> Result { match self.next() { Some(t) => t, diff --git a/src/lex/token.rs b/src/lex/token.rs index c8ecd13..e6bf454 100644 --- a/src/lex/token.rs +++ b/src/lex/token.rs @@ -68,10 +68,12 @@ pub enum Kind { Percent, PercentEq, + BreakKeyword, DependKeyword, ElseKeyword, FalseKeyword, FnKeyword, + ForKeyword, IfKeyword, IncludeKeyword, ReturnKeyword, @@ -80,6 +82,7 @@ pub enum Kind { TargetKeyword, TrueKeyword, TypeKeyword, + WhileKeyword, StringLiteral, IntLiteral, @@ -215,10 +218,12 @@ impl fmt::Display for Kind { Kind::Percent => "percent", Kind::PercentEq => "percenteq", + Kind::BreakKeyword => "keyword", Kind::DependKeyword => "keyword", Kind::ElseKeyword => "keyword", Kind::FalseKeyword => "keyword", Kind::FnKeyword => "keyword", + Kind::ForKeyword => "keyword", Kind::IfKeyword => "keyword", Kind::IncludeKeyword => "keyword", Kind::ReturnKeyword => "keyword", @@ -227,6 +232,7 @@ impl fmt::Display for Kind { Kind::TargetKeyword => "keyword", Kind::TrueKeyword => "keyword", Kind::TypeKeyword => "keyword", + Kind::WhileKeyword => "keyword", Kind::StringLiteral => "string", Kind::IntLiteral => "int", diff --git a/test.gaybuild b/test.gaybuild index 1826d9e..e5a0a67 100644 --- a/test.gaybuild +++ b/test.gaybuild @@ -16,18 +16,38 @@ fn hello(p1, p2) { # a target is a single component. # targets can depend on other targets. target kern { + # if statements work just like you expect them to ... if (2 > 3) { print("2 > 3"); } else { print("2 <= 3"); } + # ... so do while loops ... + x = 0; + while (true) { + print(x); + x += 1; + if (x == 10) { + break; + } + } + # ... and even for loops + for (x = 0; x < 10; x += 1) { + print(x); + } # functions are first-class citizens; they can be defined as inline # anonymous functions that you can treat like any other value fn_with_callback(fn(a, b) { return a + b; }); (fn(a, b) { return a + b; })(1, 2); + # math works exactly as you would expect it to owo = 1 + 2 * 3 * 4 - 5; + # and, naturally, there are also all the bitwise goodies + bitmask = 1 << 2 | 1 << 8 ^ get_bit() & 1; + # this is exactly equal to the above, but with added + # parentheses to make the operator precedences clear + bitmask = (1 << 2) | ((1 << 8) ^ (get_bit() & 1)); # the type keyword defines whether the target is supposed to be # compiled into an executable binary (exe) or a library (lib).