private DataType InferBinaryExpressionType( BinaryExpressionSyntax binaryExpression) { InferExpressionType(binaryExpression.LeftOperand); var leftType = binaryExpression.LeftOperand.Type; var @operator = binaryExpression.Operator; InferExpressionType(binaryExpression.RightOperand); var rightType = binaryExpression.RightOperand.Type; // If either is unknown, then we can't know whether there is a a problem. // Note that the operator could be overloaded if (leftType == DataType.Unknown || rightType == DataType.Unknown) { return(binaryExpression.Type = DataType.Unknown); } bool compatible; switch (@operator) { case BinaryOperator.Plus: case BinaryOperator.Minus: case BinaryOperator.Asterisk: case BinaryOperator.Slash: compatible = NumericOperatorTypesAreCompatible(ref binaryExpression.LeftOperand, ref binaryExpression.RightOperand, null); binaryExpression.Type = compatible ? leftType : DataType.Unknown; break; case BinaryOperator.EqualsEquals: case BinaryOperator.NotEqual: case BinaryOperator.LessThan: case BinaryOperator.LessThanOrEqual: case BinaryOperator.GreaterThan: case BinaryOperator.GreaterThanOrEqual: compatible = (leftType == DataType.Bool && rightType == DataType.Bool) || NumericOperatorTypesAreCompatible(ref binaryExpression.LeftOperand, ref binaryExpression.RightOperand, null); binaryExpression.Type = DataType.Bool; break; case BinaryOperator.And: case BinaryOperator.Or: compatible = leftType == DataType.Bool && rightType == DataType.Bool; binaryExpression.Type = DataType.Bool; break; default: throw NonExhaustiveMatchException.ForEnum(@operator); } if (!compatible) { diagnostics.Add(TypeError.OperatorCannotBeAppliedToOperandsOfType(file, binaryExpression.Span, @operator, binaryExpression.LeftOperand.Type, binaryExpression.RightOperand.Type)); } return(binaryExpression.Type); }
public override string ToString() { switch (Fixty) { case UnaryOperatorFixity.Prefix: return($"{Operator.ToSymbolString()}{Operand}"); case UnaryOperatorFixity.Postfix: return($"{Operand}{Operator.ToSymbolString()}"); default: throw NonExhaustiveMatchException.ForEnum(Fixty); } }
// Useful for debugging public override string ToString() { switch (State) { case PromiseState.Pending: return("⧼pending⧽"); case PromiseState.InProgress: return("⧼in progress⧽"); case PromiseState.Fulfilled: return(DataType.ToString()); default: throw NonExhaustiveMatchException.ForEnum(State); } }
private static bool CompileCode( Project project, string cacheDir, string codePath, object consoleLock) { var compiler = new CLangCompiler(); var runtimeLibrarySourcePath = System.IO.Path.Combine(cacheDir, CodeEmitter.RuntimeLibraryCodeFileName); File.WriteAllText(runtimeLibrarySourcePath, CodeEmitter.RuntimeLibraryCode, Encoding.UTF8); var runtimeLibraryHeaderPath = System.IO.Path.Combine(cacheDir, CodeEmitter.RuntimeLibraryHeaderFileName); File.WriteAllText(runtimeLibraryHeaderPath, CodeEmitter.RuntimeLibraryHeader, Encoding.UTF8); var sourceFiles = new[] { codePath, runtimeLibrarySourcePath }; var headerSearchPaths = new[] { cacheDir }; string outputPath; switch (project.Template) { case ProjectTemplate.App: { outputPath = System.IO.Path.ChangeExtension(codePath, "exe"); } break; case ProjectTemplate.Lib: { outputPath = System.IO.Path.ChangeExtension(codePath, "dll"); } break; default: throw NonExhaustiveMatchException.ForEnum(project.Template); } lock (consoleLock) { Console.WriteLine($"CLang Compiling {project.Name} ({project.Path})..."); var exitCode = compiler.Compile(ConsoleCompilerOutput.Instance, sourceFiles, headerSearchPaths, outputPath); return(exitCode == 0); } }
private Value ConvertUnaryExpressionToValue( BlockBuilder currentBlock, UnaryExpressionSyntax expression) { switch (expression.Operator) { case UnaryOperator.Not: var operand = ConvertToOperand(currentBlock, expression.Operand); return(new UnaryOperation(expression.Operator, operand, expression.Span)); case UnaryOperator.Plus: // This is a no-op return(ConvertToValue(currentBlock, expression.Operand)); default: throw NonExhaustiveMatchException.ForEnum(expression.Operator); } }
private static string EmitCode(Project project, Package package, string cacheDir) { var emittedPackages = new HashSet <Package>(); var packagesToEmit = new Queue <Package>(); packagesToEmit.Enqueue(package); var codeEmitter = new CodeEmitter(); while (packagesToEmit.TryDequeue(out var currentPackage)) { if (!emittedPackages.Contains(currentPackage)) { codeEmitter.Emit(currentPackage); emittedPackages.Add(currentPackage); packagesToEmit.EnqueueRange(currentPackage.References.Values); } } string outputPath; switch (project.Template) { case ProjectTemplate.App: { outputPath = System.IO.Path.Combine(cacheDir, "program.c"); } break; case ProjectTemplate.Lib: { outputPath = System.IO.Path.Combine(cacheDir, "lib.c"); } break; default: throw NonExhaustiveMatchException.ForEnum(project.Template); } File.WriteAllText(outputPath, codeEmitter.GetEmittedCode(), Encoding.UTF8); return(outputPath); }
private Value ConvertBinaryExpressionToValue( BlockBuilder exitBlock, BinaryExpressionSyntax expression) { switch (expression.Operator) { case BinaryOperator.Plus: case BinaryOperator.Minus: case BinaryOperator.Asterisk: case BinaryOperator.Slash: case BinaryOperator.EqualsEquals: case BinaryOperator.NotEqual: case BinaryOperator.LessThan: case BinaryOperator.LessThanOrEqual: case BinaryOperator.GreaterThan: case BinaryOperator.GreaterThanOrEqual: { // TODO handle calls to overloaded operators var leftOperand = ConvertToOperand(exitBlock, expression.LeftOperand); var rightOperand = ConvertToOperand(exitBlock, expression.RightOperand); // What matters is the type we are operating on, for comparisons, that is different than the result type which is bool var operandType = (SimpleType)expression.LeftOperand.Type; return(new BinaryOperation(leftOperand, expression.Operator, rightOperand, operandType)); } case BinaryOperator.And: case BinaryOperator.Or: { // TODO handle calls to overloaded operators // TODO handle short circuiting if needed var leftOperand = ConvertToOperand(exitBlock, expression.LeftOperand); var rightOperand = ConvertToOperand(exitBlock, expression.RightOperand); return(new BinaryOperation(leftOperand, expression.Operator, rightOperand, (SimpleType)expression.Type)); } default: throw NonExhaustiveMatchException.ForEnum(expression.Operator); } }
private DataType InferUnaryExpressionType(UnaryExpressionSyntax unaryExpression) { var operandType = InferExpressionType(unaryExpression.Operand); var @operator = unaryExpression.Operator; // If either is unknown, then we can't know whether there is a a problem // (technically not true, for example, we could know that one arg should // be a bool and isn't) if (operandType == DataType.Unknown) { return(unaryExpression.Type = DataType.Unknown); } bool typeError; switch (@operator) { case UnaryOperator.Not: typeError = operandType != DataType.Bool; unaryExpression.Type = DataType.Bool; break; case UnaryOperator.At: typeError = false; // TODO check that the expression can have a pointer taken if (operandType is Metatype) { unaryExpression.Type = DataType.Type; // constructing a type } else { unaryExpression.Type = new PointerType(operandType); // taking the address of something } break; case UnaryOperator.Question: typeError = false; // TODO check that the expression can have a pointer taken unaryExpression.Type = new PointerType(operandType); break; case UnaryOperator.Caret: switch (operandType) { case PointerType pointerType: unaryExpression.Type = pointerType.Referent; typeError = false; break; default: unaryExpression.Type = DataType.Unknown; typeError = true; break; } break; case UnaryOperator.Minus: switch (operandType) { case IntegerConstantType integerType: typeError = false; unaryExpression.Type = integerType; break; case SizedIntegerType sizedIntegerType: typeError = false; unaryExpression.Type = sizedIntegerType; break; default: unaryExpression.Type = DataType.Unknown; typeError = true; break; } break; default: throw NonExhaustiveMatchException.ForEnum(@operator); } if (typeError) { diagnostics.Add(TypeError.OperatorCannotBeAppliedToOperandOfType(file, unaryExpression.Span, @operator, operandType)); } return(unaryExpression.Type); }
/// <summary> /// Converts an expression of type `void` or `never` to a statement /// </summary> private BlockBuilder ConvertExpressionToStatement(BlockBuilder currentBlock, ExpressionSyntax expression, BlockBuilder breakToBlock) { // expression m switch (expression) { case UnaryExpressionSyntax _: case BinaryExpressionSyntax _: throw new NotImplementedException(); case InvocationSyntax invocation: //return ConvertToAssignmentStatement(currentBlock, expression); currentBlock.AddAction(ConvertInvocationToValue(currentBlock, invocation), invocation.Span); return(currentBlock); case ReturnExpressionSyntax returnExpression: if (returnExpression.ReturnValue != null) { currentBlock.AddAssignment(graph.ReturnVariable.Reference, ConvertToValue(currentBlock, returnExpression.ReturnValue), returnExpression.ReturnValue.Span); } currentBlock.AddReturn(); // There is no exit from a return block, hence null for exit block return(null); case ForeachExpressionSyntax _: case WhileExpressionSyntax _: throw new NotImplementedException(); case LoopExpressionSyntax loopExpression: var loopEntry = graph.NewEntryBlock(currentBlock); var breakTo = graph.NewBlock(); var loopExit = ConvertExpressionToStatement(loopEntry, loopExpression.Block, breakTo); loopExit?.AddGoto(loopEntry); return(breakTo); case BreakExpressionSyntax _: currentBlock.AddGoto(breakToBlock ?? throw new InvalidOperationException()); return(null); case IfExpressionSyntax ifExpression: var condition = ConvertToOperand(currentBlock, ifExpression.Condition); var thenEntry = graph.NewBlock(); var thenExit = ConvertExpressionToStatement(thenEntry, ifExpression.ThenBlock, breakToBlock); BlockBuilder elseEntry; BlockBuilder exit = null; if (ifExpression.ElseClause == null) { elseEntry = exit = graph.NewBlock(); thenExit?.AddGoto(exit); } else { elseEntry = graph.NewBlock(); var elseExit = ConvertExpressionToStatement(elseEntry, ifExpression.ElseClause, breakToBlock); if (thenExit != null || elseExit != null) { exit = graph.NewBlock(); thenExit?.AddGoto(exit); elseExit?.AddGoto(exit); } } currentBlock.AddIf(condition, thenEntry, elseEntry); return(exit); case BlockSyntax block: foreach (var statementInBlock in block.Statements) { currentBlock = ConvertToStatement(currentBlock, statementInBlock, breakToBlock); } return(currentBlock); case UnsafeExpressionSyntax unsafeExpression: return(ConvertToStatement(currentBlock, unsafeExpression.Expression, breakToBlock)); case AssignmentExpressionSyntax assignmentExpression: { var place = ConvertToPlace(assignmentExpression.LeftOperand); var value = ConvertToValue(currentBlock, assignmentExpression.RightOperand); if (assignmentExpression.Operator != AssignmentOperator.Direct) { var type = (SimpleType)assignmentExpression.RightOperand.Type; var rightOperand = ConvertToOperand(currentBlock, value, type); BinaryOperator binaryOperator; switch (assignmentExpression.Operator) { case AssignmentOperator.Plus: binaryOperator = BinaryOperator.Plus; break; case AssignmentOperator.Minus: binaryOperator = BinaryOperator.Minus; break; case AssignmentOperator.Asterisk: binaryOperator = BinaryOperator.Asterisk; break; case AssignmentOperator.Slash: binaryOperator = BinaryOperator.Slash; break; default: throw NonExhaustiveMatchException.ForEnum(assignmentExpression.Operator); } value = new BinaryOperation(place, binaryOperator, rightOperand, type); } currentBlock.AddAssignment(place, value, assignmentExpression.Span); return(currentBlock); } case ResultExpressionSyntax resultExpression: // Must be an expression of type `never` return(ConvertExpressionToStatement(currentBlock, resultExpression.Expression, breakToBlock)); default: throw NonExhaustiveMatchException.For(expression); } }
private string ConvertValue(Value value) { switch (value) { case IntegerConstant integer: return($"({ConvertType(integer.Type)}){{{integer.Value}}}"); case Utf8BytesConstant utf8BytesConstant: return($"((_byte*)u8\"{utf8BytesConstant.Value.Escape()}\")"); case BooleanConstant boolean: var booleanValue = boolean.Value ? 1 : 0; return($"({ConvertType(boolean.Type)}){{{booleanValue}}}"); case FunctionCall functionCall: { var mangledName = nameMangler.Mangle(functionCall.FunctionName); var arguments = functionCall.Self.YieldValue().Concat(functionCall.Arguments).Select(ConvertValue); return($"{mangledName}__{functionCall.Arity}({string.Join(", ", arguments)})"); } case ConstructorCall constructorCall: { var typeName = nameMangler.Mangle(constructorCall.Type); var selfArgument = $"({typeName}){{&{typeName}___vtable, malloc(sizeof({typeName}___Self))}}"; var arguments = selfArgument.YieldValue().Concat(constructorCall.Arguments.Select(ConvertValue)); return($"{typeName}___new__{constructorCall.Arity}({string.Join(", ", arguments)})"); } case DeclaredValue declaredValue: return(nameMangler.Mangle(declaredValue.Name)); case FieldAccessValue fieldAccess: return($"{ConvertValue(fieldAccess.Expression)}.{nameMangler.Mangle(fieldAccess.Field.UnqualifiedName)}"); case Place place: return(ConvertPlace(place)); case VirtualFunctionCall virtualCall: { var self = ConvertValue(virtualCall.Self); var mangledName = nameMangler.Mangle(virtualCall.FunctionName); var arity = virtualCall.Arguments.Count + 1; var arguments = virtualCall.Arguments.Select(ConvertValue).Prepend(self); return($"{self}._vtable->{mangledName}__{arity}({string.Join(", ", arguments)})"); } case BinaryOperation binaryOperation: { var left = ConvertValue(binaryOperation.LeftOperand); var right = ConvertValue(binaryOperation.RightOperand); var operationType = nameMangler.Mangle(binaryOperation.Type); string @operator; switch (binaryOperation.Operator) { // If a binary operator was emitting for a boolean operation, // then it doesn't short circuit, we just call the function case BinaryOperator.And: @operator = "and"; break; case BinaryOperator.Or: @operator = "or"; break; case BinaryOperator.Plus: @operator = "add"; break; case BinaryOperator.Minus: @operator = "sub"; break; case BinaryOperator.Asterisk: @operator = "mul"; break; case BinaryOperator.Slash: @operator = "div"; break; case BinaryOperator.EqualsEquals: @operator = "eq"; break; case BinaryOperator.NotEqual: @operator = "ne"; break; case BinaryOperator.LessThan: @operator = "lt"; break; case BinaryOperator.LessThanOrEqual: @operator = "lte"; break; case BinaryOperator.GreaterThan: @operator = "gt"; break; case BinaryOperator.GreaterThanOrEqual: @operator = "gte"; break; default: throw NonExhaustiveMatchException.ForEnum(binaryOperation.Operator); } return($"{operationType}__{@operator}({left}, {right})"); } default: throw NonExhaustiveMatchException.For(value); } }