diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 81fc82cd8f72d35ebe5a8f3276fe5690b2e0af88..f1df7714d91474791606233052063fb404b6b907 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -551,7 +551,14 @@ fn compile_expr(
             }
             Ok(ExprResult::Expr(ir::ScheduleExp::Record { fields: result }))
         }
-        parser::Expr::SetOp {
+        parser::Expr::UnaryOp { span: _, op, exp } => {
+            let exp = compile_exp_as_expr(*exp, lexer, macrostab, macros)?;
+            Ok(ExprResult::Expr(ir::ScheduleExp::UnaryOp {
+                op,
+                exp: Box::new(exp),
+            }))
+        }
+        parser::Expr::BinaryOp {
             span: _,
             op,
             lhs,
@@ -559,7 +566,7 @@ fn compile_expr(
         } => {
             let lhs = compile_exp_as_expr(*lhs, lexer, macrostab, macros)?;
             let rhs = compile_exp_as_expr(*rhs, lexer, macrostab, macros)?;
-            Ok(ExprResult::Expr(ir::ScheduleExp::SetOp {
+            Ok(ExprResult::Expr(ir::ScheduleExp::BinaryOp {
                 op,
                 lhs: Box::new(lhs),
                 rhs: Box::new(rhs),
diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index 287cd21a1ff64838942f8f2117937fd52020e75c..aa5926cb93548684664a41bf48d1783597abe888 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -18,7 +18,7 @@ pub enum Pass {
     ForkDimMerge,
     ForkExtend,
     ForkFissionBufferize,
-    ForkFission, 
+    ForkFission,
     ForkFusion,
     ForkGuardElim,
     ForkInterchange,
@@ -134,8 +134,12 @@ pub enum ScheduleExp {
         body: Vec<ScheduleStmt>,
         res: Box<ScheduleExp>,
     },
-    SetOp {
-        op: parser::SetOp,
+    UnaryOp {
+        op: parser::UnaryOp,
+        exp: Box<ScheduleExp>,
+    },
+    BinaryOp {
+        op: parser::BinaryOp,
         lhs: Box<ScheduleExp>,
         rhs: Box<ScheduleExp>,
     },
diff --git a/juno_scheduler/src/lang.l b/juno_scheduler/src/lang.l
index 1f4f8723de4d950920d503a11c9145ad1d15d24a..726264a348666e9033794493ae51c4e95ba361a4 100644
--- a/juno_scheduler/src/lang.l
+++ b/juno_scheduler/src/lang.l
@@ -46,6 +46,10 @@ false           "false"
 \|              "|"
 &               "&"
 
+!               "!"
+\|\|            "||"
+&&              "&&"
+
 panic[\t \n\r]+after "panic_after"
 print[\t \n\r]+iter  "print_iter"
 stop[\t \n\r]+after  "stop_after"
diff --git a/juno_scheduler/src/lang.y b/juno_scheduler/src/lang.y
index 55c82b9dc368e7cade9500fb852c9d48320be7d2..4500f42f5bea948764f9b47bb7bb7b3dc4e82aab 100644
--- a/juno_scheduler/src/lang.y
+++ b/juno_scheduler/src/lang.y
@@ -6,6 +6,9 @@
 %left '\\'
 %left '|'
 %left '&'
+%left '||'
+%left '&&'
+%right '!'
 %left '.' '@'
 
 %%
@@ -27,14 +30,23 @@ Stmt -> Stmt
       { Stmt::ExprStmt { span: $span, exp: $1 } }
   | 'fixpoint' FixpointLimit '{' Schedule '}'
       { Stmt::Fixpoint { span: $span, limit: $2, body: Box::new($4) } }
-  | 'if' Expr '{' Schedule '}'
-      { Stmt::IfThenElse { span: $span, cond: $2, thn: Box::new($4), els: None } }
-  | 'if' Expr '{' Schedule '}' 'else' '{' Schedule '}'
-      { Stmt::IfThenElse { span: $span, cond: $2, thn: Box::new($4), els: Some(Box::new($8)) } }
+  | 'if' Expr '{' Schedule '}' ElseStmt
+      { Stmt::IfThenElse { span: $span, cond: $2, thn: Box::new($4), els: $6 } }
   | MacroDecl
       { Stmt::MacroDecl { span: $span, def: $1 } }
   ;
 
+ElseStmt -> Option<Box<OperationList>>
+  :   { None }
+  | 'else' '{' Schedule '}'
+      { Some(Box::new($3)) }
+  | 'else' 'if' Expr '{' Schedule '}' ElseStmt
+      { Some(Box::new(OperationList::ConsStmt(
+          Stmt::IfThenElse { span: $span, cond: $3, thn: Box::new($5), els: $7 },
+          Box::new(OperationList::NilStmt()),
+        ))) }
+  ;
+
 FixpointLimit -> FixpointLimit
   : { FixpointLimit::NoLimit { span: $span } }
   | 'stop_after' 'INT'
@@ -75,11 +87,17 @@ Expr -> Expr
   | '<' Fields '>'
       { Expr::Record { span: $span, fields: rev($2) } }
   | Expr '\\' Expr
-      { Expr::SetOp { span: $span, op: SetOp::Difference, lhs: Box::new($1), rhs: Box::new($3) } }
+      { Expr::BinaryOp { span: $span, op: BinaryOp::Difference, lhs: Box::new($1), rhs: Box::new($3) } }
   | Expr '|' Expr
-      { Expr::SetOp { span: $span, op: SetOp::Union, lhs: Box::new($1), rhs: Box::new($3) } }
+      { Expr::BinaryOp { span: $span, op: BinaryOp::Union, lhs: Box::new($1), rhs: Box::new($3) } }
   | Expr '&' Expr
-      { Expr::SetOp { span: $span, op: SetOp::Intersection, lhs: Box::new($1), rhs: Box::new($3) } }
+      { Expr::BinaryOp { span: $span, op: BinaryOp::Intersection, lhs: Box::new($1), rhs: Box::new($3) } }
+  | '!' Expr
+      { Expr::UnaryOp { span: $span, op: UnaryOp::Not, exp: Box::new($2) } }
+  | Expr '||' Expr
+      { Expr::BinaryOp { span: $span, op: BinaryOp::Or, lhs: Box::new($1), rhs: Box::new($3) } }
+  | Expr '&&' Expr
+      { Expr::BinaryOp { span: $span, op: BinaryOp::And, lhs: Box::new($1), rhs: Box::new($3) } }
   ;
 
 Args -> Vec<Expr>
@@ -179,10 +197,17 @@ pub enum FixpointLimit {
 }
 
 #[derive(Copy, Clone, Debug)]
-pub enum SetOp {
+pub enum UnaryOp {
+  Not,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum BinaryOp {
   Difference,
   Union,
   Intersection,
+  Or,
+  And,
 }
 
 pub enum Expr {
@@ -195,7 +220,8 @@ pub enum Expr {
   Field       { span: Span, lhs: Box<Expr>, field: Span },
   BlockExpr   { span: Span, body: Box<OperationList> },
   Record      { span: Span, fields: Vec<(Span, Expr)> },
-  SetOp       { span: Span, op: SetOp, lhs: Box<Expr>, rhs: Box<Expr> },
+  UnaryOp     { span: Span, op: UnaryOp, exp: Box<Expr> },
+  BinaryOp    { span: Span, op: BinaryOp, lhs: Box<Expr>, rhs: Box<Expr> },
   Tuple       { span: Span, exps: Vec<Expr> },
   TupleField  { span: Span, lhs: Box<Expr>, field: Span },
 }
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 97bec5445eeeb9e9f849ef129b8ce9cf8c242798..ce558d558dceeef53f736c632176aa7fa5287a5a 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -281,6 +281,13 @@ impl ModuleSelection {
     }
 }
 
+#[derive(Debug, Clone)]
+pub enum SetOp {
+    Difference,
+    Union,
+    Intersection,
+}
+
 #[derive(Debug, Clone)]
 pub enum Value {
     Label {
@@ -303,7 +310,7 @@ pub enum Value {
         selection: Vec<Value>,
     },
     SetOp {
-        op: parser::SetOp,
+        op: SetOp,
         lhs: Box<Value>,
         rhs: Box<Value>,
     },
@@ -364,9 +371,9 @@ impl Value {
                 let lhs = lhs.as_selection(pm, funcs)?;
                 let rhs = rhs.as_selection(pm, funcs)?;
                 let res = match op {
-                    parser::SetOp::Union => lhs.union(rhs),
-                    parser::SetOp::Intersection => lhs.intersection(rhs),
-                    parser::SetOp::Difference => lhs.difference(rhs),
+                    SetOp::Union => lhs.union(rhs),
+                    SetOp::Intersection => lhs.intersection(rhs),
+                    SetOp::Difference => lhs.difference(rhs),
                 };
                 selection.add(res);
             }
@@ -1301,17 +1308,62 @@ fn interp_expr(
         ScheduleExp::Integer { val } => Ok((Value::Integer { val: *val }, false)),
         ScheduleExp::Boolean { val } => Ok((Value::Boolean { val: *val }, false)),
         ScheduleExp::String { val } => Ok((Value::String { val: val.clone() }, false)),
-        ScheduleExp::SetOp { op, lhs, rhs } => {
+        ScheduleExp::UnaryOp { op, exp } => {
+            let (exp, modified) = interp_expr(pm, exp, stringtab, env, functions)?;
+            match op {
+                parser::UnaryOp::Not => {
+                    let Value::Boolean { val } = exp else {
+                        return Err(SchedulerError::SemanticError(
+                            "Not can only be applied to boolean values".to_string(),
+                        ));
+                    };
+                    Ok((Value::Boolean { val: !val }, modified))
+                }
+            }
+        }
+        ScheduleExp::BinaryOp { op, lhs, rhs } => {
             let (lhs, lhs_mod) = interp_expr(pm, lhs, stringtab, env, functions)?;
             let (rhs, rhs_mod) = interp_expr(pm, rhs, stringtab, env, functions)?;
-            Ok((
-                Value::SetOp {
-                    op: *op,
-                    lhs: Box::new(lhs),
-                    rhs: Box::new(rhs),
-                },
-                lhs_mod || rhs_mod,
-            ))
+            match op {
+                parser::BinaryOp::Difference
+                | parser::BinaryOp::Union
+                | parser::BinaryOp::Intersection => Ok((
+                    Value::SetOp {
+                        op: match op {
+                            parser::BinaryOp::Difference => SetOp::Difference,
+                            parser::BinaryOp::Union => SetOp::Union,
+                            parser::BinaryOp::Intersection => SetOp::Intersection,
+                            _ => panic!(),
+                        },
+                        lhs: Box::new(lhs),
+                        rhs: Box::new(rhs),
+                    },
+                    lhs_mod || rhs_mod,
+                )),
+                parser::BinaryOp::Or | parser::BinaryOp::And => {
+                    let (Value::Boolean { val: lhs }, Value::Boolean { val: rhs }) = (lhs, rhs)
+                    else {
+                        return Err(SchedulerError::SemanticError(format!(
+                            "{} can only be applied to boolean values",
+                            match op {
+                                parser::BinaryOp::Or => "or",
+                                parser::BinaryOp::And => "and",
+                                _ => panic!(),
+                            },
+                        )));
+                    };
+                    Ok((
+                        Value::Boolean {
+                            val: match op {
+                                parser::BinaryOp::Or => lhs || rhs,
+                                parser::BinaryOp::And => lhs && rhs,
+                                _ => panic!(),
+                            },
+                        },
+                        lhs_mod || rhs_mod,
+                    ))
+                }
+            }
         }
         ScheduleExp::Field { collect, field } => {
             let (lhs, changed) = interp_expr(pm, collect, stringtab, env, functions)?;
@@ -2780,7 +2832,7 @@ fn run_pass(
             }
             pm.delete_gravestones();
             pm.clear_analyses();
-        },
+        }
         Pass::ForkFission => {
             assert!(args.len() == 1);
 
@@ -2794,7 +2846,6 @@ fn run_pass(
                 });
             };
 
-
             pm.make_fork_join_maps();
             pm.make_loops();
             pm.make_reduce_cycles();
@@ -2817,18 +2868,18 @@ fn run_pass(
 
             let fork_label = fork_labels[0].label;
 
-            for ((((func, fork_join_map), loop_tree), reduce_cycles), nodes_in_fork_joins,
-            ) in build_selection(pm, selection, false)
-                .into_iter()
-                .zip(fork_join_maps.iter())
-                .zip(loops.iter())
-                .zip(reduce_cycles.iter())
-                .zip(nodes_in_fork_joins.iter())
+            for ((((func, fork_join_map), loop_tree), reduce_cycles), nodes_in_fork_joins) in
+                build_selection(pm, selection, false)
+                    .into_iter()
+                    .zip(fork_join_maps.iter())
+                    .zip(loops.iter())
+                    .zip(reduce_cycles.iter())
+                    .zip(nodes_in_fork_joins.iter())
             {
                 let Some(mut func) = func else {
                     continue;
                 };
-                
+
                 let new_forks = fork_fission(
                     &mut func,
                     nodes_in_fork_joins,
@@ -2837,12 +2888,12 @@ fn run_pass(
                     fork_join_map,
                     fork_label,
                 );
-                
+
                 let created_fork_joins = &mut created_fork_joins[func.func_id().idx()];
 
                 for f in new_forks {
                     created_fork_joins.push(f);
-                };
+                }
 
                 changed |= func.modified();
             }
@@ -2899,8 +2950,7 @@ fn run_pass(
             result = Value::Record {
                 fields: new_fork_joins,
             };
-
-        },
+        }
         Pass::ForkFissionBufferize => {
             assert!(args.len() == 1 || args.len() == 2);
             let Some(Value::Label {