private Config.Executable toExecutable(JToken token, Config.Executable? parent)
		{
			var obj = (JObject) token;
			var exe = new Config.Executable();

			exe.Label = (string) (obj["label"] ?? Missing(obj, "label"));
			exe.Path = (string) (obj["path"] ?? parent?.Path ?? Missing(obj, "path"));
			exe.Args = (string) (obj["args"] ?? parent?.Args ?? "");
			exe.Multiple = obj["multiple"]?.Select(entry => this.toExecutable(entry, exe)).ToList();

			return exe;
		}
		public Config Load(String path)
		{
			var content = File.ReadAllText(path);
			var contentJson = Regex.Replace(content, @"//.*$", "", RegexOptions.Multiline); // remove comments

			var config = new Config();
			var root = (JObject) JToken.Parse(contentJson);

			config.PhpDir = (string) (root["phpDir"] ?? Missing(root, "phpDir"));
			config.Services = root["services"]?.Select(this.toService).ToList();
			config.Executables = root["executables"]?.Select(this.toExecutable).ToList();

			return config;
		}
        private Config.Executable toExecutable(JToken token, Config.Executable? parent)
        {
            var obj = (JObject) token;
            var exe = new Config.Executable();

            exe.Label = (string) (obj["label"] ?? Missing(obj, "label"));
            exe.Path = (string) (obj["path"] ?? parent?.Path ?? Missing(obj, "path"));
            exe.Args = (string) (obj["args"] ?? parent?.Args ?? "");

            exe.Env = parent?.Env ?? new Dictionary<string, string>();
            var env = (JObject) obj["env"] ?? new JObject();
            foreach (var pair in env) {
                exe.Env.Add(pair.Key, (string) pair.Value);
            }

            exe.Multiple = obj["multiple"]?.Select(entry => this.toExecutable(entry, exe)).ToList();

            return exe;
        }