[spec/interpreter] Make sources build and run with current Sphinx & Python
diff --git a/document/core/_build/html/bikeshed/index.html b/document/core/_build/html/bikeshed/index.html
index 8f7be4d..a15f955 100644
--- a/document/core/_build/html/bikeshed/index.html
+++ b/document/core/_build/html/bikeshed/index.html
@@ -166,7 +166,7 @@
     white-space: pre;
 }</style>
   <link href="https://www.w3.org/StyleSheets/TR/2016/W3C-CR" rel="stylesheet" type="text/css">
- <body class="h-entry">
+  <script type="text/javascript">MathJax.Hub.Config({TeX: {MAXBUFFER: 30*1024}})</script><body class="h-entry">
   <div class="head">
    <p data-fill-with="logo"><a class="logo" href="https://www.w3.org/"> <img alt="W3C" height="48" src="https://www.w3.org/StyleSheets/TR/2016/logos/W3C" width="72"> </a> </p>
    <h1 class="p-name no-ref" id="title">WebAssembly Core Specification</h1>
diff --git a/document/core/conf.py b/document/core/conf.py
index d410ee0..789f4ea 100644
--- a/document/core/conf.py
+++ b/document/core/conf.py
@@ -17,14 +17,16 @@
 # documentation root, use os.path.abspath to make it absolute, like shown here.
 import os
 import sys
+from datetime import date
+
 pwd = os.path.abspath('.')
-sys.path.insert(0, pwd + '/util')
+sys.path.insert(0, pwd)
 
 # -- General configuration ------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
 #
-needs_sphinx = '1.4'
+needs_sphinx = '2.3'
 
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@@ -35,8 +37,8 @@
   'sphinx.ext.mathjax',
   'sphinx.ext.ifconfig',
   'sphinx.ext.githubpages',
-  'mathdef',
-  'pseudo-lexer'
+  'util.mathdef',
+  'util.pseudo-lexer'
 ]
 
 # Add any paths that contain templates here, relative to this directory.
@@ -63,6 +65,15 @@
 editor = u'Andreas Rossberg (editor)'
 logo = 'static/webassembly.png'
 
+# The name of the GitHub repository this resides in
+repo = 'spec'
+
+# The name of the proposal it represents, if any
+proposal = ''
+
+# The draft version string (clear out for release cuts)
+draft = ' (Draft ' + date.today().strftime("%Y-%m-%d") + ')'
+
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
@@ -70,14 +81,14 @@
 # The short X.Y version.
 version = u'1.0'
 # The full version, including alpha/beta/rc tags.
-release = version + ''
+release = version + ('' if proposal == '' else ' + ') + proposal
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
 #
 # This is also used if you do content translation via gettext catalogs.
 # Usually you set "language" from the command line for these cases.
-language = None
+language = 'en'
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
@@ -186,7 +197,7 @@
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static', 'static/custom.css']
+html_static_path = ['static/custom.css']
 
 # Add any extra paths that contain custom files (such as robots.txt or
 # .htaccess) here, relative to this directory. These files are copied
@@ -241,7 +252,7 @@
 # If this is not None, a ‘Last updated on:’ timestamp is inserted at every
 # page bottom, using the given strftime() format.
 #
-html_last_updated_fmt = '%F'
+html_last_updated_fmt = '%Y-%m-%d'
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
@@ -286,7 +297,8 @@
   'pointsize': '10pt',
 
    # Additional stuff for the LaTeX preamble.
-   'preamble': '',
+   # Don't type-set cross references with emphasis.
+   'preamble': '\\renewcommand\\sphinxcrossref[1]{#1}\n',
 
    # Latex figure (float) alignment
   'figure_align': 'htbp',
@@ -476,5 +488,14 @@
 
 # Macros
 rst_prolog = """
+.. |issuelink| replace:: https://github.com/webassembly/""" + repo + """/issues/
+.. |pagelink| replace:: https://webassembly.github.io/""" + repo + """/core/
 .. include:: /""" + pwd + """/util/macros.def
 """
+
+# https://www.sphinx-doc.org/en/master/usage/extensions/math.html#confval-mathjax3_config
+# https://docs.mathjax.org/en/latest/web/configuration.html#configuration
+# https://docs.mathjax.org/en/latest/options/input/tex.html#tex-maxbuffer
+mathjax3_config = {
+    'tex': { 'maxBuffer': 30*1024 },
+}
diff --git a/document/core/util/mathdef.py b/document/core/util/mathdef.py
index c14b703..a10fc45 100644
--- a/document/core/util/mathdef.py
+++ b/document/core/util/mathdef.py
@@ -1,12 +1,9 @@
-from sphinx.ext.mathbase import math
-from sphinx.ext.mathbase import displaymath
-from sphinx.ext.mathbase import math_role
-from sphinx.ext.mathbase import MathDirective
-from sphinx.ext.mathbase import latex_visit_math
-from sphinx.ext.mathbase import latex_visit_displaymath
-from sphinx.ext.mathjax import html_visit_math
-from sphinx.ext.mathjax import html_visit_displaymath
-from sphinx.util.texescape import tex_escape_map, tex_replace_map
+from sphinx.directives.patches import MathDirective
+from sphinx.util.texescape import tex_replace_map
+from sphinx.writers.html5 import HTML5Translator
+from sphinx.writers.latex import LaTeXTranslator
+from docutils import nodes
+from docutils.nodes import math
 from docutils.parsers.rst.directives.misc import Replace
 from six import text_type
 import re
