/// <summary> /// Parses a string from the commandline and provides a typed literal /// </summary> /// <param name="arg">The argument to use</param> /// <returns>A parsed literal</returns> private static AST.LiteralExpression ParseAsLiteral(string arg) { var parsetoken = new ParseToken(0, 0, 0, arg); if (bool.TryParse(arg, out var bres)) { return(new AST.LiteralExpression(parsetoken, new AST.BooleanConstant(parsetoken, bres))); } if (int.TryParse(arg, out var ires)) { return(new AST.LiteralExpression(parsetoken, new AST.IntegerConstant(parsetoken, arg))); } if (float.TryParse(arg, out var fres)) { var els = arg.Split(".", 2); return(new AST.LiteralExpression(parsetoken, new AST.FloatingConstant(parsetoken, els.First(), els.Skip(1).FirstOrDefault() ?? string.Empty))); } return(new AST.LiteralExpression(parsetoken, new AST.StringConstant(parsetoken, arg))); }
/// <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> /// Constructs a new parserexception /// </summary> /// <param name="message">The error message</param> /// <param name="source">The location of the problem</param> public ParserException(string message, ParseToken source) : base(message) { Location = source; }
/// <summary> /// Splits the input into tokens /// </summary> /// <returns>The tokenized results.</returns> /// <param name="reader">The reader to extract the tokens for.</param> public static IEnumerable <ParseToken> Tokenize(TextReader reader) { var sb = new StringBuilder(); int cur; int line = 1; int charoffset = 1; int lineoffset = 1; while ((cur = reader.Read()) > 0) { var c = (char)cur; var iswhitespace = char.IsWhiteSpace(c); if (iswhitespace || Array.IndexOf(DELIMITERS, c) >= 0) { if (sb.Length != 0) { yield return(new ParseToken(charoffset, line, lineoffset, sb.ToString())); charoffset += sb.Length; lineoffset += sb.Length; sb.Clear(); } // Eat the entire line if we hit a comment if (c == '/' && reader.Peek() == cur) { var ct = new ParseToken(charoffset, line, lineoffset, '/' + reader.ReadLine()); charoffset += ct.Text.Length; lineoffset = 1; line++; //yield return ct; continue; } // Combine to a single token if (MULTI_CHAR_TOKENS.TryGetValue(c, out var followers) && followers.Contains(reader.Peek())) { var n = (char)reader.Read(); var ct = new ParseToken(charoffset, line, lineoffset, c.ToString() + n); charoffset += ct.Text.Length; lineoffset += ct.Text.Length; yield return(ct); continue; } if (!iswhitespace) { yield return(new ParseToken(charoffset, line, lineoffset, c.ToString())); } charoffset++; lineoffset++; if (c == '\r' || c == '\n') { if (c == '\r' && reader.Peek() == (int)'\n') { reader.Read(); } line++; lineoffset = 1; } } else { sb.Append(c); } } if (sb.Length != 0) { yield return(new ParseToken(charoffset, line, lineoffset, sb.ToString())); } }