├── .gitignore
├── .readthedocs.yaml
├── LICENSE
├── Makefile
├── _static
└── custom.css
├── conf.py
├── index.rst
├── intro.rst
├── language
├── commands.rst
├── expressions-and-constraints.rst
├── img
│ ├── belongs_to.png
│ ├── command.png
│ ├── lone-dir.png
│ ├── one-key.png
│ ├── self-relation.png
│ ├── set-user.png
│ ├── sig-a.png
│ ├── sig-with-relations.png
│ └── some-author.png
├── index.rst
├── modules.rst
├── predicates-and-functions.rst
├── sets-and-relations.rst
├── signatures.rst
└── time.rst
├── make.bat
├── modules
├── boolean.rst
├── graph.rst
├── index.rst
├── integer.rst
├── natural.rst
├── ordering.rst
├── relation.rst
├── ternary.rst
└── time.rst
├── readme.md
├── requirements.txt
├── techniques
├── boolean-fields.rst
├── dynamics.rst
├── index.rst
└── specs
│ └── dynamics
│ ├── browsing.als
│ ├── keyboard-table.als
│ ├── keyboard.als
│ └── light.als
├── tooling
├── analyzer.rst
├── img
│ ├── evaluator.png
│ ├── kodkod.png
│ ├── table.png
│ ├── temporal_model.png
│ ├── tree.png
│ └── visualizer.png
├── index.rst
├── markdown.rst
├── specs
│ ├── server.als
│ ├── visualizer-counterexample.xml
│ └── visualizer.als
├── themes.rst
└── visualizer.rst
└── utils
├── __init__.py
└── alloy.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # sphinx build folder
2 | _build/
3 |
4 | # Compiled source #
5 | ###################
6 | *.com
7 | *.class
8 | *.dll
9 | *.exe
10 | *.o
11 | *.so
12 | *.pyc
13 |
14 | # Byte-compiled / optimized / DLL files
15 | __pycache__/
16 | *.py[cod]
17 | *$py.class
18 |
19 | # Packages #
20 | ############
21 | # it's better to unpack these files and commit the raw source
22 | # git has its own built in compression methods
23 | *.7z
24 | *.dmg
25 | *.gz
26 | *.iso
27 | *.jar
28 | *.rar
29 | *.tar
30 | *.zip
31 |
32 | # Logs and databases #
33 | ######################
34 | *.log
35 | *.sql
36 | *.sqlite
37 |
38 | # OS generated files #
39 | ######################
40 | .DS_Store?
41 | ehthumbs.db
42 | Icon?
43 | Thumbs.db
44 |
45 | # Editor backup files #
46 | #######################
47 | *~
48 | .projectroot
49 | tags
50 | todo
51 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | build:
4 | os: ubuntu-20.04
5 | tools:
6 | python: "3.12"
7 |
8 | python:
9 | install:
10 | - requirements: requirements.txt
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Alloytools
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | SOURCEDIR = .
8 | BUILDDIR = _build
9 |
10 | # Put it first so that "make" without argument is like "make help".
11 | help:
12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
13 |
14 | .PHONY: help Makefile
15 |
16 | # Catch-all target: route all unknown targets to Sphinx using the new
17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
18 | %: Makefile
19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/_static/custom.css:
--------------------------------------------------------------------------------
1 | .advanced h1:before,
2 | .advanced h2:before,
3 | .advanced h3:before,
4 | .advanced h4:before {
5 | content: "[⋇] ";
6 | }
7 |
--------------------------------------------------------------------------------
/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Configuration file for the Sphinx documentation builder.
4 | #
5 | # This file does only contain a selection of the most common options. For a
6 | # full list see the documentation:
7 | # http://www.sphinx-doc.org/en/master/config
8 |
9 | # -- Path setup --------------------------------------------------------------
10 |
11 | # If extensions (or modules to document with autodoc) are in another directory,
12 | # add these directories to sys.path here. If the directory is relative to the
13 | # documentation root, use os.path.abspath to make it absolute, like shown here.
14 | #
15 | import os
16 | import sys
17 | sys.path.append(os.path.abspath('./utils'))
18 |
19 |
20 | # -- Project information -----------------------------------------------------
21 |
22 | project = 'Alloy Documentation'
23 | copyright = '2023'
24 | author = 'Hillel Wayne'
25 |
26 | # The short X.Y version
27 | version = ''
28 | # The full version, including alpha/beta/rc tags
29 | release = ''
30 |
31 |
32 | # -- General configuration ---------------------------------------------------
33 |
34 | # If your documentation needs a minimal Sphinx version, state it here.
35 | #
36 | # needs_sphinx = '1.0'
37 |
38 | # Add any Sphinx extension module names here, as strings. They can be
39 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
40 | # ones.
41 | extensions = [
42 | 'sphinx.ext.todo',
43 | 'sphinx.ext.githubpages',
44 | 'sphinx_rtd_theme',
45 | 'alloy'
46 | ]
47 |
48 | # Add any paths that contain templates here, relative to this directory.
49 | templates_path = ['_templates']
50 |
51 | # The suffix(es) of source filenames.
52 | # You can specify multiple suffix as a list of string:
53 | #
54 | # source_suffix = ['.rst', '.md']
55 | source_suffix = '.rst'
56 |
57 | # The master toctree document.
58 | master_doc = 'index'
59 |
60 | # The language for content autogenerated by Sphinx. Refer to documentation
61 | # for a list of supported languages.
62 | #
63 | # This is also used if you do content translation via gettext catalogs.
64 | # Usually you set "language" from the command line for these cases.
65 | language = "en"
66 |
67 | default_role = "any"
68 | highlight_language = "alloy"
69 |
70 | # List of patterns, relative to source directory, that match files and
71 | # directories to ignore when looking for source files.
72 | # This pattern also affects html_static_path and html_extra_path.
73 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
74 |
75 | # The name of the Pygments (syntax highlighting) style to use.
76 | pygments_style = None
77 |
78 |
79 | # -- Options for HTML output -------------------------------------------------
80 |
81 | # The theme to use for HTML and HTML Help pages. See the documentation for
82 | # a list of builtin themes.
83 | #
84 | html_theme = 'sphinx_rtd_theme'
85 |
86 | # Theme options are theme-specific and customize the look and feel of a theme
87 | # further. For a list of options available for each theme, see the
88 | # documentation.
89 | #
90 | # html_theme_options = {}
91 |
92 | # Add any paths that contain custom static files (such as style sheets) here,
93 | # relative to this directory. They are copied after the builtin static files,
94 | # so a file named "default.css" will overwrite the builtin "default.css".
95 | html_static_path = ['_static']
96 |
97 | # Custom sidebar templates, must be a dictionary that maps document names
98 | # to template names.
99 | #
100 | # The default sidebars (for documents that don't match any pattern) are
101 | # defined by theme itself. Builtin themes are using these templates by
102 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
103 | # 'searchbox.html']``.
104 | #
105 | # html_sidebars = {}
106 |
107 |
108 | # -- Options for HTMLHelp output ---------------------------------------------
109 |
110 | # Output file base name for HTML help builder.
111 | htmlhelp_basename = 'AlloyDocumentationdoc'
112 |
113 |
114 | # -- Options for LaTeX output ------------------------------------------------
115 | latex_engine = "xelatex" # solves problem of unicode characters
116 | latex_elements = {
117 | # The paper size ('letterpaper' or 'a4paper').
118 | #
119 | # 'papersize': 'letterpaper',
120 |
121 | # The font size ('10pt', '11pt' or '12pt').
122 | #
123 | # 'pointsize': '10pt',
124 |
125 | # Additional stuff for the LaTeX preamble.
126 | #
127 | # 'preamble': '',
128 |
129 | # Latex figure (float) alignment
130 | #
131 | # 'figure_align': 'htbp',
132 | }
133 |
134 | # Grouping the document tree into LaTeX files. List of tuples
135 | # (source start file, target name, title,
136 | # author, documentclass [howto, manual, or own class]).
137 | latex_documents = [
138 | (master_doc, 'AlloyDocumentation.tex', 'Alloy Documentation Documentation',
139 | 'Alloy Board', 'manual'),
140 | ]
141 |
142 |
143 | # -- Options for manual page output ------------------------------------------
144 |
145 | # One entry per manual page. List of tuples
146 | # (source start file, name, description, authors, manual section).
147 | man_pages = [
148 | (master_doc, 'alloydocumentation', 'Alloy Documentation Documentation',
149 | [author], 1)
150 | ]
151 |
152 |
153 | # -- Options for Texinfo output ----------------------------------------------
154 |
155 | # Grouping the document tree into Texinfo files. List of tuples
156 | # (source start file, target name, title, author,
157 | # dir menu entry, description, category)
158 | texinfo_documents = [
159 | (master_doc, 'AlloyDocumentation', 'Alloy Documentation Documentation',
160 | author, 'AlloyDocumentation', 'One line description of project.',
161 | 'Miscellaneous'),
162 | ]
163 |
164 |
165 | # -- Options for Epub output -------------------------------------------------
166 |
167 | # Bibliographic Dublin Core info.
168 | epub_title = project
169 |
170 | # The unique identifier of the text. This can be a ISBN number
171 | # or the project homepage.
172 | #
173 | # epub_identifier = ''
174 |
175 | # A unique identification for the text.
176 | #
177 | # epub_uid = ''
178 |
179 | # A list of files that should not be packed into the epub file.
180 | epub_exclude_files = ['search.html']
181 |
182 |
183 | # -- Extension configuration -------------------------------------------------
184 |
185 | # -- Options for todo extension ----------------------------------------------
186 |
187 | # If true, `todo` and `todoList` produce output, else they produce nothing.
188 | # We can't pass the -D flag in ReadTheDocs, so we instead customize to their specific environment
189 | if os.environ.get('READTHEDOCS'):
190 | todo_include_todos = False
191 | else:
192 | todo_include_todos = True
193 |
194 | # We use brackets, not parenthesis
195 | add_function_parentheses = False
196 |
197 | # So we get the advanced directive
198 | def setup(app):
199 | app.add_css_file('custom.css')
200 |
201 | """
202 | https://samprocter.com/2014/06/documenting-a-language-using-a-custom-sphinx-domain-and-pygments-lexer/
203 |
204 | def setup(sphinx):
205 | sys.path.insert(0, os.path.abspath('./util'))
206 | from AADLLexer import AADLLexer
207 | from AADLDomain import AADLDomain
208 | sphinx.add_lexer("aadl", AADLLexer())
209 | sphinx.add_domain(AADLDomain)
210 | """
211 |
--------------------------------------------------------------------------------
/index.rst:
--------------------------------------------------------------------------------
1 | .. Alloy Documentation documentation master file, created by
2 | sphinx-quickstart on Sun Feb 24 19:08:18 2019.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Alloy Docs
7 | ===============================================
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 | :caption: Contents:
12 |
13 | intro
14 | language/index
15 | tooling/index
16 | modules/index
17 | techniques/index
18 |
19 |
20 |
21 | Indices and tables
22 | ==================
23 |
24 | * :ref:`genindex`
25 | * :ref:`modindex`
26 | * :ref:`search`
27 |
28 | .. todolist::
29 |
--------------------------------------------------------------------------------
/intro.rst:
--------------------------------------------------------------------------------
1 | Introduction
2 | =============
3 |
4 | About Alloy
5 | --------------
6 |
7 | Alloy is a tool for analyzing systems and seeing if they are designed correctly. You can read more `here `__ and download it `here `__.
8 |
9 |
10 | About This Guide
11 | -------------------
12 |
13 | This is my tentative proposed documentation for the Alloy language.
14 |
15 | **This is not the official Alloy documentation.** It's a staging ground for material that's intended to *become* official documentation, but for now I'm writing this independently, not as part of the alloy board.
16 |
17 | **This is a work in progress.** This documentation is incomplete and changes may be made. There are also a lot of things that still need to be added.
18 |
19 | **This is meant as a reference**, not a tutorial.
20 |
21 | **This is not comprehensive**. Certain things are intentionally excluded, like bitshift operators, specific internal modules, anonymous predicates, and Alloy 3 legacy syntax. Generally these are things that I personally believe are only useful in very specific niche cases, and otherwise serve to confuse things.
22 |
23 | How to Read
24 | -----------
25 |
26 | I’ve added commentary in the markup of the pages that is invisible in
27 | the final output. If you see something
28 |
29 | ::
30 |
31 | .. like this
32 |
33 | Then it will not be rendered. If I used a special ``run`` predicate to
34 | generate a particular visualization, I put it in a comment.
35 |
36 | Anything with a ``[⋇]`` is an advanced topic and unnecessary to learn if you're just a beginner.
37 |
38 | Issues with the documentation can be raised `here `__.
39 |
40 |
41 | About the Author
42 | ---------------------
43 |
44 | I'm Hillel. I also wrote a `guide to TLA+ `__ and have a `blog `__.
45 |
--------------------------------------------------------------------------------
/language/commands.rst:
--------------------------------------------------------------------------------
1 |
2 | .. _commands:
3 |
4 | ++++++++
5 | Commands
6 | ++++++++
7 |
8 | A :dfn:`command` is what actually runs the analyzer. It can either find
9 | models that satisfy your specification, or counterexamples to given
10 | properties.
11 |
12 | By default, the analyzer will run the top command in the file. A
13 | specific command can be run under the ``Execute`` menu option.
14 |
15 | .. _run:
16 |
17 | ``run``
18 | =======
19 |
20 | **run** tells the analyzer to find a matching example of the spec.
21 |
22 | ``run pred``
23 | ------------
24 |
25 | Find examples where ``pred`` is true. If no examples match, the analyzer
26 | will suggest the predicate is inconsistent (see `unsat core `). The
27 | predicate may be consistent if the `scope ` is off.
28 |
29 | .. code:: alloy
30 |
31 | sig Node {
32 | edge: set Node
33 | }
34 |
35 | pred self_loop[n: Node] {
36 | n in n.edge
37 | }
38 |
39 | pred all_self_loop {
40 | all n: Node | self_loop[n]
41 | }
42 |
43 | run all_self_loop
44 |
45 | The analyzer will title the command as the predicate.
46 |
47 | .. image:: img/command.png
48 |
49 |
50 | ``run {constraint}``
51 | --------------------
52 |
53 | Finds an example satisfying the ad-hoc constraint in the braces.
54 |
55 | .. code:: alloy
56 |
57 | // some node with a self loop
58 | run {some n: Node | self_loop[n]}
59 |
60 | .. TIP:: The analyzer will title the command ``run${num}``. You can give the command a name by prepending the ``run`` with ``name:``:
61 |
62 | .. code:: alloy
63 |
64 | some_self_loop: run {some n: Node | self_loop[n]}
65 |
66 |
67 | .. _check:
68 |
69 |
70 | ``check``
71 | =========
72 |
73 | **check** tells the Analyzer to find a counterexample to a given
74 | constraint. You can use it to check that your specification behaves as
75 | you expect it to.
76 |
77 | .. _assert:
78 |
79 | Unlike with ``run`` commands, ``check`` uses **assertions**:
80 |
81 | .. code:: alloy
82 |
83 | assert no_self_loops {
84 | no n: Node | self_loop[n]
85 | }
86 |
87 | check no_self_loops
88 |
89 | Asserts may be used in ``check`` commands but not ``run`` commands.
90 | Assertions may not be called by other predicates or assertions.
91 |
92 | You can also call ``check`` with an ad-hoc constraint:
93 |
94 | .. code:: alloy
95 |
96 | check {no n: Node | self_loop[n]}
97 |
98 |
99 | ``check`` can also be given a named command.
100 |
101 | .. _scopes:
102 |
103 | Scopes
104 | ======
105 |
106 | All alloy models are **bounded**: they must have a maximum possible
107 | size. If not specified, the analyzer will assume that there may be up to
108 | three of each top-level signature and any number of relations. This is
109 | called the **scope**, and can be changed for each command.
110 |
111 | Given the following spec:
112 |
113 | .. code:: alloy
114 |
115 | sig A {}
116 | sig B {}
117 |
118 | We can write the following scopes:
119 |
120 | .. _exactly:
121 |
122 | - ``run {} for 5``: Analyzer will look for models with up to five
123 | instances of each A and B.
124 | - ``run {} for 5 but 2 A``: Analyzer will look for models with up to
125 | two instances of A.
126 | - ``run {} for 5 but exactly 2 A``: Analyzer will only look for models
127 | with *exactly two* A. The exact scope *may* be higher than the
128 | general scope.
129 | - ``run {} for 5 but 2 A, 3 B``: Places scopes on A and B.
130 |
131 | If you are placing scopes on all of the signatures, the ``for N but``
132 | is unnecessary: the last command can be written as
133 | ``run {} for 2 A, 3 B``.
134 |
135 | .. TIP:: When using `Arithmetic Operators `, you can specify ``Int`` like any other signature:
136 |
137 | .. code:: alloy
138 |
139 | run foo for 3 Int
140 |
141 | .. NOTE:: You cannot place scopes on relations. Instead, use a predicate.
142 |
143 | .. code:: alloy
144 |
145 | sig A {
146 | rel: A
147 | }
148 |
149 | run {#rel = 2}
150 |
151 | .. rst-class:: advanced
152 |
153 | Scopes on Subtypes
154 | ----------------------
155 |
156 |
157 | Special scopes *may* be placed on `extensional subtypes `. The following is valid:
158 |
159 | .. code:: alloy
160 |
161 | sig Plant {}
162 |
163 | sig Tree extends Plant {}
164 | sig Grass extends Plant {}
165 |
166 | run {} for 4 Plant, exactly 2 Tree
167 |
168 | ``Grass`` does not need to be scoped, as it is considered part of
169 | ``Plant``. The maximum number of atoms for a subtype is either it or its
170 | parent’s scope, whichever is lower. The parent scope is shared across
171 | all children. In this command, there are a maximum of four ``Plant``\ s,
172 | exactly two of which will be ``Tree`` atoms. Therefore there may be at
173 | most two ``Grass`` atoms.
174 |
175 | In contrast, special scopes *may not* be placed on `subset types `. The following is invalid:
176 |
177 | .. code:: alloy
178 |
179 | sig Plant {}
180 |
181 | sig Seedling in Plant {}
182 |
183 | run {} for 4 Plant, exactly 2 Seedling
184 |
185 | Since ``Seedling`` is a subset type, it may not have a scope. If you
186 | need to scope on a subtype, use a constraint:
187 |
188 | .. code:: alloy
189 |
190 | run {#Seedling = 2} for 4 Plant
191 |
192 |
193 | .. _steps:
194 |
195 | Steps
196 | --------
197 |
198 | For :doc:`dynamic ` models, the number of steps is specified as ``m..n steps``, where ``m`` is the minimum number of steps and ``n`` is the maximum. For example:
199 |
200 | .. todo:: Example goes here
201 |
202 | Writing ``for n steps`` is equivalent to ``for 1..n steps``. If no steps count is given, the number defaults to 10.
203 |
204 | * ``n steps``, which is equivalent to ``1..n steps``
205 | * ``m.. steps``, which allows for an arbitrary number of steps. This requires the use of an SMT solver, such as NuSMV, and may be very memory intensive.
206 |
207 |
208 | .. todo:: Check the above
209 |
210 |
211 | .. note:: ``m.. steps`` is always guaranteed to eventually work, because
212 |
--------------------------------------------------------------------------------
/language/expressions-and-constraints.rst:
--------------------------------------------------------------------------------
1 | +++++++++++++++++++++++++++
2 | Expressions and Constraints
3 | +++++++++++++++++++++++++++
4 |
5 |
6 | .. _expressions:
7 |
8 | Expressions
9 | ===========
10 |
11 | Expressions are anything that returns a number, boolean, or set. Boolean
12 | expressions are also called `constraints`.
13 |
14 | Information about relational expressions are found in the `sets` and `relations` chapters. There are two additional constructs that can be used with both boolean and relational
15 | expressions.
16 |
17 | .. seealso::
18 |
19 | `Integer ` expressions
20 |
21 | .. _let:
22 |
23 | Let
24 | --------
25 |
26 | ``let`` defines a local value for the purposes of the subexpression.
27 |
28 | .. code:: alloy
29 |
30 | let x = A + B, y = C + D |
31 | x + y
32 |
33 | In the context of the ``let`` expression ``x + y = (A + B) + (C + D)``.
34 | ``let`` is mostly used to simplify complex expressions and give
35 | meaningful names to intermediate computations.
36 |
37 | If writing a boolean expression, you may use a ``{}`` instead of ``|``.
38 |
39 | ``let`` bindings are not recursive. A ``let`` binding may not refer to
40 | itself or a future ``let`` binding.
41 |
42 | .. warning::
43 |
44 | As with :ref:`predicate ` parameters, ``let`` can `shadow <@>` a global value. You can use the ``@`` operator to retrieve the global value.
45 |
46 | .. _implies:
47 |
48 | implies - else
49 | --------------
50 |
51 | When used in conjunction with ``else``, ``implies`` acts as a
52 | conditional. ``p implies A else B`` returns A if p is true and B if p is
53 | false. ``p`` must be a boolean expression.
54 |
55 | If A and B are boolean expressions, then this acts as a constraint. The ``else`` can be left out if using ``implies`` as a constraint. See `below ` for details.
56 |
57 | .. _constraints:
58 |
59 | Constraints
60 | ===========
61 |
62 | .. _bar:
63 |
64 | Bar expressions
65 | ---------------
66 |
67 | A bar expression is one of the form:
68 |
69 | ::
70 |
71 | some x: Set |
72 | expr
73 |
74 | In this context, the expression is true iff ``expr`` is true. The newline is optional.
75 |
76 | Paragraph expressions
77 | ---------------------
78 |
79 | If multiple constraints are surrounded with braces, they are all
80 | ``and``-ed together. The following two are equivalent:
81 |
82 | .. code:: alloy
83 |
84 | expr1 or {
85 | expr2
86 | expr3
87 | ...
88 | }
89 |
90 | expr1 or (expr 2 and expr3 and ...)
91 |
92 | .. _constraint-types:
93 |
94 | Constraint Types
95 | ----------------
96 |
97 | All constraints can be inverted with ``not`` or ``!``. To say that A is
98 | not a subset of B, you can write ``A !in B``, ``A not in B``,
99 | ``!(A in B)``, etc.
100 |
101 | Relation Constraints
102 | ~~~~~~~~~~~~~~~~~~~~
103 |
104 | .. _=:
105 |
106 | ``=``
107 | ^^^^^
108 |
109 | ``A = B`` means that both sets of atoms or relations have the exact same
110 | elements. ``=`` *cannot* be used to compare two booleans. Use ``iff``
111 | instead.
112 |
113 | .. _in:
114 |
115 | ``in``
116 | ^^^^^^
117 |
118 | - ``A in B`` means that every element of A is also an element of B.
119 | This is also known as a “subset” relation.
120 | - ``x in A`` means that x is an element of the set A.
121 |
122 | .. note:: The above two definitions are equivalent as all atoms are singleton sets: x is the set containing x, so ``x in A`` is “the set containing just x is a subset of A”.
123 |
124 | .. _size-constraints:
125 |
126 | size constraints
127 | ^^^^^^^^^^^^^^^^
128 |
129 | There are four constraints on the number of elements in a set:
130 |
131 |
132 | - ``no A`` means A is empty.
133 | - ``some A`` means A has *at least one* element.
134 | - ``one A`` means A has *exactly one* element.
135 | - ``lone A`` means A is either empty or has exactly one element.
136 |
137 | In practice, ``no`` and ``some`` are considerably more useful than
138 | ``one`` and ``lone``.
139 |
140 | .. note:: Relations are each exactly one element, no matter the order of the relation. If ``a``, ``b,`` and ``c`` are individual atoms, ``(a -> b -> c)`` is exactly one element, while ``(a -> b) + (a -> c)`` is two.
141 |
142 | .. _disj constraint:
143 |
144 | ``disj[A, B]``
145 | ^^^^^^^^^^^^^^
146 |
147 | ``disj[A, B]`` is the predicate “A and B share no elements in common”.
148 | Any number of arguments can be used, in which case ``disj`` is
149 | *pairwise-disjoint*. This means that ``disj[A, B, C]`` is equivalent to
150 | ``disj[A, B] and disj[B, C] and disj[A, C]``.
151 |
152 | Boolean Constraints
153 | ~~~~~~~~~~~~~~~~~~~
154 |
155 | Boolean constraints operate on booleans or predicates. They can be used
156 | to create more complex constraints.
157 |
158 | All boolean constraints have two different forms, a symbolic form and an
159 | English form. For example, ``A && B`` can also be written ``A and B``.
160 |
161 | =========== =======
162 | word symbol
163 | =========== =======
164 | ``and`` ``&&``
165 | ``or`` ``||``
166 | ``not`` ``!``
167 | ``implies`` ``=>``
168 | ``iff`` ``<=>``
169 | =========== =======
170 |
171 | The first three are self-explanatory. The other two are covered below:
172 |
173 | .. TODO:: Link to the spec for precedence, really not worth copying over
174 |
175 | .. _implies constraint:
176 |
177 | ``implies`` (``=>``)
178 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
179 |
180 | ``P implies Q`` is true if Q is true whenever P is true. If P is true
181 | and Q is false, then ``P implies Q`` is false. If P is false, then
182 | ``P implies Q`` is automatically true. ``P implies Q else T`` is true if P and Q are true or if P is false and T is true.
183 |
184 | (Consider the statement ``x > 5 implies x > 3``. If we pick ``x = 4``,
185 | then we have ``false implies true``).
186 |
187 | ``iff`` (``<=>``)
188 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
189 |
190 | ``P iff Q`` is true if P and Q are both true or both false. Use this for
191 | booleans instead of ``=``.
192 |
193 | .. TIP:: ``xor[A, B]`` can be written as ``A <=> !B``.
194 |
195 | .. _quantifiers:
196 |
197 | Quantifiers
198 | ~~~~~~~~~~~
199 |
200 | A **quantifier** is an expression about the elements of a set. All of
201 | them have the form
202 |
203 | ::
204 |
205 | some x: A |
206 | expr
207 |
208 | This expression is true if ``expr`` is true for any element of the set
209 | of atoms ``A``. As with ``let``, ``x`` becomes a valid identifier in the
210 | body of the constraint.
211 |
212 | Instead of using a pipe, you can also write it as
213 |
214 | ::
215 |
216 | some x: Set {
217 | expr1
218 | ...
219 | }
220 |
221 | In which case it is treated as a standard paragraph expression.
222 |
223 | The following quantifiers are available:
224 |
225 | - ``some x: A | expr`` is true for *at least one* element in ``A``.
226 | - ``all x: A | expr`` is true for *every* element in ``A``.
227 | - ``no x: A | expr`` is **false** for every element of ``A``.
228 | - [A] ``one x: A | expr`` is true for exactly one element of ``A``.
229 | - [A] ``lone x: A`` is equivalent to
230 | ``(one x: A | expr) or (no x: A | expr)``.
231 |
232 | As `discussed below `, ``one`` and ``lone`` can have some unintuitive
233 | consequences.
234 |
235 | .. TIP:: As with all constraints, ``A`` can be any set expression. So you can write ``some x: (A + B - C).rel``, etc.
236 |
237 | .. _multiple-quantifiers:
238 |
239 | Multiple Quantifiers
240 | ^^^^^^^^^^^^^^^^^^^^
241 |
242 | There are two syntaxes to quantify over multiple elements:
243 |
244 | .. code:: alloy
245 |
246 | -- 1
247 | some x, y, ...: A | expr
248 |
249 | -- 2
250 | some x: A, y: B, ... | expr
251 |
252 | For case (1) all elements will be drawn from ``A``. For case (2) the
253 | quantifier will be over all possible combinations of elements from A and
254 | B. The two forms can be combined, as in
255 | ``all x, y: A, z: B, ... | expr``.
256 |
257 | Elements drawn do **not** need to be distinct. This means, for example,
258 | that the following is automatically false if A has any elements:
259 |
260 | .. code:: alloy
261 |
262 | all x, y: A |
263 | x.rel != y.rel
264 |
265 | .. todo:: call out distinct elements
266 |
267 | As we can pick the same element for ``x`` and ``y``. If this is not your
268 | intention, there are two ways to fix this:
269 |
270 | .. code:: alloy
271 |
272 | -- 1
273 | all x, y: A |
274 | x != y => x.rel != y.rel
275 |
276 | -- 2
277 | all disj x, y: A |
278 | x.rel != y.rel
279 |
280 | For case (1) we can still select the same element for ``x`` and ``y``;
281 | however, the ``x != y`` clause will be false, making the whole clause
282 | true. For case (2), using ``disj`` in a quantifier means we cannot
283 | select the same element for two variables.
284 |
285 | ``one`` and ``lone`` behave unintuitively when used in
286 | multiple quantifiers. The following two statements are different:
287 |
288 | .. code:: alloy
289 |
290 | one f, g: S | P[f, g] -- 1
291 | one f: S | one g: S | P[f, g] -- 2
292 |
293 | Constraint (1) is only true if there is *exactly one* pair f, g that
294 | satisfies predicate P. Constraint (2) says that there’s exactly one f
295 | such that there’s exactly one g. The following truth table will satisfy
296 | clause (2) *but not* (1):
297 |
298 | = = =======
299 | f g P[f, g]
300 | = = =======
301 | A B T
302 | A C T
303 | B A T
304 | B C T
305 | C B T
306 | C A F
307 | = = =======
308 |
309 | As C is the only one where there is *exactly one* g that satisfies P[C,
310 | g]. As a rule of thumb, use only ``some`` and ``all`` when writing
311 | multiple clauses.
312 |
313 | .. rst-class:: advanced
314 |
315 | Relational Quantifiers
316 | ^^^^^^^^^^^^^^^^^^^^^^^^^^
317 |
318 | When using a `run` command, you can define a ``some`` quantifier over a relation:
319 |
320 | .. code:: alloy
321 |
322 | sig Node {
323 | edge: set Node
324 | }
325 |
326 | pred has_self_loop {
327 | some e: edge | e = ~e
328 |
329 | }
330 |
331 | run {
332 | has_self_loop
333 | }
334 |
335 | When using a `check` command, you can define ``all`` and ``no`` quantifiers over relations:
336 |
337 | .. code:: alloy
338 |
339 | assert no_self_loops {
340 | no e: edge | e = ~e
341 | }
342 |
343 | check no_self_loops
344 |
345 | You **cannot** use ``all`` or ``no`` in a run command or use ``some`` in a check command. You **cannot** use higher-order quantifiers in the `evaluator` regardless of the command.
346 |
--------------------------------------------------------------------------------
/language/img/belongs_to.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/language/img/belongs_to.png
--------------------------------------------------------------------------------
/language/img/command.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/language/img/command.png
--------------------------------------------------------------------------------
/language/img/lone-dir.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/language/img/lone-dir.png
--------------------------------------------------------------------------------
/language/img/one-key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/language/img/one-key.png
--------------------------------------------------------------------------------
/language/img/self-relation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/language/img/self-relation.png
--------------------------------------------------------------------------------
/language/img/set-user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/language/img/set-user.png
--------------------------------------------------------------------------------
/language/img/sig-a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/language/img/sig-a.png
--------------------------------------------------------------------------------
/language/img/sig-with-relations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/language/img/sig-with-relations.png
--------------------------------------------------------------------------------
/language/img/some-author.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/language/img/some-author.png
--------------------------------------------------------------------------------
/language/index.rst:
--------------------------------------------------------------------------------
1 | Language
2 | ========
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 |
7 | signatures
8 | sets-and-relations
9 | expressions-and-constraints
10 | predicates-and-functions
11 | commands
12 | time
13 | modules
14 |
15 |
16 |
--------------------------------------------------------------------------------
/language/modules.rst:
--------------------------------------------------------------------------------
1 | .. _modules:
2 |
3 | ++++++++++++
4 | Modules
5 | ++++++++++++
6 |
7 | Alloy **modules** are similar to programming languages and act as the namespaces. Alloy comes with a standard library of :ref:`utility modules `.
8 |
9 |
10 | .. _simple-modules:
11 |
12 | Simple Modules
13 | ====================
14 |
15 | ::
16 |
17 | open util/relation as r
18 |
19 | Imports must be at the top of the file. Modules may import new signatures into the spec.
20 |
21 | Modules can be imported multiple times under different namespaces.
22 |
23 | .. _namespaces:
24 |
25 | Namespaces
26 | -----------------
27 |
28 | A module can be namespaced by importing ``as`` a name. Namespaces are accessed with ``/``. This is also called a **qualified** import.
29 |
30 | .. code::
31 |
32 | open util/relation as r
33 |
34 | -- later
35 |
36 | r/dom
37 |
38 | .. _parameterized-modules:
39 |
40 | Parameterized Modules
41 | =========================
42 |
43 | A parameterized module is "generic": its functions and predicates are defined for some arbitrary signature. When you import a parameterized module, you must pass in a signature. Its functions and predicates are then specialized to be defined for that signature.
44 |
45 | .. code:: alloy
46 |
47 | open util/ordering[A]
48 |
49 | sig A {}
50 |
51 | run {some first} -- returns an A atom
52 |
53 | Normally `ord/first ` returns an abstract ``elem``. By parameterizing the module with ``A``, the function now returns an ``A`` atom.
54 |
55 | The input must be a full signature and not a subset of one.
56 |
57 | A parameterized module can be imported multiple times using `namespaces`.
58 |
59 | .. note::
60 |
61 | The following built-in modules are parameterized: :mod:`ordering`, :mod:`time`, `graph`, and sequence.
62 |
63 | Creating Modules
64 | ========================
65 |
66 | The syntax for a module is
67 |
68 | ::
69 |
70 | module name
71 |
72 | At the beginning of the file.
73 |
74 | .. _private:
75 |
76 | Private
77 | ------------------
78 |
79 | Any module predicate, function, and signature can be preceded by ``private``, which means it will not be imported into other modules.
80 |
81 | ::
82 |
83 | module name
84 |
85 | private sig A {}
86 |
87 | .. todo:: exactly in module names. Not MVP, but should probably be brought up briefly
88 |
89 | Creating Parameterized Modules
90 | ------------------------------------
91 |
92 | ::
93 |
94 | module name[sig]
95 |
96 | -- predicates and functions should use sig
97 |
98 |
--------------------------------------------------------------------------------
/language/predicates-and-functions.rst:
--------------------------------------------------------------------------------
1 | ++++++++++++++++++++++++
2 | Predicates and Functions
3 | ++++++++++++++++++++++++
4 |
5 | .. I'm intentionally leaving out qualified predicates because they don't provide any benefits and add syntax.
6 |
7 | .. _predicates:
8 |
9 | Predicates
10 | ==========
11 |
12 | A predicate is like a programming function that returns a
13 | `boolean `. While they are a special case of Alloy
14 | functions, they are more fundamental to modeling and addressed first.
15 |
16 | Predicates take the form
17 |
18 | .. code:: alloy
19 |
20 | pred name {
21 | constraint
22 | }
23 |
24 | Once defined, predicates can be used as part of boolean expressions. The
25 | following is a valid spec:
26 |
27 | .. code:: alloy
28 |
29 | sig A {}
30 |
31 | pred at_least_one_a {
32 | some A
33 | }
34 |
35 | pred more_than_one_a {
36 | at_least_one_a and not one A
37 | }
38 |
39 | run more_than_one_a
40 |
41 | .. warning:: Predicates and functions **cannot**, in the general case, be recursive.
42 | Limited recursion is possible, see
43 | :ref:`here ` for more info.
44 |
45 | .. _parameters:
46 |
47 | Parameters
48 | ----------
49 |
50 | Predicates can also take arguments.
51 |
52 | .. code:: alloy
53 |
54 | pred foo[a: Set1, b: Set2...] {
55 | expr
56 | }
57 |
58 | The predicate is called with ``foo[x, y]``, **using brackets, not
59 | parens**. In the body of the predicate, ``a`` and ``b`` would have the
60 | corresponding values.
61 |
62 | .. _receiver-syntax:
63 |
64 | Receiver Syntax
65 | ~~~~~~~~~~~~~~~~~~~
66 |
67 | The initial argument to a predicate can be passed in via a ``.`` join.
68 | The following two are equivalent:
69 |
70 | .. code:: alloy
71 |
72 | pred[x, y, z]
73 | x.pred[y, z]
74 |
75 |
76 |
77 |
78 | .. _functions:
79 |
80 | Functions
81 | =============
82 |
83 | Alloy functions have the same structure as predicates but also return a value.
84 | Unlike functions in programming languages, they are always executed within an execution run,
85 | so their results are available in the visualisation and the evaluator even if you haven't
86 | called them directly. This is very useful for some visualisations, although the supporting
87 | code can be disorienting when transitioning from "regular" programming languages.
88 |
89 | .. code:: alloy
90 |
91 | fun name[a: Set1, b: Set2]: output_type {
92 | expression
93 | }
94 |
95 | .. tip:: if a function is constant (does not take any parameters), the
96 | analyzer casts it to a constant set. This means if we have a function of parameter
97 |
98 | .. code:: alloy
99 |
100 | fun foo: A -> B {
101 | expression
102 | }
103 |
104 | Then ``^foo`` is a valid expression.
105 |
106 | .. rst-class:: advanced
107 |
108 | .. _overloading:
109 |
110 | Overloading
111 | ------------------
112 |
113 | Predicates and functions may be overloaded, as long as it's unambiguous which function applies. The following is valid:
114 |
115 | .. code:: alloy
116 |
117 | sig A {}
118 |
119 | sig B {}
120 |
121 | pred foo[a: A] { --1
122 | a in A
123 | }
124 |
125 | pred foo[b: B] { --2
126 | b in B
127 | }
128 |
129 | run {some a: A | foo[a]}
130 |
131 | As when ``foo`` is called, it's unambiguous whether it means (1) or (2). If we instead replaced ``sig B`` with ``sig B extends A``, then it's ambiguous and the call is invalid.
132 |
133 | Overloading can happen if you import the same parameterized module twice.For example, given the following:
134 |
135 | ::
136 |
137 |
138 | open util/ordering[A]
139 | open util/ordering[B]
140 |
141 | sig A, B {}
142 | run {some first}
143 |
144 | It is unclear whether ``first`` applies to ``A`` or ``B``. To fix this, use `Namespaced imports `::
145 |
146 | open util/ordering[A] as u1
147 | open util/ordering[B] as u2
148 |
149 | sig A, B {}
150 | run {some u2/first}
151 |
152 |
153 | .. rst-class:: advanced
154 | .. _@:
155 |
156 | Parameter Overrides
157 | -----------------------
158 |
159 |
160 | The parameters of a function (or predicate) can shadow a global value.
161 | In this case, you can retrieve the original global value by using
162 | ``@val``.
163 |
164 | .. code:: alloy
165 |
166 | sig A {}
167 |
168 | pred f[A: univ, b: univ] {
169 | b in A -- function param
170 | b in @A -- global signature
171 | }
172 |
173 |
174 | .. _facts:
175 |
176 | Facts
177 | =====
178 |
179 | A fact has the same form as a global predicate:
180 |
181 | .. code:: alloy
182 |
183 | fact name {
184 | constraint
185 | }
186 |
187 | A fact is *always* considered true by the Analyzer. Any models that
188 | would violate the fact are discarded instead of checked. This means that
189 | if a potential model both violates an assertion and a fact, it is not
190 | considered a counterexample.
191 |
192 | .. code:: alloy
193 |
194 | sig A {}
195 |
196 | -- This has a counterexample
197 | check {no A}
198 |
199 | -- Unless we add this fact
200 | fact {no A}
201 |
202 | .. tip:: For facts, the name is optional. In addition, the name can be a string. So this is a valid fact:
203 |
204 | .. code::
205 |
206 | fact "no cycles" {
207 | all n: Node | n not in n.^edge
208 | }
209 |
210 | .. rst-class:: advanced
211 | .. _implicit facts:
212 |
213 | Implicit Facts
214 | ------------------
215 |
216 |
217 |
218 | You can write a fact as part of a signature. The implicit fact goes
219 | after the signature definition and relations. Inside of an implicit fact,
220 | you can get the current atom with ``this``. Fields are automatically expanded in the implicit fact to ``this.field``.
221 |
222 | ::
223 |
224 | sig Node {
225 | edge: set Node
226 | } {
227 | this not in edge
228 | }
229 |
230 | This means you cannot apply the relation to another atom
231 | of the same signature inside the implicit fact. You can access the
232 | original relation by using the ``@`` operator:
233 |
234 | ::
235 |
236 | -- undirected graphs only
237 | sig Node {
238 | , edge: set Node
239 | }
240 | {
241 | all link: edge | this in link.edge -- invalid
242 | all link: edge | this in link.@edge -- valid
243 | }
244 |
245 |
246 | .. rst-class:: advanced
247 | .. _macros:
248 |
249 | Macros
250 | ========
251 |
252 | A macro is a similar to a predicate or function, except it is expanded before runtime. For this reason, macros can be used as part of signature fields. Parameters to macros also don't need to be given types, so can accept arbitrary signatures and even boolean constraints. Macros are defined with ``let`` in the top scope.
253 |
254 |
255 | .. code:: alloy
256 |
257 | let selfrel[Sig] = { Sig -> Sig }
258 | let many[Sig] = { some Sig and not one Sig }
259 |
260 | sig A {
261 | rel: selfrel[A]
262 | }
263 |
264 | run {many[A]}
265 |
266 | See `here `__ for more information.
267 |
--------------------------------------------------------------------------------
/language/sets-and-relations.rst:
--------------------------------------------------------------------------------
1 | .. _sets-and-relations:
2 |
3 | ++++++++++++++++++
4 | Sets and Relations
5 | ++++++++++++++++++
6 |
7 |
8 | .. _sets:
9 |
10 | Sets
11 | ====
12 |
13 | A set is a collection of unique, unordered elements. All Alloy
14 | expressions use sets of atoms and :ref:`relations`. All
15 | elements of a set must all be either atoms, relations, or multirelations of the same arity, but may be different types of each category.
16 |
17 | In expressions, the name of the signature is equal to the set of all atoms in that signature. The same is true for signature fields. Given
18 |
19 | .. code:: alloy
20 |
21 | sig Teacher {}
22 | sig Student {
23 | teacher: Teacher
24 | }
25 |
26 | Then the spec recognizes ``Student`` as the set of all atoms of type
27 | ``Student``, and likewise with the ``Teacher`` signature and the
28 | ``teacher`` relationship.
29 |
30 | Everything in Alloy is a set. If ``S1`` is a ``Student`` atom, then
31 | ``S1`` is the set containing just ``S1`` as an element.
32 |
33 | There are also two special sets:
34 |
35 | .. _none:
36 | .. _univ:
37 |
38 | - ``none`` is just the empty set. Saying ``no Set`` is the same as saying ``Set = none``. See :ref:`expressions`.
39 | - ``univ`` is the set of all atoms in the model. In this example, ``univ = Student + Teacher``.
40 |
41 | .. note::
42 |
43 | By default, the analyzer also generates a set of integers for each model, which will appear in ``univ``. This can almost always be ignored in specifications (but see :ref:`#` below).
44 |
45 | Set Operators
46 | -------------
47 |
48 | Set operators can be used to construct new sets from existing ones, for
49 | use in expressions and predicates.
50 |
51 | - ``S1 + S2`` is the set of all elements in either ``S1`` or ``S2``
52 | (set union).
53 | - ``S1 - S2`` is the set of all elements in ``S1`` but not ``S2`` (set
54 | difference).
55 | - ``S1 & S2`` is the set of all elements in both ``S1`` and ``S2`` (set
56 | intersection).
57 |
58 | .. code:: alloy
59 |
60 | S1 = {A, B}
61 | S2 = {B, C}
62 |
63 | S1 + S2 = {A, B, C}
64 | S1 - S2 = {A}
65 | S1 & S2 = {B}
66 |
67 | .. _cartesian-product:
68 |
69 | ``->`` used as an operator
70 | ~~~~~~~~~~~~~~~~~~~~~~~~~~
71 |
72 | Given two sets, ``Set1 -> Set2`` is the *Cartesian product* of the two:
73 | the set of all relations that map any element of ``Set1`` to any element
74 | of ``Set2``.
75 |
76 | ::
77 |
78 | Set1 = {A, B}
79 | Set2 = {X, Y, Z}
80 |
81 | Set1 -> Set2 = {
82 | A -> X, A -> Y, A -> Z,
83 | B -> X, B -> Y, B -> Z
84 | }
85 |
86 | As with other operators, a standalone atom is the set
87 | containing that atom. So we can write ``A -> (X + Y)`` to get
88 | ``(A -> X + A -> Y)``.
89 |
90 | .. todo::
91 | 1. it's impossible to construct relations between sets, they're always flattened
92 |
93 | 2. this is the only way to construct relations outside of signatures
94 |
95 | .. TIP:: ``univ -> univ`` is the set of all possible relations in your model.
96 |
97 | .. _integers:
98 |
99 | Integers
100 | ------------
101 |
102 | Alloy has limited support for integers. To enforce bounded models, the
103 | numerical range is finite. By default, Alloy uses models with 4-bit
104 | signed integers: all integers between ``-8`` and ``7``. If an arithmetic
105 | operation would cause this to overflow, then the predicate is
106 | automatically declared false. In the :ref:`evaluator`, however, it will wrap the overflowed number.
107 |
108 | .. tip::
109 |
110 | The numerical range can be changed by placing a :ref:`scope ` on ``Int``. The number of the scope is the number of bits in the signed integers. For example, if the scope is ``5 Int``, the model will have all integers between ``-16`` and ``15``.
111 |
112 | All arithmetic operators are over the given model’s numeric range. To
113 | avoid conflict with set and relation operators, the arithmetic operators
114 | are written as :ref:`functions`:
115 |
116 | .. code:: alloy
117 |
118 | add[1, 2]
119 | sub[1, 2]
120 | mul[1, 2]
121 | div[3, 2] -- integer division, drop remainder
122 | rem[1, 2] -- remainder
123 |
124 | You can use receiver syntax for this, and write ``add[1, 2]`` as
125 | ``1.add[2]``. There are also the following comparison predicates:
126 |
127 | .. code:: alloy
128 |
129 | 1 =< 2
130 | 1 < 2
131 | 1 > 2
132 | 1 >= 2
133 | 1 != 2
134 | 1 = 2
135 |
136 | As there are no corresponding symbols for elements to overload, these
137 | operators are written as infixes.
138 |
139 | .. warning::
140 | Sets of integers have non-intuitive properties and should be used with care.
141 |
142 | .. _#:
143 |
144 | ``#``
145 | ~~~~~
146 |
147 | ``#S`` is the number of elements in ``S``.
148 |
149 |
150 | .. rst-class:: advanced
151 |
152 | Sets of numbers
153 | ~~~~~~~~~~~~~~~~~~~~~~
154 |
155 | .. _sum:
156 |
157 | For set operations, a set of numbers are treated as a set. For
158 | arithmetic operations, however, a set of numbers is first summed before
159 | applying the operator. This is equivalent to using the ``sum[]``
160 | function.
161 |
162 | ::
163 |
164 | (1 + 2) >= 3 -- true
165 | (1 + 2) <= 3 -- true
166 | (1 + 2) = 3 -- false
167 | (1 + 2).plus[0] = 3 -- true
168 | (1 + 1).plus[0] = 2 -- false
169 |
170 |
171 | .. _relations:
172 |
173 | Relations
174 | =========
175 |
176 | Given the following spec
177 |
178 | ::
179 |
180 | sig Group {}
181 | sig User {
182 | belongs_to: set Group
183 | }
184 |
185 | ``belongs_to`` describes a **relation** between ``User`` and ``Group``.
186 | Each individual relation consists of a pair of atoms, the first being
187 | ``User``, the second being ``Group``. We write an individual relation
188 | with ``->``. One possible model might have
189 |
190 | ::
191 |
192 | belongs_to = {
193 | U1 -> G1 +
194 | U2 -> G1 +
195 | U2 -> G2
196 | }
197 |
198 | Relations do *not* need to be 1-1: here two users map to ``G1`` and one
199 | user maps to both ``G1`` and ``G2``.
200 |
201 |
202 | ..
203 | Generating spec:
204 |
205 | ::
206 |
207 | abstract sig Group {}
208 | abstract sig User {
209 | belongs_to: set Group
210 | }
211 |
212 | one sig U1, U2 extends User {}
213 | one sig G1, G2 extends Group {}
214 |
215 | fact {
216 | belongs_to = {
217 | U1 -> G1 +
218 | U2 -> G1 +
219 | U2 -> G2
220 | }
221 | }
222 |
223 | .. image:: img/belongs_to.png
224 |
225 | Relations in Alloy are first class objects, and can be manipulated and
226 | used in expressions. [This assumes you already know the set operations].
227 | For example, we can reverse a relation by adding ``~`` before it:
228 |
229 | ::
230 |
231 | ~belongs_to = {
232 | G1 -> U1 +
233 | G1 -> U2 +
234 | G2 -> U2
235 | }
236 |
237 | .. _.:
238 |
239 | The . Operator
240 | --------------
241 |
242 | The dot (``.``) operator is the most common relationship operator, and has
243 | several different uses. The dot operator is left-binding: ``a.b.c`` is
244 | parsed as ``(a.b).c``, not ``a.(b.c)``.
245 |
246 | ``Set.rel``
247 | ~~~~~~~~~~~
248 |
249 | If ``Set`` is an individual atom, this returns all elements that said atom maps to. If ``Set`` is more than one atom, this gets all elements they map to.
250 |
251 | ::
252 |
253 | U1.belongs_to = G1
254 | (U1 + U2).belongs_to = {G1, G2}
255 |
256 |
257 | .. Tip:: In this case, we can find all groups in the relation with ``User.belongs_to``. However, some relations may mix different types of atoms. In that case ``univ.~rel`` is the domain of ``rel`` and ``univ.rel`` is the range of ``rel``.
258 |
259 | For `multirelations`, this will return the "tail" of the relation. Eg if ``rel = A -> B -> C``, then ``A.rel = B -> C``.
260 |
261 | ``rel.Set``
262 | ~~~~~~~~~~~
263 |
264 | Writing ``rel.Set`` is equivalent to writing ``Set.~rel``. See `~`.
265 |
266 | ::
267 |
268 | belongs_to.G1 = {U1, U2}
269 | G1.~belongs_to = {U1, U2}
270 |
271 | ``rel1.rel2``
272 | ~~~~~~~~~~~~~
273 |
274 | We can use the dot operator with two relations. It returns the inner
275 | product of the two relations. For example,
276 | given
277 |
278 | .. code:: alloy
279 |
280 | rel1 = {A -> B, B -> A}
281 | rel2 = {B -> C,
282 | B -> D, A -> E}
283 |
284 | rel1.rel2 = {
285 | A -> C,
286 | A -> D, B -> E}
287 |
288 | In our case with Users and Groups, ``belongs_to.~belongs_to`` maps every
289 | User to every other user that shares a group.
290 |
291 |
292 | .. note:: The operator isn't overloaded; it's the same operator with the same semantics for both ``Set.rel`` and ``rel1.rel2``.
293 |
294 | .. todo:: more explaining of this better
295 |
296 | .. _[]:
297 |
298 | []
299 | ~~~~~~
300 |
301 | ``rel[elem]`` is equivalent to writing ``elem.(rel)``. It has a lower
302 | precedence than the ``.`` operator, which makes it useful for
303 | `multirelations`. If we have
304 |
305 | ::
306 |
307 |
308 | sig Light {
309 | state: Color -> Time
310 | }
311 |
312 | Then ``L.state[C]`` would be all of the times ``T`` where the light ``L`` was color ``C``. The equivalent without ``[]`` would be ``C.(L.state)``.
313 |
314 |
315 | .. _iden:
316 |
317 | iden
318 | ----
319 |
320 | ``iden`` is the relationship mapping every element to itself. If we have
321 | an element ``a`` in our model, then ``(a -> a) in iden``.
322 |
323 | An example of iden’s usefulness: if we want to say that ``rel`` doesn’t
324 | have any cycles, we can say ``no iden & ^rel``.
325 |
326 | Additional Operators
327 | --------------------
328 |
329 | .. NOTE:: You cannot use ``~``, ``^``, or ``*`` with `higher-arity relations `.
330 |
331 |
332 | .. _~:
333 |
334 | ``~rel``
335 | ~~~~~~~~
336 |
337 | As mentioned, ``~rel`` is the reverse of ``rel``.
338 |
339 |
340 | .. _transitive-closure:
341 |
342 | ``^`` and ``*``
343 | ~~~~~~~~~~~~~~~
344 |
345 | These are the **transitive closure** relationships. Take the following
346 | example:
347 |
348 | .. code:: alloy
349 |
350 | sig Node {
351 | edge: set Node
352 | }
353 |
354 | ``N.edge`` is the set of all nodes that ``N`` connects to.
355 | ``N.edge.edge`` is the set of all nodes that an edge of ``N`` connects
356 | to. ``N.edge.edge.edge`` is the set of all nodes that are an edge of an
357 | edge of N, ad infinitum. If we want every node that is connected to ``N``,
358 | this is called the transitive closure and is written as ``N.^edge``.
359 |
360 | ``^`` does *not* include the original atom unless it’s transitively
361 | reachable! In the above example, ``N in N.^edge`` iff the graph has a
362 | cycle containing ``N``. If we want to also include ``N``, use
363 | ``N.*edge`` instead.
364 |
365 | ``^`` operates on the relationship, so ``^edge`` is also itself a
366 | relationship and can be manipulated like any other. We can write both
367 | ``~^edge`` and ``^~edge``. It also works on arbitrary relationships.
368 | ``U1.^(belongs_to.~belongs_to)`` is the set of people that share a group
369 | with ``U1``, or share a group with people who share a group with ``U1``,
370 | ad infinitum.
371 |
372 | .. warning::
373 | By itself ``*edge`` will include ``iden``! ``*edge = ^edge + iden``. For best results only use ``*`` immediately before joining the closure with another set.
374 |
375 | .. rst-class:: advanced
376 |
377 | Advanced Operators
378 | ----------------------
379 |
380 | .. _relation_restriction:
381 |
382 | ``<:`` and ``:>``
383 | ~~~~~~~~~~~~~~~~~
384 |
385 | ``<:`` is *domain restriction*. ``Set <: rel`` is all of the elements in
386 | ``rel`` that **start** with an element in ``Set``. ``:>`` is the *range
387 | restriction*, and works similarly: ``rel :> Set`` is all the elements of
388 | ``rel`` that **end** with an element in Set.
389 |
390 | This is mostly useful for directly manipulating relations. For example,
391 | given a set S, we can map every element to itself by doing
392 | ``S <: iden``. We can also use restrictions to disambiguate overloaded fields. If we have
393 |
394 | .. code:: alloy
395 |
396 | abstract sig Node {
397 | , edges: set Node
398 | }
399 |
400 | some sig Red, Blue extends Node {}
401 |
402 | Then ``Blue <: edges :> Red`` is the set of all edges from ``Blue`` nodes to ``Red`` ones.
403 |
404 | .. _++:
405 |
406 | ``++``
407 | ~~~~~~
408 |
409 | ``rel1 ++ rel2`` is the union of the two relations, with one exception: if any relations in ``rel1`` that share a "key" with a relation in ``rel2`` are dropped. Think of it like merging two dictionaries.
410 |
411 | Formally speaking, we have
412 |
413 | .. code:: alloy
414 |
415 | rel1 ++ rel2 = rel1 - (rel2.univ <: rel1) + rel2
416 |
417 | Some examples of ``++``:
418 |
419 | .. code:: alloy
420 |
421 | (A -> B + A -> C) ++ (A -> A) = (A -> A)
422 | (A -> B + A -> C) ++ (A -> A + A -> C) = (A -> A + A -> C)
423 | (A -> B + A -> C) ++ (C -> A) = (A -> B + A -> C + C -> A)
424 | (A -> B + B -> C) ++ (A -> A) = (A -> A + B -> C)
425 |
426 | It’s mostly useful for modeling `Time `.
427 |
428 | .. Note:: When using multirelations the two relations need the same arity, and it overrides based on only the first element in the relations.
429 |
430 | .. rst-class:: advanced
431 | .. _set-comprehensions:
432 |
433 | Set Comprehensions
434 | ~~~~~~~~~~~~~~~~~~~~~~
435 |
436 | Set comprehensions are written as
437 |
438 | .. code:: alloy
439 |
440 | {x: Set1 | expr[x]}
441 |
442 | The expression evaluates to the set of all elements of ``Set1`` where ``expr[x]`` is true. ``expr`` can be any expression and may be inline. Set comprehensions can be used anywhere a set or set expression is valid.
443 |
444 | Set comprehensions can use multiple inputs.
445 |
446 | .. code:: alloy
447 |
448 | {x: Set1, y: Set2, ... | expr[x,y]}
449 |
450 | In this case this comprehension will return relations in ``Set1 -> Set2``.
451 |
452 |
--------------------------------------------------------------------------------
/language/signatures.rst:
--------------------------------------------------------------------------------
1 | .. _signatures:
2 |
3 | ++++++++++
4 | Signatures
5 | ++++++++++
6 |
7 |
8 | A **signature** expresses a new type in your spec. It can be anything
9 | you want. Here are some example signatures:
10 |
11 | - Time
12 | - State
13 | - File
14 | - Person
15 | - Msg
16 | - Pair
17 |
18 | Alloy can generate models that have elements of each signature, called **atoms**.
19 | Take the following spec:
20 |
21 | .. code:: alloy
22 |
23 | sig A {}
24 |
25 | The following would be an example generated model:
26 |
27 | .. image:: img/sig-a.png
28 |
29 | Here we have two atoms ``A$1`` and ``A$0``. Both count as instances of the ``A`` signature. See `visualizer ` for more on how to read the visualizations.
30 |
31 | Usually we care about the relationships between the parts of our
32 | systems. We don’t just care that there are people and cars, we care
33 | which people have which cars. We do this by adding **relations**
34 | inside of the signature body.
35 |
36 | .. code:: alloy
37 |
38 | sig Person {}
39 | sig Car {}
40 |
41 | sig Registration {
42 | owner: Person,
43 | car: Car
44 | }
45 |
46 | This defines a new ``Registration`` type, where each Registration has an
47 | ``owner``, which is a Person, and a ``car``, which is a Car. The comma
48 | is required.
49 |
50 | .. a
51 |
52 | Generating spec:
53 |
54 | run {
55 | one Car
56 |
57 | # Person = 2
58 | Registration.owner = Person
59 | }
60 |
61 |
62 | .. image:: img/sig-with-relations.png
63 |
64 | .. tip::
65 |
66 | Extra commas are ignored. So you can write ``Registration`` instead like this:
67 |
68 | .. code:: alloy
69 |
70 | sig Registration {
71 | , owner: Person
72 | , car: Car
73 | }
74 |
75 | .. _signature-relations:
76 |
77 | Relations
78 | ---------
79 |
80 | The body of a signature is a list of **relations**, which show how the
81 | signatures are connected to each other. A relation in the body of a signature is also called a **field**.
82 |
83 | .. todo:: Rename use consistent "field" terminology here
84 |
85 | Relations are separated by a comma. The list can also start and end with
86 | a comma. Relations do not have to be on separate lines, as long as they are separated by commas.
87 |
88 | Relations *can* refer to the same signature. This is valid:
89 |
90 | .. code:: alloy
91 |
92 | sig Node {
93 | , edges: set Node
94 | }
95 |
96 | .. todo:: Change image to use the new 'edges' instead of 'edge'
97 |
98 | .. image:: img/self-relation.png
99 |
100 | Alloy can generate models where a relation points from an atom to itself, aka a "self-loop". For
101 | this reason we often want to add constraints to our model, such as
102 | :ref:`facts` or :ref:`predicates`.
103 |
104 | .. note::
105 |
106 | Each relation in the body of a signature actually represents a
107 | :ref:`relation ` type. If we have::
108 |
109 | sig A {r: one B}
110 |
111 | Then ``r`` is set of relations in ``A -> B``. See :ref:`sets-and-relations` for more information.
112 |
113 | Different signatures *may* have relationships with the same name as long as the relationship is not `ambiguous `.
114 |
115 | Multiplicity
116 | ~~~~~~~~~~~~
117 |
118 | Each relation has a **multiplicity**, which represents how many atoms it
119 | can include. If you do not include a multiplicity, it’s assumed to be
120 | ``one`` for individual relations and ``set`` for `multirelations`.
121 |
122 | .. All of the images here are at ~170ish pixel height. All the images should be standardized at some point.
123 |
124 | Also, we should save the XMLs for each image. I didn't do that on the first pass, so we might not end up with the same images.
125 |
126 | .. _one:
127 |
128 | one
129 | ^^^
130 |
131 | The default. ``r: one A`` states that there is *exactly one* A in the
132 | set.
133 |
134 | .. code:: alloy
135 |
136 | sig Key {}
137 |
138 | sig Lock {
139 | , key: one Key
140 | }
141 |
142 | This says that every lock has exactly one Key. This does *not* guarantee
143 | a 1-1 correspondence! Two locks can share the same key.
144 |
145 | .. image:: img/one-key.png
146 |
147 | If no multiplicity is listed, Alloy assumes to be ``one``. So the above
148 | relation can also be written as ``key: Key``.
149 |
150 | lone
151 | ^^^^
152 |
153 | ``r: lone A`` states that *either* there is one A in the set, *or* that
154 | the set is empty. You can also think of it as “optional”.
155 |
156 | .. code:: alloy
157 |
158 | sig Directory {
159 | , parent: lone Directory
160 | }
161 |
162 | This says that every directory either has one parent, *or* it does not
163 | have a parent (it’s a root directory).
164 |
165 | .. image:: img/lone-dir.png
166 |
167 | set
168 | ^^^
169 |
170 | ``r: set A`` states that there can be any number of A in the relation.
171 |
172 | .. code:: alloy
173 |
174 | sig User {}
175 | sig Region {
176 | servers: set User
177 | }
178 |
179 |
180 | ..
181 | .. code::
182 | let many[Sig] { some Sig not one Sig }
183 |
184 | run {
185 | many[User]
186 | many[Region]
187 | Region.servers = User
188 | one servers.User
189 | }
190 |
191 | .. image:: img/set-user.png
192 |
193 | some
194 | ^^^^
195 |
196 | ``r: some A`` states that there is *at least one* A in the relation.
197 |
198 | .. code:: alloy
199 |
200 | sig Author {}
201 |
202 | sig Book {
203 | by: some Author
204 | }
205 |
206 | .. image:: img/some-author.png
207 |
208 | disj
209 | ^^^^^^^^^^^
210 |
211 | ``disj`` can be prepended to any multiplicity to guarantee that it will be disjoint among all atoms. If we write
212 |
213 | .. code:: alloy
214 |
215 | sig Lock {}
216 | sig Key {
217 | lock: disj one Lock
218 | }
219 |
220 | Then every key will correspond to a *different* lock. If we instead write
221 |
222 | .. code:: alloy
223 |
224 | sig Lock {}
225 | sig Key {
226 | locks: disj some Lock
227 | }
228 |
229 | Then every key will correspond to one or more locks, but no two keys will share a lock.
230 |
231 |
232 | seq
233 | ^^^
234 |
235 | See `here `__ for more info.
236 |
237 |
238 | .. _field-expressions:
239 |
240 | Field Expressions
241 | ~~~~~~~~~~~~~~~~~~~~~~~~
242 |
243 | A field can be a simple `expression ` over other signatures.
244 |
245 | .. code:: alloy
246 |
247 | sig Resource {
248 | permissions: set (User + Group)
249 | }
250 |
251 | .. _this:
252 |
253 | In addition to full signatures, the expression may contain ``this``, which refers to the specific atom itself.
254 |
255 | .. code:: alloy
256 |
257 | sig Node {
258 | -- no self loops
259 | , edges: set Node - this
260 | }
261 |
262 | A :dfn:`dependent field` is one where the expression depends on the values of other fields in the atom. The dependencies must be fields defined either in the signature or its `supertype `.
263 |
264 | .. code:: alloy
265 |
266 | sig Item {}
267 |
268 | sig Person {
269 | , favorite: Item
270 | , second: Item - favorite
271 | }
272 |
273 | .. rst-class:: advanced
274 | .. _multirelations:
275 |
276 | Multirelations
277 | ~~~~~~~~~~~~~~~~~~
278 |
279 | .. requires: relations, logic
280 |
281 | Signatures can have multirelations as fields:
282 |
283 | .. code:: alloy
284 |
285 | sig Door {}
286 | sig Card {}
287 |
288 | sig Person {
289 | access: Card -> Door
290 | }
291 |
292 | In this case ``access`` is a ternary relationship, where each element of
293 | ``access`` is a relation of form ``Person -> Card -> Door``.
294 |
295 | Multirelations have a special kind of multiplicity:
296 |
297 | .. code:: alloy
298 |
299 | r: A m -> n B
300 |
301 | This says that each member of ``A`` is mapped to ``n`` elements of B,
302 | and ``m`` elements of ``A`` map to each element of B. If not specified,
303 | the multiplicities are assumed to be ``set``.
304 |
305 | As an aid, use the following table:
306 |
307 | +-------------+-------------+------------------------------------------+
308 | | m | n | Meaning |
309 | +=============+=============+==========================================+
310 | | set | set | No restrictions |
311 | +-------------+-------------+------------------------------------------+
312 | | set | some | Each A used at least once |
313 | +-------------+-------------+------------------------------------------+
314 | | set | one | Each A is mapped to exactly one B (total |
315 | | | | function) |
316 | +-------------+-------------+------------------------------------------+
317 | | set | lone | Each A is mapped to at most one B |
318 | | | | (partial function) |
319 | +-------------+-------------+------------------------------------------+
320 | | some | set | Each B mapped to at least once |
321 | +-------------+-------------+------------------------------------------+
322 | | some | some | Every A mapped from and every B mapped |
323 | | | | to |
324 | +-------------+-------------+------------------------------------------+
325 | | some | one | Each A used exactly once, each B used at |
326 | | | | least once |
327 | +-------------+-------------+------------------------------------------+
328 | | some | lone | Each A used at most once, each B used at |
329 | | | | least once |
330 | +-------------+-------------+------------------------------------------+
331 | | one | set | Each B used exactly once, no other |
332 | | | | restrictions (one A can map to two B |
333 | | | | atoms) |
334 | +-------------+-------------+------------------------------------------+
335 | | one | some | Each B used exactly once, each A used at |
336 | | | | least once |
337 | +-------------+-------------+------------------------------------------+
338 | | one | one | Only satisfiable if #A = #B, bijection |
339 | +-------------+-------------+------------------------------------------+
340 | | one | lone | At most #A arrows, exactly #B arrows, |
341 | | | | each A used at most once |
342 | +-------------+-------------+------------------------------------------+
343 | | lone | set | Each B used at most once |
344 | +-------------+-------------+------------------------------------------+
345 | | lone | some | Each A used at least once and each B |
346 | | | | used at most once |
347 | +-------------+-------------+------------------------------------------+
348 | | lone | one | Each A used exactly once, each B used at |
349 | | | | most once |
350 | +-------------+-------------+------------------------------------------+
351 | | lone | lone | Each A used at most once, each B used at |
352 | | | | most once |
353 | +-------------+-------------+------------------------------------------+
354 |
355 | .. todo:: Replace the autogen table with a proper list table that doesn't require exact rst formatting
356 |
357 | Not all multiplicities will have valid models. For example,
358 |
359 | ::
360 |
361 |
362 | sig A {}
363 | sig B {}
364 | one sig C {
365 | r: A one -> one B
366 | }
367 |
368 | run {} for exactly 3 A, exactly 2 B
369 |
370 | Since ``r`` must be 1-1, and there’s different numbers of A and B sigs, nothing satisfies this model.
371 |
372 | Multirelations can go higher than ternary using the same syntax, but this is generally not recommended.
373 |
374 | .. _signature-multiplicity:
375 |
376 | Signature Multiplicity
377 | --------------------------
378 |
379 | In addition to having multiplicity relationships, we can put
380 | multiplicities on the signatures themselves.
381 |
382 | .. code:: alloy
383 |
384 | one sig Foo {}
385 | some sig Bar {}
386 | //etc
387 |
388 | By default, signatures have multiplicity ``set``, and there may be zero
389 | or more in the model. By making the signature ``one``, every model will
390 | have exactly one atom of that signature. By writing ``some``, there will
391 | be at least one. By writing ``lone``, there will be zero or one.
392 |
393 |
394 | .. _subtypes:
395 |
396 | Subtypes
397 | --------
398 |
399 | We can make some signatures subtypes of other signatures.
400 |
401 | .. _subtype in:
402 |
403 | in
404 | ~~
405 |
406 | Writing ``sig Child in Parent`` creates an *inclusive* subtype: any
407 | Parent atoms may or may not also be a Child. This is also called a
408 | “subset subtype”.
409 |
410 | .. code:: alloy
411 |
412 | sig Machine {}
413 |
414 | sig Broken in Machine {}
415 | sig Online in Machine {}
416 |
417 | In this case, any Machine can also be Broken, Online, both, or neither.
418 |
419 | .. rst-class:: advanced
420 | .. _subtype-+:
421 |
422 |
423 | ``+``
424 | ^^^^^^
425 |
426 | A single inclusive subtype can be defined for many parent signatures. We
427 | can do this by using the set union operator on the parent signatures.
428 |
429 | ::
430 |
431 | sig Bill, Client {}
432 |
433 | sig Closed in Bill + Client {}
434 |
435 |
436 | .. _extends:
437 |
438 | extends
439 | ~~~~~~~
440 |
441 | Writing ``sig Child extends Parent`` creates a subtype, as with ``in``.
442 | Unlike ``in``, though, any Parent atom can only match up to *one*
443 | extension.
444 |
445 | .. code:: alloy
446 |
447 | sig Machine {}
448 |
449 | sig Server extends Machine {}
450 | sig Client extends Machine {}
451 |
452 | In this case, any Machine can also be a Server, a Client, or neither,
453 | but not both.
454 |
455 | .. todo:: [img]
456 |
457 | Something can belong to both ``extend`` and ``in`` subtypes.
458 |
459 | .. code:: alloy
460 |
461 | sig Machine {}
462 | sig Broken in Machine {}
463 |
464 | sig Server extends Machine {}
465 | sig Client extends Machine {}
466 |
467 | A Machine can be both a Server and Broken, or a Client and Broken, or just one of the three, or none at all.
468 |
469 | .. _abstract:
470 |
471 | abstract
472 | ^^^^^^^^
473 |
474 | If you make a signature ``abstract``, then all atoms of the signature will belong to extensions. There will be no atoms that are just the supertype and not any of the subtypes.
475 |
476 | .. code:: alloy
477 |
478 | abstract sig Machine {}
479 | sig Broken in Machine {}
480 |
481 | sig Server extends Machine {}
482 | sig Client extends Machine {}
483 |
484 | Here any machine **must** be either a Server or a Client. They still may
485 | or may not be Broken.
486 |
487 | .. WARNING:: If there is nothing extending an abstract signature, the abstract is ignored.
488 |
489 | .. tip:: You can place multiple signatures on the same line.
490 |
491 | .. code:: alloy
492 |
493 | sig Server, Client extends Machine {}
494 |
495 | Subtypes and Relationships
496 | ~~~~~~~~~~~~~~~~~~~~~~~~~~
497 |
498 | All subtypes are also their parent type. So if we have
499 |
500 | .. code:: alloy
501 |
502 | sig B {}
503 | sig C in B {}
504 |
505 | sig A {
506 | , b: B
507 | , c: C
508 | }
509 |
510 | Then the ``b`` relation can map to atoms of ``C``, and ``c`` cannot map to elements of ``B`` that are not also in ``C``.
511 |
512 | .. tip::
513 |
514 | If you want to map to elements of ``B`` that are not also in ``C``, you can write::
515 |
516 | sig A {
517 | , b: B - C
518 | }
519 |
520 |
521 | .. rst-class:: advanced
522 | .. _child relations:
523 |
524 | Child Relations
525 | ~~~~~~~~~~~~~~~
526 |
527 | Children automatically inherit all of their Parent fields, *and also* can define their own fields. We can have:
528 |
529 | .. code:: alloy
530 |
531 | sig Person {}
532 | sig Account {
533 | , person: Person
534 | }
535 |
536 | sig PremiumAccount in Account {
537 | , billing: Person
538 | }
539 |
540 | Then all ``Account`` atoms will have the ``person`` field, while all ``PremiumAccount`` atoms will have both a ``person`` field and a ``billing`` field.
541 |
542 | .. NOTE:: This also applies to `implicit facts`. If Account has an implicit fact, it automatically applies to PremiumAccount.
543 |
544 | It is not possible redefine a relationship, only to add additional ones.
545 |
546 | .. rst-class:: advanced
547 | .. _enums:
548 |
549 | Enums
550 | -----
551 |
552 | Enums are a special signature.
553 |
554 | .. code:: alloy
555 |
556 | enum Time {Morning, Noon, Night}
557 |
558 | The enum will always have the defined atoms in it. Additionally, the
559 | atom will have an `ordering`. In this case, Morning will be the first
560 | element, Noon the second, and Night will be the third. You can use enums
561 | in facts and predicates, but you cannot add additional properties to
562 | them.
563 |
564 | .. tip::
565 |
566 | If you want to use an enumeration with properties, you can emulate this
567 | by using ``one`` and signature extensions.
568 |
569 | .. code:: alloy
570 |
571 | abstract sig Time {}
572 |
573 | one sig Morning, Noon, Night extends Time {
574 | time: Time
575 | }
576 |
577 | You can also use this to make enumerations without a fixed number of
578 | elements, by using ``lone`` instead.
579 |
580 | .. warning::
581 |
582 | Each enum implicitly imports `ordering`. The following is invalid:
583 |
584 | ::
585 |
586 | enum A {a}
587 |
588 | enum B {b}
589 |
590 | run {some first}
591 |
592 | As it is ambiguous whether ``first`` should return ``a`` or ``b``. If you need to use both an enum inside of a dynamic model, be sure to use a `namespace ` when importing `ordering`.
593 |
--------------------------------------------------------------------------------
/language/time.rst:
--------------------------------------------------------------------------------
1 | .. _time:
2 |
3 | ++++++++++
4 | Time
5 | ++++++++++
6 |
7 | `Alloy 6 `_ added **temporal operators** to Alloy, making it easier to model dynamic systems.
8 |
9 | .. _var:
10 |
11 | Variables
12 | ============
13 |
14 | A signature or relation can be declared mutable with the ``var`` keyword:
15 |
16 | .. code:: alloy
17 |
18 | sig Server {}
19 |
20 | var sig Offline in Server {}
21 |
22 | sig Client {
23 | , var connected_to: lone Server - Offline
24 | }
25 |
26 |
27 | The set of servers that are offline is mutable: different servers can be offline in different steps. The ``connected_to`` :ref:`field ` is also mutable and can have different ``Client -> Server`` pairs in different steps.
28 |
29 | .. note:: This uses the ``in`` modifier as in the `boolean-subtyping` technique.
30 |
31 | The number of steps in a model is specified with the `steps` command.
32 |
33 | .. _steps-model:
34 |
35 | Temporal Operators
36 | ==================
37 |
38 | A dynamic model is broken into several `steps`. For each step, all ``var`` signatures and relations may change, depending on the predicates. By default, all predicates and facts only hold for the *initial* step of a variable. Eg
39 |
40 | .. code:: alloy
41 |
42 | fact "init" {
43 | no Offline
44 | no connected_to
45 | }
46 |
47 | This :ref:`fact ` says that *in the initial step*, there are no offline servers and no connection between clients and servers. There are no constraints, however, on future steps. To place constraints on future steps, put predicates inside a :dfn:`temporal operator`, like ``always`` or ``eventually``.
48 |
49 | .. code:: alloy
50 |
51 | pred spec {
52 | always no Offline
53 | eventually some connected_to
54 |
55 | }
56 |
57 | run {spec}
58 |
59 | .. todo::
60 |
61 | .. note:: see TK for how to interpret the evaluated trace.
62 |
63 | - ``always no Offline`` means that ``no Offline`` is true now, and in all future steps.
64 | - ``eventually some connected_to`` means that ``some connected_to`` is true *in at least one step*, now or in the future.
65 |
66 | Temporal operators can be combined: ``eventually always some Offline`` means that there's a step where, from that step forward, there is some Offline server.
67 |
68 | To model a "change", we relate the values of a variable between two steps. If ``connected_to`` is a ``var`` field, then ``connected_to'`` is the value of ``connected_to`` in the *next* step.
69 |
70 | .. code:: alloy
71 |
72 | pred connect[c: Client, s: Server] {
73 | c -> s not in connected_to
74 | connected_to' = connected_to ++ c -> s
75 | }
76 |
77 | In this example, ``connect`` is true or false in every step. In steps where it is true, the client is not connected to the server *and* in the next step, it is connected to the server. This represents the state of the system changing.
78 |
79 | .. _prime:
80 |
81 | ``'`` is also called the :dfn:`prime` operator. Combining primed predicates with temporal operators gives us a simple way to model system dynamics.
82 |
83 | .. code:: alloy
84 |
85 | pred spec {
86 | -- all servers always online
87 | always no Offline
88 |
89 | -- initially no connections
90 | no connected_to
91 |
92 | -- every step, a client connects to a new server
93 | always some c: Client, s: Server {
94 | c.connect[s]
95 | }
96 | }
97 |
98 | run {spec}
99 |
100 |
101 | .. index:: always, eventually, past, once, after, before, until, since, releases, triggered
102 |
103 | List of Operators
104 | -----------------
105 |
106 | Alloy operators include both *future* and *past* operators. Operators are true and false for a specific step.
107 |
108 | .. list-table:: Future temporal operators
109 | :header-rows: 1
110 |
111 | * - Operator
112 | - Meaning
113 | * - always P
114 | - P is true *and* true in all future steps
115 | * - eventually P
116 | - P is true *or* true in at least one future step
117 | * - after P
118 | - P is true in the next step
119 | * - P ; Q
120 | - Shorthand for ``P && after Q``
121 | * - Q releases P
122 | - P is true until Q is true, then P *may* become false
123 | * - P until Q
124 | - Equivalent to ``(Q releases P) and eventually Q``
125 |
126 | (``P'`` is special: instead of being true or false, it's simply the value of the P in the next step.)
127 |
128 | There are also *past* operators corresponding to each future operator. ``once P`` is the past-version of ``eventually P``: P is true *or* true in at least one *previous* step.
129 |
130 | .. list-table:: Past temporal operators
131 | :header-rows: 1
132 |
133 | * - Future Operator
134 | - Past Version
135 | * - always
136 | - historically
137 | * - eventually
138 | - once
139 | * - after
140 | - before
141 | * - triggered
142 | - releases
143 | * - since
144 | - until
145 |
146 | .. todo:: Explain the weirdness of triggered and since
147 |
148 |
149 | .. todo:: Traces?
150 |
151 | .. todo::
152 |
153 | Temporal Properties
154 | .==================
155 |
156 | To test that a property always holds, wrap it in an ``always`` operator. Eg
157 |
158 | .. code:: alloy
159 |
160 | run {spec => always property}
161 |
162 |
--------------------------------------------------------------------------------
/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/modules/boolean.rst:
--------------------------------------------------------------------------------
1 | .. als:module:: boolean
2 |
3 |
4 | +++++++++++++++
5 | boolean
6 | +++++++++++++++
7 |
8 | Emulates boolean variables by creating ``True`` and ``False`` atoms.
9 |
10 | .. code:: alloy
11 |
12 | -- module definition
13 | module util/boolean
14 |
15 | abstract sig Bool {}
16 | one sig True, False extends Bool {}
17 |
18 | pred isTrue[b: Bool] { b in True }
19 |
20 | pred isFalse[b: Bool] { b in False }
21 |
22 | In our code:
23 |
24 | .. code:: alloy
25 |
26 | -- our code
27 | open util/boolean
28 |
29 | sig Account {
30 | premium: Bool
31 | }
32 |
33 | Booleans created in this matter are not "true" booleans and cannot be used as part of regular `constraints`, IE you cannot do ``bool1 && bool2``. Instead you must use the dedicated boolean predicates, below. As such, ``boolean`` should be considered a proof-of-concept and is generally **not recommended** for use in production specs. You should instead represent booleans using :ref:`subtyping `.
34 |
35 | Functions
36 | ==========
37 |
38 | All of the following have expected logic, but return ``Bool`` atoms:
39 |
40 | * ``Not``
41 | * ``And``
42 | * ``Or``
43 | * ``Xor``
44 | * ``Nand``
45 | * ``Nor``
46 |
47 | So to emulate ``bool1 && bool2``, write ``bool1.And[bool2].isTrue``.
48 |
--------------------------------------------------------------------------------
/modules/graph.rst:
--------------------------------------------------------------------------------
1 | .. als:module:: graph
2 |
3 | ++++++++++++++
4 | graph
5 | ++++++++++++++
6 |
7 | Graph provides predicates on relations over a parameterized signature.
8 |
9 | ::
10 |
11 | open util/graph[Node]
12 |
13 | sig Node {
14 | edge: set Node
15 | }
16 |
17 | run {
18 | dag[edge]
19 | }
20 |
21 | Notice that graph is parameterized on the **signature**, but the predicate takes in a **relation**. This is so that you can apply multiple predicates to multiple different relations, or different subsets of the same relation. The graph module uses some specific terminology:
22 |
23 | This means that in a completely unconnected graph, every node is both a root and a leaf.
24 |
25 | Functions
26 | ---------
27 |
28 |
29 | .. als:function:: roots[r: node-> node]
30 |
31 | :rtype: ``set Node``
32 |
33 | Returns the set of nodes that *are not connected to* by any other node.
34 |
35 | .. warning:: this is *not* the same meaning of *root* as in the `rootedAt` predicate! For the predicate, a *root* is a node that transitively covers the whole graph. Internally, ``util/graph`` uses ``rootedAt`` and not ``roots``.
36 |
37 | .. als:function:: leaves[r: node-> node]
38 |
39 | :rtype: ``set Node``
40 |
41 | Returns the set of nodes that *do not connect to* any other node.
42 |
43 | .. note:: If ``r`` is empty, ``roots[r] = leaves[r] = Node``. If ``r`` is undirected or contains enough self loops, ``roots[r] = leaves[r] =`` :ref:`none `.
44 |
45 |
46 | .. als:function:: innerNodes[r: node-> node]
47 |
48 | :return: All nodes that aren't leaves
49 | :rtype: ``set Node``
50 |
51 |
52 | Predicates
53 | ------------
54 |
55 | .. als:predicate:: undirected [r: node->node]
56 |
57 | ``r`` is :als:pred:`symmetric`.
58 |
59 | .. als:predicate:: noSelfLoops[r: node->node]
60 |
61 | ``r`` is :als:pred:`irreflexive`.
62 |
63 | .. als:predicate:: weaklyConnected[r: node->node]
64 |
65 | For any two nodes A and B, there is a path from A to B or a path from B to A. The path may not necessarily be bidirectional.
66 |
67 | .. als:predicate:: stronglyConnected[r: node->node]
68 |
69 | For any two nodes A and B, there is a path from A to B *and* a path from B to A.
70 |
71 | .. als:predicate:: rootedAt[r: node->node, root: node]
72 |
73 | All nodes are reachable from ``root``.
74 |
75 | .. warning:: this is *not* the same meaning of *root* as in the `roots` function! For the function, a *root* is a node no node connects to. Interally, ``util/graph`` uses ``rootedAt`` and not ``roots``.
76 |
77 | .. als:predicate:: ring [r: node->node]
78 |
79 | ``r`` forms a single cycle.
80 |
81 | .. als:predicate:: dag [r: node->node]
82 |
83 | ``r`` is a :abbr:`dag (directed acyclic graph)`: there are no self-loops in the transitive closure.
84 |
85 | .. als:predicate:: forest [r: node->node]
86 |
87 | ``r`` is a dag and every node has at most one parent.
88 |
89 | .. als:predicate:: tree [r: node->node]
90 |
91 | ``r`` is a forest with a single root node.
92 |
93 | .. als:predicate:: treeRootedAt[r: node->node, root: node]
94 |
95 | ``r`` is a tree with node ``root``.
96 |
--------------------------------------------------------------------------------
/modules/index.rst:
--------------------------------------------------------------------------------
1 | .. _utils:
2 |
3 | Modules
4 | =======
5 |
6 | .. toctree::
7 | :glob:
8 |
9 | *
10 |
--------------------------------------------------------------------------------
/modules/integer.rst:
--------------------------------------------------------------------------------
1 | .. als:module:: integer
2 |
3 |
4 | +++++++++++++++
5 | integer
6 | +++++++++++++++
7 |
8 | Emulates integers.
9 |
10 | A collection of utility functions for using Integers in Alloy. Note that integer
11 | overflows are silently truncated to the current bitwidth using the 2's complement
12 | arithmetic, unless the ''forbid overflows'' option is turned on, in which case only
13 | models that do not have any overflows are analyzed.
14 |
15 | .. warning::
16 |
17 | The main challenge with this module is the distinction between ``Int`` and ``int``. ``Int`` is the set of integers that have been instantiated, whereas ``int`` returns the value of an ``Int``. You have to explicitly write ``int i`` to be able to add, subtract, and compare ``Ints``.
18 |
19 |
20 | .. code:: alloy
21 |
22 | open util/integer
23 |
24 | fact ThreeExists { // there is some integer whose value is 3
25 | some x: Int | int x = 3
26 | }
27 |
28 | fun add[a, b: Int]: Int {
29 | {i: Int | int i = int a + int b}
30 | }
31 |
32 | run add for 10 but 3 int expect 1
33 |
34 | To try this module out, in Alloy Analyzer's evaluator, you may also issue the
35 | following commands (suppose that allow generated a set with numbers ranging
36 | from ``-8`` to ``7``):
37 |
38 | .. code:: alloy
39 |
40 | 1 + 3
41 | 4
42 |
43 | 7 + 1
44 | -8
45 |
46 |
47 | Functions
48 | ==========
49 |
50 | .. als:function:: add [n1, n2: Int]
51 |
52 | :rtype: ``one Int``
53 |
54 | Returns ``n1 + n2``.
55 |
56 | .. als:function:: plus [n1, n2: Int]
57 |
58 | :rtype: ``one Int``
59 |
60 | Returns ``n1 + n2``.
61 |
62 | .. als:function:: sub [n1, n2: Int]
63 |
64 | :rtype: ``one Int``
65 |
66 | Returns ``n1 - n2``.
67 |
68 | .. als:function:: minus [n1, n2: Int]
69 |
70 | :rtype: ``one Int``
71 |
72 | Returns ``n1 - n2``.
73 |
74 | .. als:function:: mul [n1, n2: Int]
75 |
76 | :rtype: ``one Int``
77 |
78 | Returns ``n1 * n2``.
79 |
80 | .. als:function:: div [n1, n2: Int]
81 |
82 | :rtype: ``one Int``
83 |
84 | Returns the division with ''round to zero'' semantics, except the following
85 | 3 cases:
86 |
87 | * if a is 0, then it returns 0
88 |
89 | * else if b is 0, then it returns 1 if a is negative and -1 if a is positive
90 |
91 | * else if a is the smallest negative integer, and b is -1, then it returns a
92 |
93 | .. als:function:: rem [n1, n2: Int]
94 |
95 | :rtype: ``one Int``
96 |
97 | Returns the unique integer that satisfies ``a = ((a/b)*b) + remainder``.
98 |
99 | .. als:function:: negate [n: Int]
100 |
101 | :rtype: ``one Int``
102 |
103 | Returns the negation of n.
104 |
105 | .. als:function:: signum [n: Int]
106 |
107 | :rtype: ``one Int``
108 |
109 | Returns the signum of n (aka sign or sgn). In particular,
110 | ``n < 0 => ( 0 - 1 ) else ( n > 0 => 1 else 0 )``.
111 |
112 | .. als:function:: int2elem [i: Int, next: univ->univ, s: set univ]
113 |
114 | :rtype: ``lone s``
115 |
116 | Returns the ith element (zero-based) from the ``set s``
117 | in the ordering of ``next``, which is a linear ordering
118 | relation like that provided by :als:mod:`ordering`.
119 |
120 | .. als:function:: elem2int [e: univ, next: univ->univ]
121 |
122 | :rtype: ``lone Int``
123 |
124 | Returns the index of the element (zero-based) in the
125 | ordering of next, which is a linear ordering relation
126 | like that provided by :als:mod:`ordering`.
127 |
128 | .. als:function:: max
129 |
130 | :rtype: ``one Int``
131 |
132 | Returns the largest integer in the current bitwidth.
133 |
134 | .. als:function:: min
135 |
136 | :rtype: ``one Int``
137 |
138 | Returns the smallest integer in the current bitwidth.
139 |
140 | .. als:function:: next
141 |
142 | :rtype: ``Int -> Int``
143 |
144 | Maps each integer (except max) to the integer after it.
145 |
146 | .. als:function:: prev
147 |
148 | :rtype: ``Int -> Int``
149 |
150 | Maps each integer (except min) to the integer before it.
151 |
152 | .. als:function:: max [es: set Int]
153 |
154 | :rtype: ``lone Int``
155 |
156 | Given a set of integers, return the largest element.
157 |
158 | .. als:function:: min [es: set Int]
159 |
160 | :rtype: ``lone Int``
161 |
162 | Given a set of integers, return the smallest element.
163 |
164 | .. als:function:: prevs [e: Int]
165 |
166 | :rtype: ``set Int``
167 |
168 | Given an integer, return all integers prior to it.
169 |
170 | .. als:function:: nexts [e: Int]
171 |
172 | :rtype: ``set Int``
173 |
174 | Given an integer, return all integers following it.
175 |
176 | .. als:function:: larger [e1, e2: Int]
177 |
178 | :rtype: ``Int``
179 |
180 | Returns the larger of the two integers.
181 |
182 | .. als:function:: smaller [e1, e2: Int]
183 |
184 | :rtype: ``Int``
185 |
186 | Returns the smaller of the two integers.
187 |
188 | Predicates
189 | ==========
190 |
191 | .. als:predicate:: eq [n1, n2: Int]
192 |
193 | ``True`` iff n1 is equal to n2.
194 |
195 | .. als:predicate:: gt [n1, n2: Int]
196 |
197 | ``True`` iff n1 is greater than n2.
198 |
199 | .. als:predicate:: gte [n1, n2: Int]
200 |
201 | ``True`` iff n1 is greater than or equal to n2.
202 |
203 | .. als:predicate:: lt [n1, n2: Int]
204 |
205 | ``True`` iff n1 is less than n2.
206 |
207 | .. als:predicate:: lte [n1, n2: Int]
208 |
209 | ``True`` iff n1 is less than or equal to n2.
210 |
211 | .. als:predicate:: zero [n: Int]
212 |
213 | ``True`` iff n is equal to ``0``.
214 |
215 | .. als:predicate:: pos [n: Int]
216 |
217 | ``True`` iff n is positive.
218 |
219 | .. als:predicate:: neg [n: Int]
220 |
221 | ``True`` iff n is negative.
222 |
223 | .. als:predicate:: nonpos [n: Int]
224 |
225 | ``True`` iff n is non-positive.
226 |
227 | .. als:predicate:: nonneg [n: Int]
228 |
229 | ``True`` iff n is non-negative.
230 |
--------------------------------------------------------------------------------
/modules/natural.rst:
--------------------------------------------------------------------------------
1 | .. als:module:: naturals
2 |
3 |
4 | +++++++++++++++
5 | naturals
6 | +++++++++++++++
7 |
8 | Emulates natural (non-negative) numbers.
9 |
10 | This is an utility with functions and predicates for using the set of
11 | nonnegative integers (``0, 1, 2, . . .``). The number of naturals present
12 | in an analysis will be equal to the scope on Natural. Specifically, if
13 | the scope on Natural is ``N``, then the naturals ``0`` through ``N-1`` will
14 | be present.
15 |
16 | .. code:: alloy
17 |
18 | open util/natural
19 |
20 | fun sum[a: Natural, b: Natural]: Natural {
21 | {x:Natural | x = natural/add[a,b]}
22 | }
23 |
24 | run show for 3
25 |
26 | To try this module out, in Alloy Analyzer's evaluator, you may invoke the
27 | function defined above as follows:
28 |
29 | .. code:: none
30 |
31 | sum [natural/Natural1, natural/Natural1]
32 | {natural/Natural$2}
33 |
34 | sum [natural/Natural1, natural/Natural2]
35 | {}
36 |
37 |
38 | Functions
39 | ==========
40 |
41 | .. als:function:: inc [n: Natural]
42 |
43 | :rtype: ``one Natural``
44 |
45 | Returns ``n + 1``.
46 |
47 | .. als:function:: dec [n: Natural]
48 |
49 | :rtype: ``one Natural``
50 |
51 | Returns ``n - 1``.
52 |
53 |
54 | .. als:function:: add [n1, n2: Natural]
55 |
56 | :rtype: ``one Natural``
57 |
58 | Returns ``n1 + n2``.
59 |
60 | .. als:function:: sub [n1, n2: Natural]
61 |
62 | :rtype: ``one Natural``
63 |
64 | Returns ``n1 - n2``.
65 |
66 | .. als:function:: mul [n1, n2: Natural]
67 |
68 | :rtype: ``one Natural``
69 |
70 | Returns ``n1 * n2``.
71 |
72 | .. als:function:: div [n1, n2: Natural]
73 |
74 | :rtype: ``one Natural``
75 |
76 | Returns ``n1 / n2``.
77 |
78 | .. als:function:: max [ns: set Natural]
79 |
80 | :rtype: ``one Natural``
81 |
82 | Returns the maximum integer in ns.
83 |
84 | .. als:function:: min [ns: set Natural]
85 |
86 | :rtype: ``one Natural``
87 |
88 | Returns the minimum integer in ns.
89 |
90 |
91 | Predicates
92 | ==========
93 |
94 | .. als:predicate:: gt [n1, n2: Natural]
95 |
96 | ``True`` iff n1 is greater than n2.
97 |
98 | .. als:predicate:: gte [n1, n2: Natural]
99 |
100 | ``True`` iff n1 is greater than or equal to n2.
101 |
102 | .. als:predicate:: lt [n1, n2: Natural]
103 |
104 | ``True`` iff n1 is less than n2.
105 |
106 | .. als:predicate:: lte [n1, n2: Natural]
107 |
108 | ``True`` iff n1 is less than or equal to n2.
109 |
110 |
--------------------------------------------------------------------------------
/modules/ordering.rst:
--------------------------------------------------------------------------------
1 | .. als:module:: ordering
2 |
3 | ++++++++
4 | ordering
5 | ++++++++
6 |
7 | Ordering places an ordering on the parameterized signature.
8 |
9 | ::
10 |
11 | open util/ordering[A]
12 |
13 | sig A {}
14 |
15 | run {
16 | some first -- first in ordering
17 | some last -- last in ordering
18 | first.lt[last]
19 | }
20 |
21 |
22 | ``ordering`` can only be instantiated once per signature. You can, however, call it for two different signatures::
23 |
24 | open util/module[Thing1] as u1
25 | open util/module[Thing2] as u2
26 |
27 | sig Thing1 {}
28 | sig Thing2 {}
29 |
30 |
31 | .. warning::
32 |
33 | ``ordering`` forces the signature to be `exact `. This means that the following model has no instances::
34 |
35 | open util/ordering[S]
36 |
37 | sig S {}
38 |
39 | run {#S = 2} for 3
40 |
41 | In particular, be careful when using ``ordering`` as part of an assertion: the assertion may pass because of the implicit constraint!
42 |
43 | Functions
44 | ---------
45 |
46 | .. als:function:: first
47 |
48 | :return: The first element of the ordering
49 | :rtype: ``elem``
50 | :also: ``last``
51 |
52 | .. als:function:: prev
53 |
54 | :rtype: ``elem -> elem``
55 | :also: ``next``
56 |
57 | Returns the relation mapping each element to its previous element. This means it can be used as any other kind of relation::
58 |
59 | fun is_first[e: elem] {
60 | no e.prev
61 | }
62 |
63 |
64 | .. als:function:: prevs[e]
65 |
66 | :return: All elements before ``e``, excluding ``e``.
67 | :rtype: ``elem``
68 | :also: ``nexts``
69 |
70 |
71 | .. als:function:: smaller[e1, e2: elem]
72 |
73 | :return: the element that comes first in the ordering
74 | :also: larger
75 |
76 | .. als:function:: min[es: set elem]
77 |
78 | :return: The smallest element in ``es``, or the empty set if ``es`` is empty
79 | :rtype: ``lone elem``
80 | :also: max
81 |
82 |
83 | Predicates
84 | -------------
85 |
86 |
87 | .. als:predicate:: lt[e1, e2: elem]
88 |
89 | :also: ``gt``, ``lte``, ``gte``
90 |
91 | True iff ``e1 in prevs[e2]``.
92 |
93 |
--------------------------------------------------------------------------------
/modules/relation.rst:
--------------------------------------------------------------------------------
1 | .. module:: relation
2 |
3 | ===========
4 | relation
5 | ===========
6 |
7 | All functions and predicates in this module apply to any binary relation. `univ ` is the set of all atoms in the model.
8 |
9 | Functions
10 | ---------
11 |
12 | .. als:function:: dom[r: univ->univ]
13 |
14 | :rtype: ``set univ``
15 |
16 | Returns the domain of ``r``. Equivalent to ``univ.~r``.
17 |
18 | .. als:function:: ran[r: univ->univ]
19 |
20 | :rtype: ``set univ``
21 |
22 | Returns the range of ``r``. Equivalent to ``univ.r``.
23 |
24 |
25 | Predicates
26 | ----------
27 |
28 | .. als:predicate:: total[r: univ->univ, s: set]
29 |
30 | True iff every element of ``s`` appears in ``dom[r]``.
31 |
32 | .. als:predicate:: functional[r: univ->univ, s: set univ]
33 |
34 | True iff every element of ``s`` appears *at most once* in the left-relations of ``r``.
35 |
36 | .. als:predicate:: function[r: univ->univ, s: set univ]
37 |
38 | True iff every element of ``s`` appears *exactly once* in the left-relations of ``r``.
39 |
40 | .. als:predicate:: surjective[r: univ->univ, s: set univ]
41 |
42 | True iff ``s in ran[r]``.
43 |
44 | .. als:predicate:: injective[r: univ->univ, s: set univ]
45 |
46 | True iff no two elements of ``dom[r]`` map to the same element in ``s``.
47 |
48 | .. als:predicate:: bijective[r: univ->univ, s: set univ]
49 |
50 | True iff every element of ``s`` is mapped to by *exactly one* relation in ``r``. This is equivalent to being both injective and surjective. There may be relations that map to elements outside of ``s``.
51 |
52 | .. als:predicate:: bijection[r: univ->univ, d, c: set univ]
53 |
54 | True iff exactly ``r`` bijects ``d`` to ``c``.
55 |
56 | .. todo:: I think the definition is actually wrong. It's either too strict or too loose. Both of these asserts fail
57 |
58 | .. code:: alloy
59 |
60 | open util/relation
61 |
62 | sig B {}
63 | sig C {}
64 | sig A {
65 | rel: set B + C
66 | }
67 |
68 | assert {
69 | bijection[rel, A, B] => #A = #B
70 | }
71 |
72 | assert {
73 | bijective[rel :> B, B] => bijection[rel, A, B]
74 | }
75 |
76 | .. als:predicate:: reflexive[r: univ -> univ, s: set univ]
77 |
78 | ``r`` maps every element of ``s`` to itself.
79 |
80 | .. als:predicate:: irreflexive[r: univ -> univ]
81 |
82 | ``r`` does not map any element to itself.
83 |
84 | .. als:predicate:: symmetric[r: univ -> univ]
85 |
86 | ``A -> B in r implies B -> A in r``
87 |
88 | .. als:predicate:: antisymmetric[r: univ -> univ]
89 |
90 | ``A -> B in r implies B -> A notin r``. This is stronger than ``not symmetric``: *no* subset of ``r`` can be symmetric either.
91 |
92 | .. als:predicate:: transitive[r: univ -> univ]
93 |
94 | ``A -> B in r and B - > C in r implies A -> C in r``
95 |
96 | .. als:predicate:: acyclic[r: univ->univ, s: set univ]
97 |
98 | ``r`` has no cycles that have elements of ``s``.
99 |
100 |
101 | .. als:predicate:: complete[r: univ->univ, s: univ]
102 |
103 | ``all x,y:s | (x!=y => x->y in (r + ~r))``
104 |
105 | .. als:predicate:: preorder[r: univ -> univ, s: set univ]
106 |
107 | ``reflexive[r, s] and transitive[r]``
108 |
109 | .. todo:: this might also be wrong, shouldn't it only be if r is transitive over s?
110 |
111 | .. als:predicate:: equivalence[r: univ->univ, s: set univ]
112 |
113 | ``r`` is reflexive, transitive, and symmetric over s.
114 |
115 | .. als:predicate:: partialOrder[r: univ -> univ, s: set univ]
116 |
117 | ``r`` is a partial order over the set ``s``: ``preorder[r, s] and antisymmetric[r]``
118 |
119 | .. als:predicate:: totalOrder[r: univ -> univ, s: set univ]
120 |
121 | ``r`` is a total order over the set ``s``: ``partialOrder[r, s] and complete[r, s]``
122 |
--------------------------------------------------------------------------------
/modules/ternary.rst:
--------------------------------------------------------------------------------
1 | .. module:: ternary
2 |
3 | +++++++
4 | ternary
5 | +++++++
6 |
7 | ``util/ternary`` provides utility functions for working with 3-arity `multirelations`. All functions return either an element in the relation or a new transformed relation.
8 |
9 | .. list-table:: ``util/ternary``
10 | :header-rows: 1
11 |
12 | * - ``f``
13 | - ``f[a -> b -> c]``
14 | * - ``dom``
15 | - ``a``
16 | * - ``mid``
17 | - ``b``
18 | * - ``ran``
19 | - ``c``
20 | * - ``select12``
21 | - ``a -> b``
22 | * - ``select13``
23 | - ``a -> c``
24 | * - ``select23``
25 | - ``b -> c``
26 | * - ``flip12``
27 | - ``b -> a -> c``
28 | * - ``flip13``
29 | - ``c -> b -> a``
30 | * - ``flip23``
31 | - ``a -> c -> b``
32 |
33 |
--------------------------------------------------------------------------------
/modules/time.rst:
--------------------------------------------------------------------------------
1 | .. als:module:: time
2 |
3 | +++++++++++++
4 | time (legacy)
5 | +++++++++++++
6 |
7 | Automatically imports an ordered ``Time`` signature to your spec. This used to be used for modeling time before Alloy 6, but now there is a :ref:`native feature ` for it. This module is now deprecated, but you may still see older specifications that use it.
8 |
9 | .. warning::
10 |
11 | ``Time`` internally uses the :mod:`ordering` module. This means that the signature is forced to be exact.
12 |
13 | .. seealso::
14 |
15 | Module :mod:`ordering`
16 |
17 | Macros
18 | ----------
19 |
20 | .. als:macro:: dynamic[x]
21 |
22 | :arg sig x: any signature.
23 | :expansion: ``x one -> Time``
24 |
25 | ``dynamic`` can be used as part of a signature definition::
26 |
27 | open util/time
28 |
29 | abstract sig Color {}
30 | one sig Red, Green, Yellow extends Color {}
31 |
32 | sig Light {
33 | , state: dynamic[Color]
34 | }
35 |
36 | At every ``Time``, every ``Light`` will have exactly one color.
37 |
38 |
39 |
40 | .. als:macro:: dynamicSet[x]
41 |
42 | :arg sig x: any signature.
43 | :expansion: ``x -> Time``
44 |
45 | Equivalent to ``dynamic``, except that any number of elements can belong to any given time::
46 |
47 | open util/time
48 |
49 | sig Keys {}
50 |
51 | one sig Keyboard {
52 | pressed: dynamicSet[Keys]
53 | }
54 |
55 |
56 |
57 | ..
58 | .. rst-class:: advanced
59 |
60 | Then
61 | --------------
62 |
63 | .. todo:: Define step predicates first (in techniques, maybe?) (Call them actions)
64 | .. als:macro:: then[a, b, start, finish]
65 |
66 | :arg pred[Time,Time] a: the initial event
67 | :arg pred[Time,Time] b: the subsequent event
68 | :arg Time start:
69 | :arg Time finish:
70 | :expansion: ``some x:Time | a[start,x] && b[x,finish]``
71 |
72 | Permits the "chaining" of time steps. ``then`` is intended to be used as part of receiver syntax:
73 |
74 | ::
75 | fun cycle: set Color -> Color {
76 | (Red -> Green) + (Green -> Yellow) + (Yellow -> Red)
77 | }
78 |
79 | pred change[t, t': Time] {
80 | Light.state.t' = (Light.state.t).cycle
81 | t' = t.next
82 | }
83 |
84 | pred break[t, t': Time] {
85 | Light.state.t' = Red
86 | t' = t.next
87 | }
88 |
89 | run {
90 | some t: Time | change.then[break] [first, t]
91 | }
92 |
93 |
94 |
95 | let while = while3
96 |
97 | Equivalent to ``body.then[body].then[body]...`` up to three times or until ``cond[t]`` is true. Recall that every ``body`` should have ``t' = t.next``.
98 | let while1 [cond, body, t, t'] {
99 | some x:Time | (cond[t] => body[t,x] else t=x) && while0[cond,body,x,t']
100 | }
101 |
102 | let while0 [cond, body, t, t'] {
103 | !cond[t] && t=t'
104 | }
105 |
106 | ::
107 |
108 | open util/ordering[Time]
109 | sig Time { }
110 | let then [a, b, t, t'] { some x:Time | a[t,x]&& b[x,t'] }
111 |
112 | one sig Light { brightness: Int one-> Time }
113 |
114 | pred brighter [t, t': Time] {
115 | Light.brightness.t' = Light.brightness.t.plus[1]
116 | t' = t.next
117 | }
118 |
119 | pred dimmer [t, t': Time] {
120 | Light.brightness.t' = Light.brightness.t.minus[1]
121 | t' = t.next
122 | }
123 |
124 | run {
125 | some t:Time | brighter.then[dimmer].then[dimmer] [first, t]
126 | } for 4 Time
127 |
128 | While
129 | ---------
130 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Alloydocs
2 |
3 | This is the repository for development of the [Alloy](http://www.alloytools.org/) documentation. The documentation is currently hosted [here](https://alloy.readthedocs.io/en/latest/index.html) and the source lives [here](https://github.com/hwayne/alloydocs). This readme is for people who are interested in building and contributing it.
4 |
5 | ## Building
6 |
7 |
8 | The docs currently run on Sphinx. You can install Sphinx with [these instructions](https://www.sphinx-doc.org/en/master/usage/installation.html). You will also need the theme:
9 |
10 | ```
11 | python -m pip install sphinx-rtd-theme
12 | ```
13 |
14 | Docs can be built with ``make html``. Single file PDF version can be built with ``make latexpdf`` (requires LaTeX installed).
15 |
16 | ## Development
17 |
18 | The repo is [standard Sphinx](https://www.sphinx-doc.org/en/master/intro.html), using reStructured Text. Some things might use a bit too much rst as I was getting a feel for what worked and didn't here.
19 |
20 | There are two customizations:
21 |
22 | ### Custom Alloy Domain
23 |
24 | Under `utils/alloy.py`. It has roles for functions, predicates, and properly indexes them. Sphinx's standard "this is a function" flag will add parens to docs, while Alloy uses brackets. So we make the brackets part of the function title and then split them off while indexing.
25 |
26 | **TODO:** To speed up writing documentation I added an `:also:` param to functions that have "obvious" analogs. For example:
27 |
28 | ```rst
29 | .. als:predicate:: lt[e1, e2: elem]
30 |
31 | :also: ``gt``, ``lte``, ``gte``
32 |
33 | True iff ``e1 in prevs[e2]``.
34 | ```
35 |
36 | Functions defined in `:also:` don't have cross-references or indexes. We should change that.
37 |
38 | **TODO:** Currently the `alloy.py` is pretty rickety and should be cleaned up/made more idiomatic.
39 |
40 | ### advanced class
41 |
42 | Some sections are marked as "Advanced":
43 |
44 | ```rst
45 | .. rst-class:: advanced
46 | .. _enums:
47 |
48 | Enums
49 | -----
50 | ```
51 |
52 | Advanced sections have an indicator before them, currently `[⋇] `, that marks them as unnecessary for beginners to learn. I have two **TODOs** I want to add:
53 |
54 | 1. A tooltip saying "this is an advanced section" so people know what it means without having read the intro
55 | 1. A button that toggles whether the advanced sections are visible at all
56 |
57 |
58 | ## Other Misc Tasks
59 |
60 | * The reference currently isn't comprehensive.
61 | * Cross references are a bit inconsistent. Some are hyphen-cased, some use spaces. Both are valid, but I'd like to move everything to hyphens for consistency.
62 | * The location of certain topics might move around.
63 | * There are many todos in the docs. These are not visible in the "proper" version, but are there for internal discussion.
64 |
65 | ## Contributing
66 |
67 | TODO
68 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | alabaster==0.7.16
2 | Babel==2.14.0
3 | certifi==2024.2.2
4 | charset-normalizer==3.3.2
5 | colorama==0.4.6
6 | docutils==0.20.1
7 | idna==3.6
8 | imagesize==1.4.1
9 | Jinja2==3.1.3
10 | MarkupSafe==2.1.5
11 | packaging==23.2
12 | Pygments==2.17.2
13 | requests==2.31.0
14 | snowballstemmer==2.2.0
15 | Sphinx==7.2.6
16 | sphinx-rtd-theme==2.0.0
17 | sphinxcontrib-applehelp==1.0.8
18 | sphinxcontrib-devhelp==1.0.6
19 | sphinxcontrib-htmlhelp==2.0.5
20 | sphinxcontrib-jquery==4.1
21 | sphinxcontrib-jsmath==1.0.1
22 | sphinxcontrib-qthelp==1.0.7
23 | sphinxcontrib-serializinghtml==1.1.10
24 | urllib3==2.2.1
25 |
--------------------------------------------------------------------------------
/techniques/boolean-fields.rst:
--------------------------------------------------------------------------------
1 |
2 | ++++++++++++++
3 | Boolean Fields
4 | ++++++++++++++
5 |
6 | Often software properties are expressed as booleans, either true or false. But Alloy doesn't have a native boolean type. There are two ways to emulate this.
7 |
8 | For this example we want something akin to
9 |
10 | .. code::
11 |
12 | sig Account {
13 | premium: bool -- invalid, not a real type
14 | }
15 |
16 |
17 | .. _boolean-subtyping:
18 |
19 | Subtyping
20 | =========
21 |
22 | Using `in ` subtypes is the standard way to model booleans::
23 |
24 | sig Account {}
25 | sig PremiumAccount in Account {}
26 |
27 | Then the boolean can be tested with ``a in PremiumAccount``. This method has a number of advantages:
28 |
29 | * ``PremiumAccount`` and ``Account - PremiumAccount`` can be used in the signatures of other fields.
30 |
31 | * Premium accounts can have additional fields.
32 |
33 | * You can create a custom `theme ` for premium accounts.
34 |
35 | * The analyzer will generally be faster.
36 |
37 |
38 | lone fields
39 | ----------------
40 |
41 | Booleans can also be represented using ``lone``::
42 |
43 | one sig Premium {}
44 | sig Account {
45 | , premium: lone Premium
46 | }
47 |
48 | Then the boolean can be tested with ``some a.premium``, and the set of all premium accounts is ``premium.Premium``. Using lone is somewhat simpler than using a subtype, but it's less flexible overall, and the boolean cannot be used in the fields of other signatures.
49 |
50 |
51 |
52 | The ``Boolean`` module
53 | ---------------------------
54 |
55 | If subtyping is insufficient, you can also use the :mod:`boolean` module. This is generally not recommended.
56 |
--------------------------------------------------------------------------------
/techniques/dynamics.rst:
--------------------------------------------------------------------------------
1 | .. _dynamics:
2 |
3 | +++++++++++++++++++++
4 | Legacy Dynamic Models
5 | +++++++++++++++++++++
6 |
7 | A **dynamic model** represents something changing over time. As of Alloy 6, you can model dynamic systems with the :ref:`time` functions. This page is about how dynamics *used* to be modeled in Alloy, in case you need to read an old spec.
8 |
9 | ---
10 |
11 | Dynamics must be encoded as part of the relationships. This is normally done by placing an `ordering` on some signature, which then used to emulate time. There are several common ways of doing this.
12 |
13 | .. seealso::
14 |
15 | :mod:`time`
16 |
17 | A module with some utility macros to better model time.
18 |
19 |
20 | Changing Object Signature
21 | ============================
22 |
23 | When only one entity is changing and there is only one instance of the signature to consider, we can place the ordering on the signature itself. Then each atom of that signature represents the state of the entity at a different point in time.
24 |
25 | .. literalinclude:: specs/dynamics/light.als
26 | :caption: Full spec downloadable :download:`here `
27 | :lines: 1-9
28 |
29 | While there are multiple ``Light`` atoms, they all represent the same physical "traffic light", just at different points in time.
30 |
31 | The dynamic model is modeled by a state change predicate, which relates each light to the next light in the sequence.
32 |
33 |
34 | .. literalinclude:: specs/dynamics/light.als
35 | :caption: Full spec downloadable :download:`here `
36 | :lines: 10-18
37 |
38 | Conventionally `ordering` is *not* namespaced. We do it here to make the imported functions clearer.
39 |
40 | .. _trace:
41 |
42 | Traces
43 | --------------
44 |
45 | A :dfn:`trace` is a `fact ` that describes how the system will evolve, by constraining "valid models" to ones where the system evolves properly.
46 |
47 | .. trace
48 |
49 | .. literalinclude:: specs/dynamics/light.als
50 | :caption: Full spec downloadable :download:`here `
51 | :lines: 20-25
52 |
53 | `first` and ``last`` are from the `ordering` module. This sets the first light to red and every subsequent light to the next color in the chain.
54 |
55 |
56 | .. note:: We write ``Light - last`` because ``no last.next``, so the predicate would be false.
57 |
58 | Time Signatures
59 | ======================
60 |
61 | For more complex specifications we use a ``Time`` signature. This is useful if we have multiple things that are changing or multiple properties that can change. The ordering is placed on ``Time`` and the signature fields are all related to that time.
62 |
63 | .. literalinclude:: specs/dynamics/keyboard.als
64 | :caption: Full spec downloadable :download:`here `
65 | :lines: 1-3
66 |
67 | .. _dynamic-booleans:
68 |
69 | Representing Boolean Properties
70 | ---------------------------------
71 |
72 | If the changing value is a boolean, the best way to represent that is to have a field that is ``set Time``, which represents the times where that field is true:
73 |
74 | .. literalinclude:: specs/dynamics/keyboard.als
75 | :caption: Full spec downloadable :download:`here `
76 | :lines: 5-21
77 |
78 | We will assume that only one key can be pressed or released at a given time. This means that the trace must specify that some key changes *and also* that no other key changes.
79 |
80 | .. literalinclude:: specs/dynamics/keyboard.als
81 | :caption: Full spec downloadable :download:`here `
82 | :lines: 22-
83 |
84 | .. note:: We could have instead written it this way, using ``one`` instad of ``some``:
85 |
86 | .. code:: alloy
87 |
88 | all t: Time - last |
89 | one k: Key |
90 | changed[k, t]
91 |
92 | Which would have enforced that no other keys changed by default. Using ``one`` in these contexts can be error-prone, so it's avoided for the purposes of this page.
93 |
94 | Conventionally, state-change predicates are written to take two time parameters, where the trace then passes in both ``t`` and ``t.next``.
95 |
96 | ::
97 |
98 | pred release[k: Key, t, t": Time] {
99 | t in k.pressed
100 | t" not in k.pressed
101 | }
102 |
103 |
104 | Representing Arbitrary Properties
105 | ---------------------------------
106 |
107 | Information beyond booleans can be encoded with `multirelations`.
108 |
109 | .. todo:: break this up and explain what's going on
110 |
111 | .. literalinclude:: specs/dynamics/browsing.als
112 | :caption: Full spec downloadable :download:`here `
113 | :lines: 1-12
114 |
115 | Writing ``Page one -> Time`` indicates that for every User and Time, there is exactly one page. Writing ``Page -> Time`` also any number of pages per user/time.
116 |
117 | .. note:: There's no innate reason why we use ``Page -> Time`` instead of ``Time -> Page``. However, making Time the end of the multirelation is conventional.
118 |
119 |
120 | .. literalinclude:: specs/dynamics/browsing.als
121 | :caption: Full spec downloadable :download:`here `
122 | :lines: 13-28
123 |
124 | When using multirelations of form ``rel = A -> B -> Time``, we get the value of ``b`` at time ``t`` with ``a.rel.t``
125 |
126 | .. literalinclude:: specs/dynamics/browsing.als
127 | :caption: Full spec downloadable :download:`here `
128 | :lines: 29-33
129 |
130 | ``stay`` is a "stuttering" predicate which makes it valid for a user to not change at the next time step. Without it ``goto`` would have to be true for every single user at every time in the trace.
131 |
132 | .. literalinclude:: specs/dynamics/browsing.als
133 | :caption: Full spec downloadable :download:`here `
134 | :lines: 34-
135 |
136 |
137 | Common Issues
138 | ======================
139 |
140 |
141 | Incomplete Trace
142 | --------------------------------
143 |
144 | The trace must fully cover what happens to all dynamic signatures. If not, weird things happen. Consider the following alternate trace for the browsing model:
145 |
146 | ::
147 |
148 | fact Trace {
149 | at.first = history.first -- everybody starts with their initial page in history
150 | all t: Time - last |
151 | let t" = t.next {
152 | one u: User |
153 | u.stay[t, t"] or
154 | some p: Page | u.goto[p, t, t"]
155 | }
156 | }
157 |
158 | This is true iff *exactly one* User either stays or goes to a valid page. This allows them to do anything that's not covered by the two predicates. A user may go to an unlinked page, or stay on the same page but change their history to include a page they never visited.
159 |
160 | "No Models Found"
161 | --------------------------------
162 |
163 |
164 | ``ordering`` implicity makes the ordered signature `exact ` and this cannot be overridden. If a dynamic spec does not exist, it's likely due to this.
165 |
166 | ::
167 |
168 | open util/ordering[State]
169 |
170 | sig State {}
171 |
172 | run {#State = 2} -- no models
173 |
174 |
175 | .. todo::
176 | 1. Cases where multiple things relating to each other interact, like the ring election spec. So the predicates step on each others toes.
177 | 2. Writing temporal assertions
178 |
179 | Limitations
180 | ======================
181 |
182 | There are some limitations to what we can model in a dynamic system.
183 |
184 | * Alloy cannot tell if a system has **deadlocked**. A deadlock is when there is no valid next state as part of the trace. If the trace is encoded as a fact, then the entire model is discarded. If the trace is encoded as a predicate, Alloy will provide any model that doesn't match the trace as a counterexample.
185 | * Alloy cannot test that some property is guaranteed to happen in infinite time, aka **liveness**.
186 | * Alloy cannot emulate **fair** dynamic systems.
187 |
188 |
189 |
190 | .. todo:: representing with a Seq
191 |
--------------------------------------------------------------------------------
/techniques/index.rst:
--------------------------------------------------------------------------------
1 | Techniques
2 | ==============
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 |
7 |
8 | boolean-fields
9 | dynamics
10 |
11 |
--------------------------------------------------------------------------------
/techniques/specs/dynamics/browsing.als:
--------------------------------------------------------------------------------
1 | open util/ordering[Time]
2 |
3 | sig Time {}
4 |
5 | sig User {
6 | -- current place in time
7 | , at: Page one -> Time
8 |
9 | -- all visited pages
10 | , history: Page -> Time
11 | }
12 |
13 | sig Page {
14 | -- pages reachable from this page
15 | link: set Page
16 | }
17 |
18 | pred goto[u: User, p: Page, t, t": Time] {
19 | -- valid page to change to
20 | p in u.at.t.link
21 |
22 | -- change pages
23 | p = u.at.t"
24 |
25 | -- save new page in history
26 | u.history.t" = u.history.t + p
27 | }
28 |
29 | pred stay[u: User, t, t": Time] {
30 | u.at.t" = u.at.t
31 | u.history.t" = u.history.t
32 | }
33 |
34 | fact trace {
35 | -- everybody starts with their initial page in history
36 | at.first = history.first
37 | all t: Time - last |
38 | let t" = t.next {
39 | all u: User |
40 | u.stay[t, t"] or
41 | some p: Page | u.goto[p, t, t"]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/techniques/specs/dynamics/keyboard-table.als:
--------------------------------------------------------------------------------
1 | open util/ordering[Time]
2 |
3 | sig Time {
4 | pressed: set Key
5 | }
6 |
7 | sig Key {
8 | }
9 |
10 | pred press[k: Key, t: Time] {
11 | k not in t.pressed
12 | k in t.next.pressed
13 | }
14 |
15 | pred release[k: Key, t: Time] {
16 | k in t.pressed
17 | k not in t.next.pressed
18 | }
19 |
20 | pred changed[k: Key, t: Time] {
21 | press[k, t] or release[k, t]
22 | }
23 |
24 | face Trace {
25 | no first.pressed
26 | all t: Time - last |
27 | some k: Key {
28 | changed[k, t]
29 | all k": Key - k |
30 | not changed[k, t]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/techniques/specs/dynamics/keyboard.als:
--------------------------------------------------------------------------------
1 | open util/ordering[Time]
2 |
3 | sig Time {}
4 |
5 | sig Key {
6 | pressed: set Time
7 | }
8 |
9 | pred press[k: Key, t: Time] {
10 | t not in k.pressed
11 | t.next in k.pressed
12 | }
13 |
14 | pred release[k: Key, t: Time] {
15 | t in k.pressed
16 | t.next not in k.pressed
17 | }
18 |
19 | pred changed[k: Key, t: Time] {
20 | press[k, t] or release[k, t]
21 | }
22 |
23 | fact Trace {
24 | no first.~pressed
25 | all t: Time - last |
26 | some k: Key {
27 | changed[k, t]
28 | all k": Key - k |
29 | not changed[k, t]
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/techniques/specs/dynamics/light.als:
--------------------------------------------------------------------------------
1 | open util/ordering[Light] as ord
2 |
3 | abstract sig Color {}
4 | one sig Red, Yellow, Green extends Color {}
5 |
6 | sig Light {
7 | color: Color
8 | }
9 |
10 | pred change_light[l: Light] {
11 |
12 | let l" = l.(ord/next) {
13 | l.color = Red => l".color = Green
14 | l.color = Green => l".color = Yellow
15 | l.color = Yellow => l".color = Red
16 | }
17 |
18 | }
19 |
20 | fact Trace {
21 | ord/first.color = Red
22 |
23 | all l: Light - ord/last |
24 | change_light[l]
25 | }
26 |
--------------------------------------------------------------------------------
/tooling/analyzer.rst:
--------------------------------------------------------------------------------
1 | .. _analyzer:
2 |
3 | ++++++++
4 | Analyzer
5 | ++++++++
6 |
7 | The **Alloy Analyzer** is the tool that actually checks your spec.
8 |
9 | Configuring the Analyzer
10 | =====================================
11 |
12 | .. todo:: explain what Kodkod is
13 |
14 | The analyzer converts the model into a `SAT formula `__ to solve. Some of the options are configurable. By default you should not need to change any of these- most performance issues are better solved by improving the spec itself. The following all affect the runtime of the analyzer. All of these are under the “Options” toolbar of the IDE:
15 |
16 | .. todo:: maximum stack
17 |
18 | - **Allow Warnings:** When “no”, the analyzer will halt if the model has any warnings. Warnings usually, but not always, correspond to errors in the spec.
19 | - **Maximum Memory:** How much RAM the analyzer is allowed to use when solving.
20 | - **Solver:** The SAT solver to use for finding the model. Different solvers may have different performance on different specs. The SAT model and Kodkod model can also be output to a temporary file here. There are additional special options for MiniSat with `Unsat Core `, below.
21 | - **Skolem Depth:** see below
22 | - **Recursion Depth:** see below
23 | - **Record the Kodkod input/output:** when ``true``, the Kodkod output for the run can be seen by clicking the *Predicate* link in the output.
24 |
25 | .. image:: img/kodkod.png
26 |
27 | - **Prevent Overflows:** If an `arithmetic operation ` would overflow the model, the predicate is treated as false.
28 |
29 |
30 | .. todo::
31 |
32 | 1.
33 | figure out who knows Skolem Depth. From `here `__:
34 |
35 | > Skolem Depth: This controls the maximum depth of alternating
36 | universal-vs-existential quantifier that we will permit when
37 | generating a skolem function. If a formula exceeds this depth, we
38 | will not generate a skolem function for it.
39 |
40 | but that doesn’t explain it in a way that’s useful for most readers
41 |
42 | 2. Figure out who knows what "Infer partial instances". does.
43 |
44 | [We might just leave it out of this version]
45 |
46 |
47 | .. rst-class:: advanced
48 | .. _recursion:
49 |
50 | Recursion Depth
51 | -------------------
52 |
53 |
54 | Predicates and functions are normally not recursive- they may not call
55 | themselves. The following is an invalid predicate:
56 |
57 | .. code:: alloy
58 |
59 | sig Node {
60 | edge: set Node
61 | }
62 |
63 | fact {no iden & ^edge}
64 |
65 | pred binary_tree[n: Node] {
66 | #n.edge <= 2
67 | all child: n.edge |
68 | // recursive call
69 | binary_tree[child]
70 | }
71 |
72 | run {some n: Node | binary_tree[n]}
73 |
74 | This is invalid because all Alloy models must be bound, and recursive
75 | calls can lead to an unbound model. Normally you should restructure your
76 | model to not need a recursive call. If you *must* have a recursive
77 | predicate or function, you can set the ``recursion depth`` to a maximum
78 | of 3.
79 |
80 | The recursion depth is treated as a “fact”: the analyzer will not look
81 | for models with a greater recursion depth, even if it would lead to a
82 | valid example or counterexample.
83 |
84 | .. WARNING:: Increasing the recursion depth will slow down your spec.
85 |
86 |
87 |
88 |
89 |
90 |
91 | .. rst-class:: advanced
92 |
93 | .. _unsat-core:
94 |
95 | Unsat Core
96 | --------------
97 |
98 | By default Alloy is packaged with `Minisat `__,
99 | which also has an *Unsat Core*. When “MiniSat with Unsat Core” is
100 | selected as the solver, the analyzer can isolate which constraints
101 | prevent the analyzer from finding a counter/example. See
102 | `here `__ for more
103 | information.
104 |
105 | .. NOTE:: By default, the Windows version of Alloy does not come with MiniSAT.
106 |
107 | .. WARNING:: The “Core Granularity” option is not strictly increasing in terms of information: a slower setting might, in some circumstances, lead to the core providing *less* information. Given the following model:
108 |
109 | .. code:: alloy
110 |
111 | sig Node {
112 | edge: some Node
113 | }
114 |
115 | fact {some Node}
116 |
117 | run {no edge}
118 |
119 | All granularity settings will highlight three formulas *except* for
120 | “expand quantifiers”, which will only highlight two. However, all three
121 | constraints are required to make the predicate inconsistent.
122 |
--------------------------------------------------------------------------------
/tooling/img/evaluator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/tooling/img/evaluator.png
--------------------------------------------------------------------------------
/tooling/img/kodkod.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/tooling/img/kodkod.png
--------------------------------------------------------------------------------
/tooling/img/table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/tooling/img/table.png
--------------------------------------------------------------------------------
/tooling/img/temporal_model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/tooling/img/temporal_model.png
--------------------------------------------------------------------------------
/tooling/img/tree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/tooling/img/tree.png
--------------------------------------------------------------------------------
/tooling/img/visualizer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/tooling/img/visualizer.png
--------------------------------------------------------------------------------
/tooling/index.rst:
--------------------------------------------------------------------------------
1 | Tooling
2 | =======
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 |
7 | analyzer
8 | visualizer
9 | themes
10 | markdown
11 |
12 |
--------------------------------------------------------------------------------
/tooling/markdown.rst:
--------------------------------------------------------------------------------
1 | .. _markdown:
2 |
3 | ++++++++
4 | Markdown
5 | ++++++++
6 |
7 | Thanks to Peter Kriens, Alloy supports Markdown syntax. Files written in Alloy Markdown have an ``.md`` extension, just like regular Markdown files. The Alloy Analyzer can read in Markdown files the same way it reads in ``.als`` files.
8 |
9 | For an example of an Alloy Markdown file, see Peter Kriens's `Dining Philosophers`_.
10 |
11 | .. _Dining Philosophers: https://github.com/pkriens/pkriens.github.io/blob/master/philosophers.md
12 |
13 | Header
14 | ======
15 |
16 | A Markdown Alloy file must start with a YAML header: three dashes on the first line, followed by fields and values in YAML format, followed by three more dashes. For example:
17 |
18 | .. code:: yaml
19 |
20 | ---
21 | title: Dining Philosophers
22 | ---
23 |
24 |
25 | Alloy sections
26 | ==============
27 |
28 | After the YAML header, the Alloy parser interprets all text as Markdown. To start an Alloy section, use `fenced code blocks`_ with ``alloy`` as the language identifier. For example:
29 |
30 | .. _fenced code blocks: https://help.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks#fenced-code-blocks
31 |
32 | .. code::
33 |
34 | # Literate Programming
35 |
36 | Let us change our traditional attitude to the construction of programs: Instead
37 | of imagining that our main task is to instruct a computer what to do, let us
38 | concentrate rather on explaining to human beings **what** we want a computer to do.
39 |
40 | ```alloy
41 | sig Foo {} // this is Alloy syntax
42 | ```
43 |
44 | If you find that you're spending almost _all_ your time on theory, start turning
45 | _some_ attention to practical things; it will improve your theories. If you find
46 | that you're spending almost all your time on practice, start turning some attention
47 | to theoretical things; it will improve your practice.
48 |
49 |
50 | GitHub Pages support
51 | ====================
52 |
53 | The YAML header can be used in conjunction with `GitHub Pages`_. GitHub Pages allow you to maintain a website via Github, the YAML header is then used to encode metadata information such as title and layout. This `example website`_ shows the dining philosophers example rendered using Jekyll_ and served from GitHub Pages.
54 |
55 | .. _Github Pages: https://pages.github.com/
56 | .. _example website: https://www.aqute.biz/philosophers.html
57 | .. _Jekyll: https://jekyllrb.com/docs/github-pages/
58 |
59 |
--------------------------------------------------------------------------------
/tooling/specs/server.als:
--------------------------------------------------------------------------------
1 | sig Server {}
2 |
3 | sig Client {
4 | , var connected_to: lone Server
5 | }
6 |
7 | pred connect[c: Client, s: Server] {
8 | c -> s not in connected_to
9 | connected_to' = connected_to ++ c -> s
10 | }
11 |
12 | pred spec {
13 | -- initially no connections
14 | no connected_to
15 |
16 | -- every step, a client connects to a new server
17 | always some c: Client, s: Server {
18 | c.connect[s]
19 | }
20 | }
21 |
22 | run spec
--------------------------------------------------------------------------------
/tooling/specs/visualizer-counterexample.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/tooling/specs/visualizer.als:
--------------------------------------------------------------------------------
1 | abstract sig Object {}
2 |
3 | sig File extends Object {}
4 |
5 | sig Dir extends Object {contents: set Object}
6 | one sig Root extends Dir { }
7 |
8 |
9 | fact {
10 | Object in Root.*contents
11 | }
12 |
13 | assert RootTop {
14 | no o: Object | Root in o.contents
15 | }
16 | check RootTop // This assertion should produce a counterexample
17 |
18 |
--------------------------------------------------------------------------------
/tooling/themes.rst:
--------------------------------------------------------------------------------
1 | .. |dt| replace:: Default True.
2 | .. |df| replace:: Default False.
3 |
4 | .. _themes:
5 |
6 | ++++++
7 | Themes
8 | ++++++
9 |
10 | Diagrams produced in the :doc:`visualizer` can be **themed**. Options are:
11 |
12 | .. note:: All options will not be changed until you click the “Apply” button.
13 |
14 | .. todo:: I'd love to have before and after screenshots for every one of these options, but the tooling doesn't make that automatically generatable.
15 |
16 | General Graph Settings
17 | ===========================
18 |
19 | .. todo:: Explain what meta does
20 |
21 | - **Use Original Atom Names**: Instead of showing atoms as ``Atom¹``, the visualizer will show them as ``Atom$1``. Default False.
22 | - **Hide Private Sigs/Relations**: Hides signatures and relations that are internal to an imported module. Default True.
23 | - **Hide Meta Sigs/Relations**: ???
24 |
25 |
26 | Types and Sets
27 | ===================
28 |
29 | In this case, "set" refers to any nodes that are important to the visualizer, such as the results of `commands`. All settings are hierarchical: any setting labelled ``inherit`` will use the same setting as the parent type/set. Settings for sets override settings for signatures.
30 |
31 | The following settings are universal to all nodes:
32 |
33 | - **Color**, **Shape**, and **Border**.
34 | - **Name** (to the left of color): What to call the nodes. This does not affect the evaluator or tree, text, and table views.
35 | - **Show**: Whether or not the node is visible in the visualization at all. Default True.
36 | - **Hide Unconnected Nodes**: If a node doesn't have any relation to any other node, hide the node. Default True.
37 |
38 |
39 | The following are specific to signatures:
40 |
41 | - **Number Nodes**: Nodes with the same name and same type are suffixed with a number.
42 | - **Project over this Sig**: See `projections`.
43 |
44 | The following are specific to sets:
45 |
46 | - **Show as Labels:** Set membership is shown as a text label on the matching atoms.
47 | - **Show in Relation Attributes**: If the element of the set appears in another node's textual attributes, also list that it belongs to the set.
48 |
49 | .. todo:: That one NEEDS a before/after visualization.
50 |
51 | Relations
52 | =================
53 |
54 | For all of these options, we assume the relation is ``rel: A -> B``
55 |
56 | - **Show as Arcs**: Represent the relationship as an arrow from A to B. |dt|
57 | - **Show as Attributes**: Represents the relationship as the label ``rel: B`` on node A. A relationship can be shown as both arcs and attributes at the same time. |df|
58 | - **Influence Layout**: If True, the visualizer will try to account for the arrows when laying out the node graph. If False, the visualizer will first lay out the graph, and then draw the relation. |dt|
59 | - **Weight**: The visualizer will try to minimize the weights of the larger arrows first.
60 | - **Layout Backwards**: Layout the graph as if the relation was ``B -> A``. The final layout will still show the arrow as ``A -> B``. |df|
61 | - **Merge Arrows**: If True, the relationship ``A -> B + B -> A`` will be represented as a single double-sided arrow. If false, the relationship is represented as two single-sided arrows. |dt|
62 |
63 | .. _reusing-themes:
64 |
65 | Reusing Themes
66 | +++++++++++++++
67 |
68 | Themes can be saved and loaded under the ``theme`` top menu bar. Themes can also be reset here.
69 |
--------------------------------------------------------------------------------
/tooling/visualizer.rst:
--------------------------------------------------------------------------------
1 | .. _visualizer:
2 |
3 | ++++++++++
4 | Visualizer
5 | ++++++++++
6 |
7 | Given the following model
8 |
9 |
10 | .. literalinclude:: specs/visualizer.als
11 |
12 | ..
13 |
14 | ::
15 |
16 | fact "demo" {
17 | let unroot = Dir - Root |
18 | contents = Root -> unroot + unroot -> Dir + ((Dir - Root) -> File)
19 | }
20 |
21 | One counterexample to ``RootTop`` is a directory that contains the root. In the Visualizer, this looks like
22 |
23 | .. image:: img/visualizer.png
24 |
25 |
26 | This page will cover all functionality of the visualizer. See below for models using :ref:`time`.
27 |
28 | .. attention:: The XML for this example can be downloaded :download:`here `.
29 |
30 | .. Tip::
31 |
32 | The model visualizer names atoms like ``Atom$0``, ``Atom$1``, etc. This can sometimes be hard to follow in the visualizer. To give things qualified names, instead write:
33 |
34 | ::
35 |
36 | abstract sig Base {
37 | -- relations here
38 | }
39 |
40 | lone A, B, C, D extends Base {}
41 |
42 | run {} for 2 Base
43 |
44 | This will guarantee the values have better names.
45 |
46 | Menu Bar Functions
47 | ------------------
48 |
49 | File
50 | ~~~~
51 |
52 | - **Open/Export To**: Visualizations can either be exported to a
53 | graphviz diagram or to XML. Saved XML can be reloaded into the
54 | visualizer with the open option without needing to first reevaluate
55 | the spec.
56 |
57 | Instance
58 | ~~~~~~~~
59 |
60 | - **Show Next Solution/Next:** Returns another solution that satisfies the model (or is a valid counterexample). The evaluator can only move forward in solutions, not backwards. If you would like to see a previous solution, you will need to rerun the model.
61 |
62 | Themes
63 | ~~~~~~~~~~~
64 |
65 | See `reusing-themes`.
66 |
67 | Output options
68 | --------------
69 |
70 | The standard view is the **visualizer**. The other three options are
71 |
72 | - Text
73 |
74 | .. code::
75 |
76 | seq/Int={0, 1, 2, 3}
77 | String={}
78 | none={}
79 | this/Object={Dir$0, File$0, Root$0}
80 | this/Dir={Dir$0, Root$0}
81 | this/Dir<:contents={Dir$0->Dir$0, Dir$0->File$0, Dir$0->Root$0, Root$0->Dir$0}
82 | this/Root={Root$0}
83 | this/File={File$0}
84 | skolem $RootTop_o={Dir$0}
85 |
86 | - Tree
87 |
88 | .. image:: img/tree.png
89 |
90 | - Table
91 |
92 | .. code::
93 |
94 | ┌────────┬────────┐
95 | │this/Dir│contents│
96 | ├────────┼────────┤
97 | │Dir⁰ │Dir⁰ │
98 | │ ├────────┤
99 | │ │File⁰ │
100 | │ ├────────┤
101 | │ │Root⁰ │
102 | ├────────┼────────┤
103 | │Root⁰ │Dir⁰ │
104 | └────────┴────────┘
105 |
106 | ┌───────────┬────┬─────┬─────┐⁻¹
107 | │this/Object│Dir⁰│File⁰│Root⁰│
108 | └───────────┴────┴─────┴─────┘
109 |
110 | .. warning:: The table view shows the atoms in a human readable form, for example, writing ``Root⁰`` instead of ``Root$0``. In the :ref:`evaluator `, however, you need to write ``Root$0``.
111 |
112 | Themes
113 | ~~~~~~
114 |
115 | See :ref:`themes`.
116 |
117 | Magic Layout
118 | ~~~~~~~~~~~~~~~
119 |
120 | Automatically generates an appropriate theme for the visualization.
121 |
122 | .. _evaluator:
123 |
124 | Evaluator
125 | ---------
126 |
127 | The evaluator can be used to run arbitrary commands against the existing
128 | model. It cannot create new signatures, only investigate the current
129 | signatures and relations you currently have. You cannot define new
130 | functions or predicates in the evaluator, only evaluate `expressions`.
131 |
132 | .. image:: img/evaluator.png
133 |
134 | .. Note:: While the evaluator can evaluate most expressions, it does not have the full capacity of the alloy analyzer. For example, polymorphic domain restriction will not work. Additionally, integer overflow will wrap instead of raising an error.
135 |
136 | .. rst-class:: advanced
137 | .. _projections:
138 |
139 | Projection
140 | ----------
141 |
142 | Projections break a complex model into multiple subviews. Instead of showing relations from the projected signature, each relation will be represented as a text label on the corresponding targets of the relation.
143 |
144 | .. todo:: The current example of the visualizer doesn't have projections, find a better example.
145 |
146 |
147 | .. _visualizer_temporal_models:
148 |
149 | Temporal Models
150 | ---------------
151 |
152 | Models that use :ref:`variables ` are temporal models, and are shown differently. Given the following model
153 |
154 | .. literalinclude:: specs/visualizer.als
155 |
156 | The visualizer might instead look like this:
157 |
158 | .. image:: img/temporal_model.png
159 |
160 | This represents a single "trace", or sequence of steps that represent the changing variable. On the left is the *current* state, on the right is the *next* state. Clicking on a state in the sequence will jump to that state.
161 |
162 | In the evaluator, ``connected_to`` refers to the left state, and ``connected_to'`` refers to the right state. Text and table views will only show the current state.
163 |
164 | Instead of "Next [Solution]", there are four more specific options:
165 |
166 | #. **New Config**: Changes everything, like "Next" does in non-temporal models.
167 | #. **New Trace**: Keeps the current config and initial state, but finds a new trace.
168 | #. **New Init**: Keeps the non-variable signatures and relations, but changes the initial values of the variables (also giving a new trace).
169 | #. **New Fork**: Can only be used when looking at a later step. Keeps the config and all prior steps in the trace, but finds a different successor step.
170 |
171 | In order of severity, New Config changes more than New Init, which changes more than New Trace, which changes more than New Fork.
172 |
173 | .. note:: You'll get an error if you try to find a new trace after having found a new fork. You'll need to find a new config first.
--------------------------------------------------------------------------------
/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwayne/alloydocs/f99ac6bef4ce68513f543df4a1715da01f1927e0/utils/__init__.py
--------------------------------------------------------------------------------
/utils/alloy.py:
--------------------------------------------------------------------------------
1 | """
2 | utils/alloy
3 | ~~~~~~~~~~~
4 |
5 | The Alloy domain.
6 | Based off sphinx.domains.javascript
7 |
8 |
9 | :copyright: Alloy Board
10 | :license: BSD, see LICENSE for details.
11 | """
12 |
13 | """
14 | sphinx.domains.javascript
15 | The JavaScript domain.
16 |
17 | :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
18 | :license: BSD, see LICENSE for details.
19 | """
20 |
21 | from docutils import nodes
22 | from docutils.parsers.rst import directives
23 |
24 | from sphinx import addnodes
25 | from sphinx.directives import ObjectDescription
26 | from sphinx.domains import Domain, ObjType
27 | from sphinx.domains.python import _pseudo_parse_arglist
28 | from sphinx.locale import _
29 | from sphinx.roles import XRefRole
30 | from sphinx.util.docfields import Field, GroupedField, TypedField
31 | from sphinx.util.docutils import SphinxDirective
32 | from sphinx.util.nodes import make_refnode
33 |
34 | if False:
35 | # For type annotation
36 | from typing import Any, Dict, Iterator, List, Tuple # NOQA
37 | from docutils import nodes # NOQA
38 | from sphinx.application import Sphinx # NOQA
39 | from sphinx.builders import Builder # NOQA
40 | from sphinx.environment import BuildEnvironment # NOQA
41 |
42 |
43 | class AlloyObject(ObjectDescription):
44 | """
45 | Description of a Alloy object.
46 | """
47 | #: If set to ``True`` this object is callable and a `desc_parameterlist` is
48 | #: added
49 | has_arguments = False
50 |
51 | #: what is displayed right before the documentation entry
52 | display_prefix = None # type: str
53 |
54 | #: If ``allow_nesting`` is ``True``, the object prefixes will be accumulated
55 | #: based on directive nesting
56 | allow_nesting = False
57 |
58 | def handle_signature(self, sig, signode):
59 | # type: (str, addnodes.desc_signature) -> Tuple[str, str]
60 | """Breaks down construct signatures
61 |
62 | Parses out prefix and argument list from construct definition. The
63 | namespace and class will be determined by the nesting of domain
64 | directives.
65 | """
66 | sig = sig.strip()
67 | if '(' in sig and sig[-1:] == ')':
68 | member, arglist = sig.split('(', 1)
69 | member = member.strip()
70 | arglist = arglist[:-1].strip()
71 | else:
72 | member = sig
73 | arglist = None
74 | # If construct is nested, prefix the current prefix
75 | prefix = self.env.ref_context.get('als:object', None)
76 | mod_name = self.env.ref_context.get('als:module')
77 | name = member
78 | try:
79 | member_prefix, member_name = member.rsplit('.', 1)
80 |
81 | except ValueError:
82 | member_name = name
83 | member_prefix = ''
84 | finally:
85 | name = member_name
86 | if prefix and member_prefix:
87 | prefix = '.'.join([prefix, member_prefix])
88 | elif prefix is None and member_prefix:
89 | prefix = member_prefix
90 | fullname = name
91 | if prefix:
92 | fullname = '.'.join([prefix, name])
93 |
94 | signode['module'] = mod_name
95 | signode['object'] = prefix
96 | signode['fullname'] = fullname
97 |
98 | if self.display_prefix:
99 | signode += addnodes.desc_annotation(self.display_prefix,
100 | self.display_prefix)
101 | if prefix:
102 | signode += addnodes.desc_addname(prefix + '.', prefix + '.')
103 | elif mod_name:
104 | signode += addnodes.desc_addname(mod_name + '.', mod_name + '.')
105 | signode += addnodes.desc_name(name, name)
106 | if self.has_arguments:
107 | if not arglist:
108 | signode += addnodes.desc_parameterlist()
109 | else:
110 | _pseudo_parse_arglist(signode, arglist)
111 | return fullname, prefix
112 |
113 | def add_target_and_index(self, name_obj, sig, signode):
114 | # type: (Tuple[str, str], str, addnodes.desc_signature) -> None
115 | mod_name = self.env.ref_context.get('als:module')
116 | fullname = (mod_name and mod_name + '/' or '') + name_obj[0]
117 | # Alloy predicates use [], not (), so Sphinx includes the arguments in the id
118 | if "[" in fullname and fullname[0] != "[":
119 | fullname = fullname.split('[')[0]
120 | if fullname not in self.state.document.ids:
121 | signode['names'].append(fullname)
122 | signode['ids'].append(fullname.replace('$', '_S_'))
123 | signode['first'] = not self.names
124 | self.state.document.note_explicit_target(signode)
125 | objects = self.env.domaindata['als']['objects']
126 | if fullname in objects:
127 | self.state_machine.reporter.warning(
128 | 'duplicate object description of %s, ' % fullname +
129 | 'other instance in ' +
130 | self.env.doc2path(objects[fullname][0]),
131 | line=self.lineno)
132 | objects[fullname] = self.env.docname, self.objtype
133 |
134 | # Todo have this also sweep self.options.get('also', '').split(','):
135 | indextext = self.get_index_text(mod_name, name_obj)
136 | if indextext:
137 | self.indexnode['entries'].append(('single', indextext,
138 | fullname.replace('$', '_S_'),
139 | '', None))
140 |
141 | def get_index_text(self, objectname, name_obj):
142 | # type: (str, Tuple[str, str]) -> str
143 | name, obj = name_obj
144 | name = name.split('[')[0] # Hack to remove the params from fun and pred
145 | if name == '':
146 | return ''
147 | if self.objtype == 'function':
148 | return _(f"{name} (function)")
149 | elif self.objtype == 'macro':
150 | return _('%s (macro)') % (name)
151 | elif self.objtype == 'predicate':
152 | return _('%s (predicate)') % (name)
153 | return ''
154 |
155 | def before_content(self):
156 | # type: () -> None
157 | """Handle object nesting before content
158 |
159 | :py:class:`AlloyObject` represents Alloy language constructs. For
160 | constructs that are nestable, this method will build up a stack of the
161 | nesting heirarchy so that it can be later de-nested correctly, in
162 | :py:meth:`after_content`.
163 |
164 | For constructs that aren't nestable, the stack is bypassed, and instead
165 | only the most recent object is tracked. This object prefix name will be
166 | removed with :py:meth:`after_content`.
167 |
168 | The following keys are used in ``self.env.ref_context``:
169 |
170 | als:objects
171 | Stores the object prefix history. With each nested element, we
172 | add the object prefix to this list. When we exit that object's
173 | nesting level, :py:meth:`after_content` is triggered and the
174 | prefix is removed from the end of the list.
175 |
176 | als:object
177 | Current object prefix. This should generally reflect the last
178 | element in the prefix history
179 | """
180 | prefix = None
181 | if self.names:
182 | (obj_name, obj_name_prefix) = self.names.pop()
183 | prefix = obj_name_prefix.strip('.') if obj_name_prefix else None
184 | if self.allow_nesting:
185 | prefix = obj_name
186 | if prefix:
187 | self.env.ref_context['als:object'] = prefix
188 | if self.allow_nesting:
189 | objects = self.env.ref_context.setdefault('als:objects', [])
190 | objects.append(prefix)
191 |
192 | def after_content(self):
193 | # type: () -> None
194 | """Handle object de-nesting after content
195 |
196 | If this class is a nestable object, removing the last nested class prefix
197 | ends further nesting in the object.
198 |
199 | If this class is not a nestable object, the list of classes should not
200 | be altered as we didn't affect the nesting levels in
201 | :py:meth:`before_content`.
202 | """
203 | objects = self.env.ref_context.setdefault('als:objects', [])
204 | if self.allow_nesting:
205 | try:
206 | objects.pop()
207 | except IndexError:
208 | pass
209 | self.env.ref_context['als:object'] = (objects[-1] if len(objects) > 0
210 | else None)
211 |
212 |
213 | class AlloyCallable(AlloyObject):
214 | """Description of a Alloy function, macro or predicate."""
215 | has_arguments = False
216 | # True makes it tack on (), we use [] instead
217 | # Means we lose some affordances, but nothing game-changing
218 |
219 | doc_field_types = [
220 | TypedField('arguments', label=_('Arguments'),
221 | names=('argument', 'arg', 'parameter', 'param'),
222 | typerolename='func', typenames=('paramtype', 'type')),
223 | Field('returnvalue', label=_('Returns'), has_arg=False,
224 | names=('returns', 'return')),
225 | Field('also', label=_('See Also'), has_arg=False,
226 | names=('also',)),
227 | Field('returntype', label=_('Return type'), has_arg=False,
228 | names=('rtype',)),
229 | ]
230 |
231 | class AlloyMacro(AlloyCallable):
232 | """Description of an Alloy macro."""
233 | has_arguments = False
234 | display_prefix = "let "
235 |
236 | doc_field_types = [
237 | TypedField('arguments', label=_('Arguments'),
238 | names=('argument', 'arg', 'parameter', 'param'),
239 | typerolename='func', typenames=('paramtype', 'type')),
240 | Field('expandsto', label=_('Expands to'), has_arg=False,
241 | names=('expand', 'expansion')),
242 | ]
243 |
244 | class AlloyFunction(AlloyCallable):
245 | """Description of an Alloy function."""
246 | has_arguments = False
247 | display_prefix = "fun "
248 |
249 | class AlloyPredicate(AlloyCallable):
250 | """Description of an Alloy predicate."""
251 | has_arguments = False
252 | display_prefix = "pred "
253 |
254 | class AlloyModule(SphinxDirective):
255 | """
256 | Directive to mark description of a new Alloy module.
257 |
258 | This directive specifies the module name that will be used by objects that
259 | follow this directive.
260 |
261 | Options
262 | -------
263 |
264 | noindex
265 | If the ``noindex`` option is specified, no linkable elements will be
266 | created, and the module won't be added to the global module index. This
267 | is useful for splitting up the module definition across multiple
268 | sections or files.
269 |
270 | :param mod_name: Module name
271 | """
272 |
273 | has_content = False
274 | required_arguments = 1
275 | optional_arguments = 0
276 | final_argument_whitespace = False
277 | option_spec = {
278 | 'noindex': directives.flag
279 | }
280 |
281 | def run(self):
282 | # type: () -> List[nodes.Node]
283 | mod_name = self.arguments[0].strip()
284 | self.env.ref_context['als:module'] = mod_name
285 | noindex = 'noindex' in self.options
286 | ret = [] # type: List[nodes.Node]
287 | if not noindex:
288 | self.env.domaindata['als']['modules'][mod_name] = self.env.docname
289 | # Make a duplicate entry in 'objects' to facilitate searching for
290 | # the module in AlloyDomain.find_obj()
291 | self.env.domaindata['als']['objects'][mod_name] = (self.env.docname, 'module')
292 | targetnode = nodes.target('', '', ids=['module-' + mod_name],
293 | ismod=True)
294 | self.state.document.note_explicit_target(targetnode)
295 | ret.append(targetnode)
296 | indextext = _('%s (module)') % mod_name
297 | inode = addnodes.index(entries=[('single', indextext,
298 | 'module-' + mod_name, '', None)])
299 | ret.append(inode)
300 | return ret
301 |
302 |
303 | class AlloyXRefRole(XRefRole):
304 | def process_link(self, env, refnode, has_explicit_title, title, target):
305 | # type: (BuildEnvironment, nodes.Element, bool, str, str) -> Tuple[str, str]
306 | # basically what sphinx.domains.python.PyXRefRole does
307 | # TODO probably should change this to use Alloy module syntax
308 | refnode['als:object'] = env.ref_context.get('als:object')
309 | refnode['als:module'] = env.ref_context.get('als:module')
310 | if not has_explicit_title:
311 | title = title.lstrip('.')
312 | target = target.lstrip('~')
313 | if title[0:1] == '~':
314 | title = title[1:]
315 | dot = title.rfind('.')
316 | if dot != -1:
317 | title = title[dot + 1:]
318 | return title, target
319 |
320 |
321 | class AlloyDomain(Domain):
322 | """Alloy language domain."""
323 | name = 'als'
324 | label = 'Alloy'
325 |
326 | object_types = {
327 | 'function': ObjType(_('function'), 'func'),
328 | 'predicate': ObjType(_('predicate'), 'pred'),
329 | 'macro': ObjType(_('macro'), 'macro'),
330 | 'module': ObjType(_('module'), 'mod'),
331 | }
332 | directives = {
333 | 'function': AlloyFunction,
334 | 'predicate': AlloyPredicate,
335 | 'macro': AlloyMacro,
336 | 'module': AlloyModule,
337 | }
338 | roles = {
339 | 'func': AlloyXRefRole(),
340 | 'pred': AlloyXRefRole(),
341 | 'macro': AlloyXRefRole(),
342 | 'mod': AlloyXRefRole(),
343 | }
344 | initial_data = {
345 | 'objects': {}, # fullname -> docname, objtype
346 | 'modules': {}, # mod_name -> docname
347 | } # type: Dict[str, Dict[str, Tuple[str, str]]]
348 |
349 | def clear_doc(self, docname):
350 | # type: (str) -> None
351 | for fullname, (pkg_docname, _l) in list(self.data['objects'].items()):
352 | if pkg_docname == docname:
353 | del self.data['objects'][fullname]
354 | for mod_name, pkg_docname in list(self.data['modules'].items()):
355 | if pkg_docname == docname:
356 | del self.data['modules'][mod_name]
357 |
358 | def merge_domaindata(self, docnames, otherdata):
359 | # type: (List[str], Dict) -> None
360 | # XXX check duplicates
361 | for fullname, (fn, objtype) in otherdata['objects'].items():
362 | if fn in docnames:
363 | self.data['objects'][fullname] = (fn, objtype)
364 | for mod_name, pkg_docname in otherdata['modules'].items():
365 | if pkg_docname in docnames:
366 | self.data['modules'][mod_name] = pkg_docname
367 |
368 | def find_obj(self, env, mod_name, prefix, name, typ, searchorder=0):
369 | # type: (BuildEnvironment, str, str, str, str, int) -> Tuple[str, Tuple[str, str]]
370 | if name[-2:] == '()':
371 | name = name[:-2]
372 | objects = self.data['objects']
373 |
374 | searches = []
375 | if mod_name and prefix:
376 | searches.append('/'.join([mod_name, prefix, name]))
377 | if mod_name:
378 | searches.append('/'.join([mod_name, name]))
379 | if prefix:
380 | searches.append('/'.join([prefix, name]))
381 | searches.append(name)
382 |
383 | if searchorder == 0:
384 | searches.reverse()
385 |
386 | newname = None
387 | for search_name in searches:
388 | if search_name in objects:
389 | newname = search_name
390 |
391 | return newname, objects.get(newname)
392 |
393 | def resolve_xref(self, env, fromdocname, builder, typ, target, node,
394 | contnode):
395 | # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
396 | mod_name = node.get('als:module')
397 | prefix = node.get('als:object')
398 | searchorder = node.hasattr('refspecific') and 1 or 0
399 | name, obj = self.find_obj(env, mod_name, prefix, target, typ, searchorder)
400 | if not obj:
401 | return None
402 | return make_refnode(builder, fromdocname, obj[0],
403 | name.replace('$', '_S_'), contnode, name)
404 |
405 | def resolve_any_xref(self, env, fromdocname, builder, target, node,
406 | contnode):
407 | # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA
408 | mod_name = node.get('als:module')
409 | prefix = node.get('als:object')
410 | name, obj = self.find_obj(env, mod_name, prefix, target, None, 1)
411 | if not obj:
412 | return []
413 | return [('als:' + self.role_for_objtype(obj[1]),
414 | make_refnode(builder, fromdocname, obj[0],
415 | name.replace('$', '_S_'), contnode, name))]
416 |
417 | def get_objects(self):
418 | # type: () -> Iterator[Tuple[str, str, str, str, str, int]]
419 | for refname, (docname, type) in list(self.data['objects'].items()):
420 | yield refname, refname, type, docname, \
421 | refname.replace('$', '_S_'), 1
422 |
423 | def get_full_qualified_name(self, node):
424 | # type: (nodes.Element) -> str
425 | modname = node.get('als:module')
426 | prefix = node.get('als:object')
427 | target = node.get('reftarget')
428 | if target is None:
429 | return None
430 | else:
431 | return '/'.join(filter(None, [modname, prefix, target]))
432 |
433 |
434 | def setup(app):
435 | # type: (Sphinx) -> Dict[str, Any]
436 | app.add_domain(AlloyDomain)
437 |
438 | return {
439 | 'env_version': 1,
440 | 'parallel_read_safe': True,
441 | 'parallel_write_safe': True,
442 | }
443 |
444 |
--------------------------------------------------------------------------------