import json
import sys
import urllib.request
SOURCE_URL = "https://html.spec.whatwg.org/entities.json"
def load_entities():
if len(sys.argv) > 1:
with open(sys.argv[1], encoding="utf-8") as f:
return json.load(f)
with urllib.request.urlopen(SOURCE_URL) as response:
return json.load(response)
def rust_str_literal(characters):
escaped = "".join(f"\\u{{{ord(ch):x}}}" for ch in characters)
return f'"{escaped}"'
def main():
entities = load_entities()
rows = sorted(entities.items())
print("// Generated by `xtask/gen-entities.py` from")
print(f"// <{SOURCE_URL}> — do not edit by hand, regenerate instead.")
print("//")
print("// Named character reference table for the tokenizer's")
print("// \"Named character reference state\" (WHATWG HTML spec §13.2.5).")
print("// Each entry's name already includes a trailing `;` where the")
print("// spec requires one; legacy (pre-HTML5) names appear twice, once")
print("// with and once without `;`, both mapping to the same")
print("// replacement text — matching upstream `entities.json` exactly.")
print("#![allow(dead_code)] // not yet consumed; see plan/02-tokenizer.md")
print()
print(f"pub(crate) static NAMED_CHARACTER_REFERENCES: [(&str, &str); {len(rows)}] = [")
for name, entry in rows:
assert name.startswith("&")
bare_name = name[1:]
replacement = rust_str_literal(entry["characters"])
print(f' ("{bare_name}", {replacement}),')
print("];")
if __name__ == "__main__":
main()