diff --git a/src/DynareBison.yy b/src/DynareBison.yy
index 60260678c638fee083fc09d81a81e08668a117e5..4aed10f57910902da4d599927f15c28a9ae95d61 100644
--- a/src/DynareBison.yy
+++ b/src/DynareBison.yy
@@ -215,7 +215,7 @@ str_tolower(string s)
 %token ENDVAL_STEADY STEADY_SOLVE_ALGO STEADY_MAXIT STEADY_TOLF STEADY_TOLX STEADY_MARKOWITZ
 %token HOMOTOPY_MAX_COMPLETION_SHARE HOMOTOPY_MIN_STEP_SIZE HOMOTOPY_INITIAL_STEP_SIZE HOMOTOPY_STEP_SIZE_INCREASE_SUCCESS_COUNT
 %token HOMOTOPY_LINEARIZATION_FALLBACK HOMOTOPY_MARGINAL_LINEARIZATION_FALLBACK HOMOTOPY_EXCLUDE_VAREXO FROM_INITVAL_TO_ENDVAL
-%token STATIC_MFS RELATIVE_TO_INITVAL MATCHED_IRFS MATCHED_IRFS_WEIGHTS WEIGHTS
+%token STATIC_MFS RELATIVE_TO_INITVAL MATCHED_IRFS MATCHED_IRFS_WEIGHTS WEIGHTS PERPENDICULAR
 
 %token <vector<string>> SYMBOL_VEC
 
@@ -1011,6 +1011,14 @@ equation : hand_side EQUAL hand_side ';'
            { $$ = driver.add_model_equal($4, $6, $2); }
          | '[' tag_pair_list ']' hand_side ';'
            { $$ = driver.add_model_equal_with_zero_rhs($4, $2); }
+         | hand_side EQUAL hand_side PERPENDICULAR hand_side ';'
+           { $$ = driver.add_model_equal($1, $3, {}, $5); }
+         | hand_side PERPENDICULAR hand_side ';'
+           { $$ = driver.add_model_equal_with_zero_rhs($1, {}, $3); }
+         | '[' tag_pair_list ']' hand_side EQUAL hand_side PERPENDICULAR hand_side ';'
+           { $$ = driver.add_model_equal($4, $6, $2, $8); }
+         | '[' tag_pair_list ']' hand_side PERPENDICULAR hand_side ';'
+           { $$ = driver.add_model_equal_with_zero_rhs($4, $2, $6); }
          ;
 
 tag_pair_list : tag_pair_list COMMA tag_pair
diff --git a/src/DynareFlex.ll b/src/DynareFlex.ll
index 618bf379c02cb32ed799fb05feb7f384dfbdbf63..13d14405ef323eca3dab328106bd17456acc988b 100644
--- a/src/DynareFlex.ll
+++ b/src/DynareFlex.ll
@@ -1016,6 +1016,8 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
 <DYNARE_STATEMENT,DYNARE_BLOCK>nan {return token::NAN_CONSTANT;}
 <DYNARE_STATEMENT,DYNARE_BLOCK>inf {return token::INF_CONSTANT;}
 <DYNARE_STATEMENT,DYNARE_BLOCK>constants {return token::CONSTANTS;}
+<DYNARE_BLOCK>⟂ {return token::PERPENDICULAR;}
+<DYNARE_BLOCK>_\|_ {return token::PERPENDICULAR;}
 
  /* options for GSA module by Marco Ratto */
 <DYNARE_STATEMENT>identification {return token::IDENTIFICATION;}
diff --git a/src/ParsingDriver.cc b/src/ParsingDriver.cc
index c8733c18dd3dcd4233ef1e7f221ec429fa3c9a8c..7f351fdac309d52af756b55f56d4b7c8af901bed 100644
--- a/src/ParsingDriver.cc
+++ b/src/ParsingDriver.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2003-2023 Dynare Team
+ * Copyright © 2003-2024 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -2626,7 +2626,8 @@ ParsingDriver::extended_path()
 }
 
 expr_t
-ParsingDriver::add_model_equal(expr_t arg1, expr_t arg2, map<string, string> eq_tags)
+ParsingDriver::add_model_equal(expr_t arg1, expr_t arg2, map<string, string> eq_tags,
+                               expr_t complementarity_condition)
 {
   expr_t id = model_tree->AddEqual(arg1, arg2);
 
@@ -2634,6 +2635,28 @@ ParsingDriver::add_model_equal(expr_t arg1, expr_t arg2, map<string, string> eq_
     if (key == "endogenous")
       declare_or_change_type(SymbolType::endogenous, value);
 
+  if (eq_tags.contains("mcp"))
+    {
+      if (complementarity_condition)
+        error("Can't have both an 'mcp' tag and a complementarity condition after the "
+              "perpendicular symbol");
+      else
+        warning("Specifying complementarity conditions with the 'mcp' tag is obsolete. Please "
+                "consider switching to the new syntax using the perpendicular symbol.");
+    }
+
+  if (complementarity_condition)
+    {
+      if (auto bcomp = dynamic_cast<BinaryOpNode*>(complementarity_condition);
+          !(bcomp
+            && (bcomp->op_code == BinaryOpcode::less || bcomp->op_code == BinaryOpcode::lessEqual
+                || bcomp->op_code == BinaryOpcode::greater
+                || bcomp->op_code == BinaryOpcode::greaterEqual)))
+        error("The complementarity constraint must be an inequality.");
+
+      eq_tags.emplace("mcp", complementarity_condition->toString());
+    }
+
   if (eq_tags.contains("static"))
     {
       // If the equation is tagged [static]
@@ -2723,9 +2746,10 @@ ParsingDriver::add_model_equal(expr_t arg1, expr_t arg2, map<string, string> eq_
 }
 
 expr_t
-ParsingDriver::add_model_equal_with_zero_rhs(expr_t arg, map<string, string> eq_tags)
+ParsingDriver::add_model_equal_with_zero_rhs(expr_t arg, map<string, string> eq_tags,
+                                             expr_t complementarity_condition)
 {
-  return add_model_equal(arg, model_tree->Zero, move(eq_tags));
+  return add_model_equal(arg, model_tree->Zero, move(eq_tags), complementarity_condition);
 }
 
 void
diff --git a/src/ParsingDriver.hh b/src/ParsingDriver.hh
index 724aabd86f1d485a403e1ce94ca208ecaf901e38..a664d1c52e5464aab64efae1e573b0f2c06401e7 100644
--- a/src/ParsingDriver.hh
+++ b/src/ParsingDriver.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2003-2023 Dynare Team
+ * Copyright © 2003-2024 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -748,9 +748,11 @@ public:
   //! Extended path
   void extended_path();
   //! Writes token "arg1=arg2" to model tree
-  expr_t add_model_equal(expr_t arg1, expr_t arg2, map<string, string> eq_tags);
+  expr_t add_model_equal(expr_t arg1, expr_t arg2, map<string, string> eq_tags,
+                         expr_t complementarity_condition = nullptr);
   //! Writes token "arg=0" to model tree
-  expr_t add_model_equal_with_zero_rhs(expr_t arg, map<string, string> eq_tags);
+  expr_t add_model_equal_with_zero_rhs(expr_t arg, map<string, string> eq_tags,
+                                       expr_t complementarity_condition = nullptr);
   //! Writes token "arg1+arg2" to model tree
   expr_t add_plus(expr_t arg1, expr_t arg2);
   //! Writes token "arg1-arg2" to model tree