Ejemplo n.º 1
0
 /// <summary>
 /// Validates the current state
 /// </summary>
 /// <param name="state">The state to validate</param>
 public void Validate(ValidationState state)
 {
     foreach (var item in state.Modules.Values.SelectMany(x => x.All()))
     {
         if (item.Current is AST.Module m)
         {
             CheckDeclarations(m.Declarations);
         }
         else if (item.Current is AST.Network n)
         {
             CheckDeclarations(n.Declarations);
         }
         else if (item.Current is AST.Process p)
         {
             CheckDeclarations(p.Declarations);
         }
         else if (item.Current is AST.FunctionDefinition f)
         {
             CheckDeclarations(f.Declarations);
         }
         else if (item.Current is AST.TypeDefinition t)
         {
             CheckIdentifier(t.Name);
         }
     }
 }
Ejemplo n.º 2
0
        /// <summary>
        /// Returns all output signals for a process or connection
        /// </summary>
        /// <param name="state">The state object</param>
        /// <returns>The output signals</returns>
        public IEnumerable <Instance.Signal> OutputSignals(ValidationState state, Instance.Process process)
        {
            return(process.MappedParameters
                   .Where(x => x.MappedItem is Instance.Bus)
                   .SelectMany(x => {
                var isOut = x.MatchedParameter.Direction == AST.ParameterDirection.Out;
                var parentBus = x.MappedItem as Instance.Bus;

                return parentBus
                .Instances
                .OfType <Instance.Signal>()
                .Where(y =>
                       y.Source.Direction == SignalDirection.Normal
                                ? isOut
                                : !isOut
                       );
            })
                   .Concat(
                       process.Instances
                       .OfType <Instance.Bus>()
                       .SelectMany(x => x.Instances.OfType <Instance.Signal>())
                       .Where(x =>
                              state.ItemDirection[process].ContainsKey(x)
                              &&
                              state.ItemDirection[process][x] == ItemUsageDirection.Write
                              )
                       )
                   .Distinct());
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Validates the module
        /// </summary>
        /// <param name="state">The validation state</param>
        public void Validate(ValidationState state)
        {
            // Start the process with the top-level module,
            // and capture the existing instances as well
            var inst = state.TopLevel.ModuleInstance.Instances;

            state.TopLevel.ModuleInstance = CreateAndRegisterInstance(state, state.TopLevel.Module);
            state.TopLevel.ModuleInstance.Instances.AddRange(inst);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Creates all sub-instances for a network
        /// </summary>
        /// <param name="state">The state to use</param>
        /// <param name="network">The parent network instance</param>
        private Instance.Network CreateAndRegisterInstance(ValidationState state, AST.InstanceDeclaration instDecl, AST.Network network)
        {
            // Create the network instance
            var netinstance = new Instance.Network(instDecl, network);

            // We have registered the network by this name already
            //state.TryAddSymbol(netinstance.Name, netinstance);

            using (state.StartScope(network, netinstance))
                CreateAndRegisterInstances(state, netinstance.NetworkDefinition.Declarations, netinstance.Instances);

            return(netinstance);
        }
Ejemplo n.º 5
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);
                        }
                    }
                }
            }
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Creates all sub-instances for a module
        /// </summary>
        /// <param name="state">The state to use</param>
        /// <param name="module">The parent module</param>
        /// <returns></returns>
        private Instance.Module CreateAndRegisterInstance(ValidationState state, AST.Module module)
        {
            var modinstance      = new Instance.Module(module);
            var parentCollection = modinstance.Instances;

            using (var scope = state.StartScope(module, modinstance))
            {
                // TODO: Create module instances here
                foreach (var imp in module.Imports)
                {
                }

                foreach (var decl in module.Declarations)
                {
                    if (decl is AST.ConstantDeclaration cdecl)
                    {
                        var cref = new Instance.ConstantReference(cdecl);
                        scope.TryAddSymbol(cref.Name, cref, cdecl.Name);
                        parentCollection.Add(cref);
                    }
                    else if (decl is AST.EnumDeclaration edecl)
                    {
                        var e = new Instance.EnumTypeReference(edecl);
                        scope.TryAddSymbol(e.Name, e, edecl.Name);
                        using (state.StartScope(e))
                            CreateAndRegisterInstance(state, e);
                        parentCollection.Add(e);
                    }
                    else if (decl is AST.FunctionDefinition fdecl)
                    {
                        scope.TryAddSymbol(fdecl.Name.Name, fdecl, fdecl.Name);
                    }
                }

                // The entry-level network is not user-instantiated
                if (module == state.TopLevel.Module)
                {
                    modinstance.Instances.Add(
                        state.TopLevel.NetworkInstance =
                            CreateAndRegisterInstance(state, state.TopLevel.NetworkDeclaration, state.TopLevel.SourceNetwork)
                        );
                }
            }

            return(modinstance);
        }
