public static DistRequirement Parse(string requirement)
        {
            DistRequirement result = new DistRequirement();

            result.raw_string = requirement;
            if (requirement.Contains(';'))
            {
                string[] parts = requirement.Split(new char[] { ';' }, 2);
                requirement   = parts[0].Trim();
                result.marker = EnvironmentMarker.ParseEnvironmentMarker(parts[1].Trim());
            }
            if (requirement.Contains('('))
            {
                string[] parts = requirement.TrimEnd(')').Split(new char[] { '(' }, 2);
                result.name = parts[0].Trim();
                result.has_version_specifier = true;
                result.version_specifier     = new VersionSpecifier(parts[1]);
            }
            else
            {
                result.name = requirement.Trim();
                result.has_version_specifier = false;
            }
            return(result);
        }
        private static EnvironmentMarker ParseAndList(MarkerToken[] tokens, int start, int end, string marker)
        {
            int level = 0;
            int i;
            var and_indices = new List <int>();

            for (i = start; i < end; i++)
            {
                var token = tokens[i];
                if (token.EqualsToken("("))
                {
                    level++;
                }
                else if (token.EqualsToken(")"))
                {
                    level--;
                    if (level < 0)
                    {
                        throw new ArgumentException(string.Format("invalid syntax at character {0} in marker {1}", token.position, marker));
                    }
                }

                if (level == 0 && token.EqualsToken("and"))
                {
                    and_indices.Add(i);
                }
            }

            if (level != 0)
            {
                throw new ArgumentException(string.Format("unclosed ( in marker {0}", marker));
            }

            if (and_indices.Count == 0)
            {
                return(ParseComparisonList(tokens, start, end, marker));
            }

            var result = new EnvironmentMarker();

            result.submarkers    = new EnvironmentMarker[and_indices.Count + 1];
            result.submarkers[0] = ParseComparisonList(tokens, start, and_indices[0], marker);
            for (i = 0; i < and_indices.Count - 1; i++)
            {
                result.submarkers[i + 1] = ParseComparisonList(tokens, and_indices[i] + 1, and_indices[i + 1], marker);
            }
            result.submarkers[and_indices.Count] = ParseComparisonList(tokens, and_indices[and_indices.Count - 1] + 1, end, marker);
            result.type = MarkerType.And;
            return(result);
        }
        private static EnvironmentMarker ParseAtom(MarkerToken[] tokens, int start, int end, string marker)
        {
            if (tokens.Length == 0)
            {
                throw new ArgumentException(string.Format("marker is empty: {1}", marker));
            }

            var token = tokens[start];

            if (end <= start)
            {
                throw new ArgumentException(string.Format("invalid syntax at character {0} in marker {1}", token.position, marker));
            }

            if (token.EqualsToken("("))
            {
                int level = 1;
                int i;

                for (i = start + 1; i < end; i++)
                {
                    if (tokens[i].EqualsToken("("))
                    {
                        level++;
                    }
                    else if (tokens[i].EqualsToken(")"))
                    {
                        level--;
                        if (level == 0 && i < end - 1)
                        {
                            throw new ArgumentException(string.Format("invalid syntax at character {0} in marker {1}", tokens[i + 1].position, marker));
                        }
                    }
                }

                if (level != 0)
                {
                    throw new ArgumentException(string.Format("unclosed ( in marker {0}", marker));
                }

                return(ParseOrList(tokens, start + 1, end - 1, marker));
            }

            if (end != start + 1)
            {
                throw new ArgumentException(string.Format("invalid syntax at character {0} in marker {1}", tokens[start + 1].position, marker));
            }

            var result = new EnvironmentMarker();

            if (token.is_literal)
            {
                result.type = MarkerType.StringLiteral;
            }
            else if (token.token == "extra")
            {
                result.type = MarkerType.ExtraVariable;
            }
            else
            {
                result.marker_variable = (EnvironmentMarkerVariable)Enum.Parse(typeof(EnvironmentMarkerVariable), token.token);
                if (result.marker_variable >= EnvironmentMarkerVariable.python_version)
                {
                    result.type = MarkerType.VersionVariable;
                }
                else
                {
                    result.type = MarkerType.StringVariable;
                }
            }

            result.str_value = token.token;

            return(result);
        }
        private static EnvironmentMarker ParseComparisonList(MarkerToken[] tokens, int start, int end, string marker)
        {
            int level = 0;
            int i;
            var comparison_indices      = new List <int>();
            var comparison_next_indices = new List <int>();
            var comparison_types        = new List <ComparisonType>();

            for (i = start; i < end; i++)
            {
                var token = tokens[i];
                if (token.EqualsToken("("))
                {
                    level++;
                }
                else if (token.EqualsToken(")"))
                {
                    level--;
                    if (level < 0)
                    {
                        throw new ArgumentException(string.Format("invalid syntax at character {0} in marker {1}", token.position, marker));
                    }
                }

                if (level == 0)
                {
                    if (!token.is_literal && operators.ContainsKey(token.token))
                    {
                        comparison_indices.Add(i);
                        comparison_next_indices.Add(i + 1);
                        comparison_types.Add(operators[token.token]);
                    }
                    else if (i + 1 < end && token.EqualsToken("not") && tokens[i + 1].EqualsToken("in"))
                    {
                        comparison_indices.Add(i);
                        comparison_next_indices.Add(i + 2);
                        i++;
                        comparison_types.Add(ComparisonType.NotIn);
                    }
                }
            }

            if (level != 0)
            {
                throw new ArgumentException(string.Format("unclosed ( in marker {0}", marker));
            }

            if (comparison_indices.Count == 0)
            {
                return(ParseAtom(tokens, start, end, marker));
            }

            var result = new EnvironmentMarker();

            result.submarkers    = new EnvironmentMarker[comparison_indices.Count + 1];
            result.submarkers[0] = ParseAtom(tokens, start, comparison_indices[0], marker);
            for (i = 0; i < comparison_indices.Count - 1; i++)
            {
                result.submarkers[i + 1] = ParseAtom(tokens, comparison_next_indices[i], comparison_indices[i + 1], marker);
            }
            result.submarkers[comparison_indices.Count] = ParseAtom(tokens, comparison_next_indices[comparison_indices.Count - 1], end, marker);
            result.type        = MarkerType.ComparisonList;
            result.comparisons = comparison_types.ToArray();
            return(result);
        }