Skip to content

Commit

Permalink
Add return statements
Browse files Browse the repository at this point in the history
  • Loading branch information
terrajobst committed May 29, 2019
1 parent 7abf535 commit 26d79f8
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 6 deletions.
32 changes: 29 additions & 3 deletions src/Minsk/CodeAnalysis/Binding/Binder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,6 @@ private void BindFunctionDeclaration(FunctionDeclarationSyntax syntax)

var type = BindTypeClause(syntax.Type) ?? TypeSymbol.Void;

if (type != TypeSymbol.Void)
_diagnostics.XXX_ReportFunctionsAreUnsupported(syntax.Type.Span);

var function = new FunctionSymbol(syntax.Identifier.Text, parameters.ToImmutable(), type, syntax);
if (!_scope.TryDeclareFunction(function))
_diagnostics.ReportSymbolAlreadyDeclared(syntax.Identifier.Span, function.Name);
Expand Down Expand Up @@ -181,6 +178,8 @@ private BoundStatement BindStatement(StatementSyntax syntax)
return BindBreakStatement((BreakStatementSyntax)syntax);
case SyntaxKind.ContinueStatement:
return BindContinueStatement((ContinueStatementSyntax)syntax);
case SyntaxKind.ReturnStatement:
return BindReturnStatement((ReturnStatementSyntax)syntax);
case SyntaxKind.ExpressionStatement:
return BindExpressionStatement((ExpressionStatementSyntax)syntax);
default:
Expand Down Expand Up @@ -302,6 +301,33 @@ private BoundStatement BindContinueStatement(ContinueStatementSyntax syntax)
return new BoundGotoStatement(continueLabel);
}

private BoundStatement BindReturnStatement(ReturnStatementSyntax syntax)
{
var expression = syntax.Expression == null ? null : BindExpression(syntax.Expression);

if (_function == null)
{
_diagnostics.ReportInvalidReturn(syntax.ReturnKeyword.Span);
}
else
{
if (_function.Type == TypeSymbol.Void)
{
if (expression != null)
_diagnostics.ReportInvalidReturnExpression(syntax.Expression.Span, _function.Name);
}
else
{
if (expression == null)
_diagnostics.ReportMissingReturnExpression(syntax.ReturnKeyword.Span, _function.Type);
else
expression = BindConversion(syntax.Expression.Span, expression, _function.Type);
}
}

return new BoundReturnStatement(expression);
}

private BoundStatement BindExpressionStatement(ExpressionStatementSyntax syntax)
{
var expression = BindExpression(syntax.Expression, canBeVoid: true);
Expand Down
1 change: 1 addition & 0 deletions src/Minsk/CodeAnalysis/Binding/BoundNodeKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal enum BoundNodeKind
LabelStatement,
GotoStatement,
ConditionalGotoStatement,
ReturnStatement,
ExpressionStatement,

// Expressions
Expand Down
11 changes: 11 additions & 0 deletions src/Minsk/CodeAnalysis/Binding/BoundNodePrinter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,17 @@ private static void WriteConditionalGotoStatement(BoundConditionalGotoStatement
writer.WriteLine();
}

private static void WriteReturnStatement(BoundReturnStatement node, IndentedTextWriter writer)
{
writer.WriteKeyword(SyntaxKind.ReturnKeyword);
if (node.Expression != null)
{
writer.WriteSpace();
node.Expression.WriteTo(writer);
}
writer.WriteLine();
}

private static void WriteExpressionStatement(BoundExpressionStatement node, IndentedTextWriter writer)
{
node.Expression.WriteTo(writer);
Expand Down
13 changes: 13 additions & 0 deletions src/Minsk/CodeAnalysis/Binding/BoundReturnStatement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Minsk.CodeAnalysis.Binding
{
internal sealed class BoundReturnStatement : BoundStatement
{
public BoundReturnStatement(BoundExpression expression)
{
Expression = expression;
}

public override BoundNodeKind Kind => BoundNodeKind.ReturnStatement;
public BoundExpression Expression { get; }
}
}
11 changes: 11 additions & 0 deletions src/Minsk/CodeAnalysis/Binding/BoundTreeRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public virtual BoundStatement RewriteStatement(BoundStatement node)
return RewriteGotoStatement((BoundGotoStatement)node);
case BoundNodeKind.ConditionalGotoStatement:
return RewriteConditionalGotoStatement((BoundConditionalGotoStatement)node);
case BoundNodeKind.ReturnStatement:
return RewriteReturnStatement((BoundReturnStatement)node);
case BoundNodeKind.ExpressionStatement:
return RewriteExpressionStatement((BoundExpressionStatement)node);
default:
Expand Down Expand Up @@ -133,6 +135,15 @@ protected virtual BoundStatement RewriteConditionalGotoStatement(BoundConditiona
return new BoundConditionalGotoStatement(node.Label, condition, node.JumpIfTrue);
}

protected virtual BoundStatement RewriteReturnStatement(BoundReturnStatement node)
{
var expression = node.Expression == null ? null : RewriteExpression(node.Expression);
if (expression == node.Expression)
return node;

return new BoundReturnStatement(expression);
}

protected virtual BoundStatement RewriteExpressionStatement(BoundExpressionStatement node)
{
var expression = RewriteExpression(node.Expression);
Expand Down
16 changes: 14 additions & 2 deletions src/Minsk/CodeAnalysis/DiagnosticBag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,21 @@ public void ReportInvalidBreakOrContinue(TextSpan span, string text)
Report(span, message);
}

public void XXX_ReportFunctionsAreUnsupported(TextSpan span)
public void ReportInvalidReturn(TextSpan span)
{
var message = "Functions with return values are unsupported.";
var message = "The 'return' keyword can only be used inside of functions.";
Report(span, message);
}

public void ReportInvalidReturnExpression(TextSpan span, string functionName)
{
var message = $"Since the function '{functionName}' does not return a value the 'return' keyword cannot be followed by an expression.";
Report(span, message);
}

public void ReportMissingReturnExpression(TextSpan span, TypeSymbol returnType)
{
var message = $"An expression of type '{returnType}' expected.";
Report(span, message);
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/Minsk/CodeAnalysis/Evaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,13 @@ private object EvaluateStatement(BoundBlockStatement body)
case BoundNodeKind.LabelStatement:
index++;
break;
case BoundNodeKind.ReturnStatement:
var rs = (BoundReturnStatement)s;
_lastValue = rs.Expression == null ? null : EvaluateExpression(rs.Expression);
return _lastValue;
default:
throw new Exception($"Unexpected node {s.Kind}");
}

}

return _lastValue;
Expand Down
13 changes: 13 additions & 0 deletions src/Minsk/CodeAnalysis/Syntax/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ private StatementSyntax ParseStatement()
return ParseBreakStatement();
case SyntaxKind.ContinueKeyword:
return ParseContinueStatement();
case SyntaxKind.ReturnKeyword:
return ParseReturnStatement();
default:
return ParseExpressionStatement();
}
Expand Down Expand Up @@ -296,6 +298,17 @@ private StatementSyntax ParseContinueStatement()
return new ContinueStatementSyntax(keyword);
}