Ejemplo n.º 7
0
 /// <summary>
 /// The instances
 /// </summary>
 /// <param name="state">The validation state</param>
 /// <param name="instances">The instances to assign types to</param>
 /// <param name="scope">The scope to use for lookup</param>
 private void ResolveTypes(ValidationState state, IEnumerable <Instance.IInstance> instances, ScopeState scope)
 {
     foreach (var r in instances)
     {
         if (r is Instance.ConstantReference cref)
         {
             if (cref.ResolvedType == null)
             {
                 cref.ResolvedType = state.ResolveTypeName(cref.Source.DataType, scope);
             }
         }
         else if (r is Instance.Variable v)
         {
             if (v.ResolvedType == null)
             {
                 v.ResolvedType = state.ResolveTypeName(v.Source.Type, scope);
             }
         }
     }
 }
Ejemplo n.º 8
0
        /// <summary>
        /// Recursively loads import statements and registers them in the symbol tables
        /// </summary>
        /// <param name="sourcepath"></param>
        /// <param name="state"></param>
        /// <param name="module"></param>
        private static void LoadImports(string sourcepath, Validation.ValidationState state, AST.Module module)
        {
            var scope = state.CurrentScope;

            foreach (var imp in module.Imports)
            {
                var p = GetModulePath(sourcepath, imp.ModuleName);
                if (!state.Modules.ContainsKey(p))
                {
                    var m = LoadModule(p);
                    using (var sc = state.StartScope(m))
                    {
                        // Recursively load the modules
                        LoadImports(p, state, m);

                        // Register that we have now loaded this module
                        state.Modules.Add(p, m);

                        // Register symbols in this scope
                        state.RegisterSymbols(m, sc);
                    }
                }

                // Inject imported names into the symbol tables
                if (imp.SourceNames == null)
                {
                    // Import the entire module as
                    scope.TryAddSymbol(imp.LocalName.Name, state.Modules[p], imp.LocalName);
                }
                else
                {
                    // Import only the requested names, but import them without the module name
                    foreach (var n in imp.SourceNames)
                    {
                        scope.SymbolTable[n.Name] = state.FindSymbol(n, state.LocalScopes[state.Modules[p]]);
                    }
                }
            }
        }
