Beispiel #1
0
 public bool VisitDereference(Dereference dereference)
 {
     if (!IsExpressionValid(dereference.Expression))
     {
         return(false);
     }
     return(Typer.GetExpressionType(_context, _environment, dereference.Expression) != null);
 }
Beispiel #2
0
        public static IType GetExpressionType(
            Context context,
            Scope environment,
            IExpression expression)
        {
            var self = new Typer(context, environment);

            return(self.GetExpressionType(expression));
        }
Beispiel #3
0
        public bool VisitArrayLiteral(ArrayLiteral arrayLiteral)
        {
            var isValid = arrayLiteral.Elements.Aggregate(
                true,
                (current, element) => current & IsExpressionValid(element));

            if (!isValid)
            {
                return(false);
            }

            return(Typer.GetExpressionType(_context, _environment, arrayLiteral) != null);
        }
Beispiel #4
0
        public bool VisitFunctionCall(FunctionCall functionCall)
        {
            var calleeType = Typer.GetExpressionType(_context, _environment, functionCall.Callee);

            if (calleeType == null)
            {
                return(false);
            }
            if (!(calleeType is FunctionType))
            {
                _context.Error(
                    functionCall.Span,
                    $"tried to call a non-callable type \"{calleeType}\"");
                return(false);
            }

            var calleeFunctionType = (FunctionType)calleeType;

            if (functionCall.Arity != calleeFunctionType.Arity)
            {
                var s         = calleeFunctionType.Arity == 1 ? "" : "s";
                var wasOrWere = functionCall.Arity == 1 ? "was" : "were";
                _context.Error(
                    functionCall.Callee.Span,
                    $"expected {calleeFunctionType.Arity} argument{s}, " +
                    $"but {functionCall.Arity} {wasOrWere} given");
            }

            var argumentsToCheck            = Math.Min(functionCall.Arity, calleeFunctionType.Arity);
            var allArgumentsHaveCorrectType = true;

            for (var i = 0; i < argumentsToCheck; i++)
            {
                var argument      = functionCall.Arguments[i];
                var argumentType  = Typer.GetExpressionType(_context, _environment, argument);
                var parameterType = calleeFunctionType.ParameterTypes[i];
                if (parameterType.IsSame(argumentType))
                {
                    continue;
                }
                _context.Error(
                    argument.Span,
                    "type mismatch between argument and parameter; " +
                    $"expected \"{parameterType}\", but found \"{argumentType}\"");
                allArgumentsHaveCorrectType = false;
            }

            return(allArgumentsHaveCorrectType);
        }
Beispiel #5
0
        public bool VisitAddressOf(AddressOf addressOf)
        {
            if (!IsExpressionValid(addressOf.Expression))
            {
                return(false);
            }

            if (!SemanticAnalyzer.IsLValue(addressOf.Expression))
            {
                _context.Error(addressOf.Span, "cannot get the address of a temporary value");
                return(false);
            }

            return(Typer.GetExpressionType(_context, _environment, addressOf.Expression) != null);
        }
Beispiel #6
0
        public bool VisitInfixOperation(InfixOperation infixOperation)
        {
            if (!(IsExpressionValid(infixOperation.LeftOperand) &&
                  IsExpressionValid(infixOperation.RightOperand)))
            {
                return(false);
            }

            if (infixOperation.LeftOperand is InfixOperation child &&
                infixOperation.Operator.Node != child.Operator.Node)
            {
                _context.Error(
                    infixOperation.Operator.Span,
                    "operator precedence is not supported; parentheses required");
            }

            var leftOperandType = Typer.GetExpressionType(
                _context,
                _environment,
                infixOperation.LeftOperand);
            var rightOperandType = Typer.GetExpressionType(
                _context,
                _environment,
                infixOperation.RightOperand);

            if (leftOperandType != null &&
                leftOperandType.GetInfixOperationResultType(infixOperation.Operator.Node) == null)
            {
                _context.Error(
                    infixOperation.Operator.Span,
                    $"infix operation \"{infixOperation.Operator.Node}\" " +
                    $"is not defined for types \"{leftOperandType}\" and \"{rightOperandType}\"");
                return(false);
            }

            if (leftOperandType != null && leftOperandType.IsSame(rightOperandType))
            {
                return(true);
            }

            _context.Error(
                infixOperation.Operator.Span,
                "type mismatch between operands of a infix expression; " +
                $"left is \"{leftOperandType}\", but right is \"{rightOperandType}\"");
            return(false);
        }
