build.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. import subprocess
  2. from pathlib import Path
  3. import urllib.parse
  4. import os
  5. SCRIPT_DIR = Path(__file__).parent.resolve()
  6. def generate_example(
  7. filename, title, strip_asserts=True, insert_run_link=True, footer=None
  8. ):
  9. path_in = SCRIPT_DIR.parent / "examples" / f"{filename}.nbt"
  10. path_out = SCRIPT_DIR / "src" / f"example-{filename}.md"
  11. print(path_in)
  12. print(path_out)
  13. code = []
  14. with open(path_in, "r", encoding="utf-8") as fin:
  15. for line in fin:
  16. if not (strip_asserts and "assert_eq" in line):
  17. code.append(line)
  18. url = f"https://numbat.dev/?q={urllib.parse.quote_plus(''.join(code))}"
  19. with open(path_out, "w", encoding="utf-8") as fout:
  20. fout.write("<!-- This file is autogenerated! Do not modify it -->\n")
  21. fout.write("\n")
  22. fout.write(f"# {title}\n")
  23. if insert_run_link:
  24. fout.write(
  25. f'<a href="{url}"><i class="fa fa-play"></i> Run this example</a>\n'
  26. )
  27. fout.write("\n")
  28. fout.write("``` numbat\n")
  29. fout.writelines(code)
  30. fout.write("```\n")
  31. if footer:
  32. fout.write("\n")
  33. fout.write(footer)
  34. fout.write("\n")
  35. def xkcd_footer(number, img_name):
  36. footer = '<p align="center" style="margin-top: 2em">'
  37. footer += f'<a href="https://xkcd.com/{number}/"><img src="https://imgs.xkcd.com/comics/{img_name}.png" alt="XKCD {number}" style="max-width: 100%"></a>'
  38. footer += f'<br>Source: <a href="https://xkcd.com/{number}/">https://xkcd.com/{number}/</a>'
  39. footer += "</p>"
  40. return footer
  41. generate_example("acidity", "Acidity")
  42. generate_example("barometric_formula", "Barometric formula")
  43. generate_example("body_mass_index", "Body mass index")
  44. generate_example("factorial", "Factorial", strip_asserts=False)
  45. generate_example("medication_dosage", "Medication dosage")
  46. generate_example("molarity", "Molarity")
  47. generate_example("musical_note_frequency", "Musical note frequency")
  48. generate_example("paper_size", "Paper sizes")
  49. generate_example("pipe_flow_rate", "Flow rate in a pipe")
  50. generate_example("population_growth", "Population growth")
  51. generate_example("recipe", "Recipe")
  52. generate_example("voyager", "Voyager")
  53. generate_example(
  54. "xkcd_681",
  55. "XKCD 681",
  56. footer=xkcd_footer(681, "gravity_wells"),
  57. )
  58. generate_example(
  59. "xkcd_687", "XKCD 687", footer=xkcd_footer(687, "dimensional_analysis")
  60. )
  61. generate_example("xkcd_2585", "XKCD 2585", footer=xkcd_footer(2585, "rounding"))
  62. generate_example(
  63. "xkcd_2812", "XKCD 2812", footer=xkcd_footer(2812, "solar_panel_placement")
  64. )
  65. generate_example(
  66. "numbat_syntax", "Syntax overview", strip_asserts=False, insert_run_link=False
  67. )
  68. path_units = SCRIPT_DIR / "src" / "list-units.md"
  69. with open(path_units, "w", encoding="utf-8") as f:
  70. print("Generating list of units...", flush=True)
  71. subprocess.run(
  72. ["cargo", "run", "--release", "--quiet", "--example=inspect", "units"],
  73. stdout=f,
  74. text=True,
  75. )
  76. def list_of_functions(file_name, document):
  77. path = SCRIPT_DIR / "src" / f"list-functions-{file_name}.md"
  78. with open(path, "w", encoding="utf-8") as f:
  79. print(f"# {document['title']}\n", file=f, flush=True)
  80. if introduction := document.get("introduction"):
  81. print(introduction + "\n", file=f, flush=True)
  82. sections = document["sections"]
  83. if len(sections) >= 3:
  84. links = []
  85. for section in sections:
  86. if title := section.get("title"):
  87. link = title.lower().replace(" ", "-").replace(",", "")
  88. links.append(f"[{title}](#{link})")
  89. print(f"{' · '.join(links)}\n", file=f, flush=True)
  90. for section in sections:
  91. modules = section["modules"]
  92. if title := section.get("title"):
  93. print(f"## {title}\n", file=f, flush=True)
  94. print(f"Defined in: `{'`, `'.join(modules)}`\n", file=f, flush=True)
  95. for module in modules:
  96. print(
  97. f"Generating list of functions for module '{module}'...", flush=True
  98. )
  99. env = os.environ.copy()
  100. env["TZ"] = "UTC"
  101. subprocess.run(
  102. [
  103. "cargo",
  104. "run",
  105. "--release",
  106. "--quiet",
  107. "--example=inspect",
  108. "--",
  109. "functions",
  110. module,
  111. ],
  112. stdout=f,
  113. text=True,
  114. env=env,
  115. )
  116. list_of_functions(
  117. "math",
  118. {
  119. "title": "Mathematical functions",
  120. "sections": [
  121. {
  122. "title": "Basics",
  123. "modules": ["core::functions"],
  124. },
  125. {
  126. "title": "Transcendental functions",
  127. "modules": ["math::transcendental"],
  128. },
  129. {
  130. "title": "Trigonometry",
  131. "modules": ["math::trigonometry"],
  132. },
  133. {
  134. "title": "Statistics",
  135. "modules": ["math::statistics"],
  136. },
  137. {
  138. "title": "Random sampling, distributions",
  139. "modules": ["core::random", "math::distributions"],
  140. },
  141. {
  142. "title": "Number theory",
  143. "modules": ["math::number_theory"],
  144. },
  145. {
  146. "title": "Numerical methods",
  147. "modules": [
  148. "numerics::diff",
  149. "numerics::solve",
  150. "numerics::fixed_point",
  151. ],
  152. },
  153. {
  154. "title": "Geometry",
  155. "modules": ["math::geometry"],
  156. },
  157. {
  158. "title": "Algebra",
  159. "modules": ["extra::algebra"],
  160. },
  161. {
  162. "title": "Trigonometry (extra)",
  163. "modules": ["math::trigonometry_extra"],
  164. },
  165. ],
  166. },
  167. )
  168. list_of_functions(
  169. "lists",
  170. {
  171. "title": "List-related functions",
  172. "sections": [
  173. {
  174. "modules": ["core::lists"],
  175. },
  176. ],
  177. },
  178. )
  179. list_of_functions(
  180. "strings",
  181. {
  182. "title": "String-related functions",
  183. "sections": [
  184. {
  185. "modules": ["core::strings"],
  186. },
  187. ],
  188. },
  189. )
  190. list_of_functions(
  191. "datetime",
  192. {
  193. "title": "Date and time",
  194. "introduction": "See [this page](./date-and-time.md) for a general introduction to date and time handling in Numbat.",
  195. "sections": [
  196. {
  197. "modules": ["datetime::functions", "datetime::human"],
  198. },
  199. ],
  200. },
  201. )
  202. list_of_functions(
  203. "other",
  204. {
  205. "title": "Other functions",
  206. "sections": [
  207. {
  208. "title": "Error handling",
  209. "modules": ["core::error"],
  210. },
  211. {
  212. "title": "Floating point",
  213. "modules": ["core::numbers"],
  214. },
  215. {
  216. "title": "Quantities",
  217. "modules": ["core::quantities"],
  218. },
  219. {
  220. "title": "Chemical elements",
  221. "modules": ["chemistry::elements"],
  222. },
  223. {
  224. "title": "Mixed unit conversion",
  225. "modules": ["units::mixed"],
  226. },
  227. {
  228. "title": "Temperature conversion",
  229. "modules": ["physics::temperature_conversion"],
  230. },
  231. {
  232. "title": "Color format conversion",
  233. "modules": ["extra::color"],
  234. },
  235. ],
  236. },
  237. )
  238. subprocess.run(["mdbook", "build"], text=True)