From 8a46fc60a24bdfef77e37676f9deff533818434b Mon Sep 17 00:00:00 2001 From: Henil Panchal <37398093+henilp105@users.noreply.github.com> Date: Sun, 13 Aug 2023 16:21:07 +0530 Subject: [PATCH] add: Fortran Playground Code links (#184) * playground links * playground links * run only specific codebloks * check for runnable code * syntax hilighting * syntax hilighting * Update fortran_playground.py * Update fortran_playground/fortran_playground.py Co-authored-by: Zachary Moon * suggestions * resolve * robust program testing --------- Co-authored-by: Zachary Moon --- extensions/fortran_playground.py | 92 +++++++++++++++++++ source/conf.py | 4 + source/learn/quickstart/arrays_strings.md | 12 +-- source/learn/quickstart/derived_types.md | 22 ++--- source/learn/quickstart/hello_world.md | 2 +- .../quickstart/operators_control_flow.md | 20 ++-- source/learn/quickstart/organising_code.md | 16 ++-- source/learn/quickstart/variables.md | 14 +-- 8 files changed, 139 insertions(+), 43 deletions(-) create mode 100644 extensions/fortran_playground.py diff --git a/extensions/fortran_playground.py b/extensions/fortran_playground.py new file mode 100644 index 00000000000..d834db431f2 --- /dev/null +++ b/extensions/fortran_playground.py @@ -0,0 +1,92 @@ +from sphinx.directives.code import CodeBlock, parselinenos, dedent_lines, container_wrapper, logger +import urllib.parse +import requests +from requests.structures import CaseInsensitiveDict +from docutils import nodes + +url = "https://play-api.fortran-lang.org/run" + +headers = CaseInsensitiveDict() +headers["Accept"] = "application/json" +headers["Content-Type"] = "application/json" +data_dict = {"code":"","programInput":"","libs":["stdlib"]} +comp_error = [b"",b"Error",b"app/main.f90",b"

Bad Request

"] + + +class PlayCodeBlock(CodeBlock): + + def run(self): + document = self.state.document + code = '\n'.join(self.content) + location = self.state_machine.get_source_and_line(self.lineno) + linespec = self.options.get('emphasize-lines') + if linespec: + try: + nlines = len(self.content) + hl_lines = parselinenos(linespec, nlines) + if any(i >= nlines for i in hl_lines): + logger.warning(__('line number spec is out of range(1-%d): %r') % + (nlines, self.options['emphasize-lines']), + location=location) + + hl_lines = [x + 1 for x in hl_lines if x < nlines] + except ValueError as err: + return [document.reporter.warning(err, line=self.lineno)] + else: + hl_lines = None + + if 'dedent' in self.options: + location = self.state_machine.get_source_and_line(self.lineno) + lines = code.splitlines(True) + lines = dedent_lines(lines, self.options['dedent'], location=location) + code = ''.join(lines) + + literal: Element = nodes.literal_block(code, code) + if 'linenos' in self.options or 'lineno-start' in self.options: + literal['linenos'] = True + literal['classes'] += self.options.get('class', []) + literal['force'] = 'force' in self.options + if self.arguments: + # highlight language specified + literal['language'] = self.arguments[0] + else: + # no highlight language specified. Then this directive refers the current + # highlight setting via ``highlight`` directive or ``highlight_language`` + # configuration. + literal['language'] = self.env.temp_data.get('highlight_language', + self.config.highlight_language) + extra_args = literal['highlight_args'] = {} + if hl_lines is not None: + extra_args['hl_lines'] = hl_lines + if 'lineno-start' in self.options: + extra_args['linenostart'] = self.options['lineno-start'] + self.set_source_info(literal) + caption = f"Fortran Playground" + try: + literal = container_wrapper(self, literal, caption) + except ValueError as exc: + return [document.reporter.warning(exc, line=self.lineno)] + if "end program" not in code: + code = code+"\nend program" + data_dict['code'] = code + resp = requests.post(url, headers=headers, json=data_dict) + print(resp.content) + #print([i in resp.content for i in comp_error]) + if any(i in resp.content for i in comp_error): + #print("original") + return [*super().run()] + else: + #print("with link") + return [literal] + + + +def setup(app): + app.add_directive('play-code-block', PlayCodeBlock) + + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } + diff --git a/source/conf.py b/source/conf.py index 99a503c1678..d12eaff1418 100644 --- a/source/conf.py +++ b/source/conf.py @@ -32,6 +32,8 @@ "intrinsics": pathlib.Path(root, "data", "intrinsics.yml"), } +sys.path.insert(0, str(root / "extensions")) + if not all(data.exists() for data in data_files.values()): sys.path.insert(0, str(root.absolute())) # pylint: disable=import-error, unused-import @@ -68,7 +70,9 @@ "sphinx_copybutton", "sphinx.ext.intersphinx", "sphinx_jinja", + "fortran_playground", "sphinx_favicon", + ] myst_enable_extensions = [ diff --git a/source/learn/quickstart/arrays_strings.md b/source/learn/quickstart/arrays_strings.md index bfa6b52ba65..73eaa66a8d6 100644 --- a/source/learn/quickstart/arrays_strings.md +++ b/source/learn/quickstart/arrays_strings.md @@ -16,7 +16,7 @@ using the `dimension` attribute or by appending the array dimensions in parenthe **Example:** static array declaration -```fortran +```{play-code-block} fortran program arrays implicit none @@ -43,7 +43,7 @@ we can perform operations on all or part of an array using array _slicing_ notat **Example:** array slicing -```fortran +```{play-code-block} fortran program array_slice implicit none @@ -80,7 +80,7 @@ These are _allocated_ while the program is running once we know how big the arra **Example:** allocatable arrays -```fortran +```{play-code-block} fortran program allocatable implicit none @@ -105,7 +105,7 @@ when they go out of scope. **Example:** static character string -```fortran +```{play-code-block} fortran program string implicit none @@ -126,7 +126,7 @@ end program string **Example:** allocatable character string -```fortran +```{play-code-block} fortran program allocatable_string implicit none @@ -155,7 +155,7 @@ Finally, we use the intrinsic function `trim` to remove any excess spaces when p **Example:** string array -```fortran +```{play-code-block} fortran program string_array implicit none character(len=10), dimension(2) :: keys, vals diff --git a/source/learn/quickstart/derived_types.md b/source/learn/quickstart/derived_types.md index aa94943ddc8..b1dbd43c9eb 100644 --- a/source/learn/quickstart/derived_types.md +++ b/source/learn/quickstart/derived_types.md @@ -6,7 +6,7 @@ As discussed previously in [Variables](variables), there are five built-in data Here's an example of a basic derived type: -```fortran +```{play-code-block} fortran type :: t_pair integer :: i real :: x @@ -15,7 +15,7 @@ end type The syntax to create a variable of type `t_pair` and access its members is: -```fortran +```{play-code-block} fortran ! Declare type(t_pair) :: pair ! Initialize @@ -30,7 +30,7 @@ You can also initialize derived type members by invoking the derived type constr Example using the derived type constructor: -```fortran +```{play-code-block} fortran pair = t_pair(1, 0.5) ! Initialize with positional arguments pair = t_pair(i=1, x=0.5) ! Initialize with keyword arguments pair = t_pair(x=0.5, i=1) ! Keyword arguments can go in any order @@ -38,7 +38,7 @@ pair = t_pair(x=0.5, i=1) ! Keyword arguments can go in any order Example with default initialization: -```fortran +```{play-code-block} fortran type :: t_pair integer :: i = 1 real :: x = 0.5 @@ -79,7 +79,7 @@ The `sequence` attribute may be used only to declare that the following members Example with `sequence`: -```fortran +```{play-code-block} fortran type :: t_pair sequence integer :: i @@ -98,7 +98,7 @@ The attribute `bind(c)` is used to achieve compatibility between Fortran's deriv Example with `bind(c)`: -```fortran +```{play-code-block} fortran module f_to_c use iso_c_bindings, only: c_int implicit none @@ -124,7 +124,7 @@ struct c_struct { Example of a derived type with `parameterized-declaration-list` and with the attribute `public`: -```fortran +```{play-code-block} fortran module m_matrix implicit none private @@ -154,7 +154,7 @@ The attribute `extends` was added in the F2003 standard and introduces an import Example with the attribute `extends`: -```fortran +```{play-code-block} fortran module m_employee implicit none private @@ -229,7 +229,7 @@ end program test_employee Examples of common cases: -```fortran +```{play-code-block} fortran type :: t_example ! 1st case: simple built-in type with access attribute and [init] integer, private :: i = 0 @@ -259,7 +259,7 @@ A derived type can contain functions or subroutines that are _bound_ to it. We'l Here's an example of a derived type with a basic type-bound procedure: -```fortran +```{play-code-block} fortran module m_shapes implicit none private @@ -308,7 +308,7 @@ What is new: In the above example, the type-bound procedure `area` is defined as a function and can be invoked only in an expression, for example `x = sq%area()` or `print *, sq%area()`. If you define it instead as a subroutine, you can invoke it from its own `call` statement: -```fortran +```{play-code-block} fortran ! Change within module contains subroutine area(self, x) diff --git a/source/learn/quickstart/hello_world.md b/source/learn/quickstart/hello_world.md index a5d891a1aa2..cdf652c94d3 100644 --- a/source/learn/quickstart/hello_world.md +++ b/source/learn/quickstart/hello_world.md @@ -39,7 +39,7 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Once you have set up your compiler, open a new file in your favourite code editor and enter the following: -```fortran +```{play-code-block} fortran program hello ! This is a comment line; it is ignored by the compiler print *, 'Hello, World!' diff --git a/source/learn/quickstart/operators_control_flow.md b/source/learn/quickstart/operators_control_flow.md index a1618d96830..231109c8525 100644 --- a/source/learn/quickstart/operators_control_flow.md +++ b/source/learn/quickstart/operators_control_flow.md @@ -47,7 +47,7 @@ message to describe the nature of the `angle` variable: **Example:** single branch `if` -```fortran +```{play-code-block} fortran if (angle < 90.0) then print *, 'Angle is acute' end if @@ -65,7 +65,7 @@ We can add an alternative branch to the construct using the `else` keyword: **Example:** two-branch `if`-`else` -```fortran +```{play-code-block} fortran if (angle < 90.0) then print *, 'Angle is acute' else @@ -80,7 +80,7 @@ We can actually add any number of branches using `else if` to specify more condi **Example:** multi-branch `if`-`else if`-`else` -```fortran +```{play-code-block} fortran if (angle < 90.0) then print *, 'Angle is acute' else if (angle < 180.0) then @@ -105,7 +105,7 @@ to specify the start value and final value of our counting variable. **Example:** `do` loop -```fortran +```{play-code-block} fortran integer :: i do i = 1, 10 @@ -115,7 +115,7 @@ end do **Example:** `do` loop with skip -```fortran +```{play-code-block} fortran integer :: i do i = 1, 10, 2 @@ -130,7 +130,7 @@ in `while()` evaluates to `.true.`. **Example:** `do while()` loop -```fortran +```{play-code-block} fortran integer :: i i = 1 @@ -150,7 +150,7 @@ with such cases. **Example:** loop with `exit` -```fortran +```{play-code-block} fortran integer :: i do i = 1, 100 @@ -166,7 +166,7 @@ On the other hand, `cycle` skips whatever is left of the loop and goes into the **Example:** loop with `cycle` -```fortran +```{play-code-block} fortran integer :: i do i = 1, 10 @@ -190,7 +190,7 @@ A recurring case in any programming language is the use of nested loops. Nested **Example:** tagged nested loops -```fortran +```{play-code-block} fortran integer :: i, j outer_loop: do i = 1, 10 @@ -217,7 +217,7 @@ These requirements place restrictions on what can be placed within the loop body **Example:** `do concurrent()` loop -```fortran +```{play-code-block} fortran real, parameter :: pi = 3.14159265 integer, parameter :: n = 10 real :: result_sin(n) diff --git a/source/learn/quickstart/organising_code.md b/source/learn/quickstart/organising_code.md index a0053da2c72..0f1b68188b7 100644 --- a/source/learn/quickstart/organising_code.md +++ b/source/learn/quickstart/organising_code.md @@ -18,7 +18,7 @@ the dummy argument types and attributes are declared within the body of the subr **Example:** -```fortran +```{play-code-block} fortran ! Print matrix A to screen subroutine print_matrix(n,m,A) implicit none @@ -44,7 +44,7 @@ In this example, the subroutine does not modify its arguments, hence all argumen We can call this subroutine from a program using a `call` statement: -```fortran +```{play-code-block} fortran program call_sub implicit none @@ -62,7 +62,7 @@ end program call_sub ## Functions -```fortran +```{play-code-block} fortran ! L2 Norm of a vector function vector_norm(n,vec) result(norm) implicit none @@ -79,7 +79,7 @@ end function vector_norm To execute this function: -```fortran +```{play-code-block} fortran program run_fcn implicit none @@ -110,7 +110,7 @@ They can contain data objects, type definitions, procedures, and interfaces. **Example:** -```fortran +```{play-code-block} fortran module my_mod implicit none @@ -144,7 +144,7 @@ end module my_mod To `use` the module within a program: -```fortran +```{play-code-block} fortran program use_mod use my_mod implicit none @@ -160,13 +160,13 @@ end program use_mod **Example:** explicit import list -```fortran +```{play-code-block} fortran use my_mod, only: public_var ``` **Example:** aliased import -```fortran +```{play-code-block} fortran use my_mod, only: printMat=>print_matrix ``` diff --git a/source/learn/quickstart/variables.md b/source/learn/quickstart/variables.md index e461a64b387..61f9805ea94 100644 --- a/source/learn/quickstart/variables.md +++ b/source/learn/quickstart/variables.md @@ -34,7 +34,7 @@ In the following example we declare a variable for each of the built-in types. **Example:** variable declaration -```fortran +```{play-code-block} fortran program variables implicit none @@ -62,7 +62,7 @@ Once we have declared a variable, we can assign and reassign values to it using **Example:** variable assignment -```fortran +```{play-code-block} fortran amount = 10 pi = 3.1415927 frequency = (1.0, -0.5) @@ -92,7 +92,7 @@ This is commonly referred to as writing to `standard output` or `stdout`. We can use the `print` statement introduced earlier to print variable values to `stdout`: -```fortran +```{play-code-block} fortran print *, 'The value of amount (integer) is: ', amount print *, 'The value of pi (real) is: ', pi print *, 'The value of frequency (complex) is: ', frequency @@ -103,7 +103,7 @@ print *, 'The value of isOkay (logical) is: ', isOkay In a similar way, we can read values from the command window using the `read` statement: -```fortran +```{play-code-block} fortran program read_value implicit none integer :: age @@ -134,7 +134,7 @@ The usual set of arithmetic operators are available, listed in order of preceden **Example:** -```fortran +```{play-code-block} fortran program arithmetic implicit none @@ -170,7 +170,7 @@ The `iso_fortran_env` intrinsic module provides `kind` parameters for the common **Example:** explicit real `kind` -```fortran +```{play-code-block} fortran program float use, intrinsic :: iso_fortran_env, only: sp=>real32, dp=>real64 implicit none @@ -188,7 +188,7 @@ end program float **Example:** C-interoperable `kind` -```fortran +```{play-code-block} fortran program float use, intrinsic :: iso_c_binding, only: sp=>c_float, dp=>c_double implicit none