#!/bin/sh
# gen_builtin_argc.sh — generate the builtin-function arg-count table for vimlrs.
#
# Neovim's eval-function dispatch table (funcs.c `funcs[]`) is generated from the
# per-function metadata in `vendor/eval.lua`, where each entry carries an `args`
# field giving the accepted argument count:
#
#     args = N            exactly N arguments        (min = max = N)
#     args = { min, max } a closed range             (min, max)
#     args = { min }      min or more (varargs)      (min, max = unlimited)
#     (no args field)     takes no arguments         (min = max = 0)
#
# Vim checks this range at PARSE time (E118 too many / E119 too few) before the
# leaf `f_*` ever runs, which is why the ported leaf functions index argvars[]
# directly. vimlrs has no such table, so this script transcribes it from the
# vendored spec into `src/ported/eval/funcs_argc.rs`; the compiler consults it
# to reject mis-arity calls instead of letting a leaf panic on a short slice.
#
# This is a TABLE GENERATOR from the vendored spec, never an audit tool.
set -eu

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"

SRC="vendor/eval.lua"
OUT="src/ported/eval/funcs_argc.rs"

[ -f "$SRC" ] || { echo "missing $SRC" >&2; exit 1; }

{
  cat <<'HEADER'
//! GENERATED by scripts/gen_builtin_argc.sh from vendor/eval.lua — DO NOT EDIT.
//!
//! Per-builtin accepted argument-count range, transcribed from Neovim's
//! eval-function metadata (`vendor/eval.lua`, the source the `funcs[]` table in
//! funcs.c is generated from). Vim enforces this at parse time (E118/E119); the
//! compiler does the same so a mis-arity call is rejected up front rather than
//! reaching a leaf `f_*` that assumes the count was already checked.
//!
//! `MAX` (255) is the varargs sentinel — "no upper bound".

/// Varargs sentinel: the function accepts any number of trailing arguments.
pub const MAX: u8 = 255;

/// `(name, min_argc, max_argc)`, sorted by name for binary search.
pub const BUILTIN_ARGC: &[(&str, u8, u8)] = &[
HEADER

  awk '
    # Top-level function header: two-space indent, then a bareword key
    # (`name = {`) or a quoted key for Lua reserved words (`['and'] = {`).
    /^  [A-Za-z_][A-Za-z0-9_]* = \{/ {
      if (name != "") emit()
      name = $1
      min = 0; max = 0; seen = 0
      next
    }
    # \047 is the single-quote char (kept out of the shell-quoted awk program).
    /^  \[\047[A-Za-z_][A-Za-z0-9_]*\047\] = \{/ {
      if (name != "") emit()
      name = $0
      sub(/^  \[\047/, "", name); sub(/\047\].*/, "", name)
      min = 0; max = 0; seen = 0
      next
    }
    # The args field (four-space indent), once per entry.
    /^    args = / && name != "" && !seen {
      seen = 1
      v = $0
      sub(/^    args = /, "", v)
      sub(/,[ \t]*$/, "", v)          # strip the trailing structural comma
      gsub(/[{} ]/, "", v)            # leaves "N" or "min,max" or "min"
      nfld = split(v, a, ",")
      if (nfld == 2)      { min = a[1]; max = a[2] }
      else if (v ~ /,/)   { min = a[1]; max = 255 }   # defensive
      else if (index($0, "{") > 0) { min = a[1]; max = 255 }  # { min } varargs
      else                { min = a[1]; max = a[1] }  # exactly N
      next
    }
    END { if (name != "") emit() }

    function emit() {
      printf "    (\"%s\", %s, %s),\n", name, min, max
    }
  ' "$SRC"

  echo "];"
} > "$OUT"

COUNT=$(grep -c '^    ("' "$OUT")
echo "wrote $OUT ($COUNT entries)"