Beispiel #7
0
        private void CheckIfCase(IExpression condition, Block body)
        {
            if (!ExpressionAnalyzer.IsExpressionValid(_context, _currentScope, condition))
            {
                return;
            }

            var conditionType = Typer.GetExpressionType(_context, _currentScope, condition);

            if (conditionType != null && !(conditionType is BooleanType))
            {
                _context.Error(
                    condition.Span,
                    "condition does not evaluate to \"bool\" type");
            }

            AnalyzeBlock(body);
        }
Beispiel #8
0
        public bool VisitSubscriptExpression(SubscriptExpression subscriptExpression)
        {
            if (!IsExpressionValid(subscriptExpression.Operand) ||
                !IsExpressionValid(subscriptExpression.Index))
            {
                return(false);
            }

            var operandType = Typer.GetExpressionType(
                _context,
                _environment,
                subscriptExpression.Operand);

            if (operandType == null)
            {
                return(false);
            }
            if (!(operandType is ArrayType))
            {
                _context.Error(
                    subscriptExpression.Span,
                    $"cannot index into a non-array type \"{operandType}\"");
                return(false);
            }

            var indexType = Typer.GetExpressionType(
                _context,
                _environment,
                subscriptExpression.Index);

            if (indexType == null)
            {
                return(false);
            }
            if (!(indexType is IntegerType))
            {
                _context.Error(
                    subscriptExpression.Span,
                    $"type \"{operandType}\" cannot be indexed by type \"{indexType}\"");
                return(false);
            }

            return(true);
        }
Beispiel #9
0
        public void VisitAssignmentStatement(AssignmentStatement assignmentStatement)
        {
            if (!ExpressionAnalyzer.IsExpressionValid(
                    _context,
                    _currentScope,
                    assignmentStatement.Target) ||
                !ExpressionAnalyzer.IsExpressionValid(
                    _context,
                    _currentScope,
                    assignmentStatement.Value))
            {
                return;
            }

            if (!IsLValue(assignmentStatement.Target))
            {
                _context.Error(
                    assignmentStatement.Target.Span,
                    "left-hand of the assignment is not assignable");
                return;
            }

            var targetType = Typer.GetExpressionType(
                _context,
                _currentScope,
                assignmentStatement.Target);
            var valueType = Typer.GetExpressionType(
                _context,
                _currentScope,
                assignmentStatement.Value);

            if (targetType == null || valueType == null)
            {
                return;
            }
            if (!targetType.IsSame(valueType))
            {
                _context.Error(
                    assignmentStatement.Value.Span,
                    $"type mismatch; expected \"{targetType}\", but found \"{valueType}\"");
            }
        }
Beispiel #10
0
        public void VisitAssertStatement(AssertStatement assertStatement)
        {
            if (!ExpressionAnalyzer.IsExpressionValid(
                    _context,
                    _currentScope,
                    assertStatement.Condition))
            {
                return;
            }

            var conditionType = Typer.GetExpressionType(
                _context,
                _currentScope,
                assertStatement.Condition);

            if (conditionType != null && !(conditionType is BooleanType))
            {
                _context.Error(
                    assertStatement.Condition.Span,
                    $"type mismatch; expected \"bool\", but found \"{conditionType}\"");
            }
        }
