diff --git a/doc/15-language-reference.md b/doc/15-language-reference.md
index 76c73d8b0..71bfff057 100644
--- a/doc/15-language-reference.md
+++ b/doc/15-language-reference.md
@@ -668,6 +668,20 @@ This example prints the log message "Taking the 'true' branch" and the `a` varia
The value of an if/else construct is null if the condition evaluates to false and no else branch is given.
+## While Loops
+
+The `while` statement checks a condition and executes the loop body when the condition evaluates to `true`.
+This is repeated until the condition is no longer true.
+
+Example:
+
+ var num = 5
+
+ while (num > 5) {
+ log("Test")
+ num -= 1
+ }
+
## For Loops
The `for` statement can be used to iterate over arrays and dictionaries.
diff --git a/lib/config/config_lexer.ll b/lib/config/config_lexer.ll
index a48555d2f..bfa7a9851 100644
--- a/lib/config/config_lexer.ll
+++ b/lib/config/config_lexer.ll
@@ -204,6 +204,7 @@ return return T_RETURN;
for return T_FOR;
if return T_IF;
else return T_ELSE;
+while return T_WHILE;
=\> return T_FOLLOWS;
\<\< return T_SHIFT_LEFT;
\>\> return T_SHIFT_RIGHT;
diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy
index 798717bf1..6c3b683a3 100644
--- a/lib/config/config_parser.yy
+++ b/lib/config/config_parser.yy
@@ -174,6 +174,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig
%token T_FOR "for (T_FOR)"
%token T_IF "if (T_IF)"
%token T_ELSE "else (T_ELSE)"
+%token T_WHILE "while (T_WHILE)"
%token T_FOLLOWS "=> (T_FOLLOWS)"
%type identifier
@@ -644,6 +645,13 @@ lterm: type
BindToScope(expr, ScopeLocal);
$$ = new SetExpression(expr, $3, $4, @$);
}
+ | T_WHILE '(' rterm ')' rterm_scope
+ {
+ DictExpression *aloop = dynamic_cast($5);
+ aloop->MakeInline();
+
+ $$ = new WhileExpression($3, aloop, @$);
+ }
| rterm_side_effect
;
diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp
index d254281b8..2fbcfea5e 100644
--- a/lib/config/expression.cpp
+++ b/lib/config/expression.cpp
@@ -400,6 +400,14 @@ Value ConditionalExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) co
return Empty;
}
+Value WhileExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ while (m_Condition->Evaluate(frame, dhint).ToBool())
+ m_LoopBody->Evaluate(frame, dhint);
+
+ return Empty;
+}
+
Value ReturnExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
{
BOOST_THROW_EXCEPTION(InterruptExecutionError(m_Operand->Evaluate(frame)));
diff --git a/lib/config/expression.hpp b/lib/config/expression.hpp
index b729f6261..72780c880 100644
--- a/lib/config/expression.hpp
+++ b/lib/config/expression.hpp
@@ -632,6 +632,28 @@ private:
Expression *m_FalseBranch;
};
+class I2_CONFIG_API WhileExpression : public DebuggableExpression
+{
+public:
+ WhileExpression(Expression *condition, Expression *loop_body, const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo), m_Condition(condition), m_LoopBody(loop_body)
+ { }
+
+ ~WhileExpression(void)
+ {
+ delete m_Condition;
+ delete m_LoopBody;
+ }
+
+protected:
+ virtual Value DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const;
+
+private:
+ Expression *m_Condition;
+ Expression *m_LoopBody;
+};
+
+
class I2_CONFIG_API ReturnExpression : public UnaryExpression
{
public: