% Copyright 2024 Remy Blank <remy@c-space.org>
% SPDX-License-Identifier: MIT

# Code execution

The {rst:dir}`exec` directive allows executing code from the browser.

```{exec} sql
:name: sql-countries
:when: never
:class: hidden
create table countries (
  country text not null,
  country_code text not null,
  dial_code text not null,
  capital text not null,
  population int not null,
  food text
);
insert into countries values
  ('Switzerland', 'CH', '+41', 'Bern', 8776000, 'fondue!'),
  ('France', 'FR', '+33', 'Paris', 67970000, null),
  ('Germany', 'DE', '+49', 'Berlin', 83800000, null),
  ('Italy', 'IT', '+39', 'Rome', 58940000, null),
  ('Austria', 'AT', '+43', 'Vienna', 9042000, 'Kaiserschmarrn'),
  ('Lichtenstein', 'LI', '+423', 'Vaduz', 39327, null);
```

## Directive

````{rst:directive} .. exec:: runner (html | micropython | python | sql)
This directive is a {rst:dir}`code-block` that allows executing code from the
browser. It supports most of the options of {rst:dir}`code-block`, and a few
more described below.

{.rubric}
Options
```{rst:directive:option} after: name [name...]
Execute one or more {rst:dir}`exec` blocks before this block, in the same
environment.
```
```{rst:directive:option} editor: [ID]
Display the {rst:dir}`exec` block in an editor. If `ID` is provided, the
content of the editor is saved in browser local storage and restored on reload.
`ID` must be unique across all documents, e.g. a
[UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier).
```
```{rst:directive:option} include: path [path...]
:type: relative paths
Prepend the content of one or more files to the block's content.
```
```{rst:directive:option} output-style: property: value; [property: value; ...]
CSS styles to apply to the output generated by the code block, e.g.
`max-height: 10rem`. To which element the styles are applied is
runner-specific.
```
```{rst:directive:option} style: property: value; [property: value; ...]
CSS styles to apply to the code block and editor, e.g. `max-height: 20rem`.
```
```{rst:directive:option} then: name [name...]
Execute one or more {rst:dir}`exec` blocks after this block, in the same
environment.
```
```{rst:directive:option} when: value
:type: click | load | never
Determine when the block's code is executed: on user request (`click`, the
default), when the page loads (`load`) or not at all (`never`).
```
````

## Trigger

By default, {rst:dir}`exec` blocks are executed on click
({rst:dir}`:when: click <exec:when>`), with controls displayed next to the
  block.

```{exec} sql
:after: sql-countries
select * from countries where country_code = 'LI';
```

They can also be executed immediately on load
({rst:dir}`:when: load <exec:when>`) or not at all
({rst:dir}`:when: never <exec:when>`, useful for definitions that are referenced
by other blocks). The controls displayed depend on the type of block.

```{exec} sql
:after: sql-countries
:when: load
select * from countries where country_code = 'LI';
```

## Editor

Blocks can be made editable with the {rst:dir}`:editor: <exec:editor>` option.

```{exec} sql
:after: sql-countries
:editor:
select * from countries
  where population > 10000000
  order by country_code;
```

The option takes an optional editor ID. If provided, the content of the editor
is saved in browser local storage, and restored on page reload.

```{exec} sql
:after: sql-countries
:editor: a38c47e0-22fe-4d7a-8c0b-4d38e7d55ee1
```

## Sequencing

The {rst:dir}`:after: <exec:after>` option allows referencing one or more
{rst:dir}`exec` blocks on the same page to be executed before the block, in the
same environment. The referenced blocks can themselves have
{rst:dir}`:after: <exec:after>` options, forming a dependency graph.

Similarly, the {rst:dir}`:then: <exec:then>` option allows referencing one or
more {rst:dir}`exec` blocks to be executed after the block. Unlike
{rst:dir}`:after: <exec:after>`, only the blocks referenced by the
{rst:dir}`:then: <exec:then>` option of the block itself are executed; the
{rst:dir}`:then: <exec:then>` options of referenced blocks are ignored.

If a block appears more than once in the graph, only the first occurrence is
executed.

```{exec} sql
:name: sql-people
:when: never
-- :name: sql-people
create table people (first_name text not null, last_name text not null);
```

```{exec} sql
:name: sql-people-select
:when: never
-- :name: sql-people-select
select * from people;
```

