Beispiel #1
0
        private void CheckInitializer(ValidationState state, Instance.IInstance parent, IEnumerable <Declaration> declarations, Dictionary <AST.ConstantDeclaration, Instance.IInstance> constParentMap)
        {
            foreach (var item in declarations)
            {
                if (item is AST.ConstantDeclaration cdecl)
                {
                    var dependson = new HashSet <AST.ConstantDeclaration>();
                    var visited   = new HashSet <AST.ConstantDeclaration>();
                    CheckInitializer(state, parent, cdecl.Expression, dependson);

                    // Repeat lookup
                    while (dependson.Count != 0)
                    {
                        if (dependson.Contains(cdecl))
                        {
                            throw new ParserException($"Cannot have {(visited.Count == 0 ? "self" : "circular")}-refrence in a constant initializer", cdecl);
                        }

                        var work = dependson.Where(x => !visited.Contains(x)).ToList();
                        dependson = new HashSet <ConstantDeclaration>();

                        foreach (var nc in work)
                        {
                            CheckInitializer(state, constParentMap[nc], nc.Expression, dependson);
                            visited.Add(nc);
                        }
                    }
                }
                else if (item is AST.VariableDeclaration vdecl)
                {
                    if (vdecl.Initializer != null)
                    {
                        var visited = new HashSet <AST.ConstantDeclaration>();
                        CheckInitializer(state, parent, vdecl.Initializer, visited);
                    }
                }
                else if (item is AST.BusDeclaration bus)
                {
                    foreach (var signal in bus.Signals)
                    {
                        if (signal.Initializer != null)
                        {
                            var visited = new HashSet <AST.ConstantDeclaration>();
                            CheckInitializer(state, parent, signal.Initializer, visited);
                        }
                    }
                }
            }
        }
Beispiel #2
0
        private void CheckInitializer(ValidationState state, Instance.IInstance parent, Expression expr, HashSet <AST.ConstantDeclaration> visited)
        {
            if (expr is LiteralExpression)
            {
                return;
            }
            else if (expr is NameExpression ne)
            {
                var scope = state.LocalScopes[parent];
                var s     = state.FindSymbol(ne.Name, scope);
                if (s is Instance.Literal || s is Instance.EnumFieldReference)
                {
                    return;
                }

                if (s is Instance.ConstantReference cref)
                {
                    visited.Add(cref.Source);
                    return;
                }

                throw new ParserException($"Symbol {ne.Name.AsString} resolves to {s?.GetType()} but must be either a constant or a literal", ne);
            }
            else if (expr is TypeCast te)
            {
                CheckInitializer(state, parent, te.Expression, visited);
            }
            else if (expr is UnaryExpression ue)
            {
                CheckInitializer(state, parent, ue.Expression, visited);
            }
            else if (expr is BinaryExpression be)
            {
                CheckInitializer(state, parent, be.Left, visited);
                CheckInitializer(state, parent, be.Right, visited);
            }
        }
Beispiel #3
0
 /// <summary>
 /// Creates a new <see ref="ParentVisitor" />
 /// </summary>
 /// <param name="parent">The parent item</param>
 /// <param name="self">The item</param>
 public ParentVisitor(ParentVisitor parent, Instance.IInstance self)
 {
     Parent = parent;
     Self   = self ?? throw new ArgumentNullException(nameof(self));
 }
