///|
priv struct DirSpec {
  ctxs : Array[String]
  min_args : Int
  max_args : Int
  form : Int
}

///|
fn split_ctx(s : String) -> Array[String] {
  let out : Array[String] = []
  let mut start = 0
  let mut i = 0
  while i <= s.length() {
    if i == s.length() || s[i].to_int() == 124 {
      if i > start {
        out.push(slice_text(s, start, i))
      }
      start = i + 1
    }
    i = i + 1
  }
  out
}

///|
fn spec(ctxs : String, min_args : Int, max_args : Int, form : Int) -> DirSpec {
  { ctxs: split_ctx(ctxs), min_args, max_args, form, }
}

///|
fn core_specs() -> Map[String, DirSpec] {
  let m : Map[String, DirSpec] = Map([])
  m["daemon"] = spec("main", 0, 1, 0)
  m["master_process"] = spec("main", 0, 1, 0)
  m["worker_processes"] = spec("main", 1, 1, 0)
  m["worker_rlimit_nofile"] = spec("main", 1, 1, 0)
  m["worker_cpu_affinity"] = spec("main", 1, 2, 0)
  m["worker_priority"] = spec("main", 1, 1, 0)
  m["worker_shutdown_timeout"] = spec("main", 1, 1, 0)
  m["error_log"] = spec("main|http|mail|stream|server", 1, 2, 0)
  m["pid"] = spec("main", 1, 1, 0)
  m["user"] = spec("main", 1, 2, 0)
  m["load_module"] = spec("main", 1, 1, 0)
  m["env"] = spec("main", 1, 1, 0)
  m["include"] = spec("any", 1, 1, 0)
  m["thread_pool"] = spec("main", 2, 4, 0)
  m["timer_resolution"] = spec("main", 1, 1, 0)
  m["pcre_jit"] = spec("main", 1, 1, 0)
  m["lock_file"] = spec("main", 1, 1, 0)
  m["working_directory"] = spec("main", 1, 1, 0)
  m["debug_points"] = spec("main", 1, 1, 0)
  m["ssl_engine"] = spec("main", 1, 1, 0)
  m["events"] = spec("main", 0, 0, 1)
  m["http"] = spec("main", 0, 0, 1)
  m["stream"] = spec("main", 0, 0, 1)
  m["mail"] = spec("main", 0, 0, 1)
  m["accept_mutex"] = spec("events", 1, 1, 0)
  m["accept_mutex_delay"] = spec("events", 1, 1, 0)
  m["multi_accept"] = spec("events", 1, 1, 0)
  m["use"] = spec("events", 1, 1, 0)
  m["worker_connections"] = spec("events", 1, 1, 0)
  m["worker_aio_requests"] = spec("events", 1, 1, 0)
  m["debug_connection"] = spec("events", 1, 1, 0)
  m["default_type"] = spec("http|server|location", 1, 1, 0)
  m["sendfile"] = spec("http|server|location", 1, 1, 0)
  m["tcp_nopush"] = spec("http|server|location", 1, 1, 0)
  m["tcp_nodelay"] = spec("http|server|location", 1, 1, 0)
  m["keepalive_timeout"] = spec("http|server|location", 1, 2, 0)
  m["keepalive_requests"] = spec("http|server|location|upstream", 1, 1, 0)
  m["gzip"] = spec("http|server|location|if", 1, 1, 0)
  m["gzip_types"] = spec("http|server|location", 1, 32, 0)
  m["gzip_min_length"] = spec("http|server|location", 1, 1, 0)
  m["gzip_comp_level"] = spec("http|server|location", 1, 1, 0)
  m["gzip_vary"] = spec("http|server|location", 1, 1, 0)
  m["gzip_proxied"] = spec("http|server|location", 1, 8, 0)
  m["gzip_disable"] = spec("http|server|location", 1, 1, 0)
  m["upstream"] = spec("http", 1, 1, 1)
  m["log_format"] = spec("http", 2, 32, 0)
  m["access_log"] = spec("http|server|location|if|limit_except", 1, 4, 0)
  m["resolver"] = spec("http|server|location|upstream", 1, 8, 0)
  m["resolver_timeout"] = spec("http|server|location|upstream", 1, 1, 0)
  m["client_max_body_size"] = spec("http|server|location", 1, 1, 0)
  m["client_body_buffer_size"] = spec("http|server|location", 1, 1, 0)
  m["client_header_buffer_size"] = spec("http|server", 1, 1, 0)
  m["large_client_header_buffers"] = spec("http|server", 2, 2, 0)
  m["client_body_timeout"] = spec("http|server|location", 1, 1, 0)
  m["client_header_timeout"] = spec("http|server", 1, 1, 0)
  m["send_timeout"] = spec("http|server|location", 1, 1, 0)
  m["lingering_timeout"] = spec("http|server|location", 1, 1, 0)
  m["lingering_close"] = spec("http|server|location", 1, 1, 0)
  m["reset_timedout_connection"] = spec("http|server|location", 1, 1, 0)
  m["types"] = spec("http|server|location", 0, 0, 1)
  m["charset"] = spec("http|server|location|if", 1, 1, 0)
  m["charset_types"] = spec("http|server|location", 1, 32, 0)
  m["source_charset"] = spec("http|server|location|if", 1, 1, 0)
  m["map"] = spec("http|stream", 2, 2, 1)
  m["geo"] = spec("http|stream", 1, 2, 1)
  m["split_clients"] = spec("http|stream", 2, 2, 1)
  m["proxy_cache_path"] = spec("http", 1, 16, 0)
  m["proxy_temp_path"] = spec("http|server|location", 1, 4, 0)
  m["fastcgi_temp_path"] = spec("http|server|location", 1, 4, 0)
  m["uwsgi_temp_path"] = spec("http|server|location", 1, 4, 0)
  m["scgi_temp_path"] = spec("http|server|location", 1, 4, 0)
  m["ssl_protocols"] = spec("http|server", 1, 8, 0)
  m["ssl_ciphers"] = spec("http|server", 1, 1, 0)
  m["ssl_prefer_server_ciphers"] = spec("http|server", 1, 1, 0)
  m["ssl_session_cache"] = spec("http|server", 1, 2, 0)
  m["ssl_session_timeout"] = spec("http|server", 1, 1, 0)
  m["ssl_certificate"] = spec("http|server", 1, 1, 0)
  m["ssl_certificate_key"] = spec("http|server", 1, 1, 0)
  m["ssl_verify_client"] = spec("http|server", 1, 1, 0)
  m["ssl_client_certificate"] = spec("http|server", 1, 1, 0)
  m["add_header"] = spec("http|server|location|if", 2, 3, 0)
  m["root"] = spec("http|server|location|if", 1, 1, 0)
  m["alias"] = spec("location", 1, 1, 0)
  m["index"] = spec("http|server|location", 1, 16, 0)
  m["error_page"] = spec("http|server|location|if", 2, 16, 0)
  m["try_files"] = spec("server|location", 2, 16, 0)
  m["open_file_cache"] = spec("http|server|location", 1, 2, 0)
  m["server_tokens"] = spec("http|server|location", 1, 1, 0)
  m["server_names_hash_bucket_size"] = spec("http", 1, 1, 0)
  m["server_names_hash_max_size"] = spec("http", 1, 1, 0)
  m["variables_hash_max_size"] = spec("http", 1, 1, 0)
  m["variables_hash_bucket_size"] = spec("http", 1, 1, 0)
  m["limit_req_zone"] = spec("http", 3, 4, 0)
  m["limit_conn_zone"] = spec("http", 2, 2, 0)
  m["limit_req"] = spec("http|server|location", 1, 3, 0)
  m["limit_conn"] = spec("http|server|location", 2, 2, 0)
  m["auth_basic"] = spec("http|server|location|limit_except", 1, 1, 0)
  m["auth_basic_user_file"] = spec("http|server|location|limit_except", 1, 1, 0)
  m["allow"] = spec("http|server|location|limit_except", 1, 1, 0)
  m["deny"] = spec("http|server|location|limit_except", 1, 1, 0)
  m["real_ip_header"] = spec("http|server|location", 1, 1, 0)
  m["set_real_ip_from"] = spec("http|server|location", 1, 1, 0)
  m["real_ip_recursive"] = spec("http|server|location", 1, 1, 0)
  m["listen"] = spec("server", 1, 8, 0)
  m["server_name"] = spec("server", 1, 32, 0)
  m["location"] = spec("server|location", 1, 2, 1)
  m["internal"] = spec("location", 0, 0, 0)
  m["proxy_pass"] = spec("location|if|limit_except", 1, 1, 0)
  m["proxy_set_header"] = spec("http|server|location", 2, 2, 0)
  m["proxy_hide_header"] = spec("http|server|location", 1, 1, 0)
  m["proxy_pass_header"] = spec("http|server|location", 1, 1, 0)
  m["proxy_redirect"] = spec("http|server|location", 1, 2, 0)
  m["proxy_read_timeout"] = spec("http|server|location", 1, 1, 0)
  m["proxy_connect_timeout"] = spec("http|server|location", 1, 1, 0)
  m["proxy_send_timeout"] = spec("http|server|location", 1, 1, 0)
  m["proxy_buffering"] = spec("http|server|location", 1, 1, 0)
  m["proxy_buffers"] = spec("http|server|location", 2, 2, 0)
  m["proxy_buffer_size"] = spec("http|server|location", 1, 1, 0)
  m["proxy_http_version"] = spec("http|server|location", 1, 1, 0)
  m["fastcgi_pass"] = spec("location|if", 1, 1, 0)
  m["fastcgi_index"] = spec("http|server|location", 1, 1, 0)
  m["fastcgi_param"] = spec("http|server|location", 2, 3, 0)
  m["uwsgi_pass"] = spec("location|if", 1, 1, 0)
  m["scgi_pass"] = spec("location|if", 1, 1, 0)
  m["return"] = spec("server|location|if", 1, 2, 0)
  m["rewrite"] = spec("server|location|if", 2, 3, 0)
  m["set"] = spec("server|location|if", 2, 2, 0)
  m["break"] = spec("server|location|if", 0, 0, 0)
  m["if"] = spec("server|location", 1, 16, 1)
  m["limit_except"] = spec("location", 1, 8, 1)
  m["stub_status"] = spec("server|location", 0, 1, 0)
  m["expires"] = spec("http|server|location|if", 1, 2, 0)
  m["add_trailer"] = spec("http|server|location|if", 2, 3, 0)
  m["absolute_redirect"] = spec("http|server|location", 1, 1, 0)
  m["port_in_redirect"] = spec("http|server|location", 1, 1, 0)
  m["merge_slashes"] = spec("http|server", 1, 1, 0)
  m["underscores_in_headers"] = spec("http|server", 1, 1, 0)
  m["ignore_invalid_headers"] = spec("http|server", 1, 1, 0)
  m["log_not_found"] = spec("http|server|location", 1, 1, 0)
  m["log_subrequest"] = spec("http|server|location", 1, 1, 0)
  m["recursive_error_pages"] = spec("http|server|location", 1, 1, 0)
  m["msie_padding"] = spec("http|server|location", 1, 1, 0)
  m["chunked_transfer_encoding"] = spec("http|server|location", 1, 1, 0)
  m["keepalive_disable"] = spec("http|server|location", 1, 4, 0)
  m["hash"] = spec("upstream", 1, 2, 0)
  m["ip_hash"] = spec("upstream", 0, 0, 0)
  m["keepalive"] = spec("upstream", 1, 1, 0)
  m["least_conn"] = spec("upstream", 0, 0, 0)
  m["least_time"] = spec("upstream", 1, 2, 0)
  m["queue"] = spec("upstream", 1, 2, 0)
  m["random"] = spec("upstream", 0, 2, 0)
  m["zone"] = spec("upstream", 1, 2, 0)
  m["sticky"] = spec("upstream", 1, 8, 0)
  m["protocol"] = spec("mail|server", 1, 1, 0)
  m["smtp_auth"] = spec("mail|server", 1, 8, 0)
  m["imap_auth"] = spec("mail|server", 1, 8, 0)
  m["pop3_auth"] = spec("mail|server", 1, 8, 0)
  m["smtp_capabilities"] = spec("mail|server", 1, 16, 0)
  m["xclient"] = spec("mail|server", 1, 1, 0)
  m["auth_http"] = spec("mail|server", 1, 1, 0)
  m["proxy_protocol"] = spec("stream|server", 1, 1, 0)
  m["ssl_preread"] = spec("stream|server", 1, 1, 0)
  m["proxy_timeout"] = spec("stream|server", 1, 1, 0)
  m["proxy_connect_timeout"] = spec("http|server|location|stream", 1, 1, 0)
  m["proxy_next_upstream"] = spec("http|server|location|stream|server", 1, 8, 0)
  m["proxy_next_upstream_tries"] = spec("http|server|location", 1, 1, 0)
  m["proxy_next_upstream_timeout"] = spec("http|server|location", 1, 1, 0)
  m["proxy_ssl_server_name"] = spec("http|server|location", 1, 1, 0)
  m["proxy_ssl_name"] = spec("http|server|location", 1, 1, 0)
  m["proxy_ssl_verify"] = spec("http|server|location", 1, 1, 0)
  m["fastcgi_read_timeout"] = spec("http|server|location", 1, 1, 0)
  m["fastcgi_buffers"] = spec("http|server|location", 2, 2, 0)
  m["fastcgi_buffer_size"] = spec("http|server|location", 1, 1, 0)
  m["uwsgi_param"] = spec("http|server|location", 2, 3, 0)
  m["scgi_param"] = spec("http|server|location", 2, 3, 0)
  m["client_body_in_file_only"] = spec("http|server|location", 1, 1, 0)
  m["client_body_in_single_buffer"] = spec("http|server|location", 1, 1, 0)
  m["directio"] = spec("http|server|location", 1, 1, 0)
  m["directio_alignment"] = spec("http|server|location", 1, 1, 0)
  m["output_buffers"] = spec("http|server|location", 2, 2, 0)
  m["postpone_output"] = spec("http|server|location", 1, 1, 0)
  m["read_ahead"] = spec("http|server|location", 1, 1, 0)
  m["sendfile_max_chunk"] = spec("http|server|location", 1, 1, 0)
  m["subrequest_output_buffer_size"] = spec("http|server|location", 1, 1, 0)
  m["aio"] = spec("http|server|location", 1, 2, 0)
  m["aio_write"] = spec("http|server|location", 1, 1, 0)
  m["open_file_cache_valid"] = spec("http|server|location", 1, 1, 0)
  m["open_file_cache_min_uses"] = spec("http|server|location", 1, 1, 0)
  m["open_file_cache_errors"] = spec("http|server|location", 1, 1, 0)
  m["disable_symlinks"] = spec("http|server|location", 1, 3, 0)
  m["if_modified_since"] = spec("http|server|location", 1, 1, 0)
  m["max_ranges"] = spec("http|server|location", 1, 1, 0)
  m["msie_refresh"] = spec("http|server|location", 1, 1, 0)
  m["satisfy"] = spec("http|server|location", 1, 1, 0)
  m["lingering_time"] = spec("http|server|location", 1, 1, 0)
  m["keepalive_time"] = spec("http|server|location", 1, 1, 0)
  m["http2"] = spec("http|server", 1, 1, 0)
  m["http2_max_concurrent_streams"] = spec("http|server", 1, 1, 0)
  m["http3"] = spec("http|server", 1, 1, 0)
  m["quic_retry"] = spec("http|server", 1, 1, 0)
  m["ssl_early_data"] = spec("http|server", 1, 1, 0)
  m["ssl_conf_command"] = spec("http|server", 2, 2, 0)
  m["ssl_dhparam"] = spec("http|server", 1, 1, 0)
  m["ssl_ecdh_curve"] = spec("http|server", 1, 1, 0)
  m["ssl_password_file"] = spec("http|server", 1, 1, 0)
  m["ssl_session_tickets"] = spec("http|server", 1, 1, 0)
  m["ssl_stapling"] = spec("http|server", 1, 1, 0)
  m["ssl_stapling_verify"] = spec("http|server", 1, 1, 0)
  m["ssl_trusted_certificate"] = spec("http|server", 1, 1, 0)
  m["gunzip"] = spec("http|server|location", 1, 1, 0)
  m["gunzip_buffers"] = spec("http|server|location", 2, 2, 0)
  m["gzip_static"] = spec("http|server|location", 1, 1, 0)
  m
}

///|
fn ctx_in(ctxs : Array[String], cur : String) -> Bool {
  let mut i = 0
  while i < ctxs.length() {
    if ctxs[i] == "any" || ctxs[i] == cur {
      return true
    }
    i = i + 1
  }
  false
}

///|
fn lookup_spec(name : String, ctx : String) -> DirSpec? {
  if name == "server" {
    if ctx == "http" {
      return Some(spec("http", 0, 0, 1))
    } else if ctx == "upstream" {
      return Some(spec("upstream", 1, 8, 0))
    } else {
      return None
    }
  }
  core_specs().get(name)
}

///|
pub fn known_core_directive(name : String) -> Bool {
  if name == "server" || name == "#" {
    true
  } else {
    match core_specs().get(name) {
      Some(_) => true
      None => false
    }
  }
}