/// <summary>
        /// Creates an instance of <see cref="Component"/>
        /// from the values of children nodes of <see cref="Symbols.Component"/> parse tree node.
        /// </summary>
        /// <returns>
        /// A new instance of <see cref="Component"/>.
        /// </returns>
        private SpiceObject CreateComponent(ParseTreeNodeEvaluationValues values)
        {
            if (values.Count != 2 && values.Count != 3)
            {
                throw new ParseTreeEvaluationException("Error during translating parse tree to Spice Object Model");
            }

            var component = new Component(
                values.GetLexem(0),
                values.GetSpiceObject <ParameterCollection>(1),
                new SpiceLineInfo(values));

            switch ((SpiceTokenType)values.GetToken(0).Type)
            {
            case SpiceTokenType.SUFFIX:
                component.NameParameter = new SuffixParameter(values.GetLexem(0));
                break;

            default:
                component.NameParameter = new WordParameter(values.GetLexem(0));
                break;
            }

            return(component);
        }
        private SpiceObject CreatePointParameter(ParseTreeNodeEvaluationValues nt)
        {
            var values         = nt.GetSpiceObject <PointValues>(1);
            var pointParameter = new PointParameter(values, new SpiceLineInfo(nt));

            return(pointParameter);
        }
        /// <summary>
        /// Translates a SPICE parse tree to a context.
        /// </summary>
        /// <param name="root">A parse tree root.</param>
        /// <returns>A netlist.</returns>
        public SpiceObject Evaluate(ParseTreeNode root)
        {
            if (root == null)
            {
                throw new ArgumentNullException(nameof(root));
            }

            var traversal = new ParseTreeTraversal();

            // Get tree nodes in post order
            var treeNodes = traversal.GetIterativePostOrder(root);

            // Iterate over tree nodes
            foreach (var treeNode in treeNodes)
            {
                if (treeNode is ParseTreeNonTerminalNode nt)
                {
                    var items = new ParseTreeNodeEvaluationValues(nt.LineNumber, nt.StartColumnIndex, nt.EndColumnIndex, nt.FileName);

                    foreach (var child in nt.Children)
                    {
                        items.Add(treeNodesValues[child]);
                    }

                    if (!evaluators.ContainsKey(nt.Name))
                    {
                        throw new ParseTreeEvaluationException("Unsupported evaluation of parse tree node: " + nt.Name);
                    }

                    var treeNodeResult = evaluators[nt.Name](items);
                    treeNodesValues[treeNode] = new ParseTreeNonTerminalEvaluationValue
                    {
                        SpiceObject = treeNodeResult,
                        Node        = treeNode,
                    };
                }
                else
                {
                    treeNodesValues[treeNode] = new ParseTreeNodeTerminalEvaluationValue()
                    {
                        Node  = treeNode,
                        Token = ((ParseTreeTerminalNode)treeNode).Token,
                    };
                }
            }

            if (treeNodesValues[root] is ParseTreeNonTerminalEvaluationValue rootNt)
            {
                return(rootNt.SpiceObject);
            }
            else
            {
                return(null);
            }
        }
        /// <summary>
        /// Returns new instance of <see cref="ParameterCollection"/>
        /// from the values of children nodes of <see cref="Symbols.Parameters"/> parse tree node.
        /// </summary>
        /// <returns>
        /// A new instance of <see cref="ParameterCollection"/>.
        /// </returns>
        private SpiceObject CreateParameters(ParseTreeNodeEvaluationValues values)
        {
            var parameters = new ParameterCollection();

            if (values.Count == 3)
            {
                parameters.Add(values.GetSpiceObject <Parameter>(0));
                parameters.Merge(values.GetSpiceObject <ParameterCollection>(2));
            }

            return(parameters);
        }
        /// <summary>
        /// Creates an instance of <see cref="Component"/>
        /// from the values of children nodes of <see cref="Symbols.Component"/> parse tree node.
        /// </summary>
        /// <returns>
        /// A new instance of <see cref="Component"/>.
        /// </returns>
        private SpiceObject CreateComponent(ParseTreeNodeEvaluationValues values)
        {
            if (values.Count != 2 && values.Count != 3)
            {
                throw new ParseTreeEvaluationException("Error during translating parse tree to Spice Object Model");
            }

            var component = new Component(
                values.GetLexem(0),
                values.GetSpiceObject <ParameterCollection>(1),
                new SpiceLineInfo(values));

            return(component);
        }
        /// <summary>
        /// Returns new instance of <see cref="Control"/>
        /// from the values of children nodes of <see cref="Symbols.Control"/> parse tree node.
        /// </summary>
        /// <returns>
        /// A new instance of <see cref="Control"/>.
        /// </returns>
        private SpiceObject CreateControl(ParseTreeNodeEvaluationValues values)
        {
            Control       control;
            SpiceLineInfo lineInfo = new SpiceLineInfo(values);

            switch (values.GetLexem(0).ToLower())
            {
            case ".endl":
                control = new Control("endl", new ParameterCollection(), lineInfo);
                break;

            case ".if":
                control = new Control(
                    "if",
                    new ParameterCollection(
                        new List <Parameter>()
                {
                    new ExpressionParameter(values.GetLexem(1), lineInfo),
                }),
                    lineInfo);

                break;

            case ".elseif":
                control = new Control(
                    "elseif",
                    new ParameterCollection(
                        new List <Parameter>()
                {
                    new ExpressionParameter(values.GetLexem(1), lineInfo),
                }),
                    lineInfo);
                break;

            case ".else":
                control = new Control("else", new ParameterCollection(new List <Parameter>()), lineInfo);
                break;

            case ".endif":
                control = new Control("endif", new ParameterCollection(new List <Parameter>()), lineInfo);
                break;

            default:
                control = new Control(values.GetLexem(1), values.GetSpiceObject <ParameterCollection>(2), lineInfo);
                break;
            }

            return(control);
        }
        /// <summary>
        /// Returns new instance of <see cref="ReferenceParameter"/>
        /// or <see cref="ValueParameter"/> or <see cref="WordParameter"/>
        /// or <see cref="ExpressionParameter"/> or <see cref="IdentifierParameter"/>
        /// from the values of children nodes of <see cref="Symbols.ParameterSingle"/> parse tree node.
        /// </summary>
        /// <returns>
        /// A new instance of <see cref="SingleParameter"/>.
        /// </returns>
        private SpiceObject CreateParameterSingle(ParseTreeNodeEvaluationValues values)
        {
            if (values[0] is ParseTreeNodeTerminalEvaluationValue t)
            {
                var lexemValue = t.Token.Lexem;
                switch (t.Token.SpiceTokenType)
                {
                case SpiceTokenType.REFERENCE:
                    return(new ReferenceParameter(lexemValue, new SpiceLineInfo(t.Token)));

                case SpiceTokenType.DOUBLE_QUOTED_STRING:
                    return(new StringParameter(lexemValue.Trim('"'), new SpiceLineInfo(t.Token)));

                case SpiceTokenType.SINGLE_QUOTED_STRING:
                    return(new StringParameter(lexemValue.Trim('\''), new SpiceLineInfo(t.Token)));

                case SpiceTokenType.VALUE:
                    return(new ValueParameter(lexemValue, new SpiceLineInfo(t.Token)));

                case SpiceTokenType.WORD:
                    return(new WordParameter(lexemValue, new SpiceLineInfo(t.Token)));

                case SpiceTokenType.SUFFIX:
                    return(new SuffixParameter(lexemValue, new SpiceLineInfo(t.Token)));

                case SpiceTokenType.PREFIX_SINGLE:
                case SpiceTokenType.PREFIX_COMPLEX:
                    return(new PrefixParameter(lexemValue, new SpiceLineInfo(t.Token)));

                case SpiceTokenType.IDENTIFIER:
                    return(new IdentifierParameter(lexemValue, new SpiceLineInfo(t.Token)));

                case SpiceTokenType.EXPRESSION:
                    return(new ExpressionParameter(lexemValue, new SpiceLineInfo(t.Token)));

                case SpiceTokenType.EXPRESSION_BRACKET:
                    return(new ExpressionParameter(lexemValue.Replace("}", string.Empty).Replace("{", string.Empty), new SpiceLineInfo(t.Token)));

                case SpiceTokenType.EXPRESSION_SINGLE_QUOTES:
                    return(new ExpressionParameter(lexemValue.Trim('\''), new SpiceLineInfo(t.Token)));

                case SpiceTokenType.PERCENT:
                    return(new PercentParameter(lexemValue.TrimEnd('%'), new SpiceLineInfo(t.Token)));
                }
            }

            throw new ParseTreeEvaluationException("Error during translating parse tree to Spice Object Model");
        }
        /// <summary>
        /// Returns new instance of <see cref="SpiceNetlist"/>
        /// from the values of children nodes of <see cref="Symbols.Netlist"/> parse tree node.
        /// </summary>
        /// <returns>
        /// A new instance of <see cref="SpiceNetlist"/>.
        /// </returns>
        private SpiceObject CreateNetlist(ParseTreeNodeEvaluationValues values)
        {
            if (values.Count == 3)
            {
                return(new SpiceNetlist(string.Empty, values.GetSpiceObject <Statements>(1)));
            }
            else
            {
                if (values.Count == 1)
                {
                    return(new SpiceNetlist(null, new Statements()));
                }

                return(new SpiceNetlist(values.GetLexem(0), values.Count >= 3 ? values.GetSpiceObject <Statements>(2) : new Statements()));
            }
        }
        private SpiceObject CreatePoints(ParseTreeNodeEvaluationValues values)
        {
            var points = new Points();

            if (values.Count == 2)
            {
                points.Values.Add(values.GetSpiceObject <PointParameter>(0));
                points.Values.AddRange(values.GetSpiceObject <Points>(1).Values);
            }

            if (values.Count == 4)
            {
                points.Values.Add(values.GetSpiceObject <PointParameter>(1));
                points.Values.AddRange(values.GetSpiceObject <Points>(2).Values);
            }

            return(points);
        }
        /// <summary>
        /// Returns new instance of <see cref="SubCircuit"/>
        /// from the values of children nodes of <see cref="Symbols.Subckt"/> parse tree node.
        /// </summary>
        /// <returns>
        /// A new instance of <see cref="SubCircuit"/>.
        /// </returns>
        private SpiceObject CreateSubCircuit(ParseTreeNodeEvaluationValues values)
        {
            if (values.Count < 3)
            {
                throw new ParseTreeEvaluationException("Error during translating parse tree to Spice Object Model");
            }

            var subCkt = new SubCircuit(values.GetLexem(2), new Statements(), new ParameterCollection(), new SpiceLineInfo(values));

            var allParameters = values.GetSpiceObject <ParameterCollection>(3);

            // Parse nodes and parameters
            bool mode = true; // true = nodes, false = parameters

            foreach (var parameter in allParameters)
            {
                if (mode)
                {
                    // After this, only parameters will follow
                    if (parameter is SingleParameter s && s.Image.ToLower() == "params:")
                    {
                        mode = false;
                    }

                    // Parameters have started, so we will keep reading parameters
                    else if (parameter is AssignmentParameter a)
                    {
                        mode = false;
                        subCkt.DefaultParameters.Add(a);
                    }

                    // Still reading nodes
                    else if (parameter is SingleParameter s2)
                    {
                        if (s2 is WordParameter ||
                            s2 is IdentifierParameter ||
                            s2 is PrefixParameter ||
                            s2 is SuffixParameter ||
                            int.TryParse(s2.Image, out _))
                        {
                            subCkt.Pins.Add(s2);
                        }
                    }
                }
        /// <summary>
        /// Returns new instance of <see cref="SingleParameter"/>
        /// or <see cref="BracketParameter"/>
        /// or <see cref="AssignmentParameter"/>
        /// or <see cref="VectorParameter"/>
        /// from the values of children nodes of <see cref="Symbols.Parameter"/> parse tree node.
        /// </summary>
        /// <returns>
        /// A new instance of <see cref="SingleParameter"/>.
        /// </returns>
        private SpiceObject CreateParameter(ParseTreeNodeEvaluationValues values)
        {
            if (values.Count == 1)
            {
                if (values.TryToGetSpiceObject(0, out VectorParameter vp))
                {
                    return(vp);
                }

                if (values.TryToGetSpiceObject(0, out SingleParameter sp))
                {
                    return(sp);
                }

                if (values.TryToGetSpiceObject(0, out BracketParameter bp))
                {
                    return(bp);
                }

                if (values.TryToGetSpiceObject(0, out AssignmentParameter ap))
                {
                    return(ap);
                }

                if (values.TryToGetSpiceObject(0, out ExpressionEqualParameter eep))
                {
                    return(eep);
                }

                if (values.TryToGetSpiceObject(0, out PointParameter pp))
                {
                    return(pp);
                }
            }

            throw new ParseTreeEvaluationException("Error during translating parse tree to Spice Object Model");
        }
        private SpiceObject CreatePointValues(ParseTreeNodeEvaluationValues values)
        {
            var pointValues = new PointValues(new SpiceLineInfo(values));

            if (values.Count == 3)
            {
                pointValues.Items.Add(values.GetSpiceObject <SingleParameter>(0));
                pointValues.Items.AddRange(values.GetSpiceObject <PointValues>(2).Items);
            }
            else
            {
                if (values.Count == 2)
                {
                    pointValues.Items.Add(values.GetSpiceObject <SingleParameter>(0));
                    pointValues.Items.AddRange(values.GetSpiceObject <PointValues>(1).Items);
                }
                else
                {
                    pointValues.Items.Add(values.GetSpiceObject <SingleParameter>(0));
                }
            }

            return(pointValues);
        }
 /// <summary>
 /// Returns new instance of <see cref="SpiceNetlist"/>
 /// from the values of children nodes of <see cref="Symbols.Netlist"/> parse tree node.
 /// </summary>
 /// <returns>
 /// A new instance of <see cref="SpiceNetlist"/>.
 /// </returns>
 private SpiceObject CreateNetlistWithoutTitle(ParseTreeNodeEvaluationValues values)
 {
     return(new SpiceNetlist(null, values.GetSpiceObject <Statements>(0)));
 }
 private SpiceObject CreatePointValue(ParseTreeNodeEvaluationValues nt)
 {
     return((nt[0] as ParseTreeNonTerminalEvaluationValue)?.SpiceObject);
 }