Beispiel #4
0
        /// <summary>
        /// Performs the type assignment to a process instance
        /// </summary>
        /// <param name="state">The validation state to use</param>
        /// <param name="instance">The process instance to use</param>
        private static void AssignProcessTypes(ValidationState state, Instance.IInstance parent, AST.Statement[] statements, Dictionary <Expression, DataType> assignedTypes)
        {
            // Get the scope for the intance
            var defaultScope = state.LocalScopes[parent];

            // Extra expression that needs examining
            var extras = new AST.Expression[0].AsEnumerable();

            if (parent is Instance.IDeclarationContainer pdecl1)
            {
                extras = extras.Concat(
                    pdecl1.Declarations
                    // Functions are handled elsewhere and have their own scopes
                    .Where(x => !(x is AST.FunctionDefinition))
                    .SelectMany(
                        x => x.All().OfType <AST.Expression>().Select(y => y.Current)
                        )
                    );
            }

            if (parent is Instance.IParameterizedInstance pp)
            {
                extras = extras.Concat(
                    pp.MappedParameters
                    .Select(x => x.MappedItem)
                    .OfType <Instance.Bus>()
                    .SelectMany(x => x.Instances
                                .OfType <Instance.Signal>()
                                .Select(y => y.Source.Initializer)
                                .Where(y => y != null)
                                )
                    );
            }

            if (parent is Instance.IChildContainer ck)
            {
                extras = extras.Concat(
                    ck.Instances
                    .OfType <Instance.Bus>()
                    .SelectMany(x => x.Instances
                                .OfType <Instance.Signal>()
                                .Select(y => y.Source.Initializer)
                                .Where(y => y != null)
                                )
                    );
            }

            // List of statement expressions to examine for literal/constant type items
            var allExpressions = statements
                                 .All()
                                 .OfType <AST.Expression>()
                                 .Select(x => new { Item = x.Current, Scope = state.TryFindScopeForItem(x) ?? defaultScope })
                                 .Concat(extras.Select(x => new { Item = x, Scope = defaultScope }))
                                 .Concat(
                extras
                .SelectMany(x => x.All().OfType <AST.Expression>().Select(y => y.Current))
                .Select(x => new { Item = x, Scope = defaultScope })
                )
                                 .ToArray()
                                 .AsEnumerable();

            // We use multiple iterations to assign types
            // The first iteration assigns types to all literal, bus, signal and variable expressions
            foreach (var nn in allExpressions)
            {
                var item  = nn.Item;
                var scope = nn.Scope;

                // Skip duplicate assignments
                if (assignedTypes.ContainsKey(item))
                {
                    continue;
                }

                if (item is AST.LiteralExpression literal)
                {
                    if (literal.Value is AST.BooleanConstant)
                    {
                        assignedTypes[literal] = new AST.DataType(literal.SourceToken, ILType.Bool, 1);
                    }
                    else if (literal.Value is AST.IntegerConstant)
                    {
                        assignedTypes[literal] = new AST.DataType(literal.SourceToken, ILType.SignedInteger, -1);
                    }
                    else if (literal.Value is AST.FloatingConstant)
                    {
                        assignedTypes[literal] = new AST.DataType(literal.SourceToken, ILType.Float, -1);
                    }
                }
                else if (item is AST.NameExpression name)
                {
                    var symbol = state.FindSymbol(name.Name, scope);
                    var dt     = FindDataType(state, name, scope);
                    if (dt != null)
                    {
                        if (name.Name.Index.LastOrDefault() != null && dt.IsArray)
                        {
                            assignedTypes[name] = dt.ElementType;
                        }
                        else
                        {
                            assignedTypes[name] = dt;
                        }

                        if (parent is Instance.IParameterizedInstance ip)
                        {
                            state.RegisterItemUsageDirection(ip, symbol, ItemUsageDirection.Read, item);
                        }
                    }
                }
            }

            // Handle variables not used in normal expressions
            foreach (var item in statements.All().Select(x => x.Current))
            {
                var scope = defaultScope;
                if (item is AST.AssignmentStatement assignmentStatement)
                {
                    var symbol = state.FindSymbol(assignmentStatement.Name, scope);
                    if (symbol is Instance.Variable var)
                    {
                        if (var.ResolvedType == null)
                        {
                            var.ResolvedType = state.ResolveTypeName(var.Source.Type, scope);
                        }
                    }
                    else if (symbol is Instance.Signal sig)
                    {
                        if (sig.ResolvedType == null)
                        {
                            sig.ResolvedType = state.ResolveTypeName(sig.Source.Type, scope);
                        }
                    }
                    else if (symbol == null)
                    {
                        throw new ParserException($"Symbol not found: \"{assignmentStatement.Name.AsString}\"", assignmentStatement.Name.SourceToken);
                    }
                    else
                    {
                        throw new ParserException($"Can only assign to signal or variable, {assignmentStatement.Name.AsString} is {symbol.GetType().Name}", assignmentStatement.Name.SourceToken);
                    }
                }
                else if (item is AST.ForStatement forStatement)
                {
                    var forScope = state.LocalScopes[forStatement];
                    var symbol   = state.FindSymbol(forStatement.Variable.Name, forScope);
                    if (symbol is Instance.Variable var)
                    {
                        if (var.ResolvedType == null)
                        {
                            var.ResolvedType = state.ResolveTypeName(var.Source.Type, scope);
                        }
                    }
                    else if (symbol == null)
                    {
                        throw new ParserException($"Symbol not found: \"{forStatement.Variable.Name}\"", forStatement.Variable.SourceToken);
                    }
                    else
                    {
                        throw new ParserException($"Can only use variable as the counter in a for loop, {forStatement.Variable.Name} is {symbol.GetType().Name}", forStatement.Variable.SourceToken);
                    }
                }
            }

            allExpressions = statements
                             .All(AST.TraverseOrder.DepthFirstPostOrder)
                             .OfType <AST.Expression>()
                             .Select(x => new { Item = x.Current, Scope = state.TryFindScopeForItem(x) ?? defaultScope })
                             .Concat(
                extras
                .SelectMany(x => x.All(AST.TraverseOrder.DepthFirstPostOrder).OfType <AST.Expression>().Select(y => y.Current))
                .Select(x => new { Item = x, Scope = defaultScope })
                )
                             .Concat(extras.Select(x => new { Item = x, Scope = defaultScope }));

            // We are only concerned with expressions, working from leafs and up
            // At this point all literals, variables, signals, etc. should have a resolved type
            foreach (var nn in allExpressions)
            {
                var item  = nn.Item;
                var scope = nn.Scope;

                // Skip duplicate assignments
                if (assignedTypes.ContainsKey(item))
                {
                    continue;
                }

                if (item is AST.UnaryExpression unaryExpression)
                {
                    var sourceType = assignedTypes[unaryExpression.Expression];

                    switch (unaryExpression.Operation.Operation)
                    {
                    case AST.UnaryOperation.UnOp.LogicalNegation:
                        if (!sourceType.IsBoolean)
                        {
                            throw new ParserException($"Cannot perform {unaryExpression.Operation.Operation} on {sourceType}", unaryExpression);
                        }
                        break;

                    case AST.UnaryOperation.UnOp.Identity:
                    case AST.UnaryOperation.UnOp.Negation:
                        if (!sourceType.IsNumeric)
                        {
                            throw new ParserException($"Cannot perform {unaryExpression.Operation.Operation} on {sourceType}", unaryExpression);
                        }
                        break;

                    case AST.UnaryOperation.UnOp.BitwiseInvert:
                        if (!sourceType.IsInteger)
                        {
                            throw new ParserException($"Cannot perform {unaryExpression.Operation.Operation} on {sourceType}", unaryExpression);
                        }
                        break;

                    default:
                        throw new ParserException($"Unsupported unary operation: {unaryExpression.Operation.Operation}", unaryExpression);
                    }

                    // Unary operations do not change the type
                    assignedTypes[item] = sourceType;
                }
                else if (item is AST.BinaryExpression binaryExpression)
                {
                    var leftType  = assignedTypes[binaryExpression.Left];
                    var rightType = assignedTypes[binaryExpression.Right];

                    // If we have a numerical operation, verify that the operands are numeric
                    if (binaryExpression.Operation.IsNumericOperation)
                    {
                        if (!leftType.IsNumeric)
                        {
                            throw new ParserException($"The operand {binaryExpression.Left} must be numerical to be used with {binaryExpression.Operation.Operation}", binaryExpression.Left);
                        }
                        if (!rightType.IsNumeric)
                        {
                            throw new ParserException($"The operand {binaryExpression.Right} must be numerical to be used with {binaryExpression.Operation.Operation}", binaryExpression.Right);
                        }
                    }

                    // If we have a logical operation, verify that the operands are boolean
                    if (binaryExpression.Operation.IsLogicalOperation)
                    {
                        if (!leftType.IsBoolean)
                        {
                            throw new ParserException($"The operand {binaryExpression.Left} must be boolean to be used with {binaryExpression.Operation.Operation}", binaryExpression.Left);
                        }
                        if (!rightType.IsBoolean)
                        {
                            throw new ParserException($"The operand {binaryExpression.Right} must be boolean to be used with {binaryExpression.Operation.Operation}", binaryExpression.Right);
                        }
                    }

                    // If we are doing a compare operation, verify that the types can be compared
                    if (binaryExpression.Operation.IsEqualityOperation)
                    {
                        if (!state.CanEqualityCompare(leftType, rightType, scope))
                        {
                            throw new ParserException($"Cannot perform boolean operation {binaryExpression.Operation.Operation} on types {leftType} and {rightType}", binaryExpression);
                        }
                    }

                    // Special handling of bitshift, where the type of the shift count does not change they type on the input
                    if (binaryExpression.Operation.Operation == BinOp.ShiftLeft || binaryExpression.Operation.Operation == BinOp.ShiftRight)
                    {
                        if (!leftType.IsInteger)
                        {
                            throw new ParserException($"The value being shifted must be an integer type but has type {leftType}", binaryExpression.Left);
                        }
                        if (!rightType.IsInteger)
                        {
                            throw new ParserException($"The shift operand must be an integer type but has type {rightType}", binaryExpression.Right);
                        }
                        assignedTypes[binaryExpression] = leftType;
                    }
                    else
                    {
                        // Make sure we can unify the types
                        if (!state.CanUnifyTypes(leftType, rightType, scope))
                        {
                            throw new ParserException($"The types types {leftType} and {rightType} cannot be unified for use with the operation {binaryExpression.Operation.Operation}", binaryExpression);
                        }

                        // Compute the unified type
                        var unified = state.UnifiedType(leftType, rightType, scope);

                        // If the source operands do not have the unified types, inject an implicit type-cast
                        if (!object.Equals(leftType, unified))
                        {
                            assignedTypes[binaryExpression.Left = new AST.TypeCast(binaryExpression.Left, unified, false)] = unified;
                        }
                        if (!object.Equals(rightType, unified))
                        {
                            assignedTypes[binaryExpression.Right = new AST.TypeCast(binaryExpression.Right, unified, false)] = unified;
                        }

                        // Assign the type to this operation
                        switch (binaryExpression.Operation.Operation)
                        {
                        // These operations just use the unified type
                        case BinOp.Add:
                        case BinOp.Subtract:
                        case BinOp.Multiply:
                        case BinOp.Divide:
                        case BinOp.Modulo:
                        case BinOp.BitwiseAnd:
                        case BinOp.BitwiseOr:
                        case BinOp.BitwiseXor:
                            assignedTypes[binaryExpression] = unified;
                            break;

                        // These operations return a boolean result
                        case BinOp.Equal:
                        case BinOp.NotEqual:
                        case BinOp.LessThan:
                        case BinOp.LessThanOrEqual:
                        case BinOp.GreaterThan:
                        case BinOp.GreaterThanOrEqual:
                        case BinOp.LogicalAnd:
                        case BinOp.LogicalOr:
                            assignedTypes[binaryExpression] = new AST.DataType(binaryExpression.SourceToken, ILType.Bool, 1);
                            break;

                        default:
                            throw new ParserException($"Unable to handle operation: {binaryExpression.Operation.Operation}", binaryExpression);
                        }
                    }
                }
                else if (item is AST.TypeCast typecastExpression)
                {
                    // Implicit typecasts are made by the parser so we do not validate those
                    if (!typecastExpression.Explicit)
                    {
                        continue;
                    }

                    var sourceType = assignedTypes[typecastExpression.Expression];
                    var targetType = state.ResolveTypeName(typecastExpression.TargetName, scope);

                    if (!state.CanTypeCast(sourceType, targetType, scope))
                    {
                        throw new ParserException($"Cannot cast from {sourceType} to {typecastExpression.TargetName}", typecastExpression);
                    }

                    assignedTypes[typecastExpression] = targetType;
                }
                // Carry parenthesis expression types
                else if (item is AST.ParenthesizedExpression parenthesizedExpression)
                {
                    assignedTypes[item] = assignedTypes[parenthesizedExpression.Expression];
                }
            }

            // Then make sure we have assigned all targets
            foreach (var item in statements.All().OfType <AST.Statement>().Select(x => x.Current))
            {
                var scope = defaultScope;
                if (item is AST.AssignmentStatement assignmentStatement)
                {
                    var      symbol   = state.FindSymbol(assignmentStatement.Name, scope);
                    var      exprType = assignedTypes[assignmentStatement.Value];
                    DataType targetType;

                    if (symbol is Instance.Variable variableInstance)
                    {
                        targetType = state.ResolveTypeName(variableInstance.Source.Type, scope);
                    }
                    else if (symbol is Instance.Signal signalInstance)
                    {
                        targetType = state.ResolveTypeName(signalInstance.Source.Type, scope);
                    }
                    else
                    {
                        throw new ParserException($"Assignment must be to a variable or a signal", item);
                    }

                    if (targetType.IsArray && assignmentStatement.Name.Index?.LastOrDefault() != null)
                    {
                        targetType = targetType.ElementType;
                    }

                    if (!state.CanUnifyTypes(targetType, exprType, scope))
                    {
                        throw new ParserException($"Cannot assign \"{assignmentStatement.Value.SourceToken.Text}\" (with type {exprType}) to {assignmentStatement.Name.SourceToken} (with type {targetType})", item);
                    }
                    //var unified = state.UnifiedType(targetType, exprType, scope);

                    // Force the right-hand side to be the type we are assigning to
                    if (!object.Equals(exprType, targetType))
                    {
                        // Make sure we do not loose bits with implicit typecasting
                        if (exprType.BitWidth > targetType.BitWidth && targetType.BitWidth > 0)
                        {
                            throw new ParserException($"Assignment would loose precision from {exprType.BitWidth} bits to {targetType.BitWidth}", item);
                        }

                        assignedTypes[assignmentStatement.Value = new AST.TypeCast(assignmentStatement.Value, targetType, false)] = targetType;
                    }

                    if (parent is Instance.IParameterizedInstance ip)
                    {
                        state.RegisterItemUsageDirection(ip, symbol, ItemUsageDirection.Write, item);
                    }
                }
                else if (item is AST.ForStatement forStatement)
                {
                    var fromType = assignedTypes[forStatement.FromExpression];
                    var toType   = assignedTypes[forStatement.ToExpression];

                    if (!fromType.IsInteger)
                    {
                        throw new ParserException("The from/to arguments in a for loop must be integer types", forStatement.FromExpression);
                    }
                    if (!toType.IsInteger)
                    {
                        throw new ParserException("The from/to arguments in a for loop must be integer types", forStatement.ToExpression);
                    }

                    var inttype = new DataType(forStatement.Variable.Name.SourceToken, ILType.SignedInteger, -1);

                    if (fromType.BitWidth != -1)
                    {
                        assignedTypes[forStatement.FromExpression = new AST.TypeCast(forStatement.FromExpression, inttype, false)] = inttype;
                    }
                    if (toType.BitWidth != -1)
                    {
                        assignedTypes[forStatement.ToExpression = new AST.TypeCast(forStatement.ToExpression, inttype, false)] = inttype;
                    }
                }
            }
        }