/// <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); }
/// <summary> /// Initializes a new instance of the <see cref="T:SMEIL.Parser.AST.Import"/> class. /// </summary> /// <param name="token">The source token</param> /// <param name="localname">The import name.</param> public ImportStatement(ParseToken token, ImportName modulename, Identifier[] sourceNames, Identifier localname) : base(token) { ModuleName = modulename; SourceNames = sourceNames; LocalName = localname; }