From 3bc8ef0d7d2c44d12e89e62ea857c4a1d809b03b Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Mon, 20 Jul 2020 10:38:32 +0200 Subject: [PATCH] More types --- Parser/Binding/Binder.cs | 69 ++++++- Parser/Binding/BoundBinaryOperator.cs | 173 ++++++++++++++--- Parser/Binding/BoundNodeFactory.cs | 63 ++++-- Parser/Binding/BoundNodeKind.cs | 7 +- Parser/Binding/BoundRoot.cs | 155 ++++++++++++++- Parser/Binding/BoundTreeRewriter.cs | 28 ++- Parser/Binding/BoundUnaryOperator.cs | 28 ++- Parser/Binding/Conversion.cs | 34 ++++ Parser/Binding/TypeSymbol.cs | 26 +++ Parser/Binding/TypedFunctionSymbol.cs | 23 +++ Parser/Binding/TypedParameterSymbol.cs | 14 ++ Parser/Binding/TypedVariableSymbol.cs | 15 ++ Parser/Emitting/Emitter.cs | 254 ++++++++++++++++++++++--- Parser/Evaluator.cs | 4 +- Parser/Internal/DiagnosticsBag.cs | 5 + Parser/Lowering/Lowerer.cs | 86 ++++++++- Parser/MFunctions/MHelpers.cs | 13 ++ Parser/MFunctions/MOperations.cs | 37 ++++ Parser/Objects/MDoubleNumber.cs | 43 +++++ Parser/Objects/MObject.cs | 14 +- examples/helloworld/hello.m | 5 + 21 files changed, 1008 insertions(+), 88 deletions(-) create mode 100644 Parser/Binding/Conversion.cs create mode 100644 Parser/Binding/TypeSymbol.cs create mode 100644 Parser/Binding/TypedFunctionSymbol.cs create mode 100644 Parser/Binding/TypedParameterSymbol.cs create mode 100644 Parser/Binding/TypedVariableSymbol.cs diff --git a/Parser/Binding/Binder.cs b/Parser/Binding/Binder.cs index d868fde..c84550b 100644 --- a/Parser/Binding/Binder.cs +++ b/Parser/Binding/Binder.cs @@ -116,7 +116,7 @@ namespace Parser.Binding TokenKind.ExpressionStatement => BindExpressionStatement((ExpressionStatementSyntaxNode)node), TokenKind.ForStatement => - BindForStatement((ForStatementSyntaxNode)node), + BindForStatement((ForStatementSyntaxNode)node)!, TokenKind.FunctionDeclaration => BindFunctionDeclaration((FunctionDeclarationSyntaxNode)node), TokenKind.IfStatement => @@ -265,9 +265,29 @@ namespace Parser.Binding return new ParameterSymbol(parameter.Text); } - private BoundForStatement BindForStatement(ForStatementSyntaxNode node) + private BoundForStatement? BindForStatement(ForStatementSyntaxNode node) { - throw new NotImplementedException(); + var loopVariable = BindLoopVariable(node.Assignment.Lhs); + if (loopVariable is null) + { + return null; + } + + var loopedExpression = BindExpression(node.Assignment.Rhs); + var body = BindStatement(node.Body); + return ForStatement(node, loopVariable, loopedExpression, body); + } + + private BoundIdentifierNameExpression? BindLoopVariable(ExpressionSyntaxNode node) + { + if (node.Kind != TokenKind.IdentifierNameExpression) + { + _diagnostics.ReportForLoopWithoutVariable( + new TextSpan(node.Position, node.FullWidth)); + return null; + } + + return Identifier(node, ((IdentifierNameExpressionSyntaxNode)node).Name.Text); } private BoundExpressionStatement BindExpressionStatement(ExpressionStatementSyntaxNode node) @@ -342,11 +362,37 @@ namespace Parser.Binding return Assignment(node, left, right); } + private BoundExpression BindConversion(BoundExpression expression, TypeSymbol targetType) + { + var conversion = Conversion.Classify(expression.Type, targetType); + if (!conversion.Exists) + { + return new BoundErrorExpression(expression.Syntax); + } + + if (conversion.IsIdentity) + { + return expression; + } + + return Conversion(expression.Syntax, targetType, expression); + } + private BoundBinaryOperationExpression BindBinaryOperationExpression(BinaryOperationExpressionSyntaxNode node) { var left = BindExpression(node.Lhs); var right = BindExpression(node.Rhs); - return BinaryOperation(node, left, node.Operation.Kind, right); + var op = BoundBinaryOperator.GetOperator(node.Operation.Kind, left.Type, right.Type); + if (op is null) + { + throw new Exception($"Unknown binary operator '{node.Operation.Kind}'."); + } + + return BinaryOperation( + node, + BindConversion(left, op.Left), + op, + BindConversion(right, op.Right)); } private BoundCellArrayElementAccessExpression BindCellArrayElementAccessExpression(CellArrayElementAccessExpressionSyntaxNode node) @@ -416,10 +462,10 @@ namespace Parser.Binding throw new NotImplementedException(); } - private BoundNumberLiteralExpression BindNumberLiteralExpression(NumberLiteralExpressionSyntaxNode node) + private BoundNumberDoubleLiteralExpression BindNumberLiteralExpression(NumberLiteralExpressionSyntaxNode node) { var value = (double)node.Number.Value!; - return NumberLiteral(node, value); + return NumberDoubleLiteral(node, value); } private BoundExpression BindParenthesizedExpression(ParenthesizedExpressionSyntaxNode node) @@ -436,7 +482,16 @@ namespace Parser.Binding private BoundUnaryOperationExpression BindUnaryPrefixOperationExpression(UnaryPrefixOperationExpressionSyntaxNode node) { var operand = BindExpression(node.Operand); - return UnaryOperation(node, node.Operation.Kind, operand); + var op = BoundUnaryOperator.GetOperator(node.Operation.Kind, operand.Type); + if (op is null) + { + throw new Exception($"Unknown binary operator '{node.Operation.Kind}'."); + } + + return UnaryOperation( + node, + op, + BindConversion(operand, op.Result)); } private BoundUnaryOperationExpression BindUnaryPostfixOperationExpression(UnaryPostfixOperationExpressionSyntaxNode node) diff --git a/Parser/Binding/BoundBinaryOperator.cs b/Parser/Binding/BoundBinaryOperator.cs index 73dc880..fc614b3 100644 --- a/Parser/Binding/BoundBinaryOperator.cs +++ b/Parser/Binding/BoundBinaryOperator.cs @@ -4,45 +4,164 @@ namespace Parser.Binding { public class BoundBinaryOperator { - private static BoundBinaryOperator[] _operators = + private static BoundBinaryOperator[] _specificOperators = { - new BoundBinaryOperator(TokenKind.EqualsToken, BoundBinaryOperatorKind.Equals), - new BoundBinaryOperator(TokenKind.PipePipeToken, BoundBinaryOperatorKind.PipePipe), - new BoundBinaryOperator(TokenKind.AmpersandAmpersandToken, BoundBinaryOperatorKind.AmpersandAmpersand), - new BoundBinaryOperator(TokenKind.PipeToken, BoundBinaryOperatorKind.Pipe), - new BoundBinaryOperator(TokenKind.AmpersandToken, BoundBinaryOperatorKind.Ampersand), - new BoundBinaryOperator(TokenKind.LessToken, BoundBinaryOperatorKind.Less), - new BoundBinaryOperator(TokenKind.LessOrEqualsToken, BoundBinaryOperatorKind.LessOrEquals), - new BoundBinaryOperator(TokenKind.GreaterToken, BoundBinaryOperatorKind.Greater), - new BoundBinaryOperator(TokenKind.GreaterOrEqualsToken, BoundBinaryOperatorKind.GreaterOrEquals), - new BoundBinaryOperator(TokenKind.EqualsEqualsToken, BoundBinaryOperatorKind.EqualsEquals), - new BoundBinaryOperator(TokenKind.TildeEqualsToken, BoundBinaryOperatorKind.TildeEquals), - new BoundBinaryOperator(TokenKind.ColonToken, BoundBinaryOperatorKind.Colon), - new BoundBinaryOperator(TokenKind.PlusToken, BoundBinaryOperatorKind.Plus), - new BoundBinaryOperator(TokenKind.MinusToken, BoundBinaryOperatorKind.Minus), - new BoundBinaryOperator(TokenKind.StarToken, BoundBinaryOperatorKind.Star), - new BoundBinaryOperator(TokenKind.DotStarToken, BoundBinaryOperatorKind.DotStar), - new BoundBinaryOperator(TokenKind.SlashToken, BoundBinaryOperatorKind.Slash), - new BoundBinaryOperator(TokenKind.DotSlashToken, BoundBinaryOperatorKind.DotSlash), - new BoundBinaryOperator(TokenKind.BackslashToken, BoundBinaryOperatorKind.Backslash), - new BoundBinaryOperator(TokenKind.DotBackslashToken, BoundBinaryOperatorKind.DotBackslash), - new BoundBinaryOperator(TokenKind.TildeToken, BoundBinaryOperatorKind.Tilde), - new BoundBinaryOperator(TokenKind.CaretToken, BoundBinaryOperatorKind.Caret), - new BoundBinaryOperator(TokenKind.DotCaretToken, BoundBinaryOperatorKind.DotCaret), + new BoundBinaryOperator( + TokenKind.LessToken, + BoundBinaryOperatorKind.Less, + TypeSymbol.Int, + TypeSymbol.Boolean), + new BoundBinaryOperator( + TokenKind.PlusToken, + BoundBinaryOperatorKind.Plus, + TypeSymbol.Int), }; - public BoundBinaryOperator(TokenKind syntaxKind, BoundBinaryOperatorKind kind) + private static BoundBinaryOperator[] _defaultOperators = + { + new BoundBinaryOperator( + TokenKind.EqualsToken, + BoundBinaryOperatorKind.Equals, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.PipePipeToken, + BoundBinaryOperatorKind.PipePipe, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.AmpersandAmpersandToken, + BoundBinaryOperatorKind.AmpersandAmpersand, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.PipeToken, + BoundBinaryOperatorKind.Pipe, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.AmpersandToken, + BoundBinaryOperatorKind.Ampersand, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.LessToken, + BoundBinaryOperatorKind.Less, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.LessOrEqualsToken, + BoundBinaryOperatorKind.LessOrEquals, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.GreaterToken, + BoundBinaryOperatorKind.Greater, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.GreaterOrEqualsToken, + BoundBinaryOperatorKind.GreaterOrEquals, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.EqualsEqualsToken, + BoundBinaryOperatorKind.EqualsEquals, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.TildeEqualsToken, + BoundBinaryOperatorKind.TildeEquals, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.ColonToken, + BoundBinaryOperatorKind.Colon, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.PlusToken, + BoundBinaryOperatorKind.Plus, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.MinusToken, + BoundBinaryOperatorKind.Minus, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.StarToken, + BoundBinaryOperatorKind.Star, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.DotStarToken, + BoundBinaryOperatorKind.DotStar, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.SlashToken, + BoundBinaryOperatorKind.Slash, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.DotSlashToken, + BoundBinaryOperatorKind.DotSlash, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.BackslashToken, + BoundBinaryOperatorKind.Backslash, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.DotBackslashToken, + BoundBinaryOperatorKind.DotBackslash, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.TildeToken, + BoundBinaryOperatorKind.Tilde, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.CaretToken, + BoundBinaryOperatorKind.Caret, + TypeSymbol.MObject), + new BoundBinaryOperator( + TokenKind.DotCaretToken, + BoundBinaryOperatorKind.DotCaret, + TypeSymbol.MObject), + }; + + public BoundBinaryOperator( + TokenKind syntaxKind, + BoundBinaryOperatorKind kind, + TypeSymbol type) { SyntaxKind = syntaxKind; Kind = kind; + Left = type; + Right = type; + Result = type; + } + + public BoundBinaryOperator( + TokenKind syntaxKind, + BoundBinaryOperatorKind kind, + TypeSymbol operand, + TypeSymbol result) + { + SyntaxKind = syntaxKind; + Kind = kind; + Left = operand; + Right = operand; + Result = result; + } + + public BoundBinaryOperator( + TokenKind syntaxKind, + BoundBinaryOperatorKind kind, + TypeSymbol left, + TypeSymbol right, + TypeSymbol result) + { + SyntaxKind = syntaxKind; + Kind = kind; + Left = left; + Right = right; + Result = result; } public TokenKind SyntaxKind { get; } public BoundBinaryOperatorKind Kind { get; } + public TypeSymbol Left { get; } + public TypeSymbol Right { get; } + public TypeSymbol Result { get; } - internal static BoundBinaryOperator? GetOperator(TokenKind kind) + internal static BoundBinaryOperator? GetOperator(TokenKind kind, TypeSymbol left, TypeSymbol right) { - return _operators.FirstOrDefault(op => op.SyntaxKind == kind); + return _specificOperators.FirstOrDefault(op => op.SyntaxKind == kind && op.Left == left && op.Right == right) + ?? _defaultOperators.FirstOrDefault(op => op.SyntaxKind == kind); } } } diff --git a/Parser/Binding/BoundNodeFactory.cs b/Parser/Binding/BoundNodeFactory.cs index 2513301..26bf3ff 100644 --- a/Parser/Binding/BoundNodeFactory.cs +++ b/Parser/Binding/BoundNodeFactory.cs @@ -25,6 +25,11 @@ namespace Parser.Binding return new BoundBlockStatement(syntax, statements); } + public static BoundConversionExpression Conversion(SyntaxNode syntax, TypeSymbol targetType, BoundExpression expression) + { + return new BoundConversionExpression(syntax, targetType, expression); + } + public static BoundExpressionStatement ExpressionStatement( SyntaxNode syntax, BoundExpression expression, @@ -43,6 +48,15 @@ namespace Parser.Binding return new BoundIfStatement(syntax, condition, body, elseifClauses, elseClause); } + public static BoundForStatement ForStatement( + SyntaxNode syntax, + BoundIdentifierNameExpression loopVariable, + BoundExpression loopExpression, + BoundStatement body) + { + return new BoundForStatement(syntax, loopVariable, loopExpression, body); + } + public static BoundLabelStatement LabelStatement( SyntaxNode syntax, BoundLabel label) @@ -61,10 +75,9 @@ namespace Parser.Binding public static BoundBinaryOperationExpression BinaryOperation( SyntaxNode syntax, BoundExpression left, - TokenKind kind, + BoundBinaryOperator op, BoundExpression right) { - var op = BindBinaryOperator(kind); return new BoundBinaryOperationExpression(syntax, left, op, right); } @@ -127,11 +140,18 @@ namespace Parser.Binding return new BoundIdentifierNameExpression(syntax, name); } - public static BoundNumberLiteralExpression NumberLiteral( + public static BoundNumberDoubleLiteralExpression NumberDoubleLiteral( SyntaxNode syntax, double value) { - return new BoundNumberLiteralExpression(syntax, value); + return new BoundNumberDoubleLiteralExpression(syntax, value); + } + + public static BoundNumberIntLiteralExpression NumberIntLiteral( + SyntaxNode syntax, + int value) + { + return new BoundNumberIntLiteralExpression(syntax, value); } public static BoundStringLiteralExpression StringLiteral( @@ -149,25 +169,38 @@ namespace Parser.Binding return new BoundElseifClause(syntax, condition, body); } + public static BoundTypedVariableDeclaration TypedVariableDeclaration( + SyntaxNode syntax, + TypedVariableSymbol variable, + BoundExpression initializer) + { + return new BoundTypedVariableDeclaration(syntax, variable, initializer); + } + + public static BoundTypedVariableExpression TypedVariableExpression( + SyntaxNode syntax, + TypedVariableSymbol variable) + { + return new BoundTypedVariableExpression(syntax, variable); + } + public static BoundUnaryOperationExpression UnaryOperation( SyntaxNode syntax, - TokenKind kind, + BoundUnaryOperator op, BoundExpression operand) { - var op = BindUnaryOperator(kind); return new BoundUnaryOperationExpression(syntax, op, operand); } - private static BoundUnaryOperator BindUnaryOperator(TokenKind kind) + public static BoundExpression TypedFunctionCall( + SyntaxNode syntax, + TypedFunctionSymbol function, + ImmutableArray arguments) { - return BoundUnaryOperator.GetOperator(kind) - ?? throw new Exception($"Unexpected unary operator kind {kind}."); - } - - private static BoundBinaryOperator BindBinaryOperator(TokenKind kind) - { - return BoundBinaryOperator.GetOperator(kind) - ?? throw new Exception($"Unexpected binary operator kind {kind}."); + return new BoundTypedFunctionCallExpression( + syntax, + function, + arguments); } } } diff --git a/Parser/Binding/BoundNodeKind.cs b/Parser/Binding/BoundNodeKind.cs index 4158dcd..19a0963 100644 --- a/Parser/Binding/BoundNodeKind.cs +++ b/Parser/Binding/BoundNodeKind.cs @@ -21,6 +21,7 @@ LabelStatement, SwitchStatement, TryCatchStatement, + TypedVariableDeclaration, WhileStatement, // Expressions @@ -33,8 +34,10 @@ ClassInvokationExpression, CommandExpression, CompoundNameExpression, + ConversionExpression, DoubleQuotedStringLiteralExpression, EmptyExpression, + ErrorExpression, FunctionCallExpression, IdentifierNameExpression, IndirectMemberAccessExpression, @@ -44,11 +47,13 @@ NumberLiteralExpression, ParenthesizedExpression, StringLiteralExpression, + TypedFunctionCallExpression, + TypedVariableExpression, UnaryOperationExpression, UnquotedStringLiteralExpression, // Parts ElseIfClause, - ElseClause + ElseClause, } } diff --git a/Parser/Binding/BoundRoot.cs b/Parser/Binding/BoundRoot.cs index e59f7dc..e4a2a17 100644 --- a/Parser/Binding/BoundRoot.cs +++ b/Parser/Binding/BoundRoot.cs @@ -134,11 +134,24 @@ namespace Parser.Binding public class BoundForStatement : BoundStatement { - public BoundForStatement(SyntaxNode syntax) + public BoundForStatement( + SyntaxNode syntax, + BoundIdentifierNameExpression loopVariable, + BoundExpression loopedExpression, + BoundStatement body) : base(syntax) { + LoopVariable = loopVariable; + LoopedExpression = loopedExpression; + Body = body; } + public BoundIdentifierNameExpression LoopVariable { get; } + + public BoundExpression LoopedExpression { get; } + + public BoundStatement Body { get; } + public override BoundNodeKind Kind => BoundNodeKind.ForStatement; } @@ -257,6 +270,8 @@ namespace Parser.Binding : base(syntax) { } + + public abstract TypeSymbol Type { get; } } public class BoundArrayLiteralExpression : BoundExpression @@ -267,6 +282,8 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.ArrayLiteralExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); } public class BoundAssignmentExpression : BoundExpression @@ -282,6 +299,8 @@ namespace Parser.Binding public BoundExpression Right { get; } public override BoundNodeKind Kind => BoundNodeKind.AssignmentExpression; + + public override TypeSymbol Type => Right.Type; } public class BoundBinaryOperationExpression : BoundExpression @@ -299,6 +318,8 @@ namespace Parser.Binding public BoundExpression Right { get; } public override BoundNodeKind Kind => BoundNodeKind.BinaryOperationExpression; + + public override TypeSymbol Type => Op.Result; } public class BoundCellArrayElementAccessExpression : BoundExpression @@ -309,6 +330,8 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.CellArrayElementAccessExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); } public class BoundCellArrayLiteralExpression : BoundExpression @@ -319,6 +342,8 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.CellArrayLiteralExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); } public class BoundClassInvokationExpression : BoundExpression @@ -329,6 +354,8 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.ClassInvokationExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); } public class BoundCommandExpression : BoundExpression @@ -339,6 +366,8 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.CommandExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); } public class BoundCompoundNameExpression : BoundExpression @@ -349,6 +378,8 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.CompoundNameExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); } public class BoundDoubleQuotedStringLiteralExpression : BoundExpression @@ -359,6 +390,8 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.DoubleQuotedStringLiteralExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); } public class BoundEmptyExpression : BoundExpression @@ -369,6 +402,20 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.EmptyExpression; + + public override TypeSymbol Type => TypeSymbol.Null; + } + + public class BoundErrorExpression : BoundExpression + { + public BoundErrorExpression(SyntaxNode syntax) + : base(syntax) + { + } + + public override BoundNodeKind Kind => BoundNodeKind.ErrorExpression; + + public override TypeSymbol Type => TypeSymbol.Error; } public class BoundFunctionCallExpression : BoundExpression @@ -383,6 +430,27 @@ namespace Parser.Binding public BoundExpression Name { get; } public ImmutableArray Arguments { get; } public override BoundNodeKind Kind => BoundNodeKind.FunctionCallExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); + } + + public class BoundTypedFunctionCallExpression : BoundExpression + { + public BoundTypedFunctionCallExpression( + SyntaxNode syntax, + TypedFunctionSymbol function, + ImmutableArray arguments) + : base(syntax) + { + Function = function; + Arguments = arguments; + } + + public TypedFunctionSymbol Function { get; } + public ImmutableArray Arguments { get; } + public override BoundNodeKind Kind => BoundNodeKind.TypedFunctionCallExpression; + + public override TypeSymbol Type => Function.ReturnType; } public class BoundIdentifierNameExpression : BoundExpression @@ -395,6 +463,8 @@ namespace Parser.Binding public string Name { get; } public override BoundNodeKind Kind => BoundNodeKind.IdentifierNameExpression; + + public override TypeSymbol Type => TypeSymbol.MObject; } public class BoundIndirectMemberAccessExpression : BoundExpression @@ -405,6 +475,8 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.IndirectMemberAccessExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); } public class BoundLambdaExpression : BoundExpression @@ -415,6 +487,8 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.LambdaExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); } public class BoundMemberAccessExpression : BoundExpression @@ -425,6 +499,8 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.MemberAccessExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); } public class BoundNamedFunctionHandleExpression : BoundExpression @@ -435,11 +511,13 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.NamedFunctionHandleExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); } - public class BoundNumberLiteralExpression : BoundExpression + public class BoundNumberDoubleLiteralExpression : BoundExpression { - public BoundNumberLiteralExpression(SyntaxNode syntax, double value) + public BoundNumberDoubleLiteralExpression(SyntaxNode syntax, double value) : base(syntax) { Value = value; @@ -447,6 +525,22 @@ namespace Parser.Binding public double Value { get; } public override BoundNodeKind Kind => BoundNodeKind.NumberLiteralExpression; + + public override TypeSymbol Type => TypeSymbol.Double; + } + + public class BoundNumberIntLiteralExpression : BoundExpression + { + public BoundNumberIntLiteralExpression(SyntaxNode syntax, int value) + : base(syntax) + { + Value = value; + } + + public double Value { get; } + public override BoundNodeKind Kind => BoundNodeKind.NumberLiteralExpression; + + public override TypeSymbol Type => TypeSymbol.Int; } public class BoundStringLiteralExpression : BoundExpression @@ -459,6 +553,39 @@ namespace Parser.Binding public string Value { get; } public override BoundNodeKind Kind => BoundNodeKind.StringLiteralExpression; + + public override TypeSymbol Type => TypeSymbol.String; + } + + public class BoundTypedVariableDeclaration : BoundStatement + { + public BoundTypedVariableDeclaration(SyntaxNode syntax, TypedVariableSymbol variable, BoundExpression initializer) + : base(syntax) + { + Variable = variable; + Initializer = initializer; + } + + public TypedVariableSymbol Variable { get; } + + public BoundExpression Initializer { get; } + + public override BoundNodeKind Kind => BoundNodeKind.TypedVariableDeclaration; + } + + public class BoundTypedVariableExpression : BoundExpression + { + public BoundTypedVariableExpression(SyntaxNode syntax, TypedVariableSymbol variable) + : base(syntax) + { + Variable = variable; + } + + public TypedVariableSymbol Variable { get; } + + public override BoundNodeKind Kind => BoundNodeKind.TypedVariableExpression; + + public override TypeSymbol Type => Variable.Type; } public class BoundUnaryOperationExpression : BoundExpression @@ -474,6 +601,8 @@ namespace Parser.Binding public BoundUnaryOperator Op { get; } public BoundExpression Operand { get; } + + public override TypeSymbol Type => Op.Result; } public class BoundUnquotedStringLiteralExpression : BoundExpression @@ -484,6 +613,26 @@ namespace Parser.Binding } public override BoundNodeKind Kind => BoundNodeKind.UnquotedStringLiteralExpression; + + public override TypeSymbol Type => throw new System.NotImplementedException(); + } + + public class BoundConversionExpression : BoundExpression + { + public BoundConversionExpression(SyntaxNode syntax, TypeSymbol targetType, BoundExpression expression) + : base(syntax) + { + TargetType = targetType; + Expression = expression; + } + + public TypeSymbol TargetType { get; } + + public BoundExpression Expression { get; } + + public override BoundNodeKind Kind => BoundNodeKind.ConversionExpression; + + public override TypeSymbol Type => TargetType; } public class BoundElseifClause : BoundNode diff --git a/Parser/Binding/BoundTreeRewriter.cs b/Parser/Binding/BoundTreeRewriter.cs index 24e997d..42de105 100644 --- a/Parser/Binding/BoundTreeRewriter.cs +++ b/Parser/Binding/BoundTreeRewriter.cs @@ -38,6 +38,8 @@ namespace Parser.Binding RewriteSwitchStatement((BoundSwitchStatement)node), BoundNodeKind.TryCatchStatement => RewriteTryCatchStatement((BoundTryCatchStatement)node), + BoundNodeKind.TypedVariableDeclaration => + RewriteTypedVariableDeclaration((BoundTypedVariableDeclaration)node), BoundNodeKind.WhileStatement => RewriteWhileStatement((BoundWhileStatement)node), _ => @@ -45,6 +47,11 @@ namespace Parser.Binding }; } + public virtual BoundStatement RewriteTypedVariableDeclaration(BoundTypedVariableDeclaration node) + { + return node; + } + public virtual BoundStatement RewriteGotoStatement(BoundGotoStatement node) { return node; @@ -136,7 +143,7 @@ namespace Parser.Binding public virtual BoundStatement RewriteForStatement(BoundForStatement node) { - throw new NotImplementedException(); + return node; } public virtual BoundStatement RewriteExpressionStatement(BoundExpressionStatement node) @@ -220,6 +227,8 @@ namespace Parser.Binding RewriteCommandExpression((BoundCommandExpression)node), BoundNodeKind.CompoundNameExpression => RewriteCompoundNameExpression((BoundCompoundNameExpression)node), + BoundNodeKind.ConversionExpression => + RewriteConversionExpression((BoundConversionExpression)node), BoundNodeKind.DoubleQuotedStringLiteralExpression => RewriteDoubleQuotedStringLiteralExpression((BoundDoubleQuotedStringLiteralExpression)node), BoundNodeKind.EmptyExpression => @@ -237,9 +246,11 @@ namespace Parser.Binding BoundNodeKind.NamedFunctionHandleExpression => RewriteNamedFunctionHandleExpression((BoundNamedFunctionHandleExpression)node), BoundNodeKind.NumberLiteralExpression => - RewriteNumberLiteralExpression((BoundNumberLiteralExpression)node), + RewriteNumberLiteralExpression((BoundNumberDoubleLiteralExpression)node), BoundNodeKind.StringLiteralExpression => RewriteStringLiteralExpression((BoundStringLiteralExpression)node), + BoundNodeKind.TypedVariableExpression => + RewriteTypedVariableExpression((BoundTypedVariableExpression)node), BoundNodeKind.UnaryOperationExpression => RewriteUnaryOperationExpression((BoundUnaryOperationExpression)node), BoundNodeKind.UnquotedStringLiteralExpression => @@ -249,6 +260,17 @@ namespace Parser.Binding }; } + public virtual BoundExpression RewriteConversionExpression(BoundConversionExpression node) + { + var operand = RewriteExpression(node.Expression); + return Conversion(node.Syntax, node.TargetType, operand); + } + + public virtual BoundExpression RewriteTypedVariableExpression(BoundTypedVariableExpression node) + { + return node; + } + public virtual BoundExpression RewriteUnquotedStringLiteralExpression(BoundUnquotedStringLiteralExpression node) { throw new NotImplementedException(); @@ -265,7 +287,7 @@ namespace Parser.Binding return node; } - public virtual BoundExpression RewriteNumberLiteralExpression(BoundNumberLiteralExpression node) + public virtual BoundExpression RewriteNumberLiteralExpression(BoundNumberDoubleLiteralExpression node) { return node; } diff --git a/Parser/Binding/BoundUnaryOperator.cs b/Parser/Binding/BoundUnaryOperator.cs index 7006e1a..b4053c1 100644 --- a/Parser/Binding/BoundUnaryOperator.cs +++ b/Parser/Binding/BoundUnaryOperator.cs @@ -1,26 +1,42 @@ -using System.Linq; +using System; +using System.Linq; namespace Parser.Binding { public class BoundUnaryOperator { - private static BoundUnaryOperator[] _operators = + private static BoundUnaryOperator[] _specificOperators = Array.Empty(); + + private static BoundUnaryOperator[] _defaultOperators = { - new BoundUnaryOperator(TokenKind.MinusToken, BoundUnaryOperatorKind.Minus), + new BoundUnaryOperator(TokenKind.MinusToken, BoundUnaryOperatorKind.Minus, TypeSymbol.MObject), }; - public BoundUnaryOperator(TokenKind syntaxKind, BoundUnaryOperatorKind kind) + public BoundUnaryOperator(TokenKind syntaxKind, BoundUnaryOperatorKind kind, TypeSymbol type) { SyntaxKind = syntaxKind; Kind = kind; + Operand = type; + Result = type; + } + + public BoundUnaryOperator(TokenKind syntaxKind, BoundUnaryOperatorKind kind, TypeSymbol operand, TypeSymbol result) + { + SyntaxKind = syntaxKind; + Kind = kind; + Operand = operand; + Result = result; } public TokenKind SyntaxKind { get; } public BoundUnaryOperatorKind Kind { get; } + public TypeSymbol Operand { get; } + public TypeSymbol Result { get; } - internal static BoundUnaryOperator? GetOperator(TokenKind kind) + internal static BoundUnaryOperator? GetOperator(TokenKind kind, TypeSymbol operand) { - return _operators.FirstOrDefault(op => op.SyntaxKind == kind); + return _specificOperators.FirstOrDefault(op => op.SyntaxKind == kind && op.Operand == operand) + ?? _defaultOperators.FirstOrDefault(op => op.SyntaxKind == kind); } } } diff --git a/Parser/Binding/Conversion.cs b/Parser/Binding/Conversion.cs new file mode 100644 index 0000000..9db7af3 --- /dev/null +++ b/Parser/Binding/Conversion.cs @@ -0,0 +1,34 @@ +namespace Parser.Binding +{ + internal class Conversion + { + public static Conversion None = new Conversion(exists: false, isIdentity: false); + public static Conversion Identity = new Conversion(exists: true, isIdentity: true); + public static Conversion Implicit = new Conversion(exists: true, isIdentity: false); + + private Conversion(bool exists, bool isIdentity) + { + Exists = exists; + IsIdentity = isIdentity; + } + + public bool Exists { get; } + + public bool IsIdentity { get; } + + public static Conversion Classify(TypeSymbol from, TypeSymbol to) + { + if (from == to) + { + return Identity; + } + + if (to == TypeSymbol.MObject) + { + return Implicit; + } + + return None; + } + } +} diff --git a/Parser/Binding/TypeSymbol.cs b/Parser/Binding/TypeSymbol.cs new file mode 100644 index 0000000..420d1e0 --- /dev/null +++ b/Parser/Binding/TypeSymbol.cs @@ -0,0 +1,26 @@ +namespace Parser.Binding +{ + public class TypeSymbol + { + public static readonly TypeSymbol Error = new TypeSymbol("error"); + public static readonly TypeSymbol Null = new TypeSymbol("null"); + public static readonly TypeSymbol Boolean = new TypeSymbol("bool"); + public static readonly TypeSymbol Double = new TypeSymbol("double"); + public static readonly TypeSymbol Int = new TypeSymbol("int"); + public static readonly TypeSymbol String = new TypeSymbol("string"); + public static readonly TypeSymbol MObject = new TypeSymbol("mobject"); + public static readonly TypeSymbol Void = new TypeSymbol("void"); + + private TypeSymbol(string name) + { + Name = name; + } + + public string Name { get; } + + public override string ToString() + { + return Name; + } + } +} diff --git a/Parser/Binding/TypedFunctionSymbol.cs b/Parser/Binding/TypedFunctionSymbol.cs new file mode 100644 index 0000000..e610c86 --- /dev/null +++ b/Parser/Binding/TypedFunctionSymbol.cs @@ -0,0 +1,23 @@ +using System.Collections.Immutable; + +namespace Parser.Binding +{ + public class TypedFunctionSymbol + { + public TypedFunctionSymbol( + string name, + ImmutableArray parameters, + TypeSymbol returnType) + { + Name = name; + Parameters = parameters; + ReturnType = returnType; + } + + public string Name { get; } + + public ImmutableArray Parameters { get; } + + public TypeSymbol ReturnType { get; } + } +} diff --git a/Parser/Binding/TypedParameterSymbol.cs b/Parser/Binding/TypedParameterSymbol.cs new file mode 100644 index 0000000..0d61f9d --- /dev/null +++ b/Parser/Binding/TypedParameterSymbol.cs @@ -0,0 +1,14 @@ +namespace Parser.Binding +{ + public class TypedParameterSymbol + { + public TypedParameterSymbol(string name, TypeSymbol type) + { + Name = name; + Type = type; + } + + public string Name { get; } + public TypeSymbol Type { get; } + } +} \ No newline at end of file diff --git a/Parser/Binding/TypedVariableSymbol.cs b/Parser/Binding/TypedVariableSymbol.cs new file mode 100644 index 0000000..f939032 --- /dev/null +++ b/Parser/Binding/TypedVariableSymbol.cs @@ -0,0 +1,15 @@ +namespace Parser.Binding +{ + public class TypedVariableSymbol + { + public TypedVariableSymbol(string name, TypeSymbol type) + { + Name = name; + Type = type; + } + + public string Name { get; } + + public TypeSymbol Type { get; } + } +} diff --git a/Parser/Emitting/Emitter.cs b/Parser/Emitting/Emitter.cs index 27be020..51941b7 100644 --- a/Parser/Emitting/Emitter.cs +++ b/Parser/Emitting/Emitter.cs @@ -4,20 +4,34 @@ using Mono.Cecil.Rocks; using Parser.Binding; using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; namespace Parser.Emitting { + public static class BuiltInFunctions + { + public static readonly TypedFunctionSymbol Disp = new TypedFunctionSymbol( + "disp", + ImmutableArray.Create( + new TypedParameterSymbol("text", TypeSymbol.MObject)), + TypeSymbol.Void); + } + public class Emitter { private Dictionary _knownTypes = new Dictionary(); + private Dictionary _resolvedTypes = new Dictionary(); private Dictionary _functions = new Dictionary(); private MethodReference? _consoleWriteLineReference; private MethodReference? _dispReference; + private MethodReference? _lenReference; + private MethodReference? _arraySliceReference; private MethodReference? _mObjectToBool; private MethodReference? _stringToMObject; private MethodReference? _doubleToMObject; + private MethodReference? _intToMObject; private MethodReference? _getItemFromDictionary; private MethodReference? _putItemIntoDictionary; private TypeReference? _mObjectType; @@ -30,6 +44,8 @@ namespace Parser.Emitting private Dictionary _unaryOperations = new Dictionary(); private Dictionary _labels = new Dictionary(); private Dictionary _forwardLabelsToFix = new Dictionary(); + private Dictionary _typedLocals = new Dictionary(); + private Dictionary _builtInFunctions = new Dictionary(); private static TypeReference ResolveAndImportType( string typeName, @@ -132,6 +148,7 @@ namespace Parser.Emitting kind: ModuleKind.Console); var builtInTypes = new[] { + "System.Int32", "System.Object", "System.Void", "System.String", @@ -139,6 +156,12 @@ namespace Parser.Emitting "Parser.Objects.MObject" }; + var typeSymbolToKnownType = new Dictionary + { + [TypeSymbol.Int] = "System.Int32", + [TypeSymbol.MObject] = "Parser.Objects.MObject", + }; + // Resolve built-in types and methods. foreach (var typeName in builtInTypes) { @@ -146,6 +169,11 @@ namespace Parser.Emitting _knownTypes.Add(typeName, typeReference); } + foreach (var (typeSymbol, knownTypeName) in typeSymbolToKnownType) + { + _resolvedTypes.Add(typeSymbol, _knownTypes[knownTypeName]); + } + var objectType = _knownTypes["System.Object"]; var voidType = _knownTypes["System.Void"]; var stringType = _knownTypes["System.String"]; @@ -192,6 +220,20 @@ namespace Parser.Emitting assemblies: assemblies, assemblyDefinition: assemblyDefinition); + _lenReference = ResolveAndImportMethod( + typeName: "Parser.MFunctions.MHelpers", + methodName: "Len", + parameterTypeNames: new[] { "Parser.Objects.MObject" }, + assemblies: assemblies, + assemblyDefinition: assemblyDefinition); + + _arraySliceReference = ResolveAndImportMethod( + typeName: "Parser.MFunctions.MOperations", + methodName: "ArraySlice", + parameterTypeNames: new[] { "Parser.Objects.MObject", "Parser.Objects.MObject" }, + assemblies: assemblies, + assemblyDefinition: assemblyDefinition); + _mObjectToBool = ResolveAndImportMethod( typeName: "Parser.MFunctions.MHelpers", methodName: "ToBool", @@ -213,8 +255,16 @@ namespace Parser.Emitting assemblies: assemblies, assemblyDefinition: assemblyDefinition); + _intToMObject = ResolveAndImportMethod( + typeName: "Parser.Objects.MObject", + methodName: "CreateIntNumber", + parameterTypeNames: new[] { "System.Int32" }, + assemblies: assemblies, + assemblyDefinition: assemblyDefinition); + var binaryOperationNames = new Dictionary { + [BoundBinaryOperatorKind.Colon] = "Colon", [BoundBinaryOperatorKind.Plus] = "Plus", [BoundBinaryOperatorKind.Minus] = "Minus", [BoundBinaryOperatorKind.Star] = "Star", @@ -230,6 +280,10 @@ namespace Parser.Emitting [BoundUnaryOperatorKind.Minus] = "Minus", }; + _builtInFunctions.Add( + BuiltInFunctions.Disp, + _dispReference); + foreach (var (op, opName) in binaryOperationNames) { _binaryOperations[op] = ResolveAndImportMethod( @@ -306,10 +360,12 @@ namespace Parser.Emitting private void EmitFunction(LoweredFunction function, MethodDefinition methodDefinition) { + Console.WriteLine($"Emitting function '{function.Name}'."); var ilProcessor = methodDefinition.Body.GetILProcessor(); _labels.Clear(); _forwardLabelsToFix.Clear(); + _typedLocals.Clear(); // Local #0 is the dictionary with actual local variables. _currentLocals = new VariableDefinition(_stringMObjectDictionary); @@ -356,13 +412,16 @@ namespace Parser.Emitting } } + ilProcessor.Emit(OpCodes.Ret); + foreach (var (index, target) in _forwardLabelsToFix) { var targetIndex = _labels[target]; - ilProcessor.Body.Instructions[index].Operand = ilProcessor.Body.Instructions[targetIndex]; + var left = ilProcessor.Body.Instructions[index]; + var right = ilProcessor.Body.Instructions[targetIndex]; + left.Operand = right; } - ilProcessor.Emit(OpCodes.Ret); } private void EmitBlockStatement(BoundBlockStatement block, MethodDefinition methodDefinition) @@ -431,11 +490,25 @@ namespace Parser.Emitting case BoundNodeKind.ExpressionStatement: EmitExpressionStatement((BoundExpressionStatement)node, ilProcessor); break; + case BoundNodeKind.TypedVariableDeclaration: + EmitTypedVariableDeclaration((BoundTypedVariableDeclaration)node, ilProcessor); + break; default: throw new NotImplementedException($"Invalid statement kind '{node.Kind}'."); }; } + private void EmitTypedVariableDeclaration(BoundTypedVariableDeclaration node, ILProcessor ilProcessor) + { + var typeReference = _resolvedTypes[node.Variable.Type]; + var variableDefinition = new VariableDefinition(typeReference); + _typedLocals.Add(node.Variable, variableDefinition); + ilProcessor.Body.Variables.Add(variableDefinition); + + EmitExpression(node.Initializer, ilProcessor); + ilProcessor.Emit(OpCodes.Stloc, variableDefinition); + } + private void EmitExpressionStatement(BoundExpressionStatement node, ILProcessor ilProcessor) { EmitExpression(node.Expression, ilProcessor); @@ -458,6 +531,9 @@ namespace Parser.Emitting case BoundNodeKind.BinaryOperationExpression: EmitBinaryOperationExpression((BoundBinaryOperationExpression)node, ilProcessor); break; + case BoundNodeKind.ConversionExpression: + EmitConversionExpression((BoundConversionExpression)node, ilProcessor); + break; case BoundNodeKind.FunctionCallExpression: EmitFunctionCallExpression((BoundFunctionCallExpression)node, ilProcessor); break; @@ -465,16 +541,47 @@ namespace Parser.Emitting EmitIdentifierNameExpression((BoundIdentifierNameExpression)node, ilProcessor); break; case BoundNodeKind.NumberLiteralExpression: - EmitNumberLiteralExpression((BoundNumberLiteralExpression)node, ilProcessor); + EmitNumberLiteralExpression((BoundNumberDoubleLiteralExpression)node, ilProcessor); break; case BoundNodeKind.StringLiteralExpression: EmitStringLiteralExpression((BoundStringLiteralExpression)node, ilProcessor); break; + case BoundNodeKind.TypedFunctionCallExpression: + EmitTypedFunctionCallExpression((BoundTypedFunctionCallExpression)node, ilProcessor); + break; + case BoundNodeKind.TypedVariableExpression: + EmitTypedVariableExpression((BoundTypedVariableExpression)node, ilProcessor); + break; default: throw new NotImplementedException($"Invalid node kind '{node.Kind}'."); } } + private void EmitConversionExpression(BoundConversionExpression node, ILProcessor ilProcessor) + { + var fromType = node.Expression.Type; + var toType = node.TargetType; + EmitExpression(node.Expression, ilProcessor); + if ((fromType, toType) == (TypeSymbol.Double, TypeSymbol.MObject)) + { + ilProcessor.Emit(OpCodes.Call, _doubleToMObject); + } + else if ((fromType, toType) == (TypeSymbol.String, TypeSymbol.MObject)) + { + ilProcessor.Emit(OpCodes.Call, _stringToMObject); + } + else + { + throw new NotImplementedException($"Conversion of '{fromType}' to '{toType}' is not implemented."); + } + } + + private void EmitTypedVariableExpression(BoundTypedVariableExpression node, ILProcessor ilProcessor) + { + var variableDefinition = _typedLocals[node.Variable]; + ilProcessor.Emit(OpCodes.Ldloc, variableDefinition); + } + private void EmitBinaryOperationExpression(BoundBinaryOperationExpression node, ILProcessor ilProcessor) { var method = _binaryOperations[node.Op.Kind]; @@ -485,18 +592,38 @@ namespace Parser.Emitting private void EmitAssignmentExpression(BoundAssignmentExpression node, ILProcessor ilProcessor) { - if (node.Left.Kind != BoundNodeKind.IdentifierNameExpression) + var leftType = node.Left.Kind switch { - throw new Exception("Assignment to complex lvalues is not supported."); + BoundNodeKind.IdentifierNameExpression => TypeSymbol.MObject, + BoundNodeKind.TypedVariableExpression => ((BoundTypedVariableExpression)node.Left).Type, + _ => throw new Exception($"Assignment to lvalue of kind {node.Left.Kind} is not supported."), + }; + + var rightType = node.Right.Type; + + var rewrittenRight = ConvertExpression(node.Right, leftType); + + if (node.Left.Kind == BoundNodeKind.IdentifierNameExpression) + { + var left = ((BoundIdentifierNameExpression)node.Left); + ilProcessor.Emit(OpCodes.Ldloc_0); + ilProcessor.Emit(OpCodes.Ldstr, left.Name); + EmitExpression(rewrittenRight, ilProcessor); + ilProcessor.Emit(OpCodes.Callvirt, _putItemIntoDictionary); + ilProcessor.Emit(OpCodes.Ldloc_0); + ilProcessor.Emit(OpCodes.Ldstr, left.Name); + ilProcessor.Emit(OpCodes.Callvirt, _getItemFromDictionary); + } + else if (node.Left.Kind == BoundNodeKind.TypedVariableExpression) + { + var typedVariableExpression = (BoundTypedVariableExpression)node.Left; + EmitExpression(rewrittenRight, ilProcessor); + ilProcessor.Emit(OpCodes.Stloc, _typedLocals[typedVariableExpression.Variable]); + } + else + { + throw new Exception($"Assignment to lvalue of kind {node.Left.Kind} is not supported."); } - var left = ((BoundIdentifierNameExpression)node.Left); - ilProcessor.Emit(OpCodes.Ldloc_0); - ilProcessor.Emit(OpCodes.Ldstr, left.Name); - EmitExpression(node.Right, ilProcessor); - ilProcessor.Emit(OpCodes.Callvirt, _putItemIntoDictionary); - ilProcessor.Emit(OpCodes.Ldloc_0); - ilProcessor.Emit(OpCodes.Ldstr, left.Name); - ilProcessor.Emit(OpCodes.Callvirt, _getItemFromDictionary); } private void EmitIdentifierNameExpression(BoundIdentifierNameExpression node, ILProcessor ilProcessor) @@ -506,26 +633,109 @@ namespace Parser.Emitting ilProcessor.Emit(OpCodes.Callvirt, _getItemFromDictionary); } - private void EmitNumberLiteralExpression(BoundNumberLiteralExpression node, ILProcessor ilProcessor) + private void EmitNumberLiteralExpression(BoundNumberDoubleLiteralExpression node, ILProcessor ilProcessor) { ilProcessor.Emit(OpCodes.Ldc_R8, node.Value); - ilProcessor.Emit(OpCodes.Call, _doubleToMObject); } private void EmitStringLiteralExpression(BoundStringLiteralExpression node, ILProcessor ilProcessor) { ilProcessor.Emit(OpCodes.Ldstr, node.Value); - ilProcessor.Emit(OpCodes.Call, _stringToMObject); + } + + private BoundExpression? ConvertExpression(BoundExpression expression, TypeSymbol targetType) + { + var conversion = Conversion.Classify(expression.Type, targetType); + if (!conversion.Exists) + { + return null; + } + + if (conversion.IsIdentity) + { + return expression; + } + else + { + return BoundNodeFactory.Conversion(expression.Syntax, targetType, expression); + } + + } + + private BoundExpression RewriteFunctionCall(BoundFunctionCallExpression node, TypedFunctionSymbol function) + { + var numberOfArguments = node.Arguments.Length; + if (numberOfArguments != function.Parameters.Length) + { + throw new NotImplementedException($"Function '{function.Name}' expected {function.Parameters.Length} arguments, but was called with {numberOfArguments}."); + } + + var rewrittenArguments = ImmutableArray.CreateBuilder(numberOfArguments); + for (var i = 0; i < numberOfArguments; i++) + { + var argument = node.Arguments[i]; + var parameter = function.Parameters[i]; + var rewrittenArgument = ConvertExpression(argument, parameter.Type); + if (rewrittenArgument is null) + { + throw new NotImplementedException($"Argument number {i + 1} of function '{function.Name}' expects {parameter.Type}, but got {argument.Type}, and no conversion exists."); + + } + rewrittenArguments.Add(rewrittenArgument); + } + + return BoundNodeFactory.TypedFunctionCall(node.Syntax, function, rewrittenArguments.MoveToImmutable()); + } + + private void EmitTypedFunctionCallExpression(BoundTypedFunctionCallExpression node, ILProcessor ilProcessor) + { + var function = _builtInFunctions[node.Function]; + foreach (var argument in node.Arguments) + { + EmitExpression(argument, ilProcessor); + } + + ilProcessor.Emit(OpCodes.Call, function); + if (node.Function.ReturnType == TypeSymbol.Void) + { + ilProcessor.Emit(OpCodes.Ldnull); + } + else if (node.Function.ReturnType == TypeSymbol.MObject) + { + } + else + { + throw new Exception("Don't know how to cast function output."); + } } private void EmitFunctionCallExpression(BoundFunctionCallExpression node, ILProcessor ilProcessor) { if (node.Name.Kind == BoundNodeKind.IdentifierNameExpression && ((BoundIdentifierNameExpression)node.Name).Name == "disp") + { + var dispFunctionSymbol = BuiltInFunctions.Disp; + var rewrittenCall = RewriteFunctionCall(node, dispFunctionSymbol); + EmitExpression(rewrittenCall, ilProcessor); + //ilProcessor.Emit(OpCodes.Call, _dispReference); + //ilProcessor.Emit(OpCodes.Ldnull); + } else if (node.Name.Kind == BoundNodeKind.IdentifierNameExpression + && ((BoundIdentifierNameExpression)node.Name).Name == "len") { EmitExpression(node.Arguments[0], ilProcessor); - ilProcessor.Emit(OpCodes.Call, _dispReference); - ilProcessor.Emit(OpCodes.Ldnull); + ilProcessor.Emit(OpCodes.Call, _lenReference); + } + else if (node.Name.Kind == BoundNodeKind.TypedVariableExpression) + { + if (node.Arguments.Length > 1) + { + throw new Exception("Multi-dimensional array slicing is not supported."); + } + + var typedVariableExpression = (BoundTypedVariableExpression)node.Name; + EmitTypedVariableExpression(typedVariableExpression, ilProcessor); + EmitExpression(node.Arguments[0], ilProcessor); + ilProcessor.Emit(OpCodes.Call, _arraySliceReference); } else { @@ -559,11 +769,11 @@ namespace Parser.Emitting } } - private MethodInterfaceDescription ResolveFunction(BoundExpression expression) + private MethodInterfaceDescription ResolveFunction(BoundExpression node) { - if (expression.Kind == BoundNodeKind.IdentifierNameExpression) + if (node.Kind == BoundNodeKind.IdentifierNameExpression) { - var name = ((BoundIdentifierNameExpression)expression).Name; + var name = ((BoundIdentifierNameExpression)node).Name; if (_functions.TryGetValue(name, out var result)) { return result; @@ -574,7 +784,7 @@ namespace Parser.Emitting } else { - throw new NotImplementedException($"Dynamic functions calling not supported. Failed to resolve function call expression with kind '{expression.Kind}'."); + throw new NotImplementedException($"Dynamic functions calling not supported. Failed to resolve function call expression with kind '{node.Kind}'."); } } } diff --git a/Parser/Evaluator.cs b/Parser/Evaluator.cs index e4b56f6..160d175 100644 --- a/Parser/Evaluator.cs +++ b/Parser/Evaluator.cs @@ -251,7 +251,7 @@ namespace Parser BoundNodeKind.NamedFunctionHandleExpression => EvaluateNamedFunctionHandleExpression((BoundNamedFunctionHandleExpression)node), BoundNodeKind.NumberLiteralExpression => - EvaluateNumberLiteralExpression((BoundNumberLiteralExpression)node), + EvaluateNumberLiteralExpression((BoundNumberDoubleLiteralExpression)node), BoundNodeKind.StringLiteralExpression => EvaluateStringLiteralExpression((BoundStringLiteralExpression)node), BoundNodeKind.UnaryOperationExpression => @@ -420,7 +420,7 @@ namespace Parser }; } - private MObject? EvaluateNumberLiteralExpression(BoundNumberLiteralExpression node) + private MObject? EvaluateNumberLiteralExpression(BoundNumberDoubleLiteralExpression node) { return MObject.CreateDoubleNumber(node.Value); } diff --git a/Parser/Internal/DiagnosticsBag.cs b/Parser/Internal/DiagnosticsBag.cs index e81e7d0..574261f 100644 --- a/Parser/Internal/DiagnosticsBag.cs +++ b/Parser/Internal/DiagnosticsBag.cs @@ -107,5 +107,10 @@ namespace Parser.Internal { Report(span, $"Too many inputs in the call to '{functionName}'."); } + + internal void ReportForLoopWithoutVariable(TextSpan span) + { + Report(span, $"A 'for' loop must have a loop variable."); + } } } \ No newline at end of file diff --git a/Parser/Lowering/Lowerer.cs b/Parser/Lowering/Lowerer.cs index fbe3d05..95f8971 100644 --- a/Parser/Lowering/Lowerer.cs +++ b/Parser/Lowering/Lowerer.cs @@ -1,5 +1,4 @@ using Parser.Binding; -using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -10,6 +9,7 @@ namespace Parser.Lowering internal class Lowerer : BoundTreeRewriter { private int _labelNumber = 0; + private int _localVariableNumber = 0; private Lowerer() { @@ -21,6 +21,12 @@ namespace Parser.Lowering return new BoundLabel(name); } + private TypedVariableSymbol GenerateTypedLocalVariable(TypeSymbol typeSymbol) + { + var name = $"#{++_localVariableNumber}"; + return new TypedVariableSymbol(name, typeSymbol); + } + public override BoundStatement RewriteIfStatement(BoundIfStatement node) { // if cond @@ -80,6 +86,84 @@ namespace Parser.Lowering return RewriteBlockStatement(Block(node.Syntax, builder.ToArray())); } + public override BoundStatement RewriteForStatement(BoundForStatement node) + { + // for i = expr + // body + // end + // + // #array = expr + // #length = len(#array) + // #index = 0 + // i = #array(#index); + // LabelLoop: + // gotoFalse (#index < #length) LabelEnd + // body + // #index = #index + 1 + // goto LabelLoop + // LabelEnd: + var builder = ImmutableArray.CreateBuilder(); + var labelLoop = GenerateLabel(); + var labelEnd = GenerateLabel(); + var localArray = GenerateTypedLocalVariable(TypeSymbol.MObject); + var localLength = GenerateTypedLocalVariable(TypeSymbol.Int); + var localIndex = GenerateTypedLocalVariable(TypeSymbol.Int); + return RewriteBlockStatement(Block( + node.Syntax, + // #array = expr + TypedVariableDeclaration(node.Syntax, localArray, node.LoopedExpression), + // #length = len(#array) + TypedVariableDeclaration( + node.Syntax, + localLength, + FunctionCall( + node.Syntax, + Identifier(node.Syntax, "len"), + new[] { (BoundExpression)TypedVariableExpression(node.Syntax, localArray) }.ToImmutableArray())), + // #index = 0 + TypedVariableDeclaration(node.Syntax, localIndex, NumberDoubleLiteral(node.Syntax, 0.0)), + // i = #array(#index); + ExpressionStatement( + node.Syntax, + Assignment( + node.Syntax, + node.LoopVariable, + FunctionCall( + node.Syntax, + TypedVariableExpression(node.Syntax, localArray), + new[] { (BoundExpression)TypedVariableExpression(node.Syntax, localIndex) }.ToImmutableArray())), + discardResult: true), + // LabelLoop: + LabelStatement(node.Syntax, labelLoop), + // gotoFalse (#index < #length) LabelEnd + GotoIfFalse( + node.Syntax, + BinaryOperation( + node.Syntax, + TypedVariableExpression(node.Syntax, localIndex), + BoundBinaryOperator.GetOperator(TokenKind.LessToken, TypeSymbol.Int, TypeSymbol.Int)!, + TypedVariableExpression(node.Syntax, localLength)), + labelEnd), + // body + node.Body, + // #index = #index + 1; + ExpressionStatement( + node.Syntax, + Assignment( + node.Syntax, + TypedVariableExpression(node.Syntax, localIndex), + BinaryOperation( + node.Syntax, + TypedVariableExpression(node.Syntax, localIndex), + BoundBinaryOperator.GetOperator(TokenKind.PlusToken, TypeSymbol.Int, TypeSymbol.Int)!, + NumberIntLiteral(node.Syntax, 1))), + discardResult: true), + // goto LabelLoop + Goto(node.Syntax, labelLoop), + // LabelEnd: + LabelStatement(node.Syntax, labelEnd))); + } + public static BoundBlockStatement Lower(BoundStatement statement) { var lowerer = new Lowerer(); diff --git a/Parser/MFunctions/MHelpers.cs b/Parser/MFunctions/MHelpers.cs index fc975f8..d4fa446 100644 --- a/Parser/MFunctions/MHelpers.cs +++ b/Parser/MFunctions/MHelpers.cs @@ -13,6 +13,18 @@ namespace Parser.MFunctions } } + public static int Len(MObject? obj) + { + return obj switch + { + MDoubleMatrix m => m.RowCount * m.ColumnCount, + MDoubleNumber _ => 1, + MLogical _ => 1, + MCharArray c => c.Chars.Length, + _ => throw new System.Exception($"Unknown MObject type {obj.GetType()}"), + }; + } + public static bool ToBool(MObject operand) { return operand switch @@ -20,6 +32,7 @@ namespace Parser.MFunctions MDoubleNumber { Value: var value } => value != 0.0, MLogical { Value: var value } => value, MCharArray { Chars: var value } => value.Length > 0, + MDoubleMatrix m => m.RowCount > 0 && m.ColumnCount > 0, _ => throw new System.Exception($"Unknown MObject type {operand.GetType()}"), }; } diff --git a/Parser/MFunctions/MOperations.cs b/Parser/MFunctions/MOperations.cs index f454b3f..2fccee3 100644 --- a/Parser/MFunctions/MOperations.cs +++ b/Parser/MFunctions/MOperations.cs @@ -1,9 +1,34 @@ using Parser.Objects; +using System; namespace Parser.MFunctions { public static class MOperations { + public static MObject? Colon(MObject left, MObject right) + { + if (left is MDoubleNumber { Value: var lValue } + && right is MDoubleNumber { Value: var rValue }) + { + var array = CreateRangeMatrix(lValue, rValue); + return MObject.CreateDoubleMatrix(array); + } + + return null; + } + + private static double[,] CreateRangeMatrix(double lValue, double rValue) + { + var length = (int)Math.Round(rValue - lValue) + 1; + var result = new double[1, length]; + for (var i = 0; i < length; i++) + { + result[0, i] = lValue + i; + } + + return result; + } + public static MObject? Plus(MObject left, MObject right) { if (left is MDoubleNumber { Value: var lValue } @@ -101,5 +126,17 @@ namespace Parser.MFunctions return null; } + + public static MObject? ArraySlice(MObject array, MObject range) + { + if (array is MDoubleMatrix m + && range is MDoubleNumber { Value: var doubleIndex }) + { + var index = (int)doubleIndex; + return MObject.CreateDoubleNumber(m[index]); + } + + return null; + } } } diff --git a/Parser/Objects/MDoubleNumber.cs b/Parser/Objects/MDoubleNumber.cs index bb7f76f..b7bd105 100644 --- a/Parser/Objects/MDoubleNumber.cs +++ b/Parser/Objects/MDoubleNumber.cs @@ -1,7 +1,50 @@ using System.Globalization; +using System.Text; namespace Parser.Objects { + public class MDoubleMatrix : MObject + { + private MDoubleMatrix(double[,] matrix) + { + Matrix = matrix; + } + + public double[,] Matrix { get; } + + public int RowCount { get; } + + public int ColumnCount { get; } + + public ref double this[int i, int j] => ref Matrix[i, j]; + + public ref double this[int i] => ref Matrix[i % RowCount, i / RowCount]; + + public override string ToString() + { + var sb = new StringBuilder(); + for (var i = 0; i < RowCount; i++) + { + for (var j = 0; j < ColumnCount; j++) + { + if (j > 0) + { + sb.Append(' '); + } + sb.Append(Matrix[i, j]); + } + + sb.AppendLine(); + } + return sb.ToString(); + } + + public static MDoubleMatrix Create(double[,] matrix) + { + return new MDoubleMatrix(matrix); + } + } + public class MDoubleNumber : MObject { private MDoubleNumber(double value) diff --git a/Parser/Objects/MObject.cs b/Parser/Objects/MObject.cs index bebc8dc..0df472f 100644 --- a/Parser/Objects/MObject.cs +++ b/Parser/Objects/MObject.cs @@ -1,4 +1,6 @@ -namespace Parser.Objects +using System; + +namespace Parser.Objects { public abstract class MObject { @@ -7,6 +9,16 @@ return MDoubleNumber.Create(value); } + public static MDoubleNumber CreateIntNumber(int value) + { + return MDoubleNumber.Create(value); + } + + public static MDoubleMatrix CreateDoubleMatrix(double[,] matrix) + { + return MDoubleMatrix.Create(matrix); + } + public static MCharArray CreateCharArray(char[] chars) { return MCharArray.Create(chars); diff --git a/examples/helloworld/hello.m b/examples/helloworld/hello.m index d13622c..e5c74d8 100644 --- a/examples/helloworld/hello.m +++ b/examples/helloworld/hello.m @@ -5,6 +5,11 @@ f(x); x = 3; f(x); +% for i = 1:10 +% disp(i); +% end + + function f(x) disp('X was'); disp(x);