/// <summary> /// Convert an expression that yields a value and assign that value into <paramref name="resultPlace"/>. /// </summary> private void ConvertIntoPlace(IExpression expression, Place resultPlace) { switch (expression) { default: throw ExhaustiveMatch.Failed(expression); case ILoopExpression _: case IWhileExpression _: case IForeachExpression _: case IReturnExpression _: case IBreakExpression _: case INextExpression _: case IIfExpression _: case IUnsafeExpression _: case IBlockExpression _: case INoneLiteralExpression _: throw new NotImplementedException($"ConvertIntoPlace({expression.GetType().Name}, Place) Not Implemented."); case ISelfExpression exp: { // This occurs when the source code contains a simple assignment like `x = self` var symbol = exp.ReferencedSymbol; var variable = graph.VariableFor(symbol).Reference(exp.Span); currentBlock !.Add(new AssignmentInstruction(resultPlace, variable, exp.Span, CurrentScope)); } break; case IImplicitOptionalConversionExpression exp: { var operand = ConvertToOperand(exp.Expression); currentBlock !.Add(new SomeInstruction(resultPlace, exp.ConvertToType, operand, exp.Span, CurrentScope)); } break; case IAssignmentExpression exp: throw new NotImplementedException("Assignments don't have a result"); case INameExpression exp: { // This occurs when the source code contains a simple assignment like `x = y` var symbol = exp.ReferencedSymbol; var variable = graph.VariableFor(symbol).Reference(exp.Span); currentBlock !.Add(new AssignmentInstruction(resultPlace, variable, exp.Span, CurrentScope)); } break; case IBorrowExpression exp: ConvertIntoPlace(exp.Referent, resultPlace); break; case IShareExpression exp: ConvertIntoPlace(exp.Referent, resultPlace); break; case IMoveExpression exp: ConvertIntoPlace(exp.Referent, resultPlace); break; case IImplicitImmutabilityConversionExpression exp: ConvertIntoPlace(exp.Expression, resultPlace); break; case IBinaryOperatorExpression exp: { var resultType = exp.DataType.Assigned().Known(); var operandType = exp.LeftOperand.DataType.Assigned().Known(); var leftOperand = ConvertToOperand(exp.LeftOperand); var rightOperand = ConvertToOperand(exp.RightOperand); switch (exp.Operator) { default: throw ExhaustiveMatch.Failed(expression); case BinaryOperator.DotDot: case BinaryOperator.LessThanDotDot: case BinaryOperator.DotDotLessThan: case BinaryOperator.LessThanDotDotLessThan: throw new NotImplementedException("Range operator control flow not implemented"); #region Logical Operators case BinaryOperator.And: // TODO handle calls to overloaded operators // TODO handle short circuiting if needed currentBlock !.Add(new BooleanLogicInstruction(resultPlace, And, leftOperand, rightOperand, CurrentScope)); break; case BinaryOperator.Or: // TODO handle calls to overloaded operators // TODO handle short circuiting if needed currentBlock !.Add(new BooleanLogicInstruction(resultPlace, Or, leftOperand, rightOperand, CurrentScope)); break; #endregion #region Binary Math Operators case BinaryOperator.Plus: currentBlock !.Add(new NumericInstruction(resultPlace, Add, (NumericType)resultType, leftOperand, rightOperand, CurrentScope)); break; case BinaryOperator.Minus: currentBlock !.Add(new NumericInstruction(resultPlace, Subtract, (NumericType)resultType, leftOperand, rightOperand, CurrentScope)); break; case BinaryOperator.Asterisk: currentBlock !.Add(new NumericInstruction(resultPlace, Multiply, (NumericType)resultType, leftOperand, rightOperand, CurrentScope)); break; case BinaryOperator.Slash: currentBlock !.Add(new NumericInstruction(resultPlace, Divide, (NumericType)resultType, leftOperand, rightOperand, CurrentScope)); break; #endregion #region Comparisons case BinaryOperator.EqualsEquals: currentBlock !.Add(new CompareInstruction(resultPlace, Equal, (NumericType)operandType, leftOperand, rightOperand, CurrentScope)); break; case BinaryOperator.NotEqual: currentBlock !.Add(new CompareInstruction(resultPlace, NotEqual, (NumericType)operandType, leftOperand, rightOperand, CurrentScope)); break; case BinaryOperator.LessThan: currentBlock !.Add(new CompareInstruction(resultPlace, LessThan, (NumericType)operandType, leftOperand, rightOperand, CurrentScope)); break; case BinaryOperator.LessThanOrEqual: currentBlock !.Add(new CompareInstruction(resultPlace, LessThanOrEqual, (NumericType)operandType, leftOperand, rightOperand, CurrentScope)); break; case BinaryOperator.GreaterThan: currentBlock !.Add(new CompareInstruction(resultPlace, GreaterThan, (NumericType)operandType, leftOperand, rightOperand, CurrentScope)); break; case BinaryOperator.GreaterThanOrEqual: currentBlock !.Add(new CompareInstruction(resultPlace, GreaterThanOrEqual, (NumericType)operandType, leftOperand, rightOperand, CurrentScope)); break; #endregion Comparisons } } break; case IUnaryOperatorExpression exp: { var type = exp.DataType.Assigned().Known(); var operand = ConvertToOperand(exp.Operand); switch (exp.Operator) { default: throw ExhaustiveMatch.Failed(expression); case UnaryOperator.Not: case UnaryOperator.Plus: // TODO don't even allow unary plus in IL or AST, it is a noop throw new NotImplementedException($"ConvertToOperand({expression.GetType().Name}, Place) Not Implemented for {exp.Operator}."); case UnaryOperator.Minus: currentBlock !.Add(new NegateInstruction(resultPlace, (NumericType)type, operand, exp.Span, CurrentScope)); break; } } break; case IFieldAccessExpression exp: { var context = ConvertToOperand(exp.Context); var field = exp.ReferencedSymbol; currentBlock !.Add(new FieldAccessInstruction(resultPlace, context, field, exp.Span, CurrentScope)); } break; case IFunctionInvocationExpression exp: { var functionName = exp.ReferencedSymbol; var args = exp.Arguments.Select(ConvertToOperand).ToFixedList(); currentBlock !.Add(CallInstruction.ForFunction(resultPlace, functionName, args, exp.Span, CurrentScope)); } break; case IMethodInvocationExpression exp: { var methodName = exp.ReferencedSymbol; var target = ConvertToOperand(exp.Context); var args = exp.Arguments.Select(ConvertToOperand).ToFixedList(); if (exp.Context.DataType is ReferenceType) { currentBlock !.Add(new CallVirtualInstruction(resultPlace, target, methodName, args, exp.Span, CurrentScope)); } else { currentBlock !.Add(CallInstruction.ForMethod(resultPlace, target, methodName, args, exp.Span, CurrentScope)); } } break; case INewObjectExpression exp: { var constructor = exp.ReferencedSymbol; var args = exp.Arguments.Select(ConvertToOperand).ToFixedList(); var constructedType = (ObjectType)exp.DataType.Known(); currentBlock !.Add(new NewObjectInstruction(resultPlace, constructor, constructedType, args, exp.Span, CurrentScope)); } break; case IStringLiteralExpression exp: currentBlock !.Add(new LoadStringInstruction(resultPlace, exp.Value, exp.Span, CurrentScope)); break; case IBoolLiteralExpression exp: currentBlock !.Add(new LoadBoolInstruction(resultPlace, exp.Value, exp.Span, CurrentScope)); break; case IImplicitNumericConversionExpression exp: { if (exp.Expression.DataType.Assigned().Known() is IntegerConstantType constantType) { currentBlock !.Add(new LoadIntegerInstruction(resultPlace, constantType.Value, (IntegerType)exp.DataType.Assigned().Known(), exp.Span, CurrentScope)); } else { currentBlock !.Add(new ConvertInstruction(resultPlace, ConvertToOperand(exp.Expression), (NumericType)exp.Expression.DataType.Assigned().Known(), exp.ConvertToType, exp.Span, CurrentScope)); } } break; case IIntegerLiteralExpression exp: throw new InvalidOperationException( "Integer literals should have an implicit conversion around them"); case IImplicitNoneConversionExpression exp: currentBlock !.Add(new LoadNoneInstruction(resultPlace, exp.ConvertToType, exp.Span, CurrentScope)); break; } }