@@ -20,16 +17,8 @@
   return '\\href{../%s.html#%s}' % (file, id.replace('_', '-'))
 
 def html_transform_math_xref(node):
-  node['latex'] = \
-    xref_re.sub(lambda m: html_hyperlink(m.group(1), m.group(2)), node['latex'])
-
-def ext_html_visit_math(self, node):
-  html_transform_math_xref(node)
-  html_visit_math(self, node)
-
-def ext_html_visit_displaymath(self, node):
-  html_transform_math_xref(node)
-  html_visit_displaymath(self, node)
+  new_text = xref_re.sub(lambda m: html_hyperlink(m.group(1), m.group(2)), node.astext())
+  node.children[0] = nodes.Text(new_text)
 
 # Mirrors sphinx/writers/latex
 def latex_hyperlink(file, id):
@@ -39,17 +28,8 @@
   return '\\hyperref[%s:%s]' % (file, id)
 
 def latex_transform_math_xref(node):
-  node['latex'] = \
-    xref_re.sub(lambda m: latex_hyperlink(m.group(1), m.group(2)), node['latex'])
-
-def ext_latex_visit_math(self, node):
-  latex_transform_math_xref(node)
-  latex_visit_math(self, node)
-
-def ext_latex_visit_displaymath(self, node):
-  latex_transform_math_xref(node)
-  latex_visit_displaymath(self, node)
-
+  new_text = xref_re.sub(lambda m: latex_hyperlink(m.group(1), m.group(2)), node.astext())
+  node.children[0] = nodes.Text(new_text)
 
 # Expand mathdef names in math roles and directives
 
@@ -75,8 +55,7 @@
 
 def ext_math_role(role, raw, text, line, inliner, options = {}, content = []):
   text = replace_mathdefs(inliner.document, raw.split('`')[1])
-  return math_role(role, raw, text, line, inliner, options = options,
-                   content = content)
+  return [math(raw, text)], []
 
 class ExtMathDirective(MathDirective):
   def run(self):
@@ -85,7 +64,7 @@
       self.content[i] = replace_mathdefs(doc, s)
     for i, s in enumerate(self.arguments):
       self.arguments[i] = replace_mathdefs(doc, s)
-    return super(ExtMathDirective, self).run()
+    return super().run()
 
 class MathdefDirective(Replace):
   def run(self):
@@ -99,23 +78,45 @@
     doc = self.state.document
     if not hasattr(doc, 'mathdefs'):
       doc.mathdefs = {}
+    # TODO: we don't ever hit the case where len(self.content) > 1
     for i, s in enumerate(self.content):
       self.content[i] = replace_mathdefs(doc, s)
     doc.mathdefs[name] = [arity, ''.join(self.content)]
     self.content[0] = ':math:`' + self.content[0]
     self.content[-1] = self.content[-1] + '`'
-    return super(MathdefDirective, self).run()
+    return super().run()
 
+class WebAssemblyHTML5Translator(HTML5Translator):
+  """
+  Customize HTML5Translator.
+  Convert xref in math and math block nodes to hrefs.
+  """
+  def visit_math(self, node, math_env = ''):
+    html_transform_math_xref(node)
+    super().visit_math(node, math_env)
+
+  def visit_math_block(self, node, math_env  = ''):
+    html_transform_math_xref(node)
+    super().visit_math_block(node, math_env)
+
+class WebAssemblyLaTeXTranslator(LaTeXTranslator):
+  """
+  Customize LaTeXTranslator.
+  Convert xref in math and math block nodes to hyperrefs.
+  """
+  def visit_math(self, node):
+    latex_transform_math_xref(node)
+    super().visit_math(node)
+
+  def visit_math_block(self, node):
+    latex_transform_math_xref(node)
+    super().visit_math_block(node)
 
 # Setup
 
 def setup(app):
-  app.add_node(math,
-               html = (ext_html_visit_math, None),
-               latex = (ext_latex_visit_math, None))
-  app.add_node(displaymath,
-               html = (ext_html_visit_displaymath, None),
-               latex = (ext_latex_visit_displaymath, None))
+  app.set_translator('html', WebAssemblyHTML5Translator)
+  app.set_translator('latex', WebAssemblyLaTeXTranslator)
   app.add_role('math', ext_math_role)
-  app.add_directive('math', ExtMathDirective)
+  app.add_directive('math', ExtMathDirective, override = True)
   app.add_directive('mathdef', MathdefDirective)
diff --git a/interpreter/Makefile b/interpreter/Makefile
index 8d62d2b..4e59215 100644
--- a/interpreter/Makefile
+++ b/interpreter/Makefile
@@ -19,7 +19,7 @@
 
 DIRS =		util syntax binary text valid runtime exec script host main
 LIBS =		bigarray
