ExpressionEvaluationResult EvalParse(
            CommandEvaluationContext context,
            string expr,
            int outputX,
            ParseResult parseResult
            )
        {
            ExpressionEvaluationResult r = null;
            var errorText = "";

            string[] t;
            int      idx;
            string   serr;

            switch (parseResult.ParseResultType)
            {
            /*
             *  case ParseResultType.Valid:
             *  var syntaxParsingResult = parseResult.SyntaxParsingResults.First();
             *  try
             *  {
             *      var outputData = InvokeCommand(CommandEvaluationContext, syntaxParsingResult.CommandSyntax.CommandSpecification, syntaxParsingResult.MatchingParameters);
             *
             *      r = new ExpressionEvaluationResult(null, ParseResultType.Valid, outputData, (int)ReturnCode.OK, null);
             *  } catch (Exception commandInvokeError)
             *  {
             *      var commandError = commandInvokeError.InnerException ?? commandInvokeError;
             *      Errorln(commandError.Message);
             *      return new ExpressionEvaluationResult(null, parseResult.ParseResultType, null, (int)ReturnCode.Error, commandError);
             *  }
             *  break;
             */

            case ParseResultType.Empty:
                r = new ExpressionEvaluationResult(null, parseResult.ParseResultType, null, (int)ReturnCode.OK, null);
                break;

            case ParseResultType.NotValid:      /* command syntax not valid */
                var perComErrs = new Dictionary <string, List <CommandSyntaxParsingResult> >();
                foreach (var prs in parseResult.SyntaxParsingResults)
                {
                    if (prs.CommandSyntax != null)
                    {
                        if (perComErrs.TryGetValue(prs.CommandSyntax?.CommandSpecification?.Name, out var lst))
                        {
                            lst.Add(prs);
                        }
                        else
                        {
                            perComErrs.Add(prs.CommandSyntax.CommandSpecification.Name, new List <CommandSyntaxParsingResult> {
                                prs
                            });
                        }
                    }
                }

                var errs           = new List <string>();
                var minErrPosition = int.MaxValue;
                var errPositions   = new List <int>();
                foreach (var kvp in perComErrs)
                {
                    var comSyntax = kvp.Value.First().CommandSyntax;
                    foreach (var prs in kvp.Value)
                    {
                        foreach (var perr in prs.ParseErrors)
                        {
                            minErrPosition = Math.Min(minErrPosition, perr.Position);
                            errPositions.Add(perr.Position);
                            if (!errs.Contains(perr.Description))
                            {
                                errs.Add(perr.Description);
                            }
                        }
                        errorText += Br + Red + string.Join(Br + Red, errs);
                    }
                    errorText += $"{Br}{Red}for syntax: {comSyntax}{Br}";
                }

                errPositions.Sort();
                errPositions = errPositions.Distinct().ToList();

                t = new string[expr.Length + 2];
                for (int i = 0; i < t.Length; i++)
                {
                    t[i] = " ";
                }
                foreach (var errPos in errPositions)
                {
                    t[GetIndex(context, errPos, expr)] = ErrorPositionMarker;
                }
                serr = string.Join("", t);
                Error(" ".PadLeft(outputX + 1) + serr);

                Error(errorText);
                r = new ExpressionEvaluationResult(errorText, parseResult.ParseResultType, null, (int)ReturnCode.NotDefined, null);
                break;

            case ParseResultType.Ambiguous:
                errorText += $"{Red}ambiguous syntaxes:{Br}";
                foreach (var prs in parseResult.SyntaxParsingResults)
                {
                    errorText += $"{Red}{prs.CommandSyntax}{Br}";
                }
                Error(errorText);
                r = new ExpressionEvaluationResult(errorText, parseResult.ParseResultType, null, (int)ReturnCode.NotDefined, null);
                break;

            case ParseResultType.NotIdentified:
                t = new string[expr.Length + 2];
                for (int j = 0; j < t.Length; j++)
                {
                    t[j] = " ";
                }
                var err = parseResult.SyntaxParsingResults.First().ParseErrors.First();
                idx        = err.Position;
                t[idx]     = ErrorPositionMarker;
                errorText += Red + err.Description;
                serr       = string.Join("", t);
                Errorln(" ".PadLeft(outputX) + serr);
                Errorln(errorText);
                r = new ExpressionEvaluationResult(errorText, parseResult.ParseResultType, null, (int)ReturnCode.NotDefined, null);
                break;

            case ParseResultType.SyntaxError:
                t = new string[expr.Length + 2];
                for (int j = 0; j < t.Length; j++)
                {
                    t[j] = " ";
                }
                var err2 = parseResult.SyntaxParsingResults.First().ParseErrors.First();
                idx        = err2.Index;
                t[idx]     = ErrorPositionMarker;
                errorText += Red + err2.Description;
                serr       = string.Join("", t);
                Errorln(" ".PadLeft(outputX) + serr);
                Errorln(errorText);
                r = new ExpressionEvaluationResult(errorText, parseResult.ParseResultType, null, (int)ReturnCode.NotDefined, null);
                break;
            }

            return(r);
        }
        int RegisterCommandsClass(CommandEvaluationContext context, Type type, bool registerAsModule)
        {
            if (type.GetInterface(typeof(ICommandsDeclaringType).FullName) == null)
            {
                throw new Exception($"the type '{type.FullName}' must implements interface '{typeof(ICommandsDeclaringType).FullName}' to be registered as a command class");
            }
            var    comsCount = 0;
            object instance  = Activator.CreateInstance(type, new object[] { });
            var    methods   = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);

            if (registerAsModule && _modules.ContainsKey(type.FullName))
            {
                Errorln($"a module with same name than commands type '{type.FullName}' is already registered");
                return(0);
            }
            foreach (var method in methods)
            {
                var cmd = method.GetCustomAttribute <CommandAttribute>();
                if (cmd != null)
                {
                    if (!method.ReturnType.HasInterface(typeof(ICommandResult)))
                    {
                        Errorln($"class={type.FullName} method={method.Name} wrong return type. should be of type '{typeof(ICommandResult).FullName}', but is of type: {method.ReturnType.FullName}");
                    }
                    else
                    {
                        var  paramspecs  = new List <CommandParameterSpecification>();
                        bool syntaxError = false;
                        var  pindex      = 0;
                        foreach (var parameter in method.GetParameters())
                        {
                            if (pindex == 0)
                            {
                                // manadatory: param 0 is CommandEvaluationContext
                                if (parameter.ParameterType != typeof(CommandEvaluationContext))
                                {
                                    Errorln($"class={type.FullName} method={method.Name} parameter 0 ('{parameter.Name}') should be of type '{typeof(CommandEvaluationContext).FullName}', but is of type: {parameter.ParameterType.FullName}");
                                    syntaxError = true;
                                    break;
                                }
                            }
                            else
                            {
                                CommandParameterSpecification pspec = null;
                                var    paramAttr = parameter.GetCustomAttribute <ParameterAttribute>();
                                object defval    = null;
                                if (!parameter.HasDefaultValue && parameter.ParameterType.IsValueType)
                                {
                                    defval = Activator.CreateInstance(parameter.ParameterType);
                                }

                                if (paramAttr != null)
                                {
                                    // TODO: validate command specification (eg. indexs validity)
                                    pspec = new CommandParameterSpecification(
                                        parameter.Name,
                                        paramAttr.Description,
                                        paramAttr.IsOptional,
                                        paramAttr.Index,
                                        null,
                                        true,
                                        parameter.HasDefaultValue,
                                        (parameter.HasDefaultValue) ? parameter.DefaultValue : defval,
                                        parameter);
                                }
                                var optAttr = parameter.GetCustomAttribute <OptionAttribute>();
                                if (optAttr != null)
                                {
                                    var reqParamAttr = parameter.GetCustomAttribute <OptionRequireParameterAttribute>();
                                    try
                                    {
                                        pspec = new CommandParameterSpecification(
                                            parameter.Name,
                                            optAttr.Description,
                                            optAttr.IsOptional,
                                            -1,
                                            optAttr.OptionName ?? parameter.Name,
                                            optAttr.HasValue,
                                            parameter.HasDefaultValue,
                                            (parameter.HasDefaultValue) ? parameter.DefaultValue : defval,
                                            parameter,
                                            reqParamAttr?.RequiredParameterName);
                                    }
                                    catch (Exception ex)
                                    {
                                        Errorln(ex.Message);
                                    }
                                }
                                if (pspec == null)
                                {
                                    syntaxError = true;
                                    Errorln($"invalid parameter: class={type.FullName} method={method.Name} name={parameter.Name}");
                                }
                                else
                                {
                                    paramspecs.Add(pspec);
                                }
                            }
                            pindex++;
                        }

                        if (!syntaxError)
                        {
                            var cmdNameAttr = method.GetCustomAttribute <CommandNameAttribute>();

                            var cmdName = (cmdNameAttr != null && cmdNameAttr.Name != null) ? cmdNameAttr.Name
                                : (cmd.Name ?? method.Name.ToLower());

                            var cmdspec = new CommandSpecification(
                                cmdName,
                                cmd.Description,
                                cmd.LongDescription,
                                cmd.Documentation,
                                method,
                                instance,
                                paramspecs);

                            bool registered = true;
                            if (_commands.TryGetValue(cmdspec.Name, out var cmdlst))
                            {
                                if (cmdlst.Select(x => x.MethodInfo.DeclaringType == type).Any())
                                {
                                    Errorln($"command already registered: '{cmdspec.Name}' in type '{cmdspec.DeclaringTypeFullName}'");
                                    registered = false;
                                }
                                else
                                {
                                    cmdlst.Add(cmdspec);
                                }
                            }
                            else
                            {
                                _commands.Add(cmdspec.Name, new List <CommandSpecification> {
                                    cmdspec
                                });
                            }

                            if (registered)
                            {
                                _syntaxAnalyzer.Add(cmdspec);
                                comsCount++;
                            }
                        }
                    }
                }
            }
            if (registerAsModule)
            {
                if (comsCount == 0)
                {
                    Errorln($"no commands found in type '{type.FullName}'");
                }
                else
                {
                    var descAttr    = type.GetCustomAttribute <CommandsAttribute>();
                    var description = descAttr != null ? descAttr.Description : "";
                    _modules.Add(type.FullName, new CommandsModule(CommandsModule.DeclaringTypeShortName(type), description, type.Assembly, 1, comsCount, type));
                }
            }
            return(comsCount);
        }
 public void RegisterCommandsClass <T>(CommandEvaluationContext context) => RegisterCommandsClass(context, typeof(T), true);
 public int RegisterCommandsClass(CommandEvaluationContext context, Type type) => RegisterCommandsClass(context, type, true);
        void InitializeCommandProcessor(string[] args, bool printInfo = true, CommandEvaluationContext commandEvaluationContext = null)
        {
            SetArgs(args);

            cons.ForegroundColor = DefaultForeground;
            cons.BackgroundColor = DefaultBackground;

            commandEvaluationContext = commandEvaluationContext ??
                                       new CommandEvaluationContext(
                this,
                Out,
                cons.In,
                Err,
                null
                );
            CommandEvaluationContext = commandEvaluationContext;

            if (printInfo)
            {
                PrintInfo(CommandEvaluationContext);
            }

            // assume the application folder ($env.APPDATA/OrbitalShell) exists and is initialized

            var lbr = false;

            // creates user app data folders
            if (!Directory.Exists(AppDataFolderPath))
            {
                LogAppendAllLinesErrorIsEnabled = false;
                Info(ColorSettings.Log + $"creating user shell folder: '{AppDataFolderPath}' ... ", false);
                try
                {
                    Directory.CreateDirectory(AppDataFolderPath);
                    Success();
                } catch (Exception createAppDataFolderPathException)
                {
                    Fail(createAppDataFolderPathException);
                }
                lbr = true;
            }

            // initialize log file
            if (!File.Exists(LogFilePath))
            {
                Info(ColorSettings.Log + $"creating log file: '{LogFilePath}' ... ", false);
                try
                {
                    var logError = Log($"file created on {System.DateTime.Now}");
                    if (logError == null)
                    {
                        Success();
                    }
                    else
                    {
                        throw logError;
                    }
                }
                catch (Exception createLogFileException)
                {
                    LogAppendAllLinesErrorIsEnabled = false;
                    Fail(createLogFileException);
                }
                lbr = true;
            }

            // initialize user profile
            if (!File.Exists(UserProfileFilePath))
            {
                Info(ColorSettings.Log + $"creating user profile file: '{UserProfileFilePath}' ... ", false);
                try
                {
                    var defaultProfileFilePath = Path.Combine(DefaultsFolderPath, UserProfileFileName);
                    File.Copy(defaultProfileFilePath, UserProfileFilePath);
                    Success();
                }
                catch (Exception createUserProfileFileException)
                {
                    Fail(createUserProfileFileException);
                }
                lbr = true;
            }

            // create/restore commands history
            CmdsHistory = new CommandsHistory();
            var createNewHistoryFile = !File.Exists(HistoryFilePath);

            if (createNewHistoryFile)
            {
                Info(ColorSettings.Log + $"creating user commands history file: '{HistoryFilePath}' ... ", false);
            }
            try
            {
                if (createNewHistoryFile)
#pragma warning disable CS0642 // Possibilité d'instruction vide erronée
                {
                    using (var fs = File.Create(HistoryFilePath));
                }
#pragma warning restore CS0642 // Possibilité d'instruction vide erronée
                CmdsHistory.Init(AppDataFolderPath, HistoryFileName);
                if (createNewHistoryFile)
                {
                    Success();
                }
            }
            catch (Exception createUserProfileFileException)
            {
                Fail(createUserProfileFileException);
            }
            lbr |= createNewHistoryFile;

            // create/restore user aliases
            CommandsAlias = new CommandsAlias();
            var createNewCommandsAliasFile = !File.Exists(CommandsAliasFilePath);
            if (createNewCommandsAliasFile)
            {
                Info(ColorSettings.Log + $"creating user commands aliases file: '{CommandsAliasFilePath}' ... ", false);
            }
            try
            {
                if (createNewCommandsAliasFile)
                {
                    var defaultAliasFilePath = Path.Combine(DefaultsFolderPath, CommandsAliasFileName);
                    File.Copy(defaultAliasFilePath, CommandsAliasFilePath);
                }
                if (createNewCommandsAliasFile)
                {
                    Success();
                }
            }
            catch (Exception createUserProfileFileException)
            {
                Fail(createUserProfileFileException);
            }
            lbr |= createNewHistoryFile;

            // end inits
            if (lbr)
            {
                Out.Echoln();
            }

            // load kernel commands
            RegisterCommandsAssembly(CommandEvaluationContext, Assembly.GetExecutingAssembly());
#if enable_test_commands
            RegisterCommandsClass <TestCommands>();
#endif
        }