diff --git a/.github/workflows/sphinx-content-tests.js.yml b/.github/workflows/sphinx-content-tests.js.yml index 6d3441890..aee91441c 100644 --- a/.github/workflows/sphinx-content-tests.js.yml +++ b/.github/workflows/sphinx-content-tests.js.yml @@ -30,7 +30,17 @@ jobs: - name: Install OS Deps run: | sudo apt-get install -y \ - graphviz + graphviz \ + texlive-latex-base \ + texlive-latex-recommended \ + texlive-latex-extra \ + texlive-fonts-recommended \ + texlive-fonts-extra \ + latexmk \ + texlive-xetex \ + fonts-lmodern \ + fonts-open-sans \ + fonts-dejavu - name: Install dependencies run: yarn --frozen-lockfile - name: ada-actions/toolchain @@ -47,3 +57,12 @@ jobs: run: make SPHINXOPTS="-W" test_engine - name: Run SPHINX content tests run: make -k test_content + - name: Build PDF books including build/runtime output + run: make pdf_books + - name: Archive PDF books in artifact + uses: actions/upload-artifact@v2 + with: + name: learn-pdf-books + path: | + frontend/dist/pdf_books + retention-days: 1 diff --git a/content/courses/Ada_For_The_Embedded_C_Developer/chapters/01_Introduction.rst b/content/courses/Ada_For_The_Embedded_C_Developer/chapters/01_Introduction.rst index 8002c022d..cece8deca 100644 --- a/content/courses/Ada_For_The_Embedded_C_Developer/chapters/01_Introduction.rst +++ b/content/courses/Ada_For_The_Embedded_C_Developer/chapters/01_Introduction.rst @@ -24,7 +24,7 @@ program in Ada and C: [C] -.. code:: c cli_input run_button project=Courses.Ada_For_C_Embedded_Dev.Introduction.Add_Angles_C +.. code:: c cli_input run_button project=Courses.Ada_For_Embedded_C_Dev.Introduction.Add_Angles_C !main.c #include @@ -60,7 +60,7 @@ program in Ada and C: [Ada] -.. code:: ada cli_input run_button project=Courses.Ada_For_C_Embedded_Dev.Introduction.Add_Angles_Ada +.. code:: ada cli_input run_button project=Courses.Ada_For_Embedded_C_Dev.Introduction.Add_Angles_Ada with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; diff --git a/content/courses/Ada_For_The_Embedded_C_Developer/chapters/02_Perspective.rst b/content/courses/Ada_For_The_Embedded_C_Developer/chapters/02_Perspective.rst index 34e60df3c..efa39f3b8 100644 --- a/content/courses/Ada_For_The_Embedded_C_Developer/chapters/02_Perspective.rst +++ b/content/courses/Ada_For_The_Embedded_C_Developer/chapters/02_Perspective.rst @@ -119,7 +119,7 @@ program: [C] -.. code:: c run_button manual_chop project=Courses.Ada_For_C_Embedded_Dev.Perspective.Hello_World_C +.. code:: c run_button manual_chop project=Courses.Ada_For_Embedded_C_Dev.Perspective.Hello_World_C !main.c #include @@ -132,7 +132,7 @@ program: [Ada] -.. code:: ada run_button project=Courses.Ada_For_C_Embedded_Dev.Perspective.Hello_World_Ada +.. code:: ada run_button project=Courses.Ada_For_Embedded_C_Dev.Perspective.Hello_World_Ada with Ada.Text_IO; @@ -1945,7 +1945,7 @@ values from :ada:`'a'` to :ada:`'z'`: !main.c #include - void main(void) + int main(int argc, const char * argv[]) { char Arr [26]; char C = 'a'; @@ -2435,7 +2435,7 @@ Pointers to scalar objects in Ada and C look like: [Ada] -.. code:: ada run_button project=Courses.Ada_For_C_Embedded_Dev.Perspective.Access_To_Scalars +.. code:: ada run_button project=Courses.Ada_For_Embedded_C_Dev.Perspective.Access_To_Scalars procedure Main is type A_Int is access Integer; @@ -2446,7 +2446,7 @@ Pointers to scalar objects in Ada and C look like: [C] -.. code:: c run_button project=Courses.Ada_For_C_Embedded_Dev.Perspective.Pointers_To_Scalars +.. code:: c run_button project=Courses.Ada_For_Embedded_C_Dev.Perspective.Pointers_To_Scalars !main.c #include @@ -2463,7 +2463,7 @@ In Ada, an initializer can be specified with the allocation by appending [Ada] -.. code:: ada run_button project=Courses.Ada_For_C_Embedded_Dev.Perspective.Access_Initialization +.. code:: ada run_button project=Courses.Ada_For_Embedded_C_Dev.Perspective.Access_Initialization procedure Main is type A_Int is access Integer; @@ -2485,7 +2485,7 @@ objects that have gone out of scope. For example: [Ada] -.. code:: ada run_button project=Courses.Ada_For_C_Embedded_Dev.Perspective.Access_All +.. code:: ada run_button project=Courses.Ada_For_Embedded_C_Dev.Perspective.Access_All procedure Main is type A_Int is access all Integer; @@ -2497,7 +2497,7 @@ objects that have gone out of scope. For example: [C] -.. code:: c run_button project=Courses.Ada_For_C_Embedded_Dev.Perspective.Access_All_C +.. code:: c run_button project=Courses.Ada_For_Embedded_C_Dev.Perspective.Access_All_C !main.c int main(int argc, const char * argv[]) @@ -2515,7 +2515,7 @@ the access type as follows: [Ada] -.. code:: ada run_button project=Courses.Ada_For_C_Embedded_Dev.Perspective.Unchecked_Deallocation +.. code:: ada run_button project=Courses.Ada_For_Embedded_C_Dev.Perspective.Unchecked_Deallocation with Ada.Unchecked_Deallocation; @@ -2529,7 +2529,7 @@ the access type as follows: [C] -.. code:: c run_button project=Courses.Ada_For_C_Embedded_Dev.Perspective.Free +.. code:: c run_button project=Courses.Ada_For_Embedded_C_Dev.Perspective.Free !main.c #include @@ -2656,7 +2656,7 @@ Here's a first example: #include #include "proc.h" - void main (void) + int main(int argc, const char * argv[]) { int v1, v2; diff --git a/content/courses/Ada_For_The_Embedded_C_Developer/chapters/03_Concurrency.rst b/content/courses/Ada_For_The_Embedded_C_Developer/chapters/03_Concurrency.rst index 7dcb962b2..8d8b7e377 100644 --- a/content/courses/Ada_For_The_Embedded_C_Developer/chapters/03_Concurrency.rst +++ b/content/courses/Ada_For_The_Embedded_C_Developer/chapters/03_Concurrency.rst @@ -603,7 +603,7 @@ using the Ravenscar profile. For example: [Ada] -.. code:: ada compile_button project=Courses.Ada_For_C_Embedded_Dev.Concurrency.Ravenscar +.. code:: ada compile_button project=Courses.Ada_For_Embedded_C_Dev.Concurrency.Ravenscar :class: ada-expect-compile-error package My_Tasks is @@ -643,7 +643,7 @@ in the main application: [Ada] -.. code:: ada run_button project=Courses.Ada_For_C_Embedded_Dev.Concurrency.Ravenscar +.. code:: ada run_button project=Courses.Ada_For_Embedded_C_Dev.Concurrency.Ravenscar with My_Tasks; use My_Tasks; diff --git a/content/courses/Ada_For_The_Embedded_C_Developer/chapters/04_Embedded.rst b/content/courses/Ada_For_The_Embedded_C_Developer/chapters/04_Embedded.rst index 9d1e9a94c..c79f51134 100644 --- a/content/courses/Ada_For_The_Embedded_C_Developer/chapters/04_Embedded.rst +++ b/content/courses/Ada_For_The_Embedded_C_Developer/chapters/04_Embedded.rst @@ -169,7 +169,7 @@ assembler as well as source-level variables to be used for input and output: [Ada] -.. code:: ada no_button project=Courses.Ada_For_C_Embedded_Dev.Embedded.Assembly_Code +.. code:: ada no_button project=Courses.Ada_For_Embedded_C_Dev.Embedded.Assembly_Code with System.Machine_Code; use System.Machine_Code; with Interfaces; use Interfaces; @@ -350,7 +350,7 @@ obvious when looking at this code snippet: [Ada] -.. code:: ada run_button project=Courses.Ada_For_C_Embedded_Dev.Embedded.Fixed_Point +.. code:: ada run_button project=Courses.Ada_For_Embedded_C_Dev.Embedded.Fixed_Point package Fixed_Definitions is diff --git a/content/courses/Ada_For_The_Embedded_C_Developer/chapters/08_Performance.rst b/content/courses/Ada_For_The_Embedded_C_Developer/chapters/08_Performance.rst index 02e52a493..659227dfb 100644 --- a/content/courses/Ada_For_The_Embedded_C_Developer/chapters/08_Performance.rst +++ b/content/courses/Ada_For_The_Embedded_C_Developer/chapters/08_Performance.rst @@ -219,6 +219,7 @@ consider the following example in C: [C] .. code:: c manual_chop run_button project=Courses.Ada_For_Embedded_C_Dev.Performance.Division_By_Zero + :class: c-run-expect-failure !main.c #include diff --git a/content/courses/intro-to-ada/chapters/arrays.rst b/content/courses/intro-to-ada/chapters/arrays.rst index 26ec01cad..32387d437 100644 --- a/content/courses/intro-to-ada/chapters/arrays.rst +++ b/content/courses/intro-to-ada/chapters/arrays.rst @@ -12,7 +12,6 @@ Arrays in Ada are used to define contiguous collections of elements that can be selected by indexing. Here's a simple example: .. code:: ada run_button project=Courses.Intro_To_Ada.Arrays.Greet - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; @@ -72,7 +71,6 @@ to index into the array. equivalence between an array and a pointer to its initial element. .. code:: ada run_button project=Courses.Intro_To_Ada.Arrays.Array_Bounds_Example - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; @@ -113,7 +111,6 @@ Since you can use any discrete type to index an array, enumeration types are permitted. .. code:: ada run_button project=Courses.Intro_To_Ada.Arrays.Month_Example - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; @@ -192,7 +189,7 @@ an element outside of the bounds of the array, you will get a run-time error instead of accessing random memory as in unsafe languages. .. code:: ada run_button project=Courses.Intro_To_Ada.Arrays.Greet_3 - :class: ada-run, ada-run-expect-failure + :class: ada-run-expect-failure with Ada.Text_IO; use Ada.Text_IO; @@ -317,7 +314,6 @@ in that case, the bounds will need to be provided when creating instances of the type. .. code:: ada run_button project=Courses.Intro_To_Ada.Arrays.Unconstrained_Array_Example - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; @@ -414,7 +410,6 @@ literals, as we can see in the example below. end String_Literals; .. code:: ada run_button project=Courses.Intro_To_Ada.Arrays.Greet_4 - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; @@ -439,7 +434,6 @@ type if you supply an initialization, since the bounds can be deduced from the initialization expression. .. code:: ada run_button project=Courses.Intro_To_Ada.Arrays.Greet_5 - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; @@ -510,7 +504,8 @@ cases like this one, it is impossible for a compiler to know in the general case if you are assigning a value of the correct length, so this violation will generally result in a run-time error. -.. attention:: +.. admonition:: Attention + While we will learn more about this later, it is important to know that arrays are not the only types whose instances might be of unknown size at compile-time. diff --git a/content/courses/intro-to-ada/chapters/exceptions.rst b/content/courses/intro-to-ada/chapters/exceptions.rst index 6e5e32611..b05b97010 100644 --- a/content/courses/intro-to-ada/chapters/exceptions.rst +++ b/content/courses/intro-to-ada/chapters/exceptions.rst @@ -14,7 +14,7 @@ Ada exceptions are not types, but instead objects, which may be peculiar to you if you're used to the way Java or Python support exceptions. Here's how you declare an exception: -.. code:: ada no_button project=Courses.Intro_To_Ada.Exceptions.Show_Exception +.. code:: ada compile_button project=Courses.Intro_To_Ada.Exceptions.Show_Exception package Exceptions is My_Except : exception; @@ -99,7 +99,7 @@ can add it to the statements block of your current subprogram: Put ("Cannot open input file"); end Open_File; -.. attention:: +.. admonition:: Attention Exception handlers have an important restriction that you need to be careful about: Exceptions raised in the declarative diff --git a/content/courses/intro-to-ada/chapters/generics.rst b/content/courses/intro-to-ada/chapters/generics.rst index a649e3123..84759f80c 100644 --- a/content/courses/intro-to-ada/chapters/generics.rst +++ b/content/courses/intro-to-ada/chapters/generics.rst @@ -15,7 +15,7 @@ by using the keyword :ada:`generic`. For example: .. raph-amiard: We are lacking a definition/link of metaprogramming. -.. code:: ada no_button project=Courses.Intro_To_Ada.Generics.Show_Simple_Generic +.. code:: ada compile_button project=Courses.Intro_To_Ada.Generics.Show_Simple_Generic generic type T is private; @@ -37,7 +37,7 @@ want to create an algorithm that works on any integer type, or even on any type at all, whether a numeric type or not. The following example declares a formal type :ada:`T` for the :ada:`Set` procedure. -.. code:: ada no_button project=Courses.Intro_To_Ada.Generics.Show_Formal_Type_Declaration +.. code:: ada compile_button project=Courses.Intro_To_Ada.Generics.Show_Formal_Type_Declaration generic type T is private; @@ -71,7 +71,7 @@ Formal object declaration Formal objects are similar to subprogram parameters. They can reference formal types declared in the formal specification. For example: -.. code:: ada no_button project=Courses.Intro_To_Ada.Generics.Show_Formal_Object_Declaration +.. code:: ada compile_button project=Courses.Intro_To_Ada.Generics.Show_Formal_Object_Declaration generic type T is private; @@ -95,7 +95,7 @@ We don't repeat the :ada:`generic` keyword for the body declaration of a generic subprogram or package. Instead, we start with the actual declaration and use the generic types and objects we declared. For example: -.. code:: ada no_button project=Courses.Intro_To_Ada.Generics.Show_Generic_Body_Definition +.. code:: ada compile_button project=Courses.Intro_To_Ada.Generics.Show_Generic_Body_Definition generic type T is private; diff --git a/content/courses/intro-to-ada/chapters/imperative_language.rst b/content/courses/intro-to-ada/chapters/imperative_language.rst index 827db9cca..e54526c5d 100644 --- a/content/courses/intro-to-ada/chapters/imperative_language.rst +++ b/content/courses/intro-to-ada/chapters/imperative_language.rst @@ -35,24 +35,26 @@ Here's a very simple imperative Ada program: which we'll assume is in the source file :file:`greet.adb`. -If you compile that source with the GNAT compiler and run the executable, -you will get an unsurprising result. +.. only:: builder_html -.. code-block:: sh + If you compile that source with the GNAT compiler and run the executable, + you will get an unsurprising result. - $ gprbuild greet.adb - using project file [...]_default.gpr - Compile - [Ada] greet.adb - Bind - [gprbind] greet.bexch - [Ada] greet.ali - Link - [link] greet.adb + .. code-block:: sh - $ ./greet - Hello, World! - $ + $ gprbuild greet.adb + using project file [...]_default.gpr + Compile + [Ada] greet.adb + Bind + [gprbind] greet.bexch + [Ada] greet.ali + Link + [link] greet.adb + + $ ./greet + Hello, World! + $ There are several noteworthy things in the above program: @@ -118,7 +120,7 @@ and subprogram parameter modes. Ada's :ada:`if` statement is pretty unsurprising in form and function: -.. code:: ada no_button project=Courses.Intro_To_Ada.Imperative_Language.Check_Positive +.. code:: ada compile_button project=Courses.Intro_To_Ada.Imperative_Language.Check_Positive with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; @@ -156,7 +158,7 @@ integer value). Here's a slight variation on the example, which illustrates an :ada:`if` statement with an :ada:`else` part: -.. code:: ada no_button project=Courses.Intro_To_Ada.Imperative_Language.Check_Positive_2 +.. code:: ada compile_button project=Courses.Intro_To_Ada.Imperative_Language.Check_Positive_2 with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; @@ -180,7 +182,7 @@ displays the value followed by the String " is not a positive number". Our final variation illustrates an :ada:`if` statement with :ada:`elsif` sections: -.. code:: ada no_button project=Courses.Intro_To_Ada.Imperative_Language.Check_Direction +.. code:: ada compile_button project=Courses.Intro_To_Ada.Imperative_Language.Check_Direction with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; @@ -251,16 +253,17 @@ discrete range. end loop; end Greet_5a; -Executing this procedure yields the following output: +.. only:: builder_html -.. code-block:: sh + Executing this procedure yields the following output: - Hello, World! 1 - Hello, World! 2 - Hello, World! 3 - Hello, World! 4 - Hello, World! 5 + .. code-block:: sh + Hello, World! 1 + Hello, World! 2 + Hello, World! 3 + Hello, World! 4 + Hello, World! 5 A few things to note: @@ -298,15 +301,17 @@ To iterate backwards over a range, use the :ada:`reverse` keyword: end loop; end Greet_5a_Reverse; -Executing this procedure yields the following output: +.. only:: builder_html + + Executing this procedure yields the following output: -.. code-block:: sh + .. code-block:: sh - Hello, World! 5 - Hello, World! 4 - Hello, World! 3 - Hello, World! 2 - Hello, World! 1 + Hello, World! 5 + Hello, World! 4 + Hello, World! 3 + Hello, World! 2 + Hello, World! 1 The bounds of a :ada:`for` loop may be computed at run-time; they are evaluated once, before the loop body is executed. If the value of the @@ -418,7 +423,7 @@ but with some important differences. Here's an example, a variation of a program that was shown earlier with an :ada:`if` statement: -.. code:: ada no_button project=Courses.Intro_To_Ada.Imperative_Language.Check_Direction_2 +.. code:: ada compile_button project=Courses.Intro_To_Ada.Imperative_Language.Check_Direction_2 with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; @@ -542,7 +547,7 @@ A declaration cannot appear as a statement. If you need to declare a local variable amidst the statements, you can introduce a new declarative region with a block statement: -.. code:: ada no_button project=Courses.Intro_To_Ada.Imperative_Language.Greet_6 +.. code:: ada compile_button project=Courses.Intro_To_Ada.Imperative_Language.Greet_6 with Ada.Text_IO; use Ada.Text_IO; @@ -589,7 +594,7 @@ If expressions Here's an alternative version of an example we saw earlier; the :ada:`if` statement has been replaced by an :ada:`if` expression: -.. code:: ada no_button project=Courses.Intro_To_Ada.Imperative_Language.Check_Positive +.. code:: ada compile_button project=Courses.Intro_To_Ada.Imperative_Language.Check_Positive with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; diff --git a/content/courses/intro-to-ada/chapters/modular_programming.rst b/content/courses/intro-to-ada/chapters/modular_programming.rst index 379fd7853..99ba340e0 100644 --- a/content/courses/intro-to-ada/chapters/modular_programming.rst +++ b/content/courses/intro-to-ada/chapters/modular_programming.rst @@ -21,7 +21,7 @@ Packages Here is an example of a package declaration in Ada: -.. code:: ada no_button project=Courses.Intro_To_Ada.Modular_Programming.Week +.. code:: ada compile_button project=Courses.Intro_To_Ada.Modular_Programming.Week package Week is @@ -38,7 +38,6 @@ Here is an example of a package declaration in Ada: And here is how you use it: .. code:: ada run_button project=Courses.Intro_To_Ada.Modular_Programming.Week - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; with Week; @@ -121,7 +120,6 @@ In fact, we have been using the :ada:`use` clause since almost the beginning of this tutorial. .. code:: ada run_button project=Courses.Intro_To_Ada.Modular_Programming.Week - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; -- ^ Make every entity of the Ada.Text_IO package @@ -153,7 +151,7 @@ declarations and no body. That's not a mistake: in a package specification, which is what is illustrated above, you cannot declare bodies. Those have to be in the package body. -.. code:: ada no_button project=Courses.Intro_To_Ada.Modular_Programming.Operations +.. code:: ada compile_button project=Courses.Intro_To_Ada.Modular_Programming.Operations package Operations is @@ -255,7 +253,7 @@ is called :ada:`Text_IO`. In the previous examples, we've been using the Let's begin our discussion on child packages by taking our previous :ada:`Week` package: -.. code:: ada no_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages +.. code:: ada compile_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages package Week is @@ -271,7 +269,7 @@ Let's begin our discussion on child packages by taking our previous If we want to create a child package for :ada:`Week`, we may write: -.. code:: ada no_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages +.. code:: ada compile_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages package Week.Child is @@ -282,7 +280,7 @@ If we want to create a child package for :ada:`Week`, we may write: Here, :ada:`Week` is the parent package and :ada:`Child` is the child package. This is the corresponding package body of :ada:`Week.Child`: -.. code:: ada no_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages +.. code:: ada compile_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages package body Week.Child is @@ -306,7 +304,6 @@ we can use elements from this child package in a subprogram by simply writing we write :ada:`use Week.Child` in addition. For example: .. code:: ada run_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; with Week.Child; use Week.Child; @@ -326,7 +323,7 @@ the hierarchy of the previous source-code example by declaring a be the parent of the :ada:`Grandchild` package. Let's consider this implementation: -.. code:: ada no_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages +.. code:: ada compile_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages package Week.Child.Grandchild is @@ -348,7 +345,6 @@ same way as before: we can reuse the previous test application and adapt the :ada:`with` and :ada:`use`, and the function call. This is the updated code: .. code:: ada run_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; with Week.Child.Grandchild; use Week.Child.Grandchild; @@ -369,7 +365,7 @@ So far, we've seen a single child package of a parent package. However, a parent package can also have multiple children. We could extend the example above and implement a :ada:`Week.Child_2` package. For example: -.. code:: ada no_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages +.. code:: ada compile_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages package Week.Child_2 is @@ -383,7 +379,7 @@ but it's also the parent of the :ada:`Child_2` package. In the same way, This is the corresponding package body of :ada:`Week.Child_2`: -.. code:: ada no_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages +.. code:: ada compile_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages package body Week.Child_2 is @@ -397,7 +393,6 @@ This is the corresponding package body of :ada:`Week.Child_2`: We can now reference both children in our test application: .. code:: ada run_button project=Courses.Intro_To_Ada.Modular_Programming.Child_Packages - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; with Week.Child; use Week.Child; @@ -419,7 +414,7 @@ for elements declared in the package body of a parent package. Let's consider the package :ada:`Book` and its child :ada:`Additional_Operations`: -.. code:: ada no_button project=Courses.Intro_To_Ada.Modular_Programming.Visibility +.. code:: ada compile_button project=Courses.Intro_To_Ada.Modular_Programming.Visibility package Book is @@ -441,7 +436,7 @@ Let's consider the package :ada:`Book` and its child This is the body of both packages: -.. code:: ada no_button project=Courses.Intro_To_Ada.Modular_Programming.Visibility +.. code:: ada compile_button project=Courses.Intro_To_Ada.Modular_Programming.Visibility package body Book is @@ -491,7 +486,7 @@ implementation of the :ada:`Get_Extended_Author` function to retrieve this string. Likewise, we can use this strategy to implement the :ada:`Get_Extended_Title` function. This is the adapted code: -.. code:: ada no_button project=Courses.Intro_To_Ada.Modular_Programming.Visibility +.. code:: ada compile_button project=Courses.Intro_To_Ada.Modular_Programming.Visibility package body Book.Additional_Operations is @@ -510,7 +505,6 @@ string. Likewise, we can use this strategy to implement the This is a simple test application for the packages above: .. code:: ada run_button project=Courses.Intro_To_Ada.Modular_Programming.Visibility - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; with Book.Additional_Operations; use Book.Additional_Operations; diff --git a/content/courses/intro-to-ada/chapters/more_about_types.rst b/content/courses/intro-to-ada/chapters/more_about_types.rst index 234ddbc58..8fa9fdc62 100644 --- a/content/courses/intro-to-ada/chapters/more_about_types.rst +++ b/content/courses/intro-to-ada/chapters/more_about_types.rst @@ -48,7 +48,7 @@ convenient: However, note that as soon as you used a named association, all subsequent components likewise need to be specified with names associations. -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Points +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Points package Points is type Point is record @@ -73,7 +73,7 @@ in the section on :ref:`enumeration types `. Let's take a simple example: it is possible in Ada to have functions that have the same name, but different types for their parameters. -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Overloading +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Overloading package Pkg is function F (A : Integer) return Integer; @@ -87,7 +87,7 @@ overloading. One of the novel aspects of Ada's overloading facility is the ability to resolve overloading based on the return type of a function. -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Overloading +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Overloading package Pkg is type SSID is new Integer; @@ -150,7 +150,7 @@ This is where a qualified expression becomes useful. Syntactically the target of a qualified expression can be either any expression in parentheses, or an aggregate: -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Qual_Expr +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Qual_Expr package Qual_Expr is type Point is record @@ -211,7 +211,7 @@ pointers: Here is how you declare a simple pointer type, or access type, in Ada: -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Access_Types +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Access_Types package Dates is type Months is (January, February, March, April, May, June, July, @@ -295,7 +295,7 @@ Once we have declared an access type, we need a way to give variables of the types a meaningful value! You can allocate a value of an access type with the :ada:`new` keyword in Ada. -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Access_Types +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Access_Types with Dates; use Dates; @@ -309,7 +309,7 @@ with the :ada:`new` keyword in Ada. If the type you want to allocate needs constraints, you can put them in the subtype indication, just as you would do in a variable declaration: -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Access_Types +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Access_Types with Dates; use Dates; @@ -327,7 +327,7 @@ In some cases, though, allocating just by specifying the type is not ideal, so Ada also allows you to initialize along with the allocation. This is done via the qualified expression syntax: -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Access_Types +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Access_Types with Dates; use Dates; @@ -348,7 +348,7 @@ pointer. Dereferencing a pointer uses the :ada:`.all` syntax in Ada, but is often not needed - in many cases, the access value will be implicitly dereferenced for you: -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Access_Types +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Access_Types with Dates; use Dates; @@ -417,7 +417,7 @@ naturally defined through two types, a record type and an access type, that are mutually dependent. To declare mutually dependent types, you can use an incomplete type declaration: -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Simple_List +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Simple_List package Simple_List is type Node; @@ -448,7 +448,7 @@ known at compile time. This is illustrated in the example below: .. ?? an elaboration pragma is used. .. ?? Consider simplifying or restructuring the example to avoid this issue -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Var_Size_Record +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Var_Size_Record package Runtime_Length is function Compute_Max_Len return Natural; @@ -487,7 +487,7 @@ different sizes. You can get analogous functionality for records, too, using a special kind of field that is called a discriminant: -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Var_Size_Record_2 +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Var_Size_Record_2 package Var_Size_Record_2 is type Items_Array is array (Positive range <>) of Integer; @@ -534,7 +534,6 @@ specify their values in aggregates, as seen above, and you can access their values via the dot notation. .. code:: ada run_button project=Courses.Intro_To_Ada.More_About_Types.Var_Size_Record_2 - :class: ada-run with Var_Size_Record_2; use Var_Size_Record_2; with Ada.Text_IO; use Ada.Text_IO; @@ -572,7 +571,7 @@ However, discriminants can also be used to obtain the functionality of what are sometimes called "variant records": records that can contain different sets of fields. -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Variant_Record +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Variant_Record package Variant_Record is type Expr; -- Forward declaration of Expr @@ -617,7 +616,6 @@ If you try to access a field that is not valid for your record, a Here is how you could write an evaluator for expressions: .. code:: ada run_button project=Courses.Intro_To_Ada.More_About_Types.Variant_Record - :class: ada-run with Variant_Record; use Variant_Record; with Ada.Text_IO; use Ada.Text_IO; @@ -801,7 +799,7 @@ fixed-point data types |mdash| you can find more details in this discussion about the `Q format `_. We may also rewrite this code with an exact type definition: -.. code:: ada no_button project=Courses.Intro_To_Ada.More_About_Types.Normalized_Adapted_Fixed_Point_Type +.. code:: ada compile_button project=Courses.Intro_To_Ada.More_About_Types.Normalized_Adapted_Fixed_Point_Type procedure Normalized_Adapted_Fixed_Point_Type is type TQ31 is delta 2.0 ** (-31) range -1.0 .. 1.0 - 2.0 ** (-31); diff --git a/content/courses/intro-to-ada/chapters/object_oriented_programming.rst b/content/courses/intro-to-ada/chapters/object_oriented_programming.rst index b68412d02..5e29afccd 100644 --- a/content/courses/intro-to-ada/chapters/object_oriented_programming.rst +++ b/content/courses/intro-to-ada/chapters/object_oriented_programming.rst @@ -76,7 +76,7 @@ brushed on, but not really covered, up to now: You can create one or more new types from every type in Ada. Type derivation is built into the language. -.. code:: ada no_button project=Courses.Intro_To_Ada.Object_Oriented_Programming.Newtypes +.. code:: ada compile_button project=Courses.Intro_To_Ada.Object_Oriented_Programming.Newtypes package Newtypes is type Point is record @@ -184,7 +184,7 @@ functionality is added: Let's see our first tagged type declarations: -.. code:: ada no_button project=Courses.Intro_To_Ada.Object_Oriented_Programming.Tagged_Types +.. code:: ada compile_button project=Courses.Intro_To_Ada.Object_Oriented_Programming.Tagged_Types package P is type My_Class is tagged null record; @@ -329,7 +329,9 @@ type, namely an object of a classwide type. -- Dispatching: Calls My_Class.Foo end Main; -.. attention:: You can convert an object of type :ada:`Derived` to an +.. admonition:: Attention + + You can convert an object of type :ada:`Derived` to an object of type :ada:`My_Class`. This is called a *view conversion* in Ada parlance and is useful, for example, if you want to call a parent method. @@ -341,7 +343,6 @@ type, namely an object of a classwide type. affect the original one. .. code:: ada run_button project=Courses.Intro_To_Ada.Object_Oriented_Programming.Tagged_Types - :class: ada-run with P; use P; @@ -400,7 +401,6 @@ the dot notation. Any remaining parameter are passed normally: .. code:: ada run_button project=Courses.Intro_To_Ada.Object_Oriented_Programming.Tagged_Types - :class: ada-run with P; use P; @@ -435,8 +435,7 @@ applied to tagged types, as we'll see in this section. This is an example of a tagged private type: -.. code:: ada no_button project=Courses.Intro_To_Ada.Object_Oriented_Programming.Tagged_Private_Types - :class: ada-syntax-only +.. code:: ada compile_button project=Courses.Intro_To_Ada.Object_Oriented_Programming.Tagged_Private_Types package P is type T is tagged private; @@ -448,8 +447,7 @@ This is an example of a tagged private type: This is an example of a tagged limited type: -.. code:: ada no_button project=Courses.Intro_To_Ada.Object_Oriented_Programming.Tagged_Limited_Types - :class: ada-syntax-only +.. code:: ada compile_button project=Courses.Intro_To_Ada.Object_Oriented_Programming.Tagged_Limited_Types package P is type T is tagged limited record @@ -514,8 +512,7 @@ In this section, we'll discuss an useful pattern for object-oriented programming in Ada: classwide access type. Let's start with an example where we declare a tagged type :ada:`T` and a derived type :ada:`T_New`: -.. code:: ada no_button project=Courses.Intro_To_Ada.Object_Oriented_Programming.Classwide_Error - :class: ada-syntax-only +.. code:: ada compile_button project=Courses.Intro_To_Ada.Object_Oriented_Programming.Classwide_Error package P is type T is tagged null record; diff --git a/content/courses/intro-to-ada/chapters/privacy.rst b/content/courses/intro-to-ada/chapters/privacy.rst index 0061d55f2..804874625 100644 --- a/content/courses/intro-to-ada/chapters/privacy.rst +++ b/content/courses/intro-to-ada/chapters/privacy.rst @@ -47,7 +47,7 @@ Abstract data types With this high-level granularity, it might not seem obvious how to hide the implementation details of a type. Here is how it can be done in Ada: -.. code:: ada no_button project=Courses.Intro_To_Ada.Privacy.Stacks +.. code:: ada compile_button project=Courses.Intro_To_Ada.Privacy.Stacks package Stacks is type Stack is private; diff --git a/content/courses/intro-to-ada/chapters/records.rst b/content/courses/intro-to-ada/chapters/records.rst index edc873f8f..f9ee2b143 100644 --- a/content/courses/intro-to-ada/chapters/records.rst +++ b/content/courses/intro-to-ada/chapters/records.rst @@ -79,7 +79,6 @@ type mentioned above, we can access the :ada:`Year` component by writing Let's look at an example: .. code:: ada run_button project=Courses.Intro_To_Ada.Records.Record_Selection - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; @@ -147,7 +146,6 @@ using the :ada:`Year` component of :ada:`Some_Day`. Let's look at a complete example: .. code:: ada run_button project=Courses.Intro_To_Ada.Arrays.Record_Component_Renaming - :class: ada-run package Dates is diff --git a/content/courses/intro-to-ada/chapters/standard_library_containers.rst b/content/courses/intro-to-ada/chapters/standard_library_containers.rst index 2582971e8..6615ad04c 100644 --- a/content/courses/intro-to-ada/chapters/standard_library_containers.rst +++ b/content/courses/intro-to-ada/chapters/standard_library_containers.rst @@ -28,7 +28,7 @@ Instantiation Here's an example showing the instantiation and declaration of a vector :ada:`V`: -.. code:: ada no_button project=Courses.Intro_To_Ada.Standard_Library.Show_Vector_Inst +.. code:: ada compile_button project=Courses.Intro_To_Ada.Standard_Library.Show_Vector_Inst with Ada.Containers.Vectors; diff --git a/content/courses/intro-to-ada/chapters/standard_library_files_streams.rst b/content/courses/intro-to-ada/chapters/standard_library_files_streams.rst index 937dd72b9..03b8d03c1 100644 --- a/content/courses/intro-to-ada/chapters/standard_library_files_streams.rst +++ b/content/courses/intro-to-ada/chapters/standard_library_files_streams.rst @@ -68,6 +68,7 @@ Let's see an example that writes information into a new text file and then reads it back from the same file: .. code:: ada no_button project=Courses.Intro_To_Ada.Standard_Library.Show_Simple_Text_File_IO + :class: ada-run with Ada.Text_IO; use Ada.Text_IO; @@ -93,6 +94,7 @@ library also includes a :ada:`Reset` procedure, which, as the name implies, resets (erases) all the information from the file. For example: .. code:: ada no_button project=Courses.Intro_To_Ada.Standard_Library.Show_Text_File_Reset + :class: ada-run with Ada.Text_IO; use Ada.Text_IO; @@ -127,6 +129,7 @@ that context. The following example deletes a file and then tries to open the same file for reading: .. code:: ada no_button project=Courses.Intro_To_Ada.Standard_Library.Show_Text_File_Input_Except + :class: ada-run with Ada.Text_IO; use Ada.Text_IO; @@ -170,6 +173,7 @@ In the following example, we instantiate the :ada:`Ada.Sequential_IO` package for floating-point types: .. code:: ada no_button project=Courses.Intro_To_Ada.Standard_Library.Show_Seq_Float_IO + :class: ada-run with Ada.Text_IO; with Ada.Sequential_IO; @@ -204,6 +208,7 @@ following example uses a record that includes a Boolean and a floating-point value: .. code:: ada no_button project=Courses.Intro_To_Ada.Standard_Library.Show_Seq_Rec_IO + :class: ada-run with Ada.Text_IO; with Ada.Sequential_IO; @@ -265,6 +270,7 @@ the :ada:`Ada.Sequential_IO` package by the :ada:`Ada.Direct_IO` package. This is the new source code: .. code:: ada no_button project=Courses.Intro_To_Ada.Standard_Library.Show_Dir_Float_IO + :class: ada-run with Ada.Text_IO; with Ada.Direct_IO; @@ -304,6 +310,7 @@ the new position / index. You can use the :ada:`Index` function to retrieve the current index. Let's see an example: .. code:: ada no_button project=Courses.Intro_To_Ada.Standard_Library.Show_Dir_Float_In_Out_File + :class: ada-run with Ada.Text_IO; with Ada.Direct_IO; @@ -370,6 +377,7 @@ Let's look at a version of the :ada:`Show_Dir_Float_IO` procedure from the previous section that makes use of stream I/O instead of direct I/O: .. code:: ada no_button project=Courses.Intro_To_Ada.Standard_Library.Show_Float_Stream + :class: ada-run with Ada.Text_IO; with Ada.Streams.Stream_IO; use Ada.Streams.Stream_IO; @@ -421,6 +429,7 @@ The following example shows file I/O that mixes both strings of different lengths and floating-point values: .. code:: ada no_button project=Courses.Intro_To_Ada.Standard_Library.Show_String_Stream + :class: ada-run with Ada.Text_IO; with Ada.Streams.Stream_IO; use Ada.Streams.Stream_IO; diff --git a/content/courses/intro-to-ada/chapters/standard_library_numerics.rst b/content/courses/intro-to-ada/chapters/standard_library_numerics.rst index d7434f7c5..95ffeaabe 100644 --- a/content/courses/intro-to-ada/chapters/standard_library_numerics.rst +++ b/content/courses/intro-to-ada/chapters/standard_library_numerics.rst @@ -14,7 +14,7 @@ The :ada:`Ada.Numerics.Elementary_Functions` package provides common operations for floating-point types, such as square root, logarithm, and the trigonometric functions (e.g., sin, cos). For example: -.. code:: ada no_button project=Courses.Intro_To_Ada.Standard_Library.Show_Elem_Math +.. code:: ada run_button project=Courses.Intro_To_Ada.Standard_Library.Show_Elem_Math with Ada.Text_IO; use Ada.Text_IO; with Ada.Numerics; use Ada.Numerics; diff --git a/content/courses/intro-to-ada/chapters/strongly_typed_language.rst b/content/courses/intro-to-ada/chapters/strongly_typed_language.rst index 81c31cbb0..339566d29 100644 --- a/content/courses/intro-to-ada/chapters/strongly_typed_language.rst +++ b/content/courses/intro-to-ada/chapters/strongly_typed_language.rst @@ -296,7 +296,6 @@ The compiler will choose a floating-point representation that supports the required precision. For example: .. code:: ada run_button project=Courses.Intro_To_Ada.Strongly_Typed_Language.Custom_Floating_Types - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; @@ -600,7 +599,6 @@ some values while staying within a single type. This is where subtypes come into play. A subtype does not introduce a new type. .. code:: ada run_button project=Courses.Intro_To_Ada.Strongly_Typed_Language.Days_Subtype - :class: ada-run with Ada.Text_IO; use Ada.Text_IO; @@ -642,7 +640,7 @@ constraints are enforced at run time: if you violate a subtype constraint, an exception will be raised. .. code:: ada run_button project=Courses.Intro_To_Ada.Strongly_Typed_Language.Days_Subtype_Error - :class: ada-run, ada-run-expect-failure + :class: ada-run-expect-failure with Ada.Text_IO; use Ada.Text_IO; diff --git a/content/courses/intro-to-ada/chapters/subprograms.rst b/content/courses/intro-to-ada/chapters/subprograms.rst index c92af7a59..44e7f5791 100644 --- a/content/courses/intro-to-ada/chapters/subprograms.rst +++ b/content/courses/intro-to-ada/chapters/subprograms.rst @@ -17,13 +17,13 @@ does not. This example shows the declaration and definition of a function: -.. code:: ada no_button project=Courses.Intro_To_Ada.Subprograms.Increment +.. code:: ada compile_button project=Courses.Intro_To_Ada.Subprograms.Increment function Increment (I : Integer) return Integer; -- We declare (but don't define) a function with one -- parameter, returning an integer value -.. code:: ada no_button project=Courses.Intro_To_Ada.Subprograms.Increment +.. code:: ada compile_button project=Courses.Intro_To_Ada.Subprograms.Increment -- We define the Increment function @@ -44,7 +44,7 @@ section at all, for example: Here's another variation on the previous example: -.. code:: ada no_button project=Courses.Intro_To_Ada.Subprograms.Increment_By +.. code:: ada compile_button project=Courses.Intro_To_Ada.Subprograms.Increment_By function Increment_By (I : Integer := 0; @@ -57,7 +57,7 @@ C/C++, a call to a subprogram without parameters does not include parentheses. This is the implementation of the function above: -.. code:: ada no_button project=Courses.Intro_To_Ada.Subprograms.Increment_By +.. code:: ada compile_button project=Courses.Intro_To_Ada.Subprograms.Increment_By function Increment_By (I : Integer := 0; @@ -441,7 +441,7 @@ This can be useful, for example, to improve the readability of your application when you're using code from external sources that cannot be changed in your system. Let's look at an example: -.. code:: ada no_button project=Courses.Intro_To_Ada.Subprograms.Proc_Renaming +.. code:: ada compile_button project=Courses.Intro_To_Ada.Subprograms.Proc_Renaming procedure A_Procedure_With_Very_Long_Name_That_Cannot_Be_Changed (A_Message : String); diff --git a/content/courses/intro-to-ada/chapters/tasking.rst b/content/courses/intro-to-ada/chapters/tasking.rst index f5d7a2aa9..326fc7208 100644 --- a/content/courses/intro-to-ada/chapters/tasking.rst +++ b/content/courses/intro-to-ada/chapters/tasking.rst @@ -123,7 +123,7 @@ Synchronization also occurs if we move the task to a separate package. In the example below, we declare a task :ada:`T` in the package :ada:`Simple_Sync_Pkg`. -.. code:: ada no_button project=Courses.Intro_To_Ada.Tasking.Simple_Sync_Pkg +.. code:: ada compile_button project=Courses.Intro_To_Ada.Tasking.Simple_Sync_Pkg package Simple_Sync_Pkg is task T; @@ -131,7 +131,7 @@ the example below, we declare a task :ada:`T` in the package This is the corresponding package body: -.. code:: ada no_button project=Courses.Intro_To_Ada.Tasking.Simple_Sync_Pkg +.. code:: ada compile_button project=Courses.Intro_To_Ada.Tasking.Simple_Sync_Pkg with Ada.Text_IO; use Ada.Text_IO; @@ -363,7 +363,7 @@ the elapsed time (:ada:`Show_Elapsed_Time`) and a dummy :ada:`Computational_Intensive_App` procedure which is simulated by using a simple delay. This is the package specification: -.. code:: ada no_button project=Courses.Intro_To_Ada.Tasking.Show_Time +.. code:: ada compile_button project=Courses.Intro_To_Ada.Tasking.Show_Time with Ada.Real_Time; use Ada.Real_Time; @@ -385,7 +385,7 @@ simple delay. This is the package specification: And this is the package body: -.. code:: ada no_button project=Courses.Intro_To_Ada.Tasking.Show_Time +.. code:: ada compile_button project=Courses.Intro_To_Ada.Tasking.Show_Time with Ada.Text_IO; use Ada.Text_IO; diff --git a/frontend/Makefile b/frontend/Makefile index c0dbbb69e..9bf705992 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -4,7 +4,8 @@ SPHINXBUILD = sphinx-build SPHINXPROJ = learnadacorecom SPHINXCONF = sphinx BUILDDIR = dist -TEST_DRIVER = PYTHONPATH="$PYTHONPATH:sphinx" python3 tests/compile_blocks.py -B $(BUILDDIR)/test_output +SRC_TEST_DIR = $(BUILDDIR)/test_output +TEST_DRIVER = PYTHONPATH="$PYTHONPATH:sphinx:../backend" python3 tests/compile_blocks.py --keep_files -B $(SRC_TEST_DIR) MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) MKFILE_DIR := $(dir $(MKFILE_PATH)) @@ -162,6 +163,7 @@ $(BOOKS): @mkdir -p $(BUILDDIR)/pdf_books/$(SPHINX_DIR) @export SPHINX_TITLE="$(SPHINX_TITLE)"; \ export SPHINX_AUTHOR="$(SPHINX_AUTHOR)"; \ + export SRC_TEST_DIR="$(SRC_TEST_DIR)"; \ $(SPHINXBUILD) -M latexpdf $@ \ "$(BUILDDIR)" $(SPHINXOPTS) $(O) -v -c "$(SPHINXCONF)" @mv $(BUILDDIR)/latex/learnadacorecom.pdf $(BUILDDIR)/pdf_books/$(SPHINX_DIR)/$(SPHINX_PDF) diff --git a/frontend/sphinx/code_block_info.py b/frontend/sphinx/code_block_info.py new file mode 100644 index 000000000..95a85ef12 --- /dev/null +++ b/frontend/sphinx/code_block_info.py @@ -0,0 +1,59 @@ +"""Provide compiler/runtime information for code block. +""" + +from typing import List, Dict +import glob + +import os + +class CodeBlockInfo(): + """Code block info class + """ + + # Hard-coding directories: + # - build directory + # - base project directory + base_project_dir = "projects" + + def __init__(self, + project_name : str, + filename : str, + line_number: int): + """Widget constructor + """ + self.__project_name: str = project_name + self.__filename: str = filename + self.__line_number: int = line_number + self.__src_test_data_dir = "" + self.__data_available = False + + if 'SRC_TEST_DIR' in os.environ: + self.__src_test_data_dir = os.environ['SRC_TEST_DIR'] + self.__data_available = True + + def __get_project_dir(self) -> str: + return self.__project_name.replace(".", "/") + + def __get_code_block_dir(self) -> str: + return (self.__src_test_data_dir + "/" + + self.base_project_dir + "/" + + self.__get_project_dir() + "/" + + str(self.__line_number)) + + def get_info(self) -> Dict[str, str]: + """ + """ + info : Dict[str, str] = {} + + if not self.__data_available: + return info + + code_block_dir = self.__get_code_block_dir() + + for logfile in glob.glob(code_block_dir + "/*.log"): + with open(logfile) as f: + content = f.read() + log_type = os.path.splitext(os.path.basename(logfile))[0] + info[log_type] = content + + return info diff --git a/frontend/sphinx/widget/widget.py b/frontend/sphinx/widget/widget.py index ccc063186..e23ed9e9a 100644 --- a/frontend/sphinx/widget/widget.py +++ b/frontend/sphinx/widget/widget.py @@ -272,19 +272,21 @@ def parseOpts(self, opts: List[str]): self.__no_button = True elif opt == 'ada-syntax-only': self.__no_button = True - elif opt == 'ada-expect-compile-error': + elif opt in ['ada-expect-compile-error', + 'c-expect-compile-error']: # this is for testing, nothing to do here continue elif opt == 'ada-expect-prove-error': # this is for testing, nothing to do here continue - elif opt == 'ada-run': + elif opt in ['ada-run', 'c-run']: # this is for testing, nothing to do here continue - elif opt == 'ada-norun': + elif opt in ['ada-norun', 'c-norun']: # this is for testing, nothing to do here continue - elif opt == 'ada-run-expect-failure': + elif opt in ['ada-run-expect-failure', + 'c-run-expect-failure']: # this is for testing, nothing to do here continue else: diff --git a/frontend/sphinx/widget_extension.py b/frontend/sphinx/widget_extension.py index f1ae1b60e..088aae432 100644 --- a/frontend/sphinx/widget_extension.py +++ b/frontend/sphinx/widget_extension.py @@ -55,6 +55,7 @@ # Widget lib from widget.widget import Widget +from code_block_info import CodeBlockInfo # specifies the server address to set on the widgets WIDGETS_SERVER_URL = os.environ.get( @@ -77,7 +78,7 @@ class WidgetCodeDirective(Directive): 'name': directives.unchanged, } - def latex(self, widget: Widget): + def latex(self, widget: Widget, code_block_info : CodeBlockInfo): """Performs Latex parsing on nodes Used to create the PDF builds of the site. @@ -89,6 +90,7 @@ def latex(self, widget: Widget): List[nodes]: Returns a list of Latex nodes """ nodes_latex = [] + for f in widget.files: # Based on sphinx/directives/code.py @@ -108,9 +110,59 @@ def latex(self, widget: Widget): caption.source = literal.source caption.line = literal.line -# container_node += caption + container_node += caption + container_node += literal + + nodes_latex.append(container_node) + + def get_info_preamble(info_type : str) -> str: + known_info_type : Dict[str, str] = { + 'build' : '\\textbf{Build output}', + 'run' : '\\textbf{Runtime output}', + 'compile' : '\\textbf{Compilation output}', + 'prove' : '\\textbf{Prover output}' + } + if info_type in known_info_type: + return known_info_type[info_type] + else: + return "Let's " + info_type + " the example:" + + block_info : Dict[str, str] = code_block_info.get_info() + + for info_type in sorted(block_info): + + if block_info[info_type] == "": + # Do not show empty boxes + continue + + preamble_node = nodes.container( + '', literal_block=False, + classes=[]) + + preamble_raw = nodes.raw('', + get_info_preamble(info_type), + format='latex') + + preamble_node += preamble_raw + + container_node = nodes.container( + '', literal_block=True, + classes=['literal-block-wrapper']) + + literal = nodes.literal_block('', + block_info[info_type], + format='latex') + literal['language'] = 'none' + literal['source'] = info_type + + caption = nodes.caption('', info_type) + caption.source = literal.source + caption.line = literal.line + + # container_node += caption container_node += literal + nodes_latex.append(preamble_node) nodes_latex.append(container_node) return nodes_latex @@ -147,7 +199,10 @@ def run(self): # Attemping to detect HTML or Latex output by checking for 'html' in tags if 'html' not in self.state.state_machine.document.settings.env.app.tags.tags: - nodes_latex = self.latex(widget) + code_block_info = CodeBlockInfo(project_name=widget.name, + filename=self.content.items[0][0], + line_number=self.content.items[0][1] - 1) + nodes_latex = self.latex(widget, code_block_info) # insert widget into the template template = jinja_env.get_template('widget.html') diff --git a/frontend/tests/compile_blocks.py b/frontend/tests/compile_blocks.py index a60441103..d300a88b8 100755 --- a/frontend/tests/compile_blocks.py +++ b/frontend/tests/compile_blocks.py @@ -30,6 +30,7 @@ from os import path as P import colors as C import shutil +import glob import re from widget.chop import manual_chop, cheapo_gnatchop, real_gnatchop @@ -100,7 +101,10 @@ def is_empty(line): i + 1, lang_re.match(line).groups()[0] ) - project = project_re.match(line).groups()[0] + project = project_re.match(line) + if project is not None: + project = project.groups()[0] + main_file = main_re.match(line) if main_file is not None: # Retrieve actual main filename @@ -211,6 +215,8 @@ def __repr__(self): parser.add_argument('--verbose', '-v', type=bool, default=False, help='Show more information') +parser.add_argument('--keep_files', '-k', action='store_true', + help='Keep files generated in the test') parser.add_argument('--code-block', '-b', type=str, default=0) parser.add_argument('--all-diagnostics', '-A', action='store_true') parser.add_argument('--code-block-at', type=int, default=0) @@ -344,6 +350,11 @@ def extract_diagnostics(lines): projects = dict() for (i, b) in code_blocks: + if b.project is None: + print ("Error: project not set in {} at line {}".format( + rst_file, str(b.line_start))) + exit(1) + if not b.project in projects: projects[b.project] = list() projects[b.project].append((i, b)) @@ -357,7 +368,7 @@ def init_project_dir(project): project_dir = base_project_dir + "/" + project.replace(".", "/") if os.path.exists(project_dir): - shutil.rmtree(args.build_dir) + shutil.rmtree(project_dir) try: os.makedirs(project_dir) @@ -454,6 +465,13 @@ def get_main_filename(block): main_file = source_files[-1].basename return main_file + def make_project_block_dir(): + project_block_dir = str(block.line_start) + if not os.path.exists(project_block_dir): + os.makedirs(project_block_dir) + + return project_block_dir + if (('ada-run' in block.classes or 'ada-run-expect-failure' in block.classes or 'run' in block.buttons) @@ -461,7 +479,13 @@ def get_main_filename(block): ): main_file = get_main_filename(block) + project_block_dir = make_project_block_dir() + if block.language == "ada": + + def remove_string(some_text, rem): + return re.sub(".*" + rem + ".*\n?","", some_text) + try: out = run("gprbuild", "-gnata", "-gnatyg0-s", "-f", main_file) @@ -472,21 +496,32 @@ def get_main_filename(block): print_error(loc, "Failed to compile example") print(e.output) has_error = True + out = str(e.output.decode("utf-8")) + + out = remove_string(out, "using project") + with open(project_block_dir + "/build.log", u"w") as logfile: + logfile.write(out) + elif block.language == "c": try: - out = run("gcc", "-c", main_file) + cmd = ["gcc", "-o", + P.splitext(main_file)[0]] + glob.glob('*.c') + out = run(*cmd) except S.CalledProcessError as e: - if 'ada-expect-compile-error' in block.classes: + if 'c-expect-compile-error' in block.classes: compile_error = True else: print_error(loc, "Failed to compile example") print(e.output) has_error = True + out = str(e.output.decode("utf-8")) + with open(project_block_dir + "/build.log", u"w") as logfile: + logfile.write(out) if not compile_error and not has_error: if block.language == "ada": try: - run("./{}".format(P.splitext(main_file)[0])) + out = run("./{}".format(P.splitext(main_file)[0])) if 'ada-run-expect-failure' in block.classes: print_error( @@ -494,7 +529,7 @@ def get_main_filename(block): ) has_error = True - except S.CalledProcessError: + except S.CalledProcessError as e: if 'ada-run-expect-failure' in block.classes: if args.verbose: print("Running of example expectedly failed") @@ -502,23 +537,72 @@ def get_main_filename(block): print_error(loc, "Running of example failed") has_error = True + out = str(e.output.decode("utf-8")) + + with open(project_block_dir + "/run.log", u"w") as logfile: + logfile.write(out) + + elif block.language == "c": + try: + out = run("./{}".format(P.splitext(main_file)[0])) + + if 'c-run-expect-failure' in block.classes: + print_error( + loc, "Running of example should have failed" + ) + has_error = True + + except S.CalledProcessError as e: + if 'c-run-expect-failure' in block.classes: + if args.verbose: + print("Running of example expectedly failed") + else: + print_error(loc, "Running of example failed") + has_error = True + out = str(e.output.decode("utf-8")) + + with open(project_block_dir + "/run.log", u"w") as logfile: + logfile.write(out) + if 'compile' in block.buttons: + project_block_dir = make_project_block_dir() + for source_file in source_files: if block.language == "ada": try: - run("gcc", "-c", "-gnatc", "-gnatyg0-s", - source_file.basename) - except S.CalledProcessError: + out = run("gcc", "-c", "-gnatc", "-gnatyg0-s", + source_file.basename) + except S.CalledProcessError as e: if 'ada-expect-compile-error' in block.classes: compile_error = True else: print_error(loc, "Failed to compile example") has_error = True + out = str(e.output.decode("utf-8")) + + with open(project_block_dir + "/compile.log", u"w+") as logfile: + logfile.write(out) + + elif block.language == "c": + try: + out = run("gcc", "-c", source_file.basename) + except S.CalledProcessError as e: + if 'c-expect-compile-error' in block.classes: + compile_error = True + else: + print_error(loc, "Failed to compile example") + has_error = True + out = str(e.output.decode("utf-8")) + + with open(project_block_dir + "/compile.log", u"w+") as logfile: + logfile.write(out) if any(b in prove_buttons for b in block.buttons): if block.language == "ada": + project_block_dir = make_project_block_dir() + main_file = get_main_filename(block) spark_mode = True project_filename = write_project_file(main_file, @@ -552,6 +636,10 @@ def get_main_filename(block): print_error(loc, "Failed to prove example") print(e.output) has_error = True + out = str(e.output.decode("utf-8")) + + with open(project_block_dir + "/prove.log", u"w") as logfile: + logfile.write(out) else: print_error(loc, "Wrong language selected for prove button") print(e.output) @@ -579,7 +667,7 @@ def get_main_filename(block): print_error(loc, "Expected prove error, got none!") has_error = True - if (any (c in ['ada-run','ada-run-expect-failure','ada-norun'] for + if (any (c in ['ada-run-expect-failure','ada-norun'] for c in block.classes) and not 'run' in block.buttons): print_error(loc, "Expected run button, got none!") @@ -595,14 +683,15 @@ def get_main_filename(block): os.chdir(work_dir) - if os.path.exists(base_project_dir): + if os.path.exists(base_project_dir) and not args.keep_files: shutil.rmtree(base_project_dir) return analysis_error # Remove the build dir, but only if the user didn't ask for a specific # subset of code_blocks -if os.path.exists(args.build_dir) and not args.code_block: +if (os.path.exists(args.build_dir) and not args.code_block + and not args.keep_files): shutil.rmtree(args.build_dir) if not os.path.exists(args.build_dir):