// SOURCE: ARCHITECTURE/32-LANGUAGE-OFFICIAL-13-IMPLEMENTATION-5.md
// AUTO-EXTRACTED - Do not edit directly. Edit the source document.

import type { Tok } from "./tokenize";
import type { Datum } from "./datum";
import { sym, char, vec, kw } from "./datum";

export function parseAll(toks: Tok[]): Datum[] {
  const out: Datum[] = [];
  let i = 0;

  function parseOne(): Datum {
    const t = toks[i];
    if (!t) throw new Error("parse: unexpected EOF");

    if (t.tag === "Quote") {
      i++;
      const d = parseOne();
      return [sym("quote"), d];
    }

    // Quasiquote: `expr → (quasiquote expr)
    if (t.tag === "Backtick") {
      i++;
      const d = parseOne();
      return [sym("quasiquote"), d];
    }

    // Unquote: ,expr → (unquote expr)
    if (t.tag === "Comma") {
      i++;
      const d = parseOne();
      return [sym("unquote"), d];
    }

    // Unquote-splicing: ,@expr → (unquote-splicing expr)
    if (t.tag === "CommaSplice") {
      i++;
      const d = parseOne();
      return [sym("unquote-splicing"), d];
    }

    if (t.tag === "LParen") {
      i++;
      const items: Datum[] = [];
      while (true) {
        const u = toks[i];
        if (!u) throw new Error("parse: missing ')'");
        if (u.tag === "RParen") { i++; break; }
        items.push(parseOne());
      }
      return items;
    }

    // Brackets: [a b c] → (vector a b c)
    if (t.tag === "LBracket") {
      i++;
      const items: Datum[] = [];
      while (true) {
        const u = toks[i];
        if (!u) throw new Error("parse: missing ']'");
        if (u.tag === "RBracket") { i++; break; }
        items.push(parseOne());
      }
      return vec(items);
    }

    // Braces: {k1 v1 k2 v2} → (hash-map k1 v1 k2 v2)
    if (t.tag === "LBrace") {
      i++;
      const items: Datum[] = [];
      while (true) {
        const u = toks[i];
        if (!u) throw new Error("parse: missing '}'");
        if (u.tag === "RBrace") { i++; break; }
        items.push(parseOne());
      }
      return [sym("hash-map"), ...items];
    }

    if (t.tag === "RParen") {
      throw new Error("parse: unexpected ')'");
    }
    if (t.tag === "RBracket") {
      throw new Error("parse: unexpected ']'");
    }
    if (t.tag === "RBrace") {
      throw new Error("parse: unexpected '}'");
    }

    if (t.tag === "Str") {
      i++;
      return t.s;
    }

    if (t.tag === "Char") {
      i++;
      return char(t.c);
    }

    // Atom: booleans, numbers, or symbol
    if (t.tag === "Atom") {
      i++;
      const s = t.s;

      if (s === "#t") return true;
      if (s === "#f") return false;

      // number
      if (/^-?\d+(\.\d+)?$/.test(s)) return Number(s);

      // null-ish
      if (s === "null") return null;

      return sym(s);
    }

    throw new Error("parse: unreachable");
  }

  while (i < toks.length) {
    out.push(parseOne());
  }
  return out;
}
