<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using Yaclops.Extensions;
using Yaclops.Model;

namespace Yaclops.Parsing
{
    internal class ParserState
    {
        private readonly Lexer _lexer;
        private readonly string _helpVerb;
        private readonly Dictionary<string, CommandNamedParameter> _longNames = new Dictionary<string, CommandNamedParameter>();
        private readonly Dictionary<string, CommandNamedParameter> _shortNames = new Dictionary<string, CommandNamedParameter>();
        private readonly HashSet<string> _shortHelpFlags = new HashSet<string>();
        private readonly HashSet<string> _longHelpFlags = new HashSet<string>();
        private readonly Queue<CommandPositionalParameter> _positionalParameters = new Queue<CommandPositionalParameter>();
        private readonly List<string> _pendingRequired = new List<string>();
        private CommandNamedParameter _currentParameter;

        public ParserState(CommandNode startNode, Lexer lexer, IEnumerable<string> helpFlags, string helpVerb)
        {
            NamedParameters = new List<ParserNamedParameterResult>();
            PositionalParameters = new List<ParserPositionalParameterResult>();
            CurrentNode = startNode;
            ExtractParameters(CurrentNode);

            foreach (var flag in helpFlags)
            {
                if (flag.StartsWith("--"))
                {
                    _longHelpFlags.Add(flag.Substring(2));
                }
                else if (flag.StartsWith("-"))
                {
                    _shortHelpFlags.Add(flag.Substring(1));
                }
            }

            _lexer = lexer;
            _helpVerb = helpVerb;
            CurrentToken = _lexer.Pop();
        }


        public bool HelpWanted { get; private set; }
        public CommandNode CurrentNode { get; private set; }
        public IReadOnlyList<string> PendingRequired { get { return _pendingRequired; } }
        public Token CurrentToken { get; private set; }
        public List<ParserNamedParameterResult> NamedParameters { get; private set; }
        public List<ParserPositionalParameterResult> PositionalParameters { get; private set; }


        public bool Advance()
        {
            if (_currentParameter != null)
            {
                switch (CurrentToken.Kind)
                {
                    case TokenKind.EndOfInput:
                        HandleMissingNamedParameterValue();
                        return false;

                    case TokenKind.LongName:
                    case TokenKind.ShortName:
                        HandleMissingNamedParameterValue();
                        break;

                    case TokenKind.Value:
                        if (_currentParameter.IsBool && !CurrentToken.Text.IsBool())
                        {
                            NamedParameters.Add(new ParserNamedParameterResult(_currentParameter, true.ToString()));
                            _currentParameter = null;
                            return true;
                        }
                        NamedParameters.Add(new ParserNamedParameterResult(_currentParameter, CurrentToken.Text));
                        _currentParameter = null;
                        CurrentToken = _lexer.Pop();
                        return true;
                }
            }

            return HandleDefaultState();
        }



        private void HandleMissingNamedParameterValue()
        {
            if (!_currentParameter.IsBool)
            {
                throw new CommandLineParserException("No value specified for parameter: " + _currentParameter.PropertyName);
            }

            NamedParameters.Add(new ParserNamedParameterResult(_currentParameter, true.ToString()));
            _currentParameter = null;
        }



