예제 #1
0
        /// <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);
            });
        }
예제 #2
0
        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") + "\"");
            }
        }
예제 #3
0
 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));
 }
예제 #4
0
        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);
        }
예제 #5
0
        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);
        }
예제 #6
0
        /// <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);
        }
예제 #7
0
        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");
            }
예제 #8
0
        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);
        }
예제 #9
0
 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);
 }
예제 #10
0
        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);
        }
예제 #11
0
 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);
     }
 }
예제 #12
0
        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."));
                }
            }
        }