Ejemplo n.º 1
0
        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);
            }
        }
Ejemplo n.º 4
0
        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);
            }
        }
Ejemplo n.º 6
0
        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);
            }
        }
Ejemplo n.º 8
0
        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);
            }
        }