private StatementSyntax ParseReturnStatement()
{
var keyword = MatchToken(SyntaxKind.ReturnKeyword);
var keywordLine = _text.GetLineIndex(keyword.Span.Start);
var currentLine = _text.GetLineIndex(Current.Span.Start);
var isEof = Current.Kind == SyntaxKind.EndOfFileToken;
var sameLine = !isEof && keywordLine == currentLine;
var expression = sameLine ? ParseExpression() : null;
return new ReturnStatementSyntax(keyword, expression);
}

private ExpressionStatementSyntax ParseExpressionStatement()
{
var expression = ParseExpression();
Expand Down
15 changes: 15 additions & 0 deletions src/Minsk/CodeAnalysis/Syntax/ReturnStatementSyntax.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Minsk.CodeAnalysis.Syntax
{
public sealed class ReturnStatementSyntax : StatementSyntax
{
public ReturnStatementSyntax(SyntaxToken returnKeyword, ExpressionSyntax expression)
{
ReturnKeyword = returnKeyword;
Expression = expression;
}

public override SyntaxKind Kind => SyntaxKind.ReturnStatement;
public SyntaxToken ReturnKeyword { get; }
public ExpressionSyntax Expression { get; }
}
}
4 changes: 4 additions & 0 deletions src/Minsk/CodeAnalysis/Syntax/SyntaxFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ public static SyntaxKind GetKeywordKind(string text)
return SyntaxKind.IfKeyword;
case "let":
return SyntaxKind.LetKeyword;
case "return":
return SyntaxKind.ReturnKeyword;
case "to":
return SyntaxKind.ToKeyword;
case "true":
Expand Down Expand Up @@ -177,6 +179,8 @@ public static string GetText(SyntaxKind kind)
return "if";
case SyntaxKind.LetKeyword:
return "let";
case SyntaxKind.ReturnKeyword:
return "return";
case SyntaxKind.ToKeyword:
return "to";
case SyntaxKind.TrueKeyword:
Expand Down
2 changes: 2 additions & 0 deletions src/Minsk/CodeAnalysis/Syntax/SyntaxKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public enum SyntaxKind
FunctionKeyword,
IfKeyword,
LetKeyword,
ReturnKeyword,
ToKeyword,
TrueKeyword,
VarKeyword,
Expand All @@ -66,6 +67,7 @@ public enum SyntaxKind
ForStatement,
BreakStatement,
ContinueStatement,
ReturnStatement,
ExpressionStatement,

// Expressions
Expand Down

0 comments on commit 26d79f8

Please sign in to comment.