```{exec} sql
:after: sql-people
:then: sql-people-select
:editor:
-- :after: sql-people
-- :then: sql-people-select
insert into people values ('Joe', 'Bar'), ('Jack', 'Sparrow');
```

## Include file content

The {rst:dir}`:include: <exec:include>` option allows including the content of
one or more external files. The content of the files is prepended to the block's
content.

```{exec} sql
:include: people.sql
select * from people;
```

## Runners

### HTML

The {rst:dir}`{exec} html <exec>` runner displays a complete HTML document as an
`<iframe>`, with limited browsing functionality.

### MicroPython

The {rst:dir}`{exec} micropython <exec>` runner connects to an embedded system
running [MicroPython](https://micropython.org). The code can either be run from
RAM (transient, programs disappear on reset) or be written to the file `main.py`
in flash memory (permanent, runs at boot-time).

```{admonition} Note
:class: note
The `micropython` runner only works on devices supporting the
[WebSerial API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API).
Currently, this limits the use to
[Chromium-based browsers](https://en.wikipedia.org/wiki/Chromium_(web_browser)#Browsers_based_on_Chromium)
(e.g. Chrome, Edge, Brave).
```

The target device must already be programmed with a MicroPython firmware. The
procedure depends on the target device type.

- **BBC micro:bit V2:** Download the `.hex` file for the latest
  [release](https://github.com/microbit-foundation/micropython-microbit-v2/releases).
  Connect the target and mount its filesystem, then copy the file to it.
- **Raspberry Pi Pico:** Follow the
  [documentation](https://www.raspberrypi.com/documentation/microcontrollers/micropython.html#drag-and-drop-micropython)
  to download the appropriate firmware file and program the device.

To enable connecting to a target device, it must first be **paired with the
browser**. Connect the target via USB, select
"<span class="tdoc-icon fa-plug"></span>&nbsp;Connect" in the "Tools" menu
(<button class="tdoc fa-screwdriver-wrench"></button>), and select the device
in the list. This needs to be done only once; once a device is paired, it will
be connected automatically when the page loads, unless multiple paired devices
are available.

To **run a program from RAM**, click the "Run" button
(<button class="tdoc fa-play"></button>). Text input can be sent to the target
via the input field, and the program can be interrupted with the "Stop" button
(<button class="tdoc fa-stop"></button>). When the program terminates, the
target returns to the
[REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) and
accepts further commands.

To **write a program permanently to a target's flash memory**, select
"<span class="tdoc-icon fa-file-arrow-up"></span>&nbsp;Write&nbsp;to&nbsp;<code>main.py</code>"
in the "Tools" menu. The program can be removed again with
"<span class="tdoc-icon fa-trash"></span>&nbsp;Remove&nbsp;<code>main.py</code>".

### Python

The {rst:dir}`{exec} python <exec>` runner executes code through
[Pyodide](https://pyodide.org/) and
[Polyscript](https://pyscript.github.io/polyscript/). All Python blocks on a
page are executed in a shared, single-threaded interpreter.

Pyodide can be configured via the `exec:python:` {rst:dir}`metadata`. This
enables the following functionality:

- **[Copy files](https://docs.pyscript.net/latest/user-guide/configuration/#files)
  to the filesystem:** The `files` key is a mapping of URL to target path.
  Relative URLs are resolved relative to the `_static` directory. Relative
  target paths are resolved relative to `$HOME` (`/home/pyodide`). If the target
  path ends with a `/`, the filename part of the URL is used as the target
  filename.
- **[Load packages](https://docs.pyscript.net/latest/user-guide/configuration/#packages):**
  The `packages` key is a list of package references, either package names with
  optional version constraints (`docutils`, `attrs>=23.2.0`) or URLs referencing
  wheels.

```{code-block} yaml
exec:
  python:
    files:
      input.txt:                    # .../_static/input.txt => $HOME/input.txt
      db/init.sql: /tmp/            # .../_static/db/init.sql => /tmp/init.sql
      ../index.html: homepage.html  # .../index.html => $HOME/homepage.html
    packages: [sqlite3]
```

### SQL

The {rst:dir}`{exec} sql <exec>` runner uses a WebAssembly build of
[SQLite](https://sqlite.org/). Each block execution is performed against a new,
empty database.