-FLAGS = 	-cflags '-w +a-3-4-27-42-44-45 -warn-error +a'
+FLAGS = 	-cflags '-w +a-3-4-27-42-44-45-70 -warn-error +a'
 OCB =		ocamlbuild $(FLAGS) $(DIRS:%=-I %) $(LIBS:%=-libs %)
 JS =		# set to JS shell command to run JS tests
 
diff --git a/test/core/run.py b/test/core/run.py
index d9c1ec4..45a346e 100755
--- a/test/core/run.py
+++ b/test/core/run.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 from __future__ import print_function
 import argparse
@@ -12,20 +12,25 @@
 
 ownDir = os.path.dirname(os.path.abspath(sys.argv[0]))
 inputDir = ownDir
+interpDir = os.path.join(os.path.dirname(os.path.dirname(ownDir)), 'interpreter')
 outputDir = os.path.join(inputDir, "_output")
 
 parser = argparse.ArgumentParser()
-parser.add_argument("--wasm", metavar="<wasm-command>", default=os.path.join(os.getcwd(), "wasm"))
+parser.add_argument("--wasm", metavar="<wasm-command>", default=os.path.join(interpDir, "wasm"))
 parser.add_argument("--js", metavar="<js-command>")
+parser.add_argument("--generate-js-only", action='store_true')
 parser.add_argument("--out", metavar="<out-dir>", default=outputDir)
 parser.add_argument("file", nargs='*')
 arguments = parser.parse_args()
 sys.argv = sys.argv[:1]
 
+main_test_files = glob.glob(os.path.join(inputDir, "*.wast"))
+
 wasmCommand = arguments.wasm
 jsCommand = arguments.js
+generateJsOnly = arguments.generate_js_only
 outputDir = arguments.out
-inputFiles = arguments.file if arguments.file else glob.glob(os.path.join(inputDir, "*.wast"))
+inputFiles = arguments.file if arguments.file else main_test_files
 
 if not os.path.exists(wasmCommand):
   sys.stderr.write("""\
@@ -60,6 +65,14 @@
     dir, inputFile = os.path.split(inputPath)
     outputPath = os.path.join(outputDir, inputFile)
 
+    # Generate JS first, then return early if we are only generating JS.
+    jsPath = self._auxFile(outputPath.replace(".wast", ".js"))
+    logPath = self._auxFile(jsPath + ".log")
+    self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, jsPath), logPath)
+
+    if generateJsOnly:
+      return
+
     # Run original file
     expectedExitCode = 1 if ".fail." in inputFile else 0
     logPath = self._auxFile(outputPath + ".log")
@@ -68,38 +81,40 @@
     if expectedExitCode != 0:
       return
 
-    # Convert to binary and validate again
+    # Convert to binary and run again
     wasmPath = self._auxFile(outputPath + ".bin.wast")
     logPath = self._auxFile(wasmPath + ".log")
     self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, wasmPath), logPath)
     self._runCommand(('%s -d "%s"') % (wasmCommand, wasmPath), logPath)
 
-    # Convert back to text and validate again
+    # Convert back to text and run again
     wastPath = self._auxFile(wasmPath + ".wast")
     logPath = self._auxFile(wastPath + ".log")
     self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wasmPath, wastPath), logPath)
-    self._runCommand(('%s -d "%s" ') % (wasmCommand, wastPath), logPath)
+    self._runCommand(('%s -d "%s"') % (wasmCommand, wastPath), logPath)
 
     # Convert back to binary once more and compare
     wasm2Path = self._auxFile(wastPath + ".bin.wast")
     logPath = self._auxFile(wasm2Path + ".log")
     self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wastPath, wasm2Path), logPath)
-    self._runCommand(('%s -d "%s"') % (wasmCommand, wasm2Path), logPath)
     # TODO: The binary should stay the same, but OCaml's float-string conversions are inaccurate.
-    # Once we upgrade to OCaml 4.03, use sprintf "%s" for printing floats.
-    # self._compareFile(wasmPath, wasm2Path)
+    # Once we upgrade to OCaml 4.03, use sprintf "%h" for printing floats
+    # and enable the remaining tests.
+    #self._compareFile(wasmPath, wasm2Path)
 
-    # Convert to JavaScript
-    jsPath = self._auxFile(outputPath.replace(".wast", ".js"))
-    logPath = self._auxFile(jsPath + ".log")
-    self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, jsPath), logPath)
+    # Convert back to text once more and compare
+    #wast2Path = self._auxFile(wasm2Path + ".wast")
+    #logPath = self._auxFile(wast2Path + ".log")
+    #self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wasm2Path, wast2Path), logPath)
+    #self._compareFile(wastPath, wast2Path)
+
     if jsCommand != None:
       self._runCommand(('%s "%s"') % (jsCommand, jsPath), logPath)
 
 
 if __name__ == "__main__":
   if not os.path.exists(outputDir):
-    os.makedirs(outputDir)
+    os.makedirs(outputDir, exist_ok=True)
   for fileName in inputFiles:
     testName = 'test ' + os.path.basename(fileName)
     setattr(RunTests, testName, lambda self, file=fileName: self._runTestFile(file))