// 22-tags.s -- low-level tag and reference intrinsics
//
// Every Symta value is a 64-bit word. The lowest bit says
// "immediate vs heap"; the next 15 bits hold a *type tag*
// (`_tag X`); the upper 48 bits hold the *gid* (`gid_ X`).
// For heap objects the gid is an index into the GC heap; for
// tagged immediates -- like ECS entity ids -- it's a payload
// you choose yourself.
//
// `intern_ Name` mints a fresh type tag for `Name` (or returns
// the existing one if Name was interned before). `ref_ Tag Gid`
// builds a tagged immediate from a tag and a 48-bit gid.
//
// These are the building blocks of `cls`, but they're also a
// clean way to make typed integer ids -- handles, indices into
// external arrays, opaque references to FFI resources -- without
// any heap allocation.
//
// Run:  symta -f examples/22-tags.s


// ----------------------------------------------------------------
// 1. Tags of built-in values.  (`\[\]` escapes brackets.)
// ----------------------------------------------------------------
say "tag of 5     = [_tag 5]      (T_INT)"
say "tag of 1.0   = [_tag 1.0]    (T_FLOAT)"
say "tag of 'a'   = [_tag 'a']    (T_FIXTEXT)"
EmptyL []
say "tag of \[\]    = [_tag EmptyL]     (T_LIST)"
say "tag of No    = [_tag No]     (T_NO)"
say ""


// ----------------------------------------------------------------
// 2. Mint a fresh tag and build tagged immediates.
//    Two interns of the same name return the same tag.
// ----------------------------------------------------------------
HandleTag intern_ \handle
say "intern_ \\handle = [HandleTag]"
say "intern_ \\handle = [intern_ \handle]    (idempotent)"

// `textify_` is the print method for any value carrying this tag,
// analogous to the `as_text` method on a `type`-defined object.
handle.textify_ = "#<handle [gid_ Me]>"

H1 ref_ HandleTag 100
H2 ref_ HandleTag 200
say "H1 = [H1]    (tag=[_tag H1] gid=[gid_ H1])"
say "H2 = [H2]    (tag=[_tag H2] gid=[gid_ H2])"
say "same tag?  [(_tag H1) >< (_tag H2)]"
say "different gid?  [(gid_ H1) <> (gid_ H2)]"
say ""


// ----------------------------------------------------------------
// 3. Two distinct tags can coexist; a value's *meaning* depends
//    on its tag. A 32-bit Linux fd (just an int) and a 32-bit
//    SDL window id (also just an int) shouldn't be confusable --
//    so we tag them.
// ----------------------------------------------------------------
FdTag  intern_ \fd
WinTag intern_ \window
fd.textify_     = "fd:[gid_ Me]"
window.textify_ = "win:[gid_ Me]"

Fd1 ref_ FdTag  3
W1  ref_ WinTag 3            // same payload, different tag
say "Fd1 = [Fd1]    W1 = [W1]    -- same gid, distinct types"
// Tagged values intentionally don't auto-compare; you compare the
// projection you care about:
say "  same gid?  [(gid_ Fd1) >< (gid_ W1)]    (yes: both 3)"
say "  same tag?  [(_tag Fd1) >< (_tag W1)]    (no: different types)"
say ""


// ----------------------------------------------------------------
// 4. Method dispatch is by tag. We attach `name` to fd, but a
//    window doesn't get it -- and Symta tells us so.
// ----------------------------------------------------------------
fd.name = "/proc/self/fd/[gid_ Me]"
say "Fd1.name = [Fd1.name]"
// Fd1.name works; uncomment the next line to see the error:
//   say W1.name           // -> "window has no method `name`"
say ""


// ----------------------------------------------------------------
// 5. The same machinery powers `cls` entity IDs. `type point x y`
//    behind the scenes calls intern_ \point to mint a tag, and
//    every constructed point is `ref_ PointTag <heap-index>`. So
//    `_tag P` lets you do tag-based dispatch by hand.
// ----------------------------------------------------------------
type point x y
@as_text = "([$x],[$y])"

P point 3 4
say "P = [P]    tag=[_tag P]    gid=[gid_ P]"
say "is_point? [P.is_point]    (auto-generated by `type`)"
say ""


// ----------------------------------------------------------------
// 6. Use case: a slot table indexed by gid. ECS-style. We don't
//    store anything inside the entity -- it's just a tagged int --
//    so allocation and GC are free.
// ----------------------------------------------------------------
EntityTag intern_ \entity
entity.textify_ = "E#[gid_ Me]"

// per-attribute parallel tables, gid-indexed
Names  (!)
HPs    (!)

spawn Name HP =
  Id Names.l.n + 1            // simple monotonic id
  E ref_ EntityTag Id
  Names.(gid_ E) = Name
  HPs.(gid_ E) = HP
  E

E1 spawn \Hero    100
E2 spawn \Goblin   25
E3 spawn \Dragon  500

for E [E1 E2 E3]:
  G gid_ E
  say "  [E]  name=[Names.G]  hp=[HPs.G]"
