diff --git a/doc/acas_xu.rst b/doc/acas_xu.rst index d26cc6d5ee85cce40209627b33c8dad269c01e8b..5a55540e98dda69bec2298eea89cf6e5d61ed87c 100644 --- a/doc/acas_xu.rst +++ b/doc/acas_xu.rst @@ -112,7 +112,7 @@ basic programming data structures (arrays, queues, hash tables, *etc.*). Let us try to model the property :math:`\phi_1` defined earlier. We will call our theory ``ACASXU_P1``. -.. code-block:: ocaml +.. code-block:: whyml theory ACASXU_P1 end @@ -127,7 +127,7 @@ precision floating-point numbers. We thus import it in our theory using the Our file looks like this so far: -.. code-block:: ocaml +.. code-block:: whyml theory ACASXU_P1 use ieee_float.Float64 @@ -144,7 +144,7 @@ will only consider the ``AsTuple`` subtheory for this tutorial. In particular, the theory built by CAISAR is equivalent to the following WhyML file: -.. code-block:: ocaml +.. code-block:: whyml theory NeuralNetworkFilename theory AsTuple @@ -170,7 +170,7 @@ that takes a 5-elements tuple as input, and provides a 5-element tuple as output. Supposing we have a neural network named ``ACASXU_1_1.onnx``, our WhyML file looks like this: -.. code-block:: ocaml +.. code-block:: whyml theory ACASXU_P1 use ACASXU_1_1.AsTuple @@ -194,7 +194,7 @@ model the advisory *COC*. The final WhyML file looks like this: -.. code-block:: ocaml +.. code-block:: whyml theory ACASXU_P1 use ACASXU_1_1.AsTuple @@ -267,7 +267,7 @@ floating-point constant :math:`y_0` is greater than or equal to at least one of the other five outputs. This can be formalized in first-order logic as a disjunction of clauses, that can be directly encoded into WhyML as follows: -.. code-block:: ocaml +.. code-block:: whyml y0 .>= y1 \/ y0 .>= y2 \/ y0 .>= y3 \/ y0 .>= y4 @@ -287,7 +287,7 @@ from the verification goal into the theory general context as axioms. In the end, the WhyML file looks like this: -.. code-block:: ocaml +.. code-block:: whyml theory ACASXU_P3 use ACASXU_1_1.AsTuple as N11 diff --git a/doc/conf.py b/doc/conf.py index b3991ef3949154f321b22bc44d3919e31d562080..42a6c97be5114c72b61c4f133852410c8dfac530 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -14,10 +14,13 @@ # import sys # sys.path.insert(0, os.path.abspath('.')) +import os +import sys +sys.path.append(os.path.abspath('./ext')) # -- Project information ----------------------------------------------------- -project = 'Caisar' +project = 'CAISAR' copyright = '2022, Michele Alberti, Julien Girard, François Bobot' author = 'Michele Alberti, Julien Girard, François Bobot' @@ -28,6 +31,10 @@ author = 'Michele Alberti, Julien Girard, François Bobot' # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ + 'sphinx.ext.mathjax', + 'sphinx.ext.graphviz', + # 'sphinxcontrib.bibtex', + 'why3' ] # Add any paths that contain templates here, relative to this directory. diff --git a/doc/ext/why3.py b/doc/ext/why3.py new file mode 100644 index 0000000000000000000000000000000000000000..29a8fd889715a39a543d0e40d454616247e870c0 --- /dev/null +++ b/doc/ext/why3.py @@ -0,0 +1,199 @@ +from pygments.lexer import RegexLexer, words +from pygments.token import Text, Comment, Operator, Keyword, Name, String, Number, Punctuation, Error + +class WhyMLLexer(RegexLexer): + name = 'WhyML' + aliases = 'whyml' + + keywords = ( + 'abstract', 'absurd', 'alias', 'any', 'as', 'assert', 'assume', 'at', 'axiom', + 'begin', 'break', 'by', 'check', 'clone', 'coinductive', 'constant', 'continue', + 'diverges', 'do', 'done', 'downto', + 'else', 'end', 'ensures', 'epsilon', 'exception', 'exists', 'export', + 'false', 'float', 'for', 'forall', 'fun', 'function', 'ghost', 'goal', + 'if', 'import', 'in', 'inductive', 'invariant', 'label', 'lemma', 'let', + 'match', 'meta', 'module', 'mutable', 'not', 'old', + 'partial', 'predicate', 'private', 'pure', + 'raise', 'raises', 'range', 'reads', 'rec', 'ref', 'requires', 'return', 'returns', + 'scope', 'so', 'then', 'theory', 'to', 'true', 'try', 'type', 'use', 'val', 'variant', + 'while', 'with', 'writes', + ) + + tokens = { + 'root': [ + (r'\s+', Text), + (r'\(\*\)', Operator), + (r'\(\*', Comment, 'comment'), + (r'\[@[^]]*\]', Comment), + (words(keywords, suffix=r'\b'), Keyword), + (r'[-~!%^&*+=|?<>/\\]', Operator), + (r'[][{};:.,()]', Punctuation), + (r"[^\W\d][\w']*", Name), + (r'\bresult\b', Name.Builtin.Pseudo), + + (r'-?\d\d*([.]\d*)?([eE][+-]?\d\d*)', Number.Float), + (r'-?0[xX][\da-fA-F][\da-fA-F]*([.][\da-fA-F]*)?([pP][+-]?\d\d*)', Number.Float), + (r'0[xX][\da-fA-F][\da-fA-F_]*', Number.Hex), + (r'0[oO][0-7][0-7_]*', Number.Oct), + (r'0[bB][01][01_]*', Number.Bin), + (r'\d[\d_]*', Number.Integer), + + (r"'", Keyword), + (r'"', String.Double, 'string'), + ], + 'comment': [ + (r'[^(*)]+', Comment), + (r'\(\*', Comment, '#push'), + (r'\*\)', Comment, '#pop'), + (r'[(*)]', Comment), + ], + 'string': [ + (r'[^\\"]+', String.Double), + (r'\\[\\"\'ntbr]', String.Escape), + (r'\\[0-9]{3}', String.Escape), + (r'\\x[0-9a-fA-F]{2}', String.Escape), + (r'\\\n', String.Double), + (r'"', String.Double, '#pop'), + ], + } + +from sphinx.highlighting import lexers + +lexers['whyml'] = WhyMLLexer(startinline=True) + +import re +from docutils import nodes +from sphinx import addnodes +from sphinx.directives import ObjectDescription +from sphinx.domains import Domain +from sphinx.roles import XRefRole +from sphinx.util.docutils import SphinxDirective +from sphinx.util.nodes import make_refnode + +ws_re = re.compile(r'\s+') + +class Why3ToolRole(XRefRole): + def process_link(self, env, refnode, has_explicit_title, title, target): + target = ws_re.sub(' ', target) + if target.startswith('why3 '): + target = target[5:] + return title, target + +attr_re = re.compile(r'\[@(.*)\]') + +class Why3AttributeRole(XRefRole): + def process_link(self, env, refnode, has_explicit_title, title, target): + target = ws_re.sub(' ', target) + m = attr_re.match(target) + if m: + target = m.group(1) + return title, target + +class Why3ToolDirective(SphinxDirective): + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def run(self): + fullname = ws_re.sub(' ', self.arguments[0].strip()) + targetname = '%s-%s' % (self.name, fullname) + node = nodes.target('', '', ids = [targetname]) + self.state.document.note_explicit_target(node) + indexentry = '%s; command' % (fullname,) + inode = addnodes.index(entries = [('pair', indexentry, targetname, '', None)]) + domain = self.env.get_domain('why3') + domain.add_object('tool', fullname, targetname) + return [inode, node] + +class Why3Thing(ObjectDescription): + has_content = True + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + thing_index = '' + thing_kind = '' + + def handle_signature(self, sig, signode): + signode += addnodes.desc_name(text = sig) + return sig + + def add_target_and_index(self, name, sig, signode): + targetname = '%s-%s' % (self.name, name) + signode['ids'].append(targetname) + indexentry = '%s; %s' % (name, self.thing_index) + self.indexnode['entries'].append(('pair', indexentry, targetname, '', None)) + domain = self.env.get_domain('why3') + domain.add_object(self.thing_kind, name, targetname) + +class Why3Attribute(Why3Thing): + thing_index = 'attribute' + thing_kind = 'attribute' + +class Why3Debug(Why3Thing): + thing_index = 'debug flag' + thing_kind = 'debug' + +class Why3Meta(Why3Thing): + thing_index = 'meta' + thing_kind = 'meta' + +class Why3Transform(Why3Thing): + thing_index = 'transformation' + thing_kind = 'transform' + +class Why3Domain(Domain): + name = 'why3' + label = 'Why3' + roles = { + 'attribute': Why3AttributeRole(), + 'debug': XRefRole(), + 'meta': XRefRole(), + 'tool': Why3ToolRole(), + 'transform': XRefRole(), + } + directives = { + 'attribute': Why3Attribute, + 'debug': Why3Debug, + 'meta': Why3Meta, + 'tool': Why3ToolDirective, + 'transform': Why3Transform, + } + indices = { + } + initial_data = {} + initial_data['objects'] = { + 'attribute': {}, + 'debug': {}, + 'meta': {}, + 'tool': {}, + 'transform': {}, + } + + def get_objects(self): + for role, objects in self.data['objects'].items(): + prio = 0 # self.object_types[role].attrs['searchprio'] + for name, (docname, targetname) in objects.items(): + yield (name, name, role, docname, targetname, prio) + + def resolve_xref(self, env, fromdocname, builder, role, targetname, node, contnode): + resolved = self.data['objects'][role].get(targetname) + if resolved: + (todocname, targetname) = resolved + return make_refnode(builder, fromdocname, todocname, targetname, contnode, targetname) + return None + + def add_object(self, role, name, targetname): + self.data['objects'][role][name] = (self.env.docname, targetname) + + +def setup(app): + app.add_domain(Why3Domain) + + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + }