28 KiB
Contributing to the learning material
Writing guidelines
-
Source-code examples should be complete, compilable, and free of errors.
-
Valid exceptions for incomplete code are, for example, when the writer wants to highlighting a specific source-code block from a previous example.
-
Erroneous source-code examples are acceptable when highlighting an important pitfall. They should, however, be avoided as much as possible.
- In most cases, mentioning pitfalls in the text should be sufficient.
-
-
References to other programming languages should be kept at a minimum level.
-
They should be used only when highlighting important differences for common use-cases in widely-known programming languages.
-
They should be part of admonitions, so that they don't disrupt the normal text.
-
They must be optional: readers should be able to understand the lessons without having to refer to other languages.
-
-
For any topic that isn't covered in the course itself, a reference to external content may be used.
-
We encourage the use of references to computer science pages on Wikipedia whenever possible.
-
For example:
The :wikipedia:`V-model_(software_development)` is...
-
-
Admonitions can be used to either indicate that a certain text block is especially important or to indicate that it is optional.
- Although the title of an admonition can be selected according to the specific topic, the following titles are widely used and strongly recommended:
| Admonition title | Description |
|---|---|
| Attention | When a piece of information might be missed by the reader at first sight. |
| Important | To elaborate on details that are important for the current topic. |
| For further reading... | When elaborating on interesting, but not important details (which might be skipped). |
| In other languages | When comparing Ada to other programming languages. |
| In other contexts | When discussing specific terminology in other contexts. |
| Historically | When discussing historical details, e.g. comparing to an older version of Ada. |
| In the Ada Reference Manual | When indicating a relevant section from the Ada Reference Manual. |
| In the GNAT toolchain | To describe details that are specific for the GNAT toolchain, but not the Ada language. |
| CHANGELOG | When describing changes to a course/book. |
Editorial policies and guidelines
-
The content must be written in ReST format.
- Sphinx is used to generate HTML, PDF and EPUB content based on the ReST format.
-
Text must contain up to 79 characters per line.
-
A blank line between a title and the first paragraph must be used, as this improves readability for the website maintainers.
-
The global definitions file (global.txt) for Sphinx should be used — unless there is a very good reason for using alternative definitions.
-
The global definitions are included with the following format:
.. include:: ../../../global.txt -
The global definitions file may be extended if needed.
-
-
Anchors must be prefixed with a name that clearly identifies the course.
-
There are two reasons for this requirement:
-
Because of the risk of clashing with anchors from other courses.
-
If a course A is referencing a section from course B, it's easier to identify that the referenced section is located in course B.
-
-
Also, anchors must use capital letters separated by underscores.
-
For example, for a course called "Ada for desktop applications", we could select a prefix such as
Ada_Desktop_App. For an anchor to a section called "Implementing a 'Hello World' application", we could use the following anchor:.. _Ada_Desktop_App_Hello_World_App: Hello World Application -----------------------
-
-
The following format for section headers must be used:
Course part *********** This is the beginning of a new part of a course. Chapter Title ============= This is the beginning of a new chapter. Section Title ------------- This is the beginning of a new section. Subsection Title ~~~~~~~~~~~~~~~~ This is the beginning of a subsection. Sub-Subsection Title ^^^^^^^^^^^^^^^^^^^^ This is the beginning of a sub-subsection.-
Other formats for section headers must be avoided.
-
Note that not all courses have to use parts: only chapters are required. However, using parts can be useful for better structuring a course that has many chapters — for example, more than 30 chapters.
-
Note that Sphinx doesn't require a fixed format for section headers. That being said, we use the format described above for better maintainance.
-
Sections that do not use a section header should be avoided.
-
For example:
**Section Title** This is the beginning of a new section. -
These sections won't be part of the table of contents, and reader won't have a way to access them directly.
-
-
-
For any words or expressions referring to Ada keywords or elements from source-code examples, the "ada role" (
:ada:<keyword|expression>) must be used.-
This rule applies even for elements that aren't explicitly shown in source-code example.
-
For example:
If a compilation unit :ada:`Q` has a :ada:`with` dependence on package :ada:`P`, then :ada:`Q` ...
-
-
For content in a directive, 4-character indentations must be used. For example:
.. admonition:: For further reading... You may also consult... .. code:: ada run_button project=Courses.Ada_Desktop_App.Introduction.Hello_World_App.Hello_World_Terminal with Ada.Text_IO; use Ada.Text_IO; procedure Hello_World is H : String := "Hello"; W : String := "World"; begin Put_Line (H & " " & W & "!"); end Hello_World;- For Ada source-code examples, note that after the 4-character indentation for the directive, the indentation follows the GNAT code style — which makes use of a 3-character indentation in most cases.
-
For URLs, prefer using standard hyperlinks (with one trailing underscore) instead of anonymous hyperlinks. For example:
`GNAT User’s Guide Supplement for Cross Platforms <https://docs.adacore.com/gnat_ugx-docs/html/gnat_ugx/gnat_ugx.html>`_ -
For any images that are included in the content, the images in the generated HTML page must be visualized in a browser using both light and dark modes, as images might not be visible in one of those modes.
-
When an image was created with a light background in mind, but it actually makes use of a transparent background, we can use the
dark-mode-invert-imageto adapt the colors for dark mode. For example:.. image:: images/light_mode_figure.png :class: dark-mode-invert-image
-
-
|mdash|or|ndash|must be used instead of---or--.-
Actual m-dashes and n-dashes (
—or–) should be avoided. -
|mdash|or|ndash|are defined in the isopub.txt file included in the global definitions file.
-
-
For any bibliographical references, the sphinxcontrib.bibtex plug-in for Sphinx should be used.
-
Todo items must be written in this format:
.. todo:: This is a todo item...- When content is processed for publishing, those todo items are not included in the generated files.
-
When referring to the Ada compiler, never use the word "implementation" alone (as it's done in the Ada Reference Manual). Instead, explicitly mention the word compiler by writing "compiler implementation", "compiler version", "compiler toochain", etc.
- Reason: readers may assume that the word "implementation" refers to a source-code example.
Interactive snippets
Interactive code-blocks (snippets) are generated for any code inside the
code:: directive. For example:
.. code:: ada run_button project=Courses.Ada_Desktop_App.Introduction.Hello_World_App.Hello_World_Terminal
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello_World is
begin
Put_Line ("Hello World!");
end Hello_World;
Code directives
By default, Sphinx supports the code-block:: directive. When this directive
is used, the widget is not used and no post-processing takes place. Only the
features supported by Sphinx — such as syntax highlighting — are applied to
the code block.
Note that non-interactive code block using the code-block:: directive
should be avoided, as there's no testing taking place to ensure that the code
compiles or runs as expected. In fact, not even a simple syntax check takes
place, so the contributor has to ensure manually that the code is written and
working as intended.
On the other hand, when the code:: directive is used, an interactive widget
is created for the HTML output and the code block is verified. All the features
described below are available for this directive.
Language-related parameters
The following language-related parameters are available for this directive:
-
ada: indicates that the code is written in Ada or SPARK -
c: indicates that the code is written in C -
cpp: indicates that the code is written in C++ -
java: indicates that the code is written in Java -
assembly: indicates that the code is written in Assembly
Button-related parameters
The following button-related parameters are available for this directive:
| Button indicator | Description |
|---|---|
no_button |
removes all buttons |
compile_button |
compile code to check syntax |
run_button |
run code in editor |
prove_button |
prove SPARK code |
prove_flow_button |
examine SPARK data and control flow |
prove_report_all_button |
prove SPARK code and report all findings. |
prove_flow_report_all_button |
examine SPARK data and control flow and report all findings |
submit_button |
submit code for a lab |
Project parameter and code accumulation
A project parameter must be provided. For this parameter, we use the
following syntax: project=<name>. For example:
.. code:: ada run_button project=Courses.Ada_Desktop_App.Introduction.Hello_World_App.Hello_World_Terminal
Dots in the project name (.) indicate the position of the code example in the
tree structure — corresponding to the directory tree within the contents
directory — and within the book/course itself. For the example above, we can
identify the following elements:
-
Courses: indicates a course in the content/courses/ directory. -
Ada_Desktop_App: an abbreviation indicating the current course (in this case, "Ada for desktop applications"). -
Introduction: the name or abbreviation of the current chapter ("Introduction"). -
Hello_World_App: the name or abbreviation of the current section within the chapter ("Hello World Application"). -
Hello_World_Terminal: name or abbreviation of the current code example.
Code blocks that share the same project name "accumulate" code. For example:
.. code:: ada compile_button project=Courses.Ada_Desktop_App.Introduction.Packages.Simple_Package
package P is
function Is_OK (T : Float)
return Boolean;
end P;
At this point, the project consists of the following code:
package P is
function Is_OK (T : Float)
return Boolean;
end P;
We can then add another code block — after the code block above — using the same project name as the previous code block:
.. code:: ada run_button project=Courses.Ada_Desktop_App.Introduction.Packages.Simple_Package
with P; use P;
procedure Show_P is
begin
null;
end Show_P;
At this point, the project consists of the following code:
package P is
function Is_OK (T : Float)
return Boolean;
end P;
with P; use P;
procedure Show_P is
begin
null;
end Show_P;
We may replace the Show_P procedure by adding another code block and using
the same project name and procedure name:
.. code:: ada run_button project=Courses.Ada_Desktop_App.Introduction.Packages.Simple_Package
with Ada.Text_IO; use Ada.Text_IO;
with P; use P;
procedure Show_P is
V : Float := 1.0;
begin
if Is_OK (V) then
Put_Line ("Value is OK: " & V'Image);
end if;
end Show_P;
At this point, the project is changed to the following code:
package P is
function Is_OK (T : Float)
return Boolean;
end P;
with Ada.Text_IO; use Ada.Text_IO;
with P; use P;
procedure Show_P is
V : Float := 1.0;
begin
if Is_OK (V) then
Put_Line ("Value is OK: " & V'Image);
end if;
end Show_P;
Code extraction / manual chop
The code inside code:: directives is extracted into a list of files.
The files are extracted the following way:
-
for valid Ada code,
gnatchopis run on the entirety of the snippet -
for C code, the files should be named explicitly, with a marker of the form
!<basename>placed at the beginning of each file in the snippet. This mechanism is also activated if the argumentmanual_chopis passed to thecode::directive. For instance:.. code:: c run_button manual_chop !main.c #include <stdio.h> int main(int argc, const char * argv[]) { printf("Hello World\n"); return 0; } -
for Ada code, the argument
manual_chopcan be used as well. This preventsgnatchopfrom being called for the code block. For example:.. code:: ada manual_chop compile_button project=Courses.Ada_Desktop_App.Introduction.Packages.Simple_Package !p.ads package P is function Is_OK (T : Float) return Boolean; end P;
GNAT compiler switches
A switches parameter can be provided. For this parameter, we use the
following syntax: switches=Compiler(<comma-separated list of GNAT switches);.
For example:
.. code:: ada no_button project=Courses.Ada_Desktop_App.Types.User_Defined_Literals.Simple_Example switches=Compiler(-gnat2022);
package Activation_States is
type Activation_State is (Unknown, Off, On)
with Integer_Literal =>
Integer_To_Activation_State;
function Integer_To_Activation_State
(S : String)
return Activation_State;
end Activation_States;
The following switches are available:
| Switch | Description |
|---|---|
-g |
optimize for debug |
-O0 |
no optimizations |
-gnata |
assertions enabled |
-gnatW8 |
UTF-8 encoding of source-code files |
-gnatwa |
activate most optional warnings |
-gnatyg0-s |
check default code style |
-gnatyM50 |
check maximum line length of 50 characters |
-gnatyM80 |
check maximum line length of 80 characters |
-gnato |
enable numeric overflow checking |
-gnato0 |
suppresses overflow checking |
-gnato11 |
handling of intermediate overflow: strict mode |
-gnato21 |
handling of intermediate overflow: strict mode |
| minimized mode within assertions, pre/postconditions, | |
| and type invariants | |
-gnato22 |
handling of intermediate overflow: minimized mode |
-gnato23 |
handling of intermediate overflow: minimized mode; |
| eliminated mode within assertions, pre/postconditions, | |
| and type invariants | |
-gnateE |
generate extra information in exception messages |
-gnat2022 |
use Ada 2022 standard instead of default version |
-gnatX |
enable GNAT implementation extensions and latest Ada version |
In the case of mutually-exclusive switches (such as -gnato, -gnato0,
-gnato11, -gnato21, -gnato22, and -gnato23), only one of the switches
can be used in a code:: directive.
Alternative GNAT toolchain
By default, the standard version of the GNAT toolchain specified in the frontend is used in the testing phase. The list of versions for each specific tool from the GNAT toolchain can be found in this file:
frontend/py_modules/code_projects/toolchain.ini
An alternative version of a tool can be selected for a specific code block
using the gnat=, gnatprove= or gprbuild= parameters. For example:
.. code:: ada no_button gnat=12.2.0-1 gnatprove=12.1.0-1 project=Courses.SPARK_Course.Introduction.Example_01
In this example, GNAT 12.2.0-1 and GNATprove 12.1.0-1 are used instead of the default version of each tool.
Note that this tool selection mechanism can only be used when the no_button
parameter is specified. Also, only the versions listed in the file mentioned
above can be used.
Code block classes
For special cases, a class can be added to the source code block. For
example:
.. code:: ada no_button project=Courses.Embedded_Ada.Exceptions.Classification_Of_Errors.Data_Validity_Bounded_Error
:class: ada-run
with Ada.Text_IO; use Ada.Text_IO;
procedure Show_Bounded_Error is
subtype Int_1_10 is
Integer range 1 .. 10;
I1 : Int_1_10;
I1_Overlay : Integer
with Address => I1'Address,
Import,
Volatile;
begin
I1_Overlay := 0;
Put_Line ("I1 = " & I1'Image);
I1 := I1 + 1;
Put_Line ("I1 = " & I1'Image);
end Show_Bounded_Error;```
The .. code:: directive above includes the no_button parameter, so no
button is available for this code block in the HTML output. Nevertheless,
because of ada-run class, the code extracted from this code block is also
run in the testing phase — in addition to being built. The relevant information
from the test (e.g. compiler and runtime output) is displayed next to code
block in the generated HTML or e-book output.
The following classes are available for the testing phase:
-
ada-nocheck: testing of this specific code block is completely skipped. -
nosyntax-check: code must not be checked for syntax errors. (Note that the code block is still compiled in the testing phase.) -
ada-syntax-only: compiler should only check for syntax errors (no compilation).
If an error is expected during the testing phase, one of the following classes must be used:
-
ada-expect-compile-error: a compilation error is expected. -
ada-run-expect-failure: a run-time error is expected.
When the no_button parameter is used, the following classes are available to
compile or run the code examples:
-
ada-compileandc-compile: to compile Ada or C code, respectively. -
ada-runandc-run: to build and run Ada or C code, respectively. -
ada-prove,ada-prove-flow,ada-prove-flow-report-all,ada-prove-report-all: to prove the code in various forms using gnatprove.
These classes are useful to generate static output (such as compiler warnings and runtime output) that is included with a code example in the generated output.
When the run_button parameter is used, the following classes are available:
ada-norunandc-norun: to explicitly deactivate the run of Ada or C code, respectively, during the testing phase.
Lab exercises
Laboratory exercises (or labs) can be created by using an adapted version of
the the code:: directive. Readers use these labs to test their knowledge by
interacting with the code widget and checking whether the resulting code is OK
or not.
In order to maintain consistency, the following guidelines should be followed:
-
Each section (or subsection) should contain only one lab.
-
The description of a lab should include the following topics:
| Topic | Description |
|---|---|
| Goal | The goal of the lab (ideally, in one phrase) |
| Steps | The steps required to complete the lab |
| Requirements | What the lab is supposed to do |
| Remarks | Any remarks or hints that might be useful |
-
After the description, the interactive code block for the lab should be provided.
- Note that providing guiding hints in the lab source code — such as "Implement the application here!" — is highly recommended.
This is an example of a complete lab:
Hello World
-----------
**Goal**: create a "Hello World!" application.
**Steps**:
#. Complete the :ada:`Main` procedure.
**Requirements**:
#. The application must display the message "Hello World!".
**Remarks**:
#. The part that you have to modify is indicated by the
:ada:`-- Implement the application here!` comment in the
source code.
.. code:: ada lab=Labs.Intro_To_Ada.Imperative_Language.Hello_World
-- START LAB IO BLOCK
in 0:
out 0:Hello World!
-- END LAB IO BLOCK
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
begin
-- Implement the application here!
null;
end Main;
Lab code block
A code directive for a lab must use the lab parameter (with the lab= syntax)
instead of the project parameter (project=).
The lab code block includes an I/O block — which is placed before the actual Ada code — describing the test cases. Each test case contains the input that is provided to the test application — which is built upon the lab source code — and the expected output for that input.
In the I/O block, the input is indicated by the in <number>: entries, while
the corresponding expected output is indicated by the out <number>: entries.
For instance, for the first test case of a lab, in 0: and out 0: have to be
provided; for the second test case, in 1: and out 1: have to be provided,
and so on.
This is an example of a complete lab I/O block:
-- START LAB IO BLOCK
in 0:John
out 0:Hello John!
in 1:Joanna
out 1:Hello Joanna!
-- END LAB IO BLOCK
Newlines from the standard output must be replaced by whitespaces in the corresponding output entries. For example, if the expected output of a specific test case is the following:
Months:
- January
- February
then the corresponding output entry in the I/O block is:
out 0:Months: - January - February
In most cases, labs must process input data, which is provided to the test application in the standard input at runtime. Because of this dependency on input data, some sort of command-line parsing must be part of the Ada code of each lab — unless, of course, a specific lab doesn't require any input. For each test case, the actual output of the application is extracted by the test environment and compared to the expected output.
As an example, this is the complete code for a lab:
.. code:: ada lab=Labs.Intro_To_Ada.Imperative_Language.Greetings
-- START LAB IO BLOCK
in 0:John
out 0:Hello John!
in 1:Joanna
out 1:Hello Joanna!
-- END LAB IO BLOCK
with Ada.Command_Line; use Ada.Command_Line;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
procedure Greet (Name : String) is
begin
-- Implement the application here!
null;
end Greet;
begin
if Argument_Count < 1 then
Put_Line ("ERROR: missing arguments! Exiting...");
return;
elsif Argument_Count > 1 then
Put_Line ("Ignoring additional arguments...");
end if;
Greet (Argument (1));
end Main;
Note that using command-line arguments to indicate a specific test case is also
a valid approach when creating labs. For example, you could declare a
Test_Case_Index enumeration in the lab source code and let the command-line
arguments trigger each test case at runtime:
.. code:: ada lab=Labs.Intro_To_Ada.Modular_Programming.Monthly_Check
-- START LAB IO BLOCK
in 0:January_Chk
out 0:January
in 1:February_Chk
out 1:February
-- END LAB IO BLOCK
package Months is
-- type Month is...
procedure Display_Month (M : Month);
end Months;
with Ada.Text_IO; use Ada.Text_IO;
package body Months is
procedure Display_Month (M : Month);
end Months;
with Ada.Command_Line; use Ada.Command_Line;
with Ada.Text_IO; use Ada.Text_IO;
with Months; use Months;
procedure Main is
type Test_Case_Index is
(January_Chk, February_Chk);
procedure Check (TC : Test_Case_Index) is
begin
case TC is
when January_Chk =>
Display_Month (Jan);
when February_Chk =>
Display_Month (Feb);
end case;
end Check;
begin
if Argument_Count < 1 then
Put_Line ("ERROR: missing arguments! Exiting...");
return;
elsif Argument_Count > 1 then
Put_Line ("Ignoring additional arguments...");
end if;
Check (Test_Case_Index'Value (Argument (1)));
end Main;
In this case, in 0: and in 1: contain the enumeration name, which
corresponds to a test case triggered by the Check procedure.