Ejemplo n.º 9
0
        public void Validate(ValidationState state)
        {
            var constParentMap = state.AllInstances
                                 .OfType <Instance.IDeclarationContainer>()
                                 .SelectMany(x =>
                                             x.Declarations
                                             .OfType <AST.ConstantDeclaration>()
                                             .Select(y => new {
                Constant = y,
                Parent   = (Instance.IInstance)x
            })
                                             )
                                 .GroupBy(x => x.Constant)
                                 .ToDictionary(
                x => x.Key,
                x => x.First().Parent
                );

            foreach (var e in state.AllInstances.OfType <Instance.IDeclarationContainer>())
            {
                CheckInitializer(state, e, e.Declarations, constParentMap);
            }
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Creates all enum fields for an enum
        /// </summary>
        /// <param name="state">The state to use</param>
        /// <param name="parent">The enum instance</param>
        private void CreateAndRegisterInstance(ValidationState state, Instance.EnumTypeReference parent)
        {
            var scope = state.CurrentScope;

            var cur = 0;

            foreach (var field in parent.Source.Fields)
            {
                var ix = field.Value;
                if (ix < 0)
                {
                    ix = cur;
                }

                var s = new Instance.EnumFieldReference(parent, field, ix);

                parent.Fields.Add(field.Name.Name, s);
                scope.TryAddSymbol(field.Name.Name, s, field.Name);
                parent.Instances.Add(s);

                cur = ix + 1;
            }
        }
Ejemplo n.º 11
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);
            }
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Loads the module and its imports
        /// </summary>
        /// <param name="file">The main module</param>
        /// <param name="toplevel">The top-level network or null</param>
        /// <returns>The state for the loaded modules</returns>
        public static Validation.ValidationState LoadModuleAndImports(string file, string toplevel, string[] arguments)
        {
            // Basic setup
            var state     = new Validation.ValidationState();
            var rootscope = state.CurrentScope;
            var toResolve = new Stack <AST.Module>();

            state.TopLevel.Module         = LoadModule(file);
            state.TopLevel.ModuleInstance = new Instance.Module(state.TopLevel.Module);

            // Find the entry network
            var networks = state.TopLevel.Module.Entities.OfType <AST.Network>().ToList();

            if (string.IsNullOrWhiteSpace(toplevel))
            {
                if (networks.Count == 0)
                {
                    throw new ArgumentException("The main module contains no networks?");
                }
                if (networks.Count != 1)
                {
                    throw new ArgumentException($"The main module contains {networks.Count} networks, please specify a network name");
                }
                state.TopLevel.SourceNetwork = networks.First();
            }
            else
            {
                var namednetworks = networks.Where(x => string.Equals(x.Name.Name, toplevel, StringComparison.OrdinalIgnoreCase)).ToList();
                if (networks.Count == 0)
                {
                    throw new ArgumentException($"The main module contains no networks named \"{toplevel}\"");
                }
                if (networks.Count != 1)
                {
                    throw new ArgumentException($"The main module contains {networks.Count} network named \"{toplevel}\"");
                }
                state.TopLevel.SourceNetwork = namednetworks.First();
            }

            // Wire up the top-level network parameters
            var dummyparsetoken = new ParseToken(0, 0, 0, "__commandline__");
            var name            = new AST.Identifier(new ParseToken(0, 0, 0, "__main__"));

            state.TopLevel.CommandlineArguments = arguments = arguments ?? new string[0];
            state.Modules[file] = state.TopLevel.Module;
            state.LocalScopes[state.TopLevel.Module] = rootscope;


            // Recursively load and resolve imports
            LoadImports(file, state, state.TopLevel.Module);

            // Register the symbols from the main module in the root scope
            state.RegisterSymbols(state.TopLevel.Module, rootscope);

            // Check that all parameters in the top-level network are explicitly typed
            var untypedparam = state.TopLevel.SourceNetwork.Parameters.FirstOrDefault(x => x.ExplictType == null);

            if (untypedparam != null)
            {
                throw new ParserException("All parameters to the top-level network must have an explict type", untypedparam);
            }

            // Prepare for the parameters to the top-level network
            var pmap = new AST.ParameterMap[state.TopLevel.SourceNetwork.Parameters.Length];
            var externalargumentindex = 0;

            for (var i = 0; i < pmap.Length; i++)
            {
                var p        = state.TopLevel.SourceNetwork.Parameters[i];
                var realtype = state.ResolveTypeName(p.ExplictType, rootscope);
                if (realtype == null)
                {
                    throw new ParserException($"Unable to find type: {p.ExplictType.SourceToken.Text}", p);
                }

                if (realtype.IsValue)
                {
                    if (p.Direction == AST.ParameterDirection.Out)
                    {
                        throw new ParserException($"A value-type parameter cannot be sent as output: {p.Name}", p);
                    }
                    if (externalargumentindex >= state.TopLevel.CommandlineArguments.Length)
                    {
                        throw new ParserException($"No value provided for the parameter {p.Name} in the commandline inputs", p);
                    }
                    var argtext = state.TopLevel.CommandlineArguments[externalargumentindex++];
                    var literal = ParseAsLiteral(argtext);
                    var littype = new AST.DataType(new ParseToken(0, 0, 0, argtext), literal.Value.Type, -1);
                    if (!state.CanTypeCast(littype, realtype, rootscope))
                    {
                        throw new ParserException($"Parsed {argtext} to {littype} but cannot interpret as {realtype} which is required for parameter {p.Name}", p);
                    }

                    pmap[i] = new AST.ParameterMap(p.SourceToken, p.Name, literal);
                    rootscope.TryAddSymbol(p.Name.Name, literal, p.Name);
                }
                else if (realtype.IsBus)
                {
                    var typedef = (AST.TypeDefinition)state.FindTypeDefinition(p.ExplictType.Alias, rootscope);

                    // Create a new bus as a stand-in for the input or output
                    var newbus = new Instance.Bus(
                        new AST.BusDeclaration(
                            dummyparsetoken,
                            p.Name,
                            realtype
                            .Shape
                            .Signals
                            .Select(x =>
                                    new AST.BusSignalDeclaration(
                                        dummyparsetoken,
                                        new AST.Identifier(new ParseToken(0, 0, 0, x.Key)),
                                        x.Value.Type,
                                        typedef.Initializers[x.Key],
                                        x.Value.Direction
                                        )
                                    ).ToArray(),
                            null
                            )
                        );

                    newbus.Instances.AddRange(
                        newbus
                        .Source
                        .Signals
                        .Select(x =>
                                new Instance.Signal(newbus, x)
                    {
                        ResolvedType = state.ResolveTypeName(x.Type, rootscope)
                    }
                                )
                        );

                    newbus.ResolvedSignalTypes =
                        newbus
                        .Instances
                        .OfType <Instance.Signal>()
                        .ToDictionary(x => x.Name, x => x.ResolvedType);

                    if (p.Direction == AST.ParameterDirection.Out)
                    {
                        state.TopLevel.OutputBusses.Add(newbus);
                    }
                    else if (p.Direction == AST.ParameterDirection.In)
                    {
                        state.TopLevel.InputBusses.Add(newbus);
                    }
                    else
                    {
                        throw new ParserException($"Cannot use a top-level bus with direction {p.Direction}", p);
                    }

                    pmap[i] = new AST.ParameterMap(p.SourceToken, p.Name, AST.EnumerationExtensions.AsExpression(p.Name));
                    rootscope.TryAddSymbol(p.Name.Name, newbus, p.Name);

                    // Register signals
                    using (var sc = state.StartScope(newbus))
                        foreach (var s in newbus.Instances.OfType <Instance.Signal>())
                        {
                            sc.TryAddSymbol(s.Name, s, s.Source);
                        }

                    state.TopLevel.ModuleInstance.Instances.Add(newbus);
                }
                else
                {
                    throw new ParserException($"Unexpected type: {realtype}", p);
                }
            }

            // Check that we have at least one output bus
            if (state.TopLevel.OutputBusses.Count == 0)
            {
                throw new ParserException("The top-level network must have at least one output bus", state.TopLevel.SourceNetwork);
            }
            if (state.TopLevel.CommandlineArguments.Length > externalargumentindex)
            {
                throw new ParserException($"Too many arguments on commandline, expected {externalargumentindex} but got {state.TopLevel.CommandlineArguments.Length}", state.TopLevel.SourceNetwork);
            }

            state.TopLevel.NetworkDeclaration = new AST.InstanceDeclaration(
                dummyparsetoken,
                new AST.InstanceName(dummyparsetoken, name, null),
                name,
                pmap
                );

            state.TopLevel.NetworkInstance = new Instance.Network(
                state.TopLevel.NetworkDeclaration,
                state.TopLevel.SourceNetwork
                );

            return(state);
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Wires up parameters for a parameterized instance
        /// </summary>
        /// <param name="state">The validation state</param>
        /// <param name="sourceinstance">The instance to wire up</param>
        private void WireUpParameters(ValidationState state, Instance.IParameterizedInstance sourceinstance)
        {
            if (sourceinstance.MappedParameters.Count < sourceinstance.SourceParameters.Length)
            {
                if (sourceinstance.MappedParameters.Count != 0)
                {
                    throw new Exception("Unexpected half-filled parameter list");
                }

                var position = 0;
                var anynamed = false;
                var map      = new Instance.MappedParameter[sourceinstance.SourceParameters.Length];
                var scope    = state.LocalScopes[sourceinstance];

                // Map for getting the parameter index of a name
                var namelist = sourceinstance
                               .SourceParameters
                               .Zip(
                    Enumerable.Range(0, sourceinstance.SourceParameters.Length),
                    (p, i) => new { i, p.Name.Name }
                    );

                var collisions = namelist
                                 .GroupBy(x => x.Name)
                                 .Where(x => x.Count() != 1)
                                 .FirstOrDefault();

                if (collisions != null)
                {
                    throw new ParserException($"Multiple arguments named {collisions.Key}, positions: {string.Join(", ", collisions.Select(x => x.i.ToString())) }", sourceinstance.SourceParameters[collisions.Last().i].Name);
                }

                var nameindexmap = namelist.ToDictionary(x => x.Name, x => x.i);

                foreach (var p in sourceinstance.ParameterMap)
                {
                    var pos = position;
                    if (p.Name == null)
                    {
                        if (anynamed)
                        {
                            throw new ParserException($"Cannot have positional arguments after named arguments", p);
                        }
                    }
                    else
                    {
                        anynamed = true;
                        if (!nameindexmap.TryGetValue(p.Name.Name, out pos))
                        {
                            throw new ParserException($"No parameter named {p.Name.Name} in {sourceinstance.SourceName}", sourceinstance.SourceItem);
                        }
                    }

                    if (map[pos] != null)
                    {
                        throw new ParserException($"Double argument for {sourceinstance.SourceParameters[pos].Name.Name} detected", sourceinstance.SourceItem);
                    }

                    // Extract the parameter definition
                    var sourceparam = sourceinstance.SourceParameters[pos];

                    Instance.IInstance value;
                    var tc = p.Expression as AST.TypeCast;

                    if (tc != null)
                    {
                        value = state.ResolveSymbol(tc.Expression, scope);
                    }
                    else
                    {
                        value = state.ResolveSymbol(p.Expression, scope);
                    }

                    if (value == null)
                    {
                        throw new ParserException("Unable to resolve expression", p.Expression.SourceToken);
                    }

                    var itemtype      = state.InstanceType(value);
                    var parametertype =
                        sourceparam.ExplictType == null
                        ? itemtype
                        : state.ResolveTypeName(sourceparam.ExplictType, scope);

                    if (parametertype.IsValue && sourceparam.Direction == AST.ParameterDirection.Out)
                    {
                        throw new ParserException($"Cannot use a value-type parameter as output: {sourceparam.SourceToken}", sourceparam);
                    }

                    // We need to expand both types to intrinsics to remove any type aliases that need lookups
                    var intrinsic_itemtype      = state.ResolveToIntrinsics(itemtype, scope);
                    var intrinsic_parametertype = state.ResolveToIntrinsics(parametertype, scope);

                    // If the input is a typecast (and required) we wire it through a process
                    if (tc != null && !state.CanUnifyTypes(intrinsic_itemtype, intrinsic_parametertype, scope))
                    {
                        var typecast_target = state.ResolveToIntrinsics(state.ResolveTypeName(tc.TargetName, scope), scope);
                        var typecast_source = sourceparam.Direction == ParameterDirection.In ? intrinsic_itemtype : intrinsic_parametertype;

                        var sourceSignals = typecast_source
                                            .Shape
                                            .Signals
                                            .Select(x => x.Key)
                                            .ToHashSet();

                        var shared_shape =
                            typecast_target
                            .Shape
                            .Signals
                            .Where(x => sourceSignals.Contains(x.Key))
                            .Select(x => new AST.BusSignalDeclaration(
                                        p.SourceToken,
                                        new AST.Identifier(
                                            new ParseToken(0, 0, 0, x.Key)
                                            ),
                                        x.Value.Type,
                                        null,
                                        x.Value.Direction
                                        ))
                            .ToArray();

                        if (sourceSignals.Count != shared_shape.Length)
                        {
                            throw new ParserException($"The typecast is invalid as the names do not match", p.SourceToken);
                        }

                        var proc = IdentityHelper.CreateTypeCastProcess(
                            state,
                            scope,
                            p.SourceToken,
                            tc.Expression,
                            new AST.Name(p.SourceToken, new[] {
                            new AST.Identifier(new ParseToken(0, 0, 0, sourceinstance.Name)),
                            sourceparam.Name
                        }, null).AsExpression(),
                            shared_shape,
                            shared_shape
                            );

                        throw new ParserException($"Typecasts inside process instantiations are not currently supported", p.SourceToken);
                        // using (state.StartScope(proc))
                        //     CreateAndRegisterInstance(state, proc);
                        // parentCollection.Add(proc);
                    }

                    // Check argument compatibility
                    if (!state.CanUnifyTypes(intrinsic_itemtype, intrinsic_parametertype, scope))
                    {
                        throw new ParserException($"Cannot use {p.Expression.SourceToken} of type {intrinsic_itemtype.ToString()} as the argument for {sourceparam.Name.SourceToken} of type {intrinsic_parametertype}", p.Expression);
                    }

                    // Check that the type we use as input is "larger" than the target
                    var unified = state.UnifiedType(intrinsic_itemtype, parametertype, scope);
                    if (!object.Equals(unified, intrinsic_itemtype))
                    {
                        throw new ParserException($"Cannot use {p.Expression.SourceToken} of type {intrinsic_itemtype.ToString()} as the argument for {sourceparam.Name.SourceToken} of type {intrinsic_parametertype}", p.Expression);
                    }

                    map[pos] = new Instance.MappedParameter(p, sourceparam, value, parametertype);
                    var localname = map[pos].LocalName;

                    // Register the instance in the local symbol table to allow
                    // refering to the instance with the parameter name
                    scope.TryAddSymbol(localname, value, sourceparam.Name);
                    position++;
                }

                if (map.Any(x => x == null))
                {
                    throw new ParserException("Argument missing", sourceinstance.SourceItem);
                }

                sourceinstance.MappedParameters.AddRange(map);
            }
        }
Ejemplo n.º 14
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;
                    }
                }
            }
        }
