public string GenerateHTML(ComputerModel model, string stylesheet = null) { XmlDocument html = (XmlDocument)HtmlTemplate.CloneNode(true); GenerateTitle(model, html); if (stylesheet != null) { ReplaceStylesheet(html, stylesheet); } XmlElement div = html.GetElementsByTagName("div").OfType <XmlElement>().First(e => e.GetAttribute("class") == "boxes"); foreach (Pin pin in model.Pins.OrderBy(p => p.Offset)) { XmlElement template = (XmlElement)CheckboxTemplate.DocumentElement.CloneNode(true); XmlElement input = (XmlElement)template.GetElementsByTagName("input").Item(0); XmlElement label = (XmlElement)template.GetElementsByTagName("label").Item(0); if (pin.Type == PinType.Intermediate) { input.SetAttribute("class", "intermediate-box"); template.RemoveChild(label); } else { if (pin.FirstOfType) { label.InnerText = string.Format(label.InnerText, pin.NameOfType); } else { template.RemoveChild(label); } if (pin.Type == PinType.Input) { input.SetAttribute("class", "input-box"); } else { input.SetAttribute("class", "output-box"); } } foreach (XmlNode node in template.ChildNodes) { div.AppendChild(html.ImportNode(node, true)); } } StringBuilder str = new StringBuilder(); str.AppendLine("<!DOCTYPE html>"); XmlWriterSettings settings = new XmlWriterSettings { OmitXmlDeclaration = true }; using (XmlWriter writer = XmlWriter.Create(str, settings)) { html.WriteTo(writer); } return(str.ToString()); }
public string GenerateCSS(ComputerModel model) { StringBuilder css = new StringBuilder(); css.AppendLine(CssHeader); foreach (TruthTable table in model.TruthTables) { foreach (bool[] key in table.Table.Keys) { bool value = table.Table[key]; int i = 0; css.Append(".run:checked ~ .boxes "); foreach (Pin pin in table.Inputs.OrderBy(p => p.Offset)) { AddressPin(model, css, pin, key[i++]); css.Append(" ~ "); } AddressPin(model, css, table.Output, value); css.Append(", .run:checked ~ .boxes "); i = 0; foreach (Pin pin in table.Inputs.OrderBy(p => p.Offset)) { AddressPin(model, css, pin, key[i++]); css.Append(" ~ "); } AddressPin(model, css, table.Output, value); css.Append(" + svg {"); css.Append(ValidCss); css.AppendLine(" }"); i = 0; css.Append(".run:checked ~ .boxes "); foreach (Pin pin in table.Inputs.OrderBy(p => p.Offset)) { AddressPin(model, css, pin, key[i++]); css.Append(" ~ "); } AddressPin(model, css, table.Output, !value); css.Append(", .run:checked ~ .boxes "); i = 0; foreach (Pin pin in table.Inputs.OrderBy(p => p.Offset)) { AddressPin(model, css, pin, key[i++]); css.Append(" ~ "); } AddressPin(model, css, table.Output, !value); css.Append(" + svg {"); css.Append(InvalidCss); css.AppendLine(" }"); } } return(css.ToString()); }
void Run() { try { FileSystemWatcher watcher = new FileSystemWatcher(Directory, string.Concat(ChipName, ".hdl")); ComputerModel model = GenerateModel(); lock (ModelLock) { CurrentModel = model; } InitialParseSemaphore.Release(); while (true) { watcher.WaitForChanged(WatcherChangeTypes.All); Console.WriteLine("Changes detected. Reparsing file."); model = GenerateModel(); lock (ModelLock) { CurrentModel = model; } } } catch (ThreadInterruptedException) { } }
static void Main(string[] args) { Options opts = new Options(args); if (opts.ParseFailureReason != null) { Console.Error.WriteLine(opts.ParseFailureReason); Console.Error.WriteLine(); Options.PrintHelp(Console.Error); Environment.Exit(1); } else if (opts.ShowHelp) { Options.PrintHelp(Console.Out); } else if (opts.ShowVersion) { Options.PrintVersion(Console.Out); } else { FileParser parser = new FileParser(opts.Input); Translator translator = new Translator(); if (opts.Output == null) { using (HttpListener listener = new HttpListener()) { listener.Prefixes.Add(string.Format("http://+:{0}/", opts.Port)); listener.Start(); parser.WaitForInitialParse(); Console.WriteLine("Listening on http://localhost:{0}/...", opts.Port); while (true) { HttpListenerContext ctx = listener.GetContext(); ComputerModel model = parser.Model; string content; if (ctx.Request.Url.PathAndQuery == "/style.css") { content = translator.GenerateCSS(model); ctx.Response.ContentType = "text/css"; } else { content = translator.GenerateHTML(model); ctx.Response.ContentType = "text/html"; } byte[] buffer = Encoding.UTF8.GetBytes(content); ctx.Response.ContentLength64 = buffer.Length; ctx.Response.OutputStream.Write(buffer, 0, buffer.Length); ctx.Response.OutputStream.Close(); } } } else { parser.WaitForInitialParse(); ComputerModel model = parser.Model; parser.Stop(); string stylesheet = Path.Combine(Path.GetDirectoryName(opts.Output), string.Concat(Path.GetFileNameWithoutExtension(opts.Output), ".css")); File.WriteAllText(opts.Output, translator.GenerateHTML(model, Path.GetFileName(stylesheet))); File.WriteAllText(stylesheet, translator.GenerateCSS(model)); Console.WriteLine("Document written to {0}", opts.Output); } } }
ComputerModel GenerateModel() { ComputerModel model = new ComputerModel { Name = ChipName }; foreach (ReadLine line in ReadFile(ChipName, "", new Dictionary <string, string>())) { Console.WriteLine(string.Join(" ", line.Tokens)); switch (line.Tokens[0]) { case "input": case "output": case "pin": if (line.Tokens.Length != 3) { Console.Error.WriteLine("Invalid {0} command: invalid number of arguments ({1}:{2})", line.Tokens[0], line.FileName, line.LineNumber); return(model); } PinType type; switch (line.Tokens[0]) { case "input": type = PinType.Input; break; case "output": type = PinType.Output; break; default: type = PinType.Intermediate; break; } int width; if (!int.TryParse(line.Tokens[2], out width) || width < 1) { Console.Error.WriteLine("Invalid {0} command: invalid width '{1}' ({2}:{3})", line.Tokens[0], line.Tokens[2], line.FileName, line.LineNumber); return(model); } if (width == 1) { model.Pins.Add(new Pin { Type = type, Name = line.Tokens[1], NameOfType = line.Tokens[1], FirstOfType = true }); } else { model.Pins.AddRange(Enumerable.Range(1, width).Select(i => new Pin { Type = type, Name = string.Format("{0}{1}", line.Tokens[1], width - i), NameOfType = line.Tokens[1], FirstOfType = i == 1 })); } break; case "truth": if (line.Tokens.Length < 5) { Console.Error.WriteLine("Invalid truth command: invalid number of arguments ({0}:{1})", line.FileName, line.LineNumber); return(model); } TruthTable table = new TruthTable { Inputs = line.Tokens[1].Split(',').Select(s => model.Pins.FirstOrDefault(p => p.Name == s)).ToList(), Output = model.Pins.FirstOrDefault(p => p.Name == line.Tokens[2] && p.Type != PinType.Input) }; if (table.Inputs.Any(p => p == null) || table.Output == null) { Console.Error.WriteLine("Unknown pin reference in truth table ({0}:{1})", line.FileName, line.LineNumber); return(model); } if (line.Tokens.Length != 3 + Math.Pow(2, table.Inputs.Count)) { Console.Error.WriteLine("Invalid truth command: invalid number of arguments ({0}:{1})", line.FileName, line.LineNumber); return(model); } bool[] key = new bool[table.Inputs.Count]; Array.Fill(key, false); for (int i = 3; i < line.Tokens.Length; ++i) { bool value; switch (line.Tokens[i]) { case "0": value = false; break; case "1": value = true; break; default: Console.Error.WriteLine("Invalid truth command: invalid value ({0}:{1})", line.FileName, line.LineNumber); return(model); } bool[] copy = new bool[key.Length]; Array.Copy(key, copy, key.Length); table.Table[copy] = value; for (int j = key.Length - 1; j >= 0; --j) { if (key[j]) { key[j] = false; } else { key[j] = true; break; } } } model.TruthTables.Add(table); break; default: Console.Error.WriteLine("Invalid command '{0}' ({1}:{2})", line.Tokens[0], line.FileName, line.LineNumber); return(model); } } int offset = 0; foreach (Pin pin in model.Pins.Where(p => p.Type == PinType.Input)) { pin.Offset = offset++; } HashSet <Pin> unmapped = model.Pins.Where(p => p.Type == PinType.Intermediate).ToHashSet(); while (unmapped.Count > 0) { Pin[] toMap = unmapped.Where(p => model.TruthTables.Where(t => t.Output == p).All(t => t.Inputs.All(p2 => !unmapped.Contains(p2)))).ToArray(); foreach (Pin pin in toMap) { pin.Offset = offset++; unmapped.Remove(pin); } if (toMap.Length == 0) { Console.Error.WriteLine("Unable to map pins without recursion."); Console.Error.WriteLine("The following pins failed:"); foreach (Pin pin in unmapped) { Console.Error.WriteLine(" {0}", pin.Name); } return(model); } } foreach (Pin pin in model.Pins.Where(p => p.Type == PinType.Output)) { pin.Offset = offset++; } return(model); }
void AddressPin(ComputerModel model, StringBuilder css, Pin pin, bool value) { css.AppendFormat("input:nth-of-type({0}):{1}", pin.Offset + 1, value ? "checked" : "not(:checked)"); }
void GenerateTitle(ComputerModel model, XmlDocument html) { XmlElement title = (XmlElement)html.GetElementsByTagName("title").Item(0); title.InnerText = string.Format(title.InnerText, model.Name); }