figures.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. #! /usr/bin/env python
  2. __copyright__ = """
  3. Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  4. Exceptions. See /LICENSE for license information.
  5. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. """
  7. fmt = "svg"
  8. def escape(s):
  9. return (
  10. s.replace("&", "&")
  11. .replace("<", "&lt;")
  12. .replace(">", "&gt;")
  13. .replace("[", "&#91;")
  14. .replace("]", "&#93;")
  15. )
  16. def tablejoin(items, separator):
  17. data = ("<td>%s</td>" % separator).join(
  18. "<td>%s</td>" % item for item in items
  19. )
  20. return '<table border="0"><tr>%s</tr></table>' % data
  21. def code(s):
  22. # FIXME: GraphViz's handling of font metrics appears to be pretty broken.
  23. # Add a little extra width to each character with a non-code-font space to
  24. # compensate.
  25. codefont = "".join(
  26. (
  27. '<font face="SFMono-Regular,Consolasl,Liberation Mono,'
  28. + 'Menlo,monospace" point-size="10.2">%s</font> '
  29. )
  30. % escape(part)
  31. for part in s
  32. )
  33. return (
  34. '<table border="0" bgcolor="#f2f3f3"><tr><td>%s</td></tr></table>'
  35. % codefont
  36. )
  37. def math(s):
  38. # Render math in italics but otherwise unchanged.
  39. return "<i>%s</i>" % s
  40. def raw(s):
  41. return s
  42. LtR = ' shape="rarrow"'
  43. RtL = ' shape="larrow"'
  44. NonAssoc = ""
  45. out = None
  46. num = 0
  47. def group(ops, assoc=NonAssoc, style=code):
  48. global num
  49. num = num + 1
  50. name = "op%d" % num
  51. print(
  52. " %s [label=<%s>%s]"
  53. % (
  54. name,
  55. tablejoin((style(op) for op in ops), ", "),
  56. assoc,
  57. ),
  58. file=out,
  59. )
  60. return name
  61. def edge(a, b):
  62. print(" %s -> %s" % (a, b), file=out)
  63. def combine(name, items):
  64. if len(items) <= 1:
  65. return items
  66. print(" %s [label=<<i>%s</i>> shape=ellipse]" % (name, name), file=out)
  67. res = name
  68. for i in items:
  69. edge(i, name)
  70. return [res]
  71. def graph(f):
  72. import subprocess
  73. outfile = open(f.__name__ + "." + fmt, "w")
  74. process = subprocess.Popen(
  75. ["dot", "-T" + fmt],
  76. stdin=subprocess.PIPE,
  77. stdout=outfile,
  78. encoding="utf8"
  79. # ["cat"], stdin=subprocess.PIPE, stdout=outfile, encoding='utf8'
  80. )
  81. global out
  82. out = process.stdin
  83. # print >>out, ' node [shape="rectangle" style="rounded" fontname="Arial"]'
  84. print(
  85. """
  86. digraph {
  87. layout = dot
  88. rankdir = TB
  89. rank = "min"
  90. node [shape="none" fontsize="12" height="0"
  91. fontname="BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif"]
  92. edge [dir="none"]
  93. """.strip(),
  94. file=out,
  95. )
  96. f()
  97. print("}", file=out)
  98. process.communicate()
  99. return f
  100. @graph
  101. def example():
  102. term = group(["(...)"], NonAssoc)
  103. mul = group(["a * b"], LtR)
  104. add = group(["a + b"], LtR)
  105. shl = group(["a << b"], NonAssoc)
  106. compare = group(["a == b"], NonAssoc)
  107. edge(term, mul)
  108. edge(mul, add)
  109. edge(term, shl)
  110. edge(add, compare)
  111. edge(shl, compare)