/// <summary> /// Computes the graphical circuit. /// </summary> public async Task Compute() { // Load the input file if (string.IsNullOrWhiteSpace(Filename)) { _logger?.Post(new DiagnosticMessage(SeverityLevel.Error, "SC001", "No filename specified")); return; } if (!File.Exists(Filename)) { _logger?.Post(new DiagnosticMessage(SeverityLevel.Error, "SC001", $"Could not find file")); return; } string simpleCircuitScript = File.ReadAllText(Filename); // Load the CSS file if any _cssScript = null; if (!string.IsNullOrWhiteSpace(CssFilename)) { if (!File.Exists(CssFilename)) { _logger?.Post(new DiagnosticMessage(SeverityLevel.Error, "SC001", $"Could not find file '{CssFilename}'")); return; } _cssScript = File.ReadAllText(CssFilename); } else { // Include a CSS file if there is one with the same name as the input filename string tryCssFilename = Path.GetFileNameWithoutExtension(Filename) + ".css"; if (File.Exists(tryCssFilename)) { _cssScript = File.ReadAllText(tryCssFilename); } } if (_cssScript != null) { // Add the default script unless there is a comment of the form "/* exclude default */" if (!Regex.IsMatch(_cssScript, @"^/\*\s*exclude\s+default\s*\*/$")) { _cssScript = GraphicalCircuit.DefaultStyle + Environment.NewLine + _cssScript; } } // Now we can start parsing the input file _circuit = await Task.Run(() => { var lexer = SimpleCircuitLexer.FromString(simpleCircuitScript, Path.GetFileName(Filename)); var context = new ParsingContext() { Diagnostics = _logger }; Parser.Parser.Parse(lexer, context); // Solve it already // context.Circuit.Solve(_logger); return(context.Circuit); }); }
static void Main(string[] args) { var script = @"// Horizontal chains T <r> Xia <r> NAND1 <r> Xoa <r> T T <r> Xib <r> NAND2 <r> Xob <r> T // The cross-coupled wires NAND1[b] <l d a -20 d> Xob Xoa <d a -160 d r> [b]NAND2 (X NAND1[o] <0> [o]NAND2) // Finally flip the bottom one - NAND2.Flipped = true "; var logger = new Logger(); var lexer = SimpleCircuitLexer.FromString(script); var context = new ParsingContext { Diagnostics = logger }; Parser.Parse(lexer, context); context.Circuit.Metadata.Add("script", script); // Draw the component if (context.Circuit.Count > 0 && logger.ErrorCount == 0) { var doc = context.Circuit.Render(logger); using var sw = new StringWriter(); using (var xml = XmlWriter.Create(sw, new XmlWriterSettings { OmitXmlDeclaration = true })) doc.WriteTo(xml); if (File.Exists("tmp.html")) { File.Delete("tmp.html"); } using (var fw = new StreamWriter(File.OpenWrite("tmp.html"))) { fw.WriteLine("<html>"); fw.WriteLine("<head>"); fw.WriteLine("</head>"); fw.WriteLine("<body>"); fw.WriteLine(sw.ToString()); fw.WriteLine("</body>"); fw.WriteLine("</html>"); } Process.Start(@"""C:\Program Files\Google\Chrome\Application\chrome.exe""", "\"" + Path.Combine(Directory.GetCurrentDirectory(), "tmp.html") + "\""); } }
private void ParseLines(SimpleCircuitLexer lexer, Circuit ckt) { do { ParseLine(lexer, ckt); if (!lexer.Is(TokenType.Newline) && !lexer.Is(TokenType.EndOfContent)) { throw new ParseException($"Expected a new line", lexer.Line, lexer.Position); } while (lexer.Is(TokenType.Newline)) { lexer.Next(); } }while (!lexer.Is(TokenType.EndOfContent)); }
private IComponent ParseComponentLabel(SimpleCircuitLexer lexer, Circuit ckt) { var component = GetComponent(ParseName(lexer), ckt); if (lexer.Is(TokenType.OpenBracket, "(")) { lexer.Next(); var label = ParseLabel(lexer); if (component is ILabeled lbl) { lbl.Label = label; } lexer.Check(TokenType.CloseBracket, ")"); } return(component); }
private PinDescription ParsePin(SimpleCircuitLexer lexer, Circuit ckt) { var result = new PinDescription(); result.ComponentName = lexer.Content; result.Component = ParseComponentLabel(lexer, ckt); if (lexer.Is(TokenType.OpenBracket, "[")) { lexer.Next(); var pin = ParseName(lexer); lexer.Check(TokenType.CloseBracket, "]"); result.AfterName = pin; result.After = (TranslatingPin)result.Component.Pins[pin]; } return(result); }
/// <summary> /// Parses the specified description. /// </summary> /// <param name="input">The description.</param> public Circuit Parse(string input) { var lexer = new SimpleCircuitLexer(input); _subcircuits.Clear(); if (!lexer.Next()) { return(new Circuit()); } var ckt = new Circuit { Style = Style }; ParseLines(lexer, ckt); return(ckt); }
private void ParseEquation(SimpleCircuitLexer lexer, Circuit ckt) { if (!lexer.Is(TokenType.Dash)) { throw new ParseException($"A dash was expected", lexer.Line, lexer.Position); } lexer.Next(); // Add the first equation var a = ParseSum(lexer, ckt); lexer.Check(TokenType.Equals); var b = ParseSum(lexer, ckt); if (a is Function fa && b is Function fb) { ckt.Add(fa - fb, $"keep {fa} and {fb} equal"); }
private PinDescription ParseDoublePin(SimpleCircuitLexer lexer, Circuit ckt) { string beforePin = null; if (lexer.Is(TokenType.OpenBracket, "[")) { lexer.Next(); beforePin = ParseName(lexer); lexer.Check(TokenType.CloseBracket, "]"); } var result = ParsePin(lexer, ckt); if (beforePin != null) { result.BeforeName = beforePin; result.Before = (TranslatingPin)result.Component.Pins[beforePin]; } return(result); }
private string ParseDirection(SimpleCircuitLexer lexer) { if (lexer.Is(TokenType.Word)) { switch (lexer.Content) { case "u": case "d": case "l": case "r": var dir = lexer.Content; lexer.Next(); return(dir); } } if (lexer.Is(TokenType.Question)) { lexer.Next(); return("?"); } throw new ParseException($"Expected a direction", lexer.Line, lexer.Position); }
private List <WireDescription> ParseWire(SimpleCircuitLexer lexer) { var wires = new List <WireDescription>(); lexer.Check(TokenType.OpenBracket, "<"); do { var direction = ParseDirection(lexer); wires.Add(new WireDescription { Direction = direction, Length = -1.0 }); if (lexer.Is(TokenType.Number)) { wires[wires.Count - 1].Length = double.Parse(lexer.Content, System.Globalization.CultureInfo.InvariantCulture); lexer.Next(); } }while (!lexer.Is(TokenType.CloseBracket, ">")); lexer.Next(); return(wires); }
private void ParseLine(SimpleCircuitLexer lexer, Circuit ckt) { if (lexer.Is(TokenType.Newline)) { return; } else if (lexer.Is(TokenType.Dash)) { ParseEquation(lexer, ckt); } else if (lexer.Is(TokenType.Dot)) { ParseOption(lexer, ckt); } else if (lexer.Is(TokenType.Word)) { ParseChain(lexer, ckt); } else { throw new ParseException($"Could not read {lexer.Content}", lexer.Line, lexer.Position); } }
private void ParseChain(SimpleCircuitLexer lexer, Circuit ckt) { var start = ParsePin(lexer, ckt); PinDescription end = null; while (lexer.Is(TokenType.OpenBracket)) { if (start.Component is Point pts) { pts.Wires++; } var wires = ParseWire(lexer); end = ParseDoublePin(lexer, ckt); // String them together var lastPin = start.After ?? (TranslatingPin)start.Component.Pins.Last(p => p is TranslatingPin); var wire = new Wire(lastPin); ckt.Add(wire); for (var i = 0; i < wires.Count; i++) { TranslatingPin nextPin; if (i < wires.Count - 1) { var pt = new Point("X:" + (_anonIndex++)); ckt.Add(pt); nextPin = (TranslatingPin)pt.Pins.First(p => p is TranslatingPin); pt.Wires += 2; } else { nextPin = end.Before ?? (TranslatingPin)end.Component.Pins.First(p => p is TranslatingPin); if (end.Component is Point pte) { pte.Wires++; } } // Create a new segment for our wire var length = new Unknown($"W{++_wireIndex}.Length", UnknownTypes.Length); wire.To(nextPin, length); var dir = wires[i].Direction.ToLower(); // Constrain the positions switch (dir) { case "u": ckt.Add(nextPin.X - lastPin.X, $"align {nextPin} with {lastPin} (line {lexer.Line})"); ckt.Add(lastPin.Y - nextPin.Y - length, $"define wire {length} (line {lexer.Line})"); break; case "d": ckt.Add(nextPin.X - lastPin.X, $"align {nextPin} with {lastPin} (line {lexer.Line})"); ckt.Add(nextPin.Y - lastPin.Y - length, $"define wire {length} (line {lexer.Line})"); break; case "l": ckt.Add(lastPin.X - nextPin.X - length, $"define wire {length} (line {lexer.Line})"); ckt.Add(lastPin.Y - nextPin.Y, $"align {nextPin} and {lastPin} (line {lexer.Line})"); break; case "r": ckt.Add(nextPin.X - lastPin.X - length, $"define wire {length} (line {lexer.Line})"); ckt.Add(lastPin.Y - nextPin.Y, $"align {nextPin} and {lastPin} (line {lexer.Line})"); break; } // Constrain the directions if (lastPin is IRotating rlastPin) { switch (dir) { case "u": ckt.Add(rlastPin.NormalY + 1, $"point {rlastPin} up (line {lexer.Line})"); ckt.Add(rlastPin.NormalX, $"point {rlastPin} up (line {lexer.Line})"); break; case "d": ckt.Add(rlastPin.NormalY - 1, $"point {rlastPin} down (line {lexer.Line})"); ckt.Add(rlastPin.NormalX, $"point {rlastPin} down (line {lexer.Line})"); break; case "l": ckt.Add(rlastPin.NormalY, $"point {rlastPin} left (line {lexer.Line})"); ckt.Add(rlastPin.NormalX + 1, $"point {rlastPin} left (line {lexer.Line})"); break; case "r": ckt.Add(rlastPin.NormalY, $"point {rlastPin} right (line {lexer.Line})"); ckt.Add(rlastPin.NormalX - 1, $"point {rlastPin} right (line {lexer.Line})"); break; case "?": ckt.Add(lastPin.X + rlastPin.NormalX * length - nextPin.X, $"point {rlastPin} to {nextPin} (line {lexer.Line})"); ckt.Add(lastPin.Y + rlastPin.NormalY * length - nextPin.Y, $"point {rlastPin} to {nextPin} (line {lexer.Line})"); break; } } if (nextPin is IRotating rnextPin) { switch (dir) { case "u": ckt.Add(rnextPin.NormalY - 1, $"point {rnextPin} up (line {lexer.Line})"); ckt.Add(rnextPin.NormalX, $"point {rnextPin} up (line {lexer.Line})"); break; case "d": ckt.Add(rnextPin.NormalY + 1, $"point {rnextPin} down (line {lexer.Line})"); ckt.Add(rnextPin.NormalX, $"point {rnextPin} down (line {lexer.Line})"); break; case "l": ckt.Add(rnextPin.NormalY, $"point {rnextPin} left (line {lexer.Line})"); ckt.Add(rnextPin.NormalX - 1, $"point {rnextPin} left (line {lexer.Line})"); break; case "r": ckt.Add(rnextPin.NormalY, $"point {rnextPin} right (line {lexer.Line})"); ckt.Add(rnextPin.NormalX + 1, $"point {rnextPin} right (line {lexer.Line})"); break; case "?": ckt.Add(nextPin.X + rnextPin.NormalX * length - lastPin.X, $"point {rnextPin} to {lastPin} (line {lexer.Line})"); ckt.Add(nextPin.Y + rnextPin.NormalY * length - lastPin.Y, $"point {rnextPin} to {lastPin} (line {lexer.Line})"); break; } } // Fix the wire length if necessary if (wires[i].Length >= 0) { ckt.Add(length - wires[i].Length, $"fix wire length {length} to {wires[i].Length} (line {lexer.Line})"); } lastPin = nextPin; } start = end; } if (end != null && end.AfterName != null) { if (end.BeforeName == null) { Warn(this, new WarningEventArgs($"Pin {end.AfterName} was specified at the end of line {lexer.Line}, but it isn't used. Did you mean \"[{end.AfterName}]{end.ComponentName}\" instead of \"{end.ComponentName}[{end.AfterName}]\"?")); } else { Warn(this, new WarningEventArgs($"Pin {end.AfterName} was specified at the end of line {lexer.Line}, but it isn't used.")); } } }