diff --git a/Parser/Binding/Binder.cs b/Parser/Binding/Binder.cs index 354562c..ba0b8b1 100644 --- a/Parser/Binding/Binder.cs +++ b/Parser/Binding/Binder.cs @@ -132,11 +132,6 @@ namespace Parser.Binding }; } - private BoundWhileStatement BindWhileStatement(WhileStatementSyntaxNode node) - { - throw new NotImplementedException(); - } - private BoundTryCatchStatement BindTryCatchStatement(TryCatchStatementSyntaxNode node) { throw new NotImplementedException(); @@ -290,6 +285,13 @@ namespace Parser.Binding return Identifier(node, ((IdentifierNameExpressionSyntaxNode)node).Name.Text); } + private BoundWhileStatement BindWhileStatement(WhileStatementSyntaxNode node) + { + var condition = BindConversion(BindExpression(node.Condition), TypeSymbol.Boolean); + var body = BindStatement(node.Body); + return WhileStatement(node, condition, body); + } + private BoundExpressionStatement BindExpressionStatement(ExpressionStatementSyntaxNode node) { var expression = BindExpression(node.Expression); diff --git a/Parser/Binding/BoundNodeFactory.cs b/Parser/Binding/BoundNodeFactory.cs index 26bf3ff..845453e 100644 --- a/Parser/Binding/BoundNodeFactory.cs +++ b/Parser/Binding/BoundNodeFactory.cs @@ -133,6 +133,14 @@ namespace Parser.Binding return new BoundGotoStatement(syntax, label); } + public static BoundWhileStatement WhileStatement( + SyntaxNode syntax, + BoundExpression condition, + BoundStatement body) + { + return new BoundWhileStatement(syntax, condition, body); + } + public static BoundIdentifierNameExpression Identifier( SyntaxNode syntax, string name) diff --git a/Parser/Binding/BoundRoot.cs b/Parser/Binding/BoundRoot.cs index 041abb9..5810d05 100644 --- a/Parser/Binding/BoundRoot.cs +++ b/Parser/Binding/BoundRoot.cs @@ -256,12 +256,18 @@ namespace Parser.Binding public class BoundWhileStatement : BoundStatement { - public BoundWhileStatement(SyntaxNode syntax) + public BoundWhileStatement(SyntaxNode syntax, BoundExpression condition, BoundStatement body) : base(syntax) { + Condition = condition; + Body = body; } public override BoundNodeKind Kind => BoundNodeKind.WhileStatement; + + public BoundExpression Condition { get; } + + public BoundStatement Body { get; } } public abstract class BoundExpression : BoundNode diff --git a/Parser/Binding/BoundTreeRewriter.cs b/Parser/Binding/BoundTreeRewriter.cs index 2074331..db3273d 100644 --- a/Parser/Binding/BoundTreeRewriter.cs +++ b/Parser/Binding/BoundTreeRewriter.cs @@ -70,7 +70,9 @@ namespace Parser.Binding public virtual BoundStatement RewriteWhileStatement(BoundWhileStatement node) { - throw new NotImplementedException(); + var condition = RewriteExpression(node.Condition); + var body = RewriteStatement(node.Body); + return WhileStatement(node.Syntax, condition, body); } public virtual BoundStatement RewriteTryCatchStatement(BoundTryCatchStatement node) diff --git a/Parser/Binding/Conversion.cs b/Parser/Binding/Conversion.cs index 9db7af3..d4c4ae0 100644 --- a/Parser/Binding/Conversion.cs +++ b/Parser/Binding/Conversion.cs @@ -28,6 +28,11 @@ return Implicit; } + if (to == TypeSymbol.Boolean) + { + return Implicit; + } + return None; } } diff --git a/Parser/Emitting/Emitter.cs b/Parser/Emitting/Emitter.cs index 57b2c59..16a5015 100644 --- a/Parser/Emitting/Emitter.cs +++ b/Parser/Emitting/Emitter.cs @@ -467,8 +467,13 @@ namespace Parser.Emitting private void EmitConditionalGoto(BoundConditionalGotoStatement node, ILProcessor ilProcessor) { - EmitExpression(node.Condition, ilProcessor); - ilProcessor.Emit(OpCodes.Call, _mObjectToBool); + var condition = ConvertExpression(node.Condition, TypeSymbol.Boolean); + if (condition is null) + { + throw new Exception("Cannot cast a condition in GOTO to boolean."); + } + + EmitExpression(condition, ilProcessor); var instruction = node.GotoIfTrue ? OpCodes.Brtrue : OpCodes.Brfalse; if (_labels.TryGetValue(node.Label, out var target)) { @@ -569,7 +574,7 @@ namespace Parser.Emitting { ilProcessor.Emit(OpCodes.Call, _doubleToMObject); } - if ((fromType, toType) == (TypeSymbol.Int, TypeSymbol.MObject)) + else if ((fromType, toType) == (TypeSymbol.Int, TypeSymbol.MObject)) { ilProcessor.Emit(OpCodes.Call, _intToMObject); } @@ -577,6 +582,10 @@ namespace Parser.Emitting { ilProcessor.Emit(OpCodes.Call, _stringToMObject); } + else if ((fromType, toType) == (TypeSymbol.MObject, TypeSymbol.Boolean)) + { + ilProcessor.Emit(OpCodes.Call, _mObjectToBool); + } else { throw new NotImplementedException($"Conversion of '{fromType}' to '{toType}' is not implemented."); diff --git a/Parser/Lowering/Lowerer.cs b/Parser/Lowering/Lowerer.cs index 95f8971..7950fb9 100644 --- a/Parser/Lowering/Lowerer.cs +++ b/Parser/Lowering/Lowerer.cs @@ -86,6 +86,41 @@ namespace Parser.Lowering return RewriteBlockStatement(Block(node.Syntax, builder.ToArray())); } + public override BoundStatement RewriteWhileStatement(BoundWhileStatement node) + { + // while cond + // body + // end + // + // | + // | + // V + // + // LabelLoop: + // gotoFalse cond LabelEnd + // body + // goto LabelLoop + // LabelEnd: + var labelLoop = GenerateLabel(); + var labelEnd = GenerateLabel(); + var result = Block( + node.Syntax, + // LabelLoop: + LabelStatement(node.Syntax, labelLoop), + // gotoFalse cond LabelEnd + GotoIfFalse( + node.Syntax, + node.Condition, + labelEnd), + // body + node.Body, + // goto LabelLoop + Goto(node.Syntax, labelLoop), + // LabelEnd: + LabelStatement(node.Syntax, labelEnd)); + return RewriteBlockStatement(result); + } + public override BoundStatement RewriteForStatement(BoundForStatement node) { // for i = expr @@ -102,7 +137,6 @@ namespace Parser.Lowering // #index = #index + 1 // goto LabelLoop // LabelEnd: - var builder = ImmutableArray.CreateBuilder(); var labelLoop = GenerateLabel(); var labelEnd = GenerateLabel(); var localArray = GenerateTypedLocalVariable(TypeSymbol.MObject); diff --git a/examples/helloworld/hello.m b/examples/helloworld/hello.m index e5c74d8..22a2fa2 100644 --- a/examples/helloworld/hello.m +++ b/examples/helloworld/hello.m @@ -5,6 +5,12 @@ f(x); x = 3; f(x); +i = 1; +while i <= 10 + disp(i); + i = i + 1; +end + % for i = 1:10 % disp(i); % end