3f8db4c17a
The complete MJCF surface in one hand-maintained file: 144 elements, 8 shared attribute groups, 47 enums, 1,497 typed attributes with defaults, the presence-constraint inventory previously visible only as hand-written reader checks, and the save policies previously visible only as hand-written writer logic. The language is a small IDL: elements bound to their mjSpec structs, typed attributes with arities and defaults, enum keyword sets with C bindings, reusable and variant groups, explicit name/reference namespaces (id<ns>/ref<ns>, following dm_control's identifier/reference model), child cardinalities, presence constraints (exclusive/together/requires/oneof over attribute bundles), bitwise flag sets, identity constants (set field = CONST), fixed char arrays (chars[n], arity counting characters), numeric range facets, and two escape hatches: reading=custom (no typed binding is generated; both reading and saving are hand-written) and writing=custom (the binding drives the reader, the save policy is hand-written). doc/generate/mjcf_schema.py is the dependency-free parser and semantic validator; errors report file:line; 55 unit tests. The language is documented by the cheat-sheet legend at the top of the schema file. The schema was bootstrapped by extraction from the sources of record -- the MJCF[] table, the mjMap keyword tables, the ~660 ReadAttr*/MapValue call sites, mjspec.h struct fields, and the default-constructors in user_init.c and engine_init.c -- then hand-curated. Same-tag elements that differ by context are distinct declarations carrying an xml= facet; worldbody, frame and replicate carry alias=body, mirroring mjXSchema::NameMatch. The top-level order is by dependency, what a saved file should read like: front matter, declarations before use, the tree, the sections that reference it, the data tail. PiperOrigin-RevId: 958060695 Change-Id: Ie10fd9f0ef202a3626f4d635d02c8731a4d287df