Beispiel #11
0
        public void VisitWhileStatement(WhileStatement whileStatement)
        {
            if (ExpressionAnalyzer.IsExpressionValid(
                    _context,
                    _currentScope,
                    whileStatement.Condition))
            {
                var conditionType = Typer.GetExpressionType(
                    _context,
                    _currentScope,
                    whileStatement.Condition);
                if (conditionType != null && !conditionType.IsSame(new BooleanType()))
                {
                    _context.Error(
                        whileStatement.Condition.Span,
                        "while loop condition does not evaluate to type \"bool\"");
                }
            }

            _loopNestLevel++;
            AnalyzeBlock(whileStatement.Body);
            _loopNestLevel--;
        }
Beispiel #12
0
        public bool VisitPrefixOperation(PrefixOperation prefixOperation)
        {
            if (!IsExpressionValid(prefixOperation.Operand))
            {
                return(false);
            }

            var operandType = Typer.GetExpressionType(
                _context,
                _environment,
                prefixOperation.Operand);

            if (operandType == null ||
                operandType.GetPrefixOperationResultType(prefixOperation.Operator.Node) != null)
            {
                return(true);
            }

            _context.Error(
                prefixOperation.Span,
                "cannot apply unary operator " +
                $"{prefixOperation.Operator.Node.GetDescription()} to type \"{operandType}\"");
            return(false);
        }
Beispiel #13
0
        public void VisitVariableDefinition(VariableDefinition variableDefinition)
        {
            if (variableDefinition.TypeSignature != null && variableDefinition.Initializer != null)
            {
                var variableType = TypeSignatureParser.ParseTypeSignature(
                    _context,
                    variableDefinition.TypeSignature);

                if (!ExpressionAnalyzer.IsExpressionValid(
                        _context,
                        _currentScope,
                        variableDefinition.Initializer))
                {
                    return;
                }

                var initializerType = Typer.GetExpressionType(
                    _context,
                    _currentScope,
                    variableDefinition.Initializer);

                if (variableType != null && !variableType.IsSame(initializerType))
                {
                    // We made sure in the StatementParser that a definition without both initializer
                    // and type signature is not a valid AST item and discarded it with an error.
                    Debug.Assert(variableDefinition.Initializer != null);
                    _context.Error(
                        variableDefinition.Initializer.Span,
                        $"expected type \"{variableType}\", but found \"{initializerType}\"");
                }

                _astContext.AddNodeType(variableDefinition.NodeId, variableType);
            }
            else if (variableDefinition.TypeSignature != null &&
                     variableDefinition.Initializer == null)
            {
                var variableType =
                    TypeSignatureParser.ParseTypeSignature(
                        _context,
                        variableDefinition.TypeSignature);

                _astContext.AddNodeType(variableDefinition.NodeId, variableType);
            }
            else // variableDefinition.TypeSignature == null && variableDefinition.Initializer != null
            {
                if (!ExpressionAnalyzer.IsExpressionValid(
                        _context,
                        _currentScope,
                        variableDefinition.Initializer))
                {
                    return;
                }

                var initializerType = Typer.GetExpressionType(
                    _context,
                    _currentScope,
                    variableDefinition.Initializer);
                switch (initializerType)
                {
                case VoidType _:
                    // Again, the linter is overly sensitive. There's no way for the Initializer
                    // field to be null at this point.
                    Debug.Assert(variableDefinition.Initializer != null);
                    _context.Error(
                        variableDefinition.Initializer.Span,
                        "type \"void\" cannot be assigned to a variable");
                    break;

                case null:
                    return;

                default:
                    _astContext.AddNodeType(variableDefinition.NodeId, initializerType);
                    break;
                }
            }

            if (!_currentScope.DefineSymbol(
                    variableDefinition,
                    _astContext.GetNodeType(variableDefinition.NodeId)))
            {
                _context.Error(
                    variableDefinition.Span,
                    $"redefined previously defined symbol \"{variableDefinition.Identifier}\"");
            }
        }