        private bool HandleDefaultState()
        {
            switch (CurrentToken.Kind)
            {
                case TokenKind.EndOfInput:
                    return false;

                case TokenKind.LongName:
                    // TODO - check for prefixes?
                    if (_longNames.ContainsKey(CurrentToken.Text))
                    {
                        _currentParameter = _longNames[CurrentToken.Text];
                        CurrentToken = _lexer.Pop();
                        return true;
                    }
                    if (_longHelpFlags.Contains(CurrentToken.Text))
                    {
                        HelpWanted = true;
                        CurrentToken = _lexer.Pop();
                        return true;
                    }
                    throw new CommandLineParserException("Unknown named parameter: " + CurrentToken.Text);

                case TokenKind.ShortName:
                    if (_shortNames.ContainsKey(CurrentToken.Text))
                    {
                        _currentParameter = _shortNames[CurrentToken.Text];
                        CurrentToken = _lexer.Pop();
                        return true;
                    }
                    if (_shortHelpFlags.Contains(CurrentToken.Text))
                    {
                        HelpWanted = true;
                        CurrentToken = _lexer.Pop();
                        return true;
                    }
                    throw new CommandLineParserException("Unknown named parameter: " + CurrentToken.Text);

                case TokenKind.Value:
                    if (_positionalParameters.Any())
                    {
                        var commandParam = _positionalParameters.Peek();
                        if (commandParam.IsList)
                        {
                            // A list; create a new param or add to an existing one
                            if ((PositionalParameters.Count == 0) || (PositionalParameters.Last().Parameter != commandParam))
                            {
                                if (commandParam.IsRequired)
                                {
                                    _pendingRequired.Remove(commandParam.PropertyName);
                                }
                                PositionalParameters.Add(new ParserPositionalParameterResult(commandParam, CurrentToken.Text));
                            }
                            else
                            {
                                PositionalParameters.Last().Values.Add(CurrentToken.Text);
                            }
                        }
                        else
                        {
                            // Not a list; always create a new param
                            PositionalParameters.Add(new ParserPositionalParameterResult(commandParam, CurrentToken.Text));
                            _positionalParameters.Dequeue();
                        }

                        CurrentToken = _lexer.Pop();
                        return true;
                    }

                    if (CurrentNode is Command)
                    {
                        throw new CommandLineParserException("Unexpected positional parameter: " + CurrentToken.Text);
                    }

                    if (CurrentNode is CommandGroup)
                    {<commit_msg>BUGFIX: non-list required parameters were always being reported as missing<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using Yaclops.Extensions;
using Yaclops.Model;

namespace Yaclops.Parsing
{
    internal class ParserState
    {
        private readonly Lexer _lexer;
        private readonly string _helpVerb;
        private readonly Dictionary<string, CommandNamedParameter> _longNames = new Dictionary<string, CommandNamedParameter>();
        private readonly Dictionary<string, CommandNamedParameter> _shortNames = new Dictionary<string, CommandNamedParameter>();
        private readonly HashSet<string> _shortHelpFlags = new HashSet<string>();
        private readonly HashSet<string> _longHelpFlags = new HashSet<string>();
        private readonly Queue<CommandPositionalParameter> _positionalParameters = new Queue<CommandPositionalParameter>();
        private readonly List<string> _pendingRequired = new List<string>();
        private CommandNamedParameter _currentParameter;

        public ParserState(CommandNode startNode, Lexer lexer, IEnumerable<string> helpFlags, string helpVerb)
        {
            NamedParameters = new List<ParserNamedParameterResult>();
            PositionalParameters = new List<ParserPositionalParameterResult>();
            CurrentNode = startNode;
            ExtractParameters(CurrentNode);

            foreach (var flag in helpFlags)
            {
                if (flag.StartsWith("--"))
                {
                    _longHelpFlags.Add(flag.Substring(2));
                }
                else if (flag.StartsWith("-"))
                {
                    _shortHelpFlags.Add(flag.Substring(1));
                }
            }

            _lexer = lexer;
            _helpVerb = helpVerb;
            CurrentToken = _lexer.Pop();
        }


        public bool HelpWanted { get; private set; }
        public CommandNode CurrentNode { get; private set; }
        public IReadOnlyList<string> PendingRequired { get { return _pendingRequired; } }
        public Token CurrentToken { get; private set; }
        public List<ParserNamedParameterResult> NamedParameters { get; private set; }
        public List<ParserPositionalParameterResult> PositionalParameters { get; private set; }


        public bool Advance()
        {
            if (_currentParameter != null)
            {
                switch (CurrentToken.Kind)
                {
                    case TokenKind.EndOfInput:
                        HandleMissingNamedParameterValue();
                        return false;

                    case TokenKind.LongName:
                    case TokenKind.ShortName:
                        HandleMissingNamedParameterValue();
                        break;

                    case TokenKind.Value:
                        if (_currentParameter.IsBool && !CurrentToken.Text.IsBool())
                        {
                            NamedParameters.Add(new ParserNamedParameterResult(_currentParameter, true.ToString()));
                            _currentParameter = null;
                            return true;
                        }
                        NamedParameters.Add(new ParserNamedParameterResult(_currentParameter, CurrentToken.Text));
                        _currentParameter = null;
                        CurrentToken = _lexer.Pop();
                        return true;
                }
            }

            return HandleDefaultState();
        }



        private void HandleMissingNamedParameterValue()
        {
            if (!_currentParameter.IsBool)
            {
                throw new CommandLineParserException("No value specified for parameter: " + _currentParameter.PropertyName);
            }

            NamedParameters.Add(new ParserNamedParameterResult(_currentParameter, true.ToString()));
            _currentParameter = null;
        }



        private bool HandleDefaultState()
        {
            switch (CurrentToken.Kind)
            {
                case TokenKind.EndOfInput:
                    return false;

                case TokenKind.LongName:
                    // TODO - check for prefixes?
                    if (_longNames.ContainsKey(CurrentToken.Text))
                    {
                        _currentParameter = _longNames[CurrentToken.Text];
                        CurrentToken = _lexer.Pop();
                        return true;
                    }
                    if (_longHelpFlags.Contains(CurrentToken.Text))
                    {
                        HelpWanted = true;
                        CurrentToken = _lexer.Pop();
                        return true;
                    }
                    throw new CommandLineParserException("Unknown named parameter: " + CurrentToken.Text);

                case TokenKind.ShortName:
                    if (_shortNames.ContainsKey(CurrentToken.Text))
                    {
                        _currentParameter = _shortNames[CurrentToken.Text];
                        CurrentToken = _lexer.Pop();
                        return true;
                    }
                    if (_shortHelpFlags.Contains(CurrentToken.Text))
                    {
                        HelpWanted = true;
                        CurrentToken = _lexer.Pop();
                        return true;
                    }
                    throw new CommandLineParserException("Unknown named parameter: " + CurrentToken.Text);

                case TokenKind.Value:
                    if (_positionalParameters.Any())
                    {
                        var commandParam = _positionalParameters.Peek();
                        if (commandParam.IsList)
                        {
                            // A list; create a new param or add to an existing one
                            if ((PositionalParameters.Count == 0) || (PositionalParameters.Last().Parameter != commandParam))
                            {
                                if (commandParam.IsRequired)
                                {
                                    _pendingRequired.Remove(commandParam.PropertyName);
                                }
                                PositionalParameters.Add(new ParserPositionalParameterResult(commandParam, CurrentToken.Text));
                            }
                            else
                            {
                                PositionalParameters.Last().Values.Add(CurrentToken.Text);
                            }
                        }
                        else
                        {
                            if (commandParam.IsRequired)
                            {
                                _pendingRequired.Remove(commandParam.PropertyName);
                            }
                            // Not a list; always create a new param
                            PositionalParameters.Add(new ParserPositionalParameterResult(commandParam, CurrentToken.Text));
                            _positionalParameters.Dequeue();
                        }

                        CurrentToken = _lexer.Pop();
                        return true;
                    }

                    if (CurrentNode is Command)
                    {
                        throw new CommandLineParserException("Unexpected positional parameter: " + CurrentToken.Text);
                    }

                    if (CurrentNode is CommandGroup)
                    {
{'fraction_non_alphanumeric': 0.0675584380489123, 'fraction_numerical': 0.00040535062829347385, 'mean_word_length': 1.2819485124094343, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2957742', 'n_tokens_mistral': 3171, 'n_tokens_neox': 2946, 'n_words': 660}