Ejemplo n.º 15
0
        /// <summary>
        /// Validates the module
        /// </summary>
        /// <param name="state">The validation state</param>
        public void Validate(ValidationState state)
        {
            // Keep a list of unscheduled processes
            var remainingprocesses = state.AllInstances
                                     .OfType <Instance.Process>()
                                     .ToList();

            // Keep track of wavefronts of processes
            var roots = new List <List <Instance.Process> >();

            // Map all output signals to their writers
            var writers = remainingprocesses
                          .SelectMany(
                x => OutputSignals(state, x)
                .Select(z => new { P = x, S = z })
                )
                          .GroupBy(x => x.S)
                          .ToDictionary(x => x.Key, y => y.Select(x => x.P).ToList());

            // The code in SME handles double writes, but FPGA tools generally dislike double writers
            var doublewrite = writers
                              .Where(x => x.Value.Count > 1)
                              .Select(x => x.Key)
                              .FirstOrDefault();

            if (doublewrite != null)
            {
                var dblnames = string.Join(Environment.NewLine, writers[doublewrite].Select(x => x.Name));
                throw new ParserException($"Multiple writers found for signal {doublewrite.Name}: {Environment.NewLine} {dblnames}", doublewrite.Source);
            }

            // Find all signals that are inputs
            var inputSignals = state
                               .TopLevel
                               .InputBusses
                               .SelectMany(
                x => x.Instances
                .OfType <Instance.Signal>()
                .Where(y => y.Source.Direction == SignalDirection.Normal)
                )
                               .Concat(
                state
                .TopLevel
                .OutputBusses
                .SelectMany(
                    x => x.Instances
                    .OfType <Instance.Signal>()
                    .Where(y => y.Source.Direction == SignalDirection.Inverse)
                    )
                )
            ;

            // Register all inputs as having no writers
            foreach (var s in inputSignals)
            {
                writers.Add(s, new List <Instance.Process>());
            }

            // Prepare a list of signals processed by all writers
            var ready = new HashSet <Instance.Signal>(
                writers.Where(x => x.Value.Count == 0).Select(x => x.Key)
                );

            // Find signals with no writers
            var orphansSignal = remainingprocesses
                                .SelectMany(
                x => InputSignals(state, x)
                .Where(z => !writers.ContainsKey(z))
                )
                                .FirstOrDefault();

            if (orphansSignal != null)
            {
                throw new ParserException($"No writers found for signal {orphansSignal.Name}: {orphansSignal.Source.SourceToken}", orphansSignal.Source);
            }

            // Find all processes that each process depends on
            var dependsOn = remainingprocesses
                            .SelectMany(
                x => InputSignals(state, x)
                .Select(z => new {
                P = x,
                D = writers[z]
            }
                        )
                )
                            .GroupBy(x => x.P)
                            .ToDictionary(x => x.Key, x => x.SelectMany(y => y.D).ToArray());

            // Keep removing processes until all have been scheduled
            while (remainingprocesses.Count > 0)
            {
                var current = new List <Instance.Process>();

                // Find all processes where all signals are ready
                // and remove them from the list of waiters
                for (var i = remainingprocesses.Count - 1; i >= 0; i--)
                {
                    var rp             = remainingprocesses[i];
                    var allInputsReady =
                        InputSignals(state, rp)
                        .All(x => ready.Contains(x));

                    if (allInputsReady)
                    {
                        current.Add(rp);
                        remainingprocesses.RemoveAt(i);
                    }
                }

                // If a round does not remove any items, we have a circular dependency
                if (current.Count == 0)
                {
                    throw new Exception("Cicular bus dependency detected, remaining processes: " + string.Join(Environment.NewLine, remainingprocesses.Select(x => x.Name)));
                }

                // Register all signals that are now fully written
                var completedsignals = current
                                       .SelectMany(
                    x => OutputSignals(state, x)
                    )
                                       .Where(
                    x => !ready.Contains(x) && !writers[x]
                    .Any(
                        y => remainingprocesses.Contains(y)
                        )
                    )
                                       .Distinct();

                foreach (var n in completedsignals)
                {
                    ready.Add(n);
                }

                roots.Add(current);
            }

            state.DependencyGraph   = dependsOn;
            state.SuggestedSchedule = roots;
        }