Skip to content

Commit

Permalink
add: Fortran Playground Code links (#184)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* suggestions

* resolve

* robust program testing

---------

Co-authored-by: Zachary Moon <[email protected]>
  • Loading branch information
henilp105 and zmoon committed Aug 13, 2023
1 parent 7c8edb8 commit 8a46fc6
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 43 deletions.
92 changes: 92 additions & 0 deletions extensions/fortran_playground.py
Original file line number Diff line number Diff line change
@@ -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"<ERROR>",b"Error",b"app/main.f90",b"<h1>Bad Request</h1>"]


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"<a href='https://play.fortran-lang.org/?code={urllib.parse.quote(code)}' target='_blank'>Fortran Playground</a>"
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,
}

4 changes: 4 additions & 0 deletions source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -68,7 +70,9 @@
"sphinx_copybutton",
"sphinx.ext.intersphinx",
"sphinx_jinja",
"fortran_playground",
"sphinx_favicon",

]

myst_enable_extensions = [
Expand Down
12 changes: 6 additions & 6 deletions source/learn/quickstart/arrays_strings.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -105,7 +105,7 @@ when they go out of scope.

**Example:** static character string

```fortran
```{play-code-block} fortran
program string
implicit none
Expand All @@ -126,7 +126,7 @@ end program string

**Example:** allocatable character string

```fortran
```{play-code-block} fortran
program allocatable_string
implicit none
Expand Down Expand Up @@ -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
Expand Down
22 changes: 11 additions & 11 deletions source/learn/quickstart/derived_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -30,15 +30,15 @@ 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
```

Example with default initialization:

```fortran
```{play-code-block} fortran
type :: t_pair
integer :: i = 1
real :: x = 0.5
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion source/learn/quickstart/hello_world.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!'
Expand Down
20 changes: 10 additions & 10 deletions source/learn/quickstart/operators_control_flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -115,7 +115,7 @@ end do

**Example:** `do` loop with skip

```fortran
```{play-code-block} fortran
integer :: i
do i = 1, 10, 2
Expand All @@ -130,7 +130,7 @@ in `while()` evaluates to `.true.`.

**Example:** `do while()` loop

```fortran
```{play-code-block} fortran
integer :: i
i = 1
Expand All @@ -150,7 +150,7 @@ with such cases.

**Example:** loop with `exit`

```fortran
```{play-code-block} fortran
integer :: i
do i = 1, 100
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 8a46fc6

Please sign in to comment.