| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- #!/usr/bin/env python3
- """extract_demos.py - build the m2 gtk-demo catalog from the GTK C demo sources.
- Reads the per-demo `.c` files under gtk/demos/gtk-demo and emits
- `demos/DemoCatalog.def` / `demos/DemoCatalog.mod`: the metadata (name,
- title, keywords, description) of every demo, so the Modula-2 browser can
- list and search them.
- Usage:
- tools/extract_demos.py [--src gtk/demos/gtk-demo] [--out demos]
- """
- import argparse
- import glob
- import os
- import re
- import sys
- MAX_NAME = 48
- MAX_TITLE = 80
- MAX_KEYWORDS = 256
- MAX_DESCRIPTION = 512
- def m2_str(text, limit):
- """Escape a Python string for a Modula-2 string literal of bounded size."""
- text = " ".join(text.split())
- if len(text) > limit:
- text = text[:limit]
- text = text.encode("ascii", "replace").decode("ascii")
- text = text.replace("\\", " ").replace('"', "'")
- return text
- def parse(path):
- src = open(path, encoding="utf-8", errors="replace").read()
- name = os.path.basename(path)[:-2]
- title = ""
- keywords = ""
- description = ""
- m = re.match(r"\ufeff?\s*/\*(.*?)\*/", src, re.S)
- if m:
- lines = [l.strip(" *\t") for l in m.group(1).splitlines()]
- lines = [l for l in lines if l]
- if lines:
- title = lines[0]
- desc = []
- for l in lines[1:]:
- if l.startswith("#Keywords:"):
- keywords = l.split(":", 1)[1].strip()
- else:
- desc.append(l)
- description = " ".join(desc)
- # the do_<name> function defines the demo id used by the C registry
- ids = re.findall(r"\bdo_([a-z0-9_]+)\s*\(", src)
- if ids:
- name = ids[0]
- return name, title, keywords, description
- def main():
- ap = argparse.ArgumentParser()
- ap.add_argument("--src", default="gtk/demos/gtk-demo")
- ap.add_argument("--out", default="demos")
- args = ap.parse_args()
- files = sorted(glob.glob(os.path.join(args.src, "*.c")))
- entries = []
- for f in files:
- name, title, kw, desc = parse(f)
- if name == "main":
- continue
- entries.append((name, title or name.replace("_", " "), kw, desc))
- entries.sort(key=lambda e: e[1].lower())
- os.makedirs(args.out, exist_ok=True)
- with open(os.path.join(args.out, "DemoCatalog.def"), "w") as f:
- f.write(
- 'DEFINITION MODULE DemoCatalog ;\n\n'
- '(*\n'
- ' m2-GTK4 - the catalogue of gtk-demo demos, generated from the\n'
- ' GTK C demo sources by tools/extract_demos.py. Provides the\n'
- ' title/keywords metadata used by the demo browser.\n'
- '*)\n\n'
- 'EXPORT UNQUALIFIED DemoCatalogCount, DemoCatalogGet;\n\n'
- 'PROCEDURE DemoCatalogCount () : CARDINAL;\n\n'
- '(* copy the metadata of demo i into the caller buffers *)\n'
- 'PROCEDURE DemoCatalogGet (i: CARDINAL; VAR name, title, keywords,\n'
- ' description: ARRAY OF CHAR);\n\n'
- 'END DemoCatalog.\n'
- )
- with open(os.path.join(args.out, "DemoCatalog.mod"), "w") as f:
- f.write("IMPLEMENTATION MODULE DemoCatalog ;\n\n")
- f.write("TYPE\n Entry = RECORD\n")
- f.write(" name: ARRAY [0..%d] OF CHAR;\n" % (MAX_NAME - 1))
- f.write(" title: ARRAY [0..%d] OF CHAR;\n" % (MAX_TITLE - 1))
- f.write(" keywords: ARRAY [0..%d] OF CHAR;\n" % (MAX_KEYWORDS - 1))
- f.write(" description: ARRAY [0..%d] OF CHAR;\n" % (MAX_DESCRIPTION - 1))
- f.write(" END;\n\n")
- f.write("VAR\n entries: ARRAY [0..%d] OF Entry;\n n: CARDINAL;\n\n" % (len(entries) - 1))
- f.write(
- "PROCEDURE Copy (VAR dst: ARRAY OF CHAR; src: ARRAY OF CHAR);\n"
- "VAR i: CARDINAL;\n"
- "BEGIN\n"
- " i := 0;\n"
- " WHILE (i <= HIGH(dst)) AND (i <= HIGH(src)) AND (src[i] # 0C) DO\n"
- " dst[i] := src[i]; INC(i)\n"
- " END;\n"
- " IF i <= HIGH(dst) THEN dst[i] := 0C END\n"
- "END Copy;\n\n"
- "PROCEDURE DemoCatalogCount () : CARDINAL;\nBEGIN\n RETURN n\nEND DemoCatalogCount;\n\n"
- "PROCEDURE DemoCatalogGet (i: CARDINAL; VAR name, title, keywords,\n"
- " description: ARRAY OF CHAR);\n"
- "BEGIN\n"
- " IF i < n THEN\n"
- " Copy(name, entries[i].name);\n"
- " Copy(title, entries[i].title);\n"
- " Copy(keywords, entries[i].keywords);\n"
- " Copy(description, entries[i].description)\n"
- " ELSE\n"
- " name[0] := 0C; title[0] := 0C; keywords[0] := 0C;\n"
- " description[0] := 0C\n"
- " END\n"
- "END DemoCatalogGet;\n\n"
- "BEGIN\n"
- " n := %d;\n" % len(entries)
- )
- for i, (name, title, kw, desc) in enumerate(entries):
- f.write(' entries[%d].name := "%s";\n' % (i, m2_str(name, MAX_NAME - 1)))
- f.write(' entries[%d].title := "%s";\n' % (i, m2_str(title, MAX_TITLE - 1)))
- f.write(' entries[%d].keywords := "%s";\n' % (i, m2_str(kw, MAX_KEYWORDS - 1)))
- f.write(' entries[%d].description := "%s";\n' % (i, m2_str(desc, MAX_DESCRIPTION - 1)))
- f.write("END DemoCatalog.\n")
- sys.stderr.write("extract_demos: %d demos -> %s/DemoCatalog.{def,mod}\n"
- % (len(entries), args.out))
- if __name__ == "__main__":
- main()
|