5 files changed, 75 insertions(+), 0 deletions(-)

M src/ast.rs
M src/format.rs
M src/hir.rs
M src/parser.rs
A => tests/programs/test_while1.gt
M src/ast.rs +4 -0
@@ 196,6 196,10 @@ pub enum Expr {
     Loop {
         body: Vec<Expr>,
     },
+    While {
+        cond: Box<Expr>,
+        body: Vec<Expr>,
+    },
     Lambda {
         signature: Signature,
         body: Vec<Expr>,

          
M src/format.rs +7 -0
@@ 228,6 228,13 @@ fn unparse_expr(e: &Expr, indent: usize,
             unparse_exprs(body, indent + 1, out)?;
             writeln!(out, "end")
         }
+        E::While { cond, body } => {
+            write!(out, "while ")?;
+            unparse_expr(cond, 0, out)?;
+            writeln!(out, " do")?;
+            unparse_exprs(body, indent + 1, out)?;
+            writeln!(out, "end")
+        }
         E::Lambda { signature, body } => {
             write!(out, "fn")?;
             unparse_sig(signature, out)?;

          
M src/hir.rs +19 -0
@@ 636,6 636,25 @@ fn lower_expr(expr: &ast::Expr) -> ExprN
             let nbody = lower_exprs(body);
             Loop { body: nbody }
         }
+        E::While { cond, body } => {
+            // While loops just get turned into a Loop containing
+            // if not cond then break end
+            let inverted_cond = E::UniOp {
+                op: UOp::Not,
+                rhs: cond.clone(),
+            };
+            let test = lower_expr(&inverted_cond);
+            let brk = vec![ExprNode::new(Break)];
+            // As per above, we need to always have an "else" end case
+            let else_case = ExprNode::bool(true);
+            let else_exprs = lower_exprs(&[ast::Expr::TupleCtor { body: vec![] }]);
+            let if_expr = ExprNode::new(If {
+                cases: vec![(test, brk), (else_case, else_exprs)],
+            });
+            let mut nbody = lower_exprs(body);
+            nbody.insert(0, if_expr);
+            Loop { body: nbody }
+        }
         E::Lambda { signature, body } => {
             let nsig = lower_signature(signature);
             let nbody = lower_exprs(body);

          
M src/parser.rs +18 -0
@@ 150,6 150,8 @@ pub enum TokenKind {
     Else,
     #[regex("loop[ \n]*")]
     Loop,
+    #[regex("while[ \n]*")]
+    While,
     #[regex("do[ \n]*")]
     Do,
     #[token("return")]

          
@@ 1010,6 1012,7 @@ impl<'input> Parser<'input> {
             T::Let => self.parse_let(),
             T::If => self.parse_if(),
             T::Loop => self.parse_loop(),
+            T::While => self.parse_while_loop(),
             T::Do => self.parse_block(),
             T::Fn => self.parse_lambda(),
             T::Return => self.parse_return(),

          
@@ 1290,6 1293,21 @@ impl<'input> Parser<'input> {
         ast::Expr::Loop { body }
     }
 
+    /// while = "while" expr "do" {expr} "end"
+    fn parse_while_loop(&mut self) -> ast::Expr {
+        self.expect(T::While);
+        let cond = self
+            .parse_expr(0)
+            .expect("While loop condition was not an expression?");
+        self.expect(T::Do);
+        let body = self.parse_exprs();
+        self.expect(T::End);
+        ast::Expr::While {
+            cond: Box::new(cond),
+            body,
+        }
+    }
+
     /// block = "do" {expr} "end"
     fn parse_block(&mut self) -> ast::Expr {
         self.expect(T::Do);

          
A => tests/programs/test_while1.gt +27 -0
@@ 0,0 1,27 @@ 
+-- Format:
+--   status: success
+-- Compile:
+--   status: success
+--
+-- Run:
+--   status: success
+--   stdout: 21
+
+fn main() {} =
+    __println(fib(8))
+end
+
+-- Iterative fib implementation, with a while loop
+fn fib(num I32) I32 =
+    let mut x I32 = 0
+    let mut y I32 = 1
+    let mut z I32 = 0
+    let mut i I32 = 0
+    while i < num do
+        z = x + y
+        x = y
+        y = z
+        i = i + 1
+    end
+    x
+end