├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── DESIGN.md ├── LICENSE ├── README.md ├── backend ├── Docker │ ├── Dockerfile │ ├── fpm.toml │ └── main.f90 ├── Pipfile ├── app.py ├── fpm.toml ├── tutorial.yml └── wsgi.py ├── frontend ├── .env ├── package.json ├── public │ ├── fortran-logo.png │ ├── index.html │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── Editor.js │ ├── InputBox.js │ ├── Navbar.js │ ├── TutorialCard.js │ ├── fortran-logo.png │ ├── index.css │ ├── index.js │ └── tutorial.json └── systemd ├── initialize-services.sh ├── playground-reverse-proxy.service └── playground-server.service /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy frontend 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | 16 | - name: Install Node 17 | uses: actions/setup-node@v2 18 | with: 19 | node-version: 16.x 20 | 21 | - name: Install dependencies 📦 22 | working-directory: ./frontend 23 | run: npm install 24 | 25 | - name: Build 🔨 26 | working-directory: ./frontend 27 | run: | 28 | npm run build 29 | touch build/.nojekyll 30 | echo play.fortran-lang.org > build/CNAME 31 | env: 32 | REACT_APP_PLAYGROUND_API_URL: https://play-api.fortran-lang.org 33 | 34 | - name: Deploy 🚀 35 | uses: JamesIves/github-pages-deploy-action@v4.4.0 36 | with: 37 | branch: gh-pages 38 | folder: ./frontend/build 39 | single-commit: true 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | frontend/node_modules 2 | -------------------------------------------------------------------------------- /DESIGN.md: -------------------------------------------------------------------------------- 1 | # The Fortran Playground Design 2 | 3 | This document describes the design of the software that implements the Fortran 4 | Playground. 5 | Currently covered is the design of the frontend component of the system. 6 | The design of the backend will be documented in a later update. 7 | 8 | The playground consists of a frontend built using React which communicates with 9 | a Flask App responsible for executing user codes in a sandboxed environment 10 | using a Docker Container. 11 | 12 | ## Frontend 13 | 14 | There are various components which handle different parts of the website. 15 | All the styling is done using React-Bootstrap components. 16 | 17 | ### Editor 18 | 19 | We use an Ace Editor port for React called 20 | [React-Ace](https://github.com/securingsincity/react-ace). 21 | All information about Editor settings is stored under `frontend/src/Editor.js`. 22 | The editor settings are passed down as props to the react-ace component and 23 | offer various configuration options check 24 | [this page](https://securingsincity.github.io/react-ace/) to experiment with 25 | different parameters. 26 | For example, to change the theme import it from react-ace library: 27 | ``` 28 | import "ace-builds/src-noconflict/theme-github"; 29 | ``` 30 | and change the theme prop inside `Editor.js`. 31 | 32 | ### Navbar 33 | 34 | The navbar is configured under `frontend/src/Navbar.js`. 35 | It uses the default bootstrap navbar component. 36 | 37 | ### Tutorial 38 | 39 | The content for tutorial is stored inside the tutorial.json file, which is 40 | generated by the backend from `tutorial.yml` file. 41 | A tutorial part consists of 3 sections- a title, content inside it and the code 42 | for the respective exercise. 43 | To add a new exercise append the three parameters inside the file at 44 | `src/backend/tutorial.yml`, e.g.: 45 | 46 | ``` 47 | - title: Heading for exercise 48 | content: A breif explanation for the topic 49 | code: "Code for the exercise" 50 | ``` 51 | 52 | Restart the server so it can compile the yaml into a JSON. 53 | The changes will be immediately reflected. 54 | To update the tutorial you can commit this JSON to the main repository via a 55 | pull request. 56 | 57 | Please make sure that you configure the code properly with proper string 58 | formatting. 59 | This can be done by entering the code inside the editor first and copying the 60 | outputted "code" parameter inside the POST request. 61 | This also ensures your code works properly. 62 | 63 | The tutorial is handled by the `TutorialCard` component insidde 64 | `src/TutorialCard.js` with respective props for Heading and Title. 65 | The change for code happens automatically by the `tutFunc` function inside 66 | `App.js` bounded by the switching buttons. 67 | 68 | ### InputBox 69 | 70 | The InputBox is a separate component which has props passed down from `App.js` 71 | to effectively manage state for the input. 72 | 73 | ### App 74 | 75 | The source file `frontend/src/App.js` houses all of our states and major 76 | functionalities (API Calls, Library Selection). 77 | 78 | 1. States: 79 | * Text: Code inside the editor. 80 | * Output: Output the is received from the backend after execution on container. 81 | * Input: User input for the code that is to be executed 82 | * Exercise: Current exercise the user is on 83 | There are also states which hold information on status for various toggleable 84 | elements on the site: 85 | * isLoading: Spinner until output is received from API 86 | * inputOn: Input box button toggle 87 | * showTutorial: Prompt for the quickstart tutorial 88 | * show: Pop up modal for library selection 89 | * stdlibOn: State that manages whether stdlib has been selected by user or not 90 | 2. Buttons for the tutorial are managed by `goRight` and `goLeft` functions, 91 | they iterate on the tutorial.json using `exercise` state. 92 | 3. API: POST Request to the backend server is done via `handleClick` function. 93 | The request contains 3 parameters code, the input for it and the libraries 94 | selected by the user. 95 | The response from this request is stored in the output state. 96 | 4. Custom styling from buttons is stored under the style tag inside run-button 97 | class. 98 | 5. The Output Section is inside the terminal class. 99 | All elements in this section are made via Bootstrap Cards. 100 | The Tutorial Card Component is also inside the terminal class. 101 | 6. The Modal Pop up for library selection is also a part of this section. 102 | It holds the switches for libraries toggle. 103 | 7. To add a new library in the frontend, add a new `` component 104 | with appropriate props and create a new state for the library. 105 | For example: 106 | ``` 107 | const [packageOn, ispackageOn] = useState(false) 108 | ``` 109 | ``` 110 | 116 | ``` 117 | Then create a new `onLibSelect` function as follows: 118 | ``` 119 | const onLibSelect = () => { 120 | setpackageOn(!packageOn); 121 | if(!packageOn){ 122 | libs.push("library-name") 123 | } 124 | else{ 125 | if(libs.includes("library-name")){ 126 | libs = libs.filter(item => item !== "library-name") 127 | 128 | } 129 | } 130 | ``` 131 | 132 | ### Styling 133 | 134 | The styling for the app is defined in `frontend/src/App.css`. 135 | All styles are applied from a single CSS file. 136 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 The Fortran Programming Language 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Fortran Playground 2 | 3 | This is an interactive Fortran playground in the browser. 4 | Its main purpose is for newcomers to easily get a taste for the language 5 | and learn the essentials of Fortran programming. 6 | 7 | Follow the instructions below if you want to run the Fortran Playground server 8 | on your own computer. 9 | For the design of the software, see [this document](DESIGN.md). 10 | 11 | ## Getting started 12 | 13 | ### Get the code 14 | 15 | ``` 16 | git clone https://github.com/fortran-lang/playground 17 | cd playground 18 | ``` 19 | 20 | ### Install dependencies 21 | 22 | The key dependencies for the Fortran playground are: 23 | 24 | * [Python](https://www.python.org/) for the backend server; 25 | * [Docker](https://www.docker.com/) for the container on the backend server; 26 | * [Node.js](https://nodejs.org/) for the frontend server. 27 | 28 | Ensure these are installed on your system before proceeding. 29 | 30 | ### Set up the backend server 31 | 32 | #### Build the Docker image 33 | 34 | The Fortran playground relies on a Docker container in which the backend server 35 | runs the compiler and executes the code. 36 | For the playground to work, we need to be able to run `docker` as a non-root 37 | user, that is, without `sudo`. 38 | See the [additional configuration instructions](https://docs.docker.com/engine/install/linux-postinstall/) 39 | for how to do that on Linux. 40 | 41 | When ready, type: 42 | 43 | ``` 44 | cd backend/Docker 45 | docker build -t playground-prod . 46 | ``` 47 | 48 | To confirm that it worked, type `docker images` and you should see 49 | `playground-prod` in the list of images under the `REPOSITORY` column, for example: 50 | 51 | ``` 52 | REPOSITORY TAG IMAGE ID CREATED SIZE 53 | playground-prod latest 8c2439e40e81 1 hour ago 201MB 54 | ``` 55 | 56 | Now move one directory up where we will set up the Python environment and the 57 | required libraries: 58 | 59 | ``` 60 | cd .. 61 | ``` 62 | 63 | #### Install the Python libraries 64 | 65 | The Python backend server and the packages that it depends on are managed using 66 | pipenv. 67 | If you don't have pipenv installed, you can install it using pip: 68 | 69 | ``` 70 | pip install --user pipenv 71 | ``` 72 | 73 | Or, if you don't have pip, use your OS's preferred package manager to install 74 | it. 75 | You can learn more about pipenv [here](https://pipenv.pypa.io/en/latest/). 76 | 77 | When ready, type: 78 | 79 | ``` 80 | pipenv install 81 | ``` 82 | 83 | #### Run the backend server 84 | 85 | To run the development server (Flask), type: 86 | 87 | ``` 88 | pipenv run flask run 89 | ``` 90 | 91 | While running, you can try to make an example HTTP request to the server from 92 | another terminal window using `curl`: 93 | 94 | ``` 95 | curl \ 96 | --location \ 97 | --request POST '127.0.0.1:5000/run' \ 98 | --header 'Content-Type: application/json' \ 99 | --data-raw '{ 100 | "code": "program hello\r\n print *, \"Hello, World!\"\r\nend program hello\r\n", 101 | "programInput": "", 102 | "libs" : [] 103 | }' 104 | ``` 105 | 106 | If everything is set up correctly so far, you should get the following response: 107 | 108 | ``` 109 | {"executed":" Hello, World!\n"} 110 | ``` 111 | 112 | ### Set up the frontend server 113 | 114 | From the top-level directory of the Fortran playground, navigate to the 115 | frontend directory: 116 | 117 | ``` 118 | cd frontend 119 | ``` 120 | 121 | To install the Node.js dependencies, type: 122 | 123 | ``` 124 | npm install 125 | ``` 126 | 127 | Run the server by typing: 128 | 129 | ``` 130 | REACT_APP_PLAYGROUND_API_URL=http://localhost:5000 npm start 131 | ``` 132 | 133 | This should open the Fortran playground in your default web browser. 134 | If not, navigate to http://localhost:3000 in your browser to start the 135 | playground. 136 | 137 | The `REACT_APP_PLAYGROUND_API_URL` must be set in the environment 138 | (or, alternatively, in the `.env` file in the `frontend/` directory) 139 | to the URL value of the Python backend server to use. 140 | For example, if you're running the Python backend server locally in development 141 | mode, set `REACT_APP_PLAYGROUND_API_URL` to `http://localhost:5000`. 142 | If deploying to production, `REACT_APP_PLAYGROUND_API_URL` should be set to 143 | `https://play-api.fortran-lang.org`. 144 | 145 | ### Loading Fortran code from your website in the playground 146 | 147 | The Playground can Load code from your website by adding a parameter `code` to the URL of Playground. Please **Note:** that the value of the parameter code has to be fortran code which has been **URL Encoded** . This can be done by using JS function `encodeURIComponent()` with the code as its parameter. 148 | 149 | Example: https://play.fortran-lang.org/?code=program+hello%0D%0A++%21+This+is+a+comment+line%3B+it+is+ignored+by+the+compiler%0D%0A++print+%2A%2C+%27Hello%2C+World%21%27%0D%0Aend+program+hello%0D%0A 150 | 151 | Here, the fortran program for Hello World has been URL encoded and set to parameter `code` in the URL. 152 | 153 | 154 | ## Deploying to production 155 | 156 | This is a guide for deploying the Python backend to production. 157 | 158 | ### Set up the VM 159 | 160 | This is a setup guide to host the server on AWS EC2. 161 | It assumes you already have an account. 162 | The steps may be somewhat different on other cloud providers. 163 | 164 | 1. Start the EC2 Launch Instance Wizard. 165 | 2. Select Ubuntu 22.04 LTS x86 as your OS under the AMI section. 166 | 2. Select the `t2.micro` instance if you want to stay within the free tier and 167 | keep the configuration as default. 168 | 3. Select the amount storage you need; 20 GBs will suffice and will stay under 169 | the free tier. Go to next step and leave tags as default. 170 | 4. Configure the security group to allow: 171 | * SSH on port 22; 172 | * HTTP on port 80; 173 | * HTTPS on port 443; 174 | All ports should allow the source to be "Anywhere", i.e. 0.0.0.0/0. 175 | 5. Select or create your SSH key pair, and keep your private key safe. 176 | 177 | Next, attach an Elastic IP to this instance so that the VM receives a static IP 178 | address. 179 | 180 | 1. Select Elastic IP in the navigation panel, and create a new Elastic IP. 181 | 2. Associate this Elastic IP to the instance you've just created. 182 | 3. Go to your instance and note this Elastic IP 183 | (you'll see it listed under the v4 IP address). 184 | 185 | ### Connect to and prepare the VM 186 | 187 | 1. Locate your SSH private key file that you created or selected in step 5 188 | and connect to your server by using SSH: 189 | ``` 190 | ssh -i ubuntu@ 191 | ``` 192 | 2. Update your instance 193 | ``` 194 | sudo apt update 195 | sudo apt upgrade 196 | ``` 197 | If prompted to reboot after the upgrade is complete, reboot the VM from the AWS 198 | control panel and log back in to the VM using SSH as described in the previous 199 | step. 200 | 3. Download [Caddy](https://caddyserver.com). 201 | We will use Caddy as a reverse-proxy server in front of the Python server 202 | as well as for the SSL certificate. 203 | The downloaded file is a static binary program. 204 | Make it executable (`chmod +x caddy`) and place it in a directory that is 205 | meant for external programs, for example `/opt/caddy-2.5.2/bin` or similar. 206 | This guide, and the 207 | [reverse-proxy service file](systemd/playground-reverse-proxy.service) 208 | assume that Caddy is executable and present in `/opt/caddy-2.5.2/bin`. 209 | 3. Follow the instructions from the [Getting started section](#getting-started) 210 | to get the playground code, install Docker and the Python backend dependencies. 211 | Before running the `pipenv install` step for the Python backend, make a 212 | directory for the virtual environment using `mkdir .venv`. 213 | This will allow us to run the production server directly from 214 | `playground/backend/.venv/bin`. 215 | 216 | ### Create or update the DNS record 217 | 218 | This guide assumes that the backend server of the Fortran Playground 219 | will serve at the play-api.fortran-lang.org subdomain. 220 | Regardless of whether this is the first deployment to production or not, 221 | ensure that there is an A record for play-api.fortran-lang.org that 222 | points to the public IP address of the VM. 223 | 224 | ### Start the backend server and reverse proxy 225 | 226 | systemd service files for the Python backend server and reverse proxy 227 | are provided in the playground/systemd directory. 228 | To start and enable the services, type: 229 | 230 | ``` 231 | cd systemd 232 | sudo ./initialize-services.sh 233 | ``` 234 | 235 | which will copy the service files to `/etc/systemd/system`, 236 | start them, and enable them so they start on every boot. 237 | 238 | ## Reporting issues 239 | 240 | Please report any issues or suggestions for improvement by opening a 241 | [new GitHub issue](https://github.com/fortran-lang/playground/issues/new). 242 | -------------------------------------------------------------------------------- /backend/Docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | # Install system dependencies 4 | RUN apk add --no-cache \ 5 | bash \ 6 | bash-completion \ 7 | bash-doc \ 8 | gfortran \ 9 | git \ 10 | musl-dev \ 11 | wget 12 | 13 | # Create a non-root user 14 | RUN adduser -D fortran 15 | USER fortran 16 | WORKDIR /home/fortran 17 | 18 | # Set up fpm 19 | RUN wget https://github.com/fortran-lang/fpm/releases/download/v0.6.0/fpm-0.6.0-linux-x86_64 -4 -O fpm && \ 20 | chmod u+x fpm 21 | RUN mkdir playground && \ 22 | mkdir playground/app && \ 23 | mkdir playground/libraries 24 | 25 | # Fetch libraries 26 | WORKDIR /home/fortran/playground/libraries 27 | 28 | # Set up stdlib 29 | RUN git clone https://github.com/fortran-lang/stdlib 30 | WORKDIR /home/fortran/playground/libraries/stdlib 31 | RUN git checkout stdlib-fpm 32 | WORKDIR /home/fortran/playground 33 | COPY fpm.toml /home/fortran/playground/fpm.toml 34 | COPY main.f90 /home/fortran/playground/app/main.f90 35 | RUN /home/fortran/fpm build 36 | -------------------------------------------------------------------------------- /backend/Docker/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "playground" 2 | author = "Ashirwad Mishra" 3 | maintainer = "ashirwad.golu@gmail.com" 4 | 5 | [build] 6 | auto-executables = true 7 | auto-tests = true 8 | auto-examples = true 9 | 10 | [install] 11 | library = false 12 | 13 | [dependencies.stdlib] 14 | path = "libraries/stdlib" 15 | -------------------------------------------------------------------------------- /backend/Docker/main.f90: -------------------------------------------------------------------------------- 1 | program sample 2 | print*, "Sample Program" 3 | end program sample -------------------------------------------------------------------------------- /backend/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | flask = "*" 8 | docker = "*" 9 | flask-cors = "*" 10 | pyyaml = "*" 11 | tomlkit = "*" 12 | gunicorn = "*" 13 | 14 | [dev-packages] 15 | -------------------------------------------------------------------------------- /backend/app.py: -------------------------------------------------------------------------------- 1 | # importing dependencies - Flask for server and docker to communicate with docker API 2 | from flask import Flask, jsonify, request 3 | from flask_cors import CORS, cross_origin 4 | import docker 5 | import os 6 | import tarfile 7 | import yaml 8 | import json 9 | import tomlkit 10 | 11 | app = Flask(__name__) 12 | 13 | cors = CORS(app) 14 | 15 | app.config["CORS_HEADERS"] = "Content-Type" 16 | 17 | # Starting container 18 | client = docker.from_env() 19 | container = client.containers.run( 20 | "playground-prod", 21 | tty=True, 22 | detach=True, 23 | network_disabled=True 24 | ) 25 | 26 | #Converting tutorial YAML 27 | with open('tutorial.yml', 'r') as file: 28 | configuration = yaml.safe_load(file) 29 | 30 | with open('../frontend/src/tutorial.json', 'w+') as json_file: 31 | json.dump(configuration, json_file) 32 | 33 | 34 | # Editing the file with code inside editor 35 | def edit_file(code, input, libs): 36 | Fortran_file = open("./main.f90", "w+") #main source code from editor 37 | Fortran_file.write(code) 38 | Fortran_file.close() 39 | program_input = open("./program_input.txt", "w+") #user input 40 | program_input.write(input) 41 | program_input.close() 42 | #Generating fpm.toml for fpm processing 43 | with open("fpm.toml", mode="rt", encoding="utf-8") as fp: 44 | fpm = tomlkit.load(fp) 45 | if "stdlib" in libs: 46 | fpm["dependencies"] = {'stdlib' : {'path' : "libraries/stdlib"}} 47 | else: 48 | fpm["dependencies"] = {} 49 | with open("fpm.toml", mode="wt", encoding="utf-8") as fp: 50 | tomlkit.dump(fpm, fp) 51 | 52 | # Copying file with fortran code to container 53 | def copy_to(src, dst, container): 54 | dst = dst 55 | container = container 56 | 57 | os.chdir(os.path.dirname(src)) 58 | srcname = os.path.basename(src) 59 | tar = tarfile.open(src + ".tar", mode="w") 60 | try: 61 | tar.add(srcname) 62 | finally: 63 | tar.close() 64 | 65 | data = open(src + ".tar", "rb").read() 66 | container.put_archive(os.path.dirname(dst), data) 67 | 68 | 69 | # Executing code inside container and getting it's output 70 | def execute_code_in_container(): 71 | copy_to('./main.f90', '/home/fortran/playground/app/main.f90', container) 72 | copy_to('./program_input.txt', '/home/fortran/playground/program_input.txt', container) 73 | copy_to('./fpm.toml','/home/fortran/playground/fpm.toml', container) 74 | container.exec_run('sh -c "/home/fortran/fpm build"') 75 | a = container.exec_run('sh -c "cat program_input.txt | timeout 15s /home/fortran/fpm run"',demux=True) 76 | 77 | return a 78 | 79 | 80 | # API Endpoint to submit code 81 | @app.route("/run", methods=["POST", "GET", "OPTIONS"]) 82 | @cross_origin() 83 | def run_code(): 84 | data = request.get_json() 85 | edit_file(data["code"], data["programInput"], data["libs"]) 86 | code_result = execute_code_in_container() 87 | if code_result.output[0] == None: 88 | output = jsonify({"executed": ""}) 89 | if '' in code_result.output[1].decode(): 90 | output = jsonify({"executed" : code_result.output[1].decode()}) 91 | 92 | return output, 202 93 | output = jsonify({"executed": code_result.output[0].decode()}) 94 | 95 | return output, 202 96 | 97 | 98 | if __name__ == "__main__": 99 | app.run(host='0.0.0.0') 100 | -------------------------------------------------------------------------------- /backend/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "playground" 2 | author = "Ashirwad Mishra" 3 | maintainer = "ashirwad.golu@gmail.com" 4 | 5 | [build] 6 | auto-executables = true 7 | auto-tests = true 8 | auto-examples = true 9 | 10 | [install] 11 | library = false 12 | 13 | [dependencies] 14 | -------------------------------------------------------------------------------- /backend/tutorial.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - title: Hello World 3 | content: Let us start with our first Hello World program 4 | code: "program hello\r\n ! This is a comment line; it is ignored by the compiler\r\n 5 | \ print *, 'Hello, World!'\r\nend program hello\r\n" 6 | - title: Declaring variables 7 | content: Variable names must start with a letter and can consist of letters, numbers and underscores. In the following example we declare a variable for each of the built-in types. 8 | code: "program variables\r\n implicit none\r\n\r\n integer :: amount\r\n real :: pi\r\n complex :: frequency\r\n character :: initial\r\n logical :: isOkay\r\n\r\nend program variables" 9 | - title: Declaring variables 10 | content: Once we have declared a variable, we can assign and reassign values to it using the assignment operator =. 11 | code: "program variables\r\n implicit none\r\n\r\n integer :: amount\r\n real :: pi\r\n complex :: frequency\r\n character :: initial\r\n logical :: isOkay\r\n\r\n amount = 10\r\n pi = 3.1415927\r\n frequency = (1.0, -0.5)\r\n initial = 'A'\r\n isOkay = .false.\r\nend program variables" 12 | - title: Standard output 13 | content: We can use the print statement introduced earlier to print variable values to stdout 14 | code: "program variables\r\n implicit none\r\n\r\n integer :: amount\r\n real :: pi\r\n complex :: frequency\r\n character :: initial\r\n logical :: isOkay\r\n\r\n amount = 10\r\n pi = 3.1415927\r\n frequency = (1.0, -0.5)\r\n initial = 'A'\r\n isOkay = .false.\r\n \r\n print *, 'The value of amount (integer) is: ', amount\r\n print *, 'The value of pi (real) is: ', pi\r\n\r\nend program variables" 15 | - title: Standard input 16 | content: In a similar way, we can read values from the command window using the read statement. Enter values in the box below. 17 | code: "program read_value\r\n implicit none\r\n integer :: age\r\n\r\n print *, 'Please enter your age: '\r\n read(*,*) age\r\n\r\n print *, 'Your age is: ', age\r\n\r\nend program read_value" 18 | - title: Expressions 19 | content: | 20 | The usual set of arithmetic operators are available, listed in order or precedence - 21 | ** - Exponent 22 | * - Multiplication 23 | / - Division 24 | + - Addition 25 | - - Subtraction 26 | 27 | code: "program arithmetic\r\n implicit none\r\n\r\n real :: pi\r\n real :: radius\r\n real :: height\r\n real :: area\r\n real :: volume\r\n\r\n pi = 3.1415927\r\n\r\n print *, 'Enter cylinder base radius:'\r\n read(*,*) radius\r\n\r\n print *, 'Enter cylinder height:'\r\n read(*,*) height\r\n\r\n area = pi * radius**2.0\r\n volume = area * height\r\n\r\n print *, 'Cylinder radius is: ', radius\r\n print *, 'Cylinder height is: ', height\r\n print *, 'Cylinder base area is: ', area\r\n print *, 'Cylinder volume is: ', volume\r\n\r\nend program arithmetic" 28 | 29 | - title: Floating-point precision 30 | content: The desired floating-point precision can be explicitly declared using a kind parameter. The iso_fortran_env intrinsic module provides kind parameters for the common 32-bit and 64-bit floating-point types. 31 | code: "program float\r\n use, intrinsic :: iso_fortran_env, only: sp=>real32, dp=>real64\r\n implicit none\r\n\r\n real(sp) :: float32\r\n real(dp) :: float64\r\n\r\n float32 = 1.0_sp ! Explicit suffix for literal constants\r\n float64 = 1.0_dp\r\n\r\nend program float" 32 | 33 | - title: Arrays and strings 34 | content: | 35 | More often than not, we need to store and operate on long lists of numbers as opposed to just the single scalar variables that we have been using so far; in computer programming such lists are called arrays. 36 | Arrays are multidimensional variables that contain more than one value where each value is accessed using one or more indices. 37 | There are two common notations for declaring array variables using the dimension attribute or by appending the array dimensions in parentheses to the variable name. 38 | 39 | code: "program arrays\r\n implicit none\r\n\r\n ! 1D integer array\r\n integer, dimension(10) :: array1\r\n\r\n ! An equivalent array declaration\r\n integer :: array2(10)\r\n\r\n ! 2D real array\r\n real, dimension(10, 10) :: array3\r\n\r\n ! Custom lower and upper index bounds\r\n real :: array4(0:9)\r\n real :: array5(-5:5)\r\n\r\nend program arrays\r\n" 40 | 41 | - title: Array slicing 42 | content: A powerful feature of the Fortran language is its built-in support for array operations; we can perform operations on all or part of an array using array slicing notation 43 | code: "program array_slice\r\n implicit none\r\n\r\n integer :: i\r\n integer :: array1(10) ! 1D integer array of 10 elements\r\n integer :: array2(10, 10) ! 2D integer array of 100 elements\r\n\r\n array1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ! Array constructor\r\n array1 = [(i, i = 1, 10)] ! Implied do loop constructor\r\n array1(:) = 0 ! Set all elements to zero\r\n array1(1:5) = 1 ! Set first five elements to one\r\n array1(6:) = 1 ! Set all elements after five to one\r\n\r\n print *, array1(1:10:2) ! Print out elements at odd indices\r\n print *, array2(:,1) ! Print out the first column in a 2D array\r\n print *, array1(10:1:-1) ! Print an array in reverse\r\n\r\nend program array_slice\r\n" 44 | 45 | - title: Allocatable (dynamic) arrays 46 | content: | 47 | So far we have specified the size of our array in our program code—this type of array is known as a static array since its size is fixed when we compile our program. 48 | Quite often, we do not know how big our array needs to be until we run our program, for example, if we are reading data from a file of unknown size. 49 | For this problem, we need allocatable arrays. These are allocated while the program is running once we know how big the array needs to be. 50 | code: "program allocatable\r\n implicit none\r\n\r\n integer, allocatable :: array1(:)\r\n integer, allocatable :: array2(:,:)\r\n\r\n allocate(array1(10))\r\n allocate(array2(10,10))\r\n\r\n ! ...\r\n\r\n deallocate(array1)\r\n deallocate(array2)\r\n\r\nend program allocatable\r\n" 51 | 52 | - title: Character strings 53 | content: Example - static character string 54 | code: "program string\r\n implicit none\r\n\r\n character(len=4) :: first_name\r\n character(len=5) :: last_name\r\n character(10) :: full_name\r\n\r\n first_name = 'John'\r\n last_name = 'Smith'\r\n\r\n ! String concatenation\r\n full_name = first_name//' '//last_name\r\n\r\n print *, full_name\r\n\r\nend program string\r\n" 55 | 56 | - title: Character strings 57 | content: Example - allocatable character string 58 | code: "program allocatable_string\r\n implicit none\r\n\r\n character(:), allocatable :: first_name\r\n character(:), allocatable :: last_name\r\n\r\n ! Explicit allocation statement\r\n allocate(character(4) :: first_name)\r\n first_name = 'John'\r\n\r\n ! Allocation on assignment\r\n last_name = 'Smith'\r\n\r\n print *, first_name//' '//last_name\r\n\r\nend program allocatable_string\r\n" 59 | 60 | - title: Array of strings 61 | content: An array of strings can be expressed in Fortran as an array of character variables. All elements in a character array have equal length. However, strings of varying lengths can be provided as input to the array constructor, as shown in the example below. They will be truncated or right-padded with spaces if they are longer or shorter, respectively, than the declared length of the character array. Finally, we use the intrinsic function trim to remove any excess spaces when printing the values to the standard output. 62 | code: "program string_array\r\n implicit none\r\n character(len=10), dimension(2) :: keys, vals\r\n\r\n keys = [character(len=10) :: \"user\", \"dbname\"]\r\n vals = [character(len=10) :: \"ben\", \"motivation\"]\r\n\r\n call show(keys, vals)\r\n\r\n contains\r\n\r\n subroutine show(akeys, avals)\r\n character(len=*), intent(in) :: akeys(:), avals(:)\r\n integer :: i\r\n\r\n do i = 1, size(akeys)\r\n print *, trim(akeys(i)), \": \", trim(avals(i))\r\n end do\r\n\r\n end subroutine show\r\n\r\nend program string_array\r\n" 63 | 64 | - title: Operators and flow control 65 | content: | 66 | One of the powerful advantages of computer algorithms, compared to simple mathematical formulae, comes in the form of program branching whereby the program can decide which instructions to execute next based on a logical condition. 67 | There are two main forms of controlling program flow: 68 | Conditional (if): choose program path based on a boolean (true or false) value 69 | Loop: repeat a portion of code multiple times 70 | code : "" 71 | 72 | - title: Relational operators 73 | content: | 74 | Before we use a conditional branching operator, we need to be able to form a logical expression. 75 | To form a logical expression, the following set of relational operators are available: 76 | Operator - Alternative - Description 77 | == - .eq. - Tests for equality of two operands 78 | /= .ne. Test for inequality of two operands 79 | > .gt. Tests if left operand is strictly greater than right operand 80 | < .lt. Tests if left operand is strictly less than right operand 81 | >= .ge. Tests if left operand is greater than or equal to right operand 82 | <= .le. Tests if left operand is less than or equal to right operand 83 | code: "" 84 | 85 | - title: Logical Operators 86 | content: | 87 | There’s the following logical operators: 88 | .and. TRUE if both left and right operands are TRUE 89 | .or. TRUE if either left or right or both operands are TRUE 90 | .not. TRUE if right operand is FALSE 91 | .eqv. TRUE if left operand has same logical value as right operand 92 | .neqv. TRUE if left operand has the opposite logical value as right operand 93 | code: "" 94 | 95 | - title: Conditional construct (if) 96 | content: In the following examples, a conditional if construct is used to print out a message to describe the nature of the angle variable, 97 | In this first example, the code within the if construct is only executed if the test expression (angle < 90.0) is true. 98 | Tip - It is good practice to indent code within constructs such as if and do to make code more readable. 99 | 100 | code: "program conditional\r\n \r\n real :: angle\r\n angle = 60\r\n \r\n if (angle < 90.0) then\r\n print *, 'Angle is acute'\r\n end if\r\n \r\nend program conditional\r\n\r\n" 101 | 102 | - title: Conditional construct (if-else) 103 | content: We can add an alternative branch to the construct using the else keyword 104 | code: "program conditional\r\n \r\n real :: angle\r\n angle = 120\r\n \r\n if (angle < 90.0) then\r\n print *, 'Angle is acute'\r\n else\r\n print *, 'Angle is obtuse'\r\n end if\r\n\r\nend program conditional\r\n\r\n" 105 | 106 | - title: Conditional construct (if) 107 | content: Now there are two branches in the if construct, but only one branch is executed depending on the logical expression following the if keyword. 108 | We can actually add any number of branches using else if to specify more conditions 109 | When multiple conditional expressions are used, each conditional expression is tested only if none of the previous expressions have evaluated to true. 110 | code: "program conditional\r\n \r\n real :: angle\r\n angle = 200\r\n \r\n if (angle < 90.0) then\r\n print *, 'Angle is acute'\r\n else if (angle < 180.0) then\r\n print *, 'Angle is obtuse'\r\n else\r\n print *, 'Angle is reflex'\r\n end if\r\n\r\nend program conditional\r\n\r\n" 111 | 112 | - title: Loop constructs (do) 113 | content: | 114 | In this example, a do loop construct is used to print out the numbers in a sequence. 115 | The do loop has an integer counter variable which is used to track which iteration 116 | of the loop is currently executing. 117 | In this example we use a common name for this counter variable i. 118 | When we define the start of the do loop, we use our counter variable name followed by an equals (=) sign 119 | to specify the start value and final value of our counting variable. 120 | code: "program conditional\r\n \r\n integer :: i\r\n\r\n do i = 1, 10\r\n print *, i\r\n end do\r\n\r\nend program conditional\r\n\r\n" 121 | 122 | - title: Loop constructs (do) 123 | content: Here's how to perform a do loop with skip 124 | code: "program conditional\r\n \r\n integer :: i\r\n \r\n do i = 1, 10, 2\r\n print *, i ! Print odd numbers\r\n end do\r\n\r\nend program conditional\r\n\r\n" 125 | 126 | - title: Conditional loop (do while) 127 | content: A condition may be added to a do loop with the while keyword. The loop will be executed while the condition given in while() evaluates to .true.. 128 | code: "program loop\r\n \r\n integer :: i\r\n \r\n i = 1\r\n do while (i < 11)\r\n print *, i\r\n i = i + 1\r\n end do\r\n ! Here i = 11\r\n\r\n\r\nend program loop" 129 | 130 | - title: Loop control statements (cycle) 131 | content: Most often than not, loops need to be stopped if a condition is met. Fortran provides two executable statements to deal with such cases. 132 | exit is used to quit the loop prematurely. It is usually enclosed inside an if. 133 | code: "program loop\r\n \r\n integer :: i\r\n \r\n do i = 1, 100\r\n if (i > 10) then\r\n exit ! Stop printing numbers\r\n end if\r\n print *, i\r\n end do\r\n ! Here i = 11\r\n\r\nend program loop" 134 | 135 | - title: Loop control statements (cycle) 136 | content: On the other hand, cycle skips whatever is left of the loop and goes into the next cycle. 137 | Note - When used within nested loops, the cycle and exit statements operate on the innermost loop. 138 | code: "program loop\r\n \r\n integer :: i\r\n \r\n do i = 1, 10\r\n if (mod(i, 2) == 0) then\r\n cycle ! Don't print even numbers\r\n end if\r\n print *, i\r\n end do\r\n\r\nend program loop" 139 | 140 | - title: Nested loop control - tags 141 | content: | 142 | A recurring case in any programming language is the use of nested loops. Nested loops refer to loops that exist within another loop. Fortran allows the programmer to tag or name each loop. If loops are tagged, there are two potential benefits: 143 | 1. The readability of the code may be improved (when the naming is meaningful). 144 | 2. exit and cycle may be used with tags, which allows for very fine-grained control of the loops. 145 | code: "program nested\r\n \r\n integer :: i, j\r\n \r\n outer_loop: do i = 1, 10\r\n inner_loop: do j = 1, 10\r\n if ((j + i) > 10) then ! Print only pairs of i and j that add up to 10\r\n cycle outer_loop ! Go to the next iteration of the outer loop\r\n end if\r\n print *, 'I=', i, ' J=', j, ' Sum=', j + i\r\n end do inner_loop\r\n end do outer_loop\r\n \r\n do i = 1, 10\r\n if (mod(i, 2) == 0) then\r\n cycle ! Don't print even numbers\r\n end if\r\n print *, i\r\n end do\r\n\r\nend program nested" 146 | 147 | - title: Parallelizable loop (do concurrent) 148 | content: The do concurrent loop is used to explicitly specify that the inside of the loop has no interdependencies; this informs the compiler that it may use parallelization/SIMD to speed up execution of the loop and conveys programmer intention more clearly. More specifically, this means that any given loop iteration does not depend on the prior execution of other loop iterations. It is also necessary that any state changes that may occur must only happen within each do concurrent loop. These requirements place restrictions on what can be placed within the loop body. 149 | code: "program nested\r\n \r\n real, parameter :: pi = 3.14159265\r\n integer, parameter :: n = 10\r\n real :: result_sin(n)\r\n integer :: i\r\n \r\n do concurrent (i = 1:n) ! Careful, the syntax is slightly different\r\n result_sin(i) = sin(i * pi/4.)\r\n end do\r\n \r\n print *, result_sin\r\n\r\nend program nested" 150 | 151 | - title: Parallelizable loop (do concurrent) 152 | content: Important - Simply replacing a do loop with a do concurrent does not guarantee parallel execution. The explanation given above does not detail all the requirements that need to be met in order to write a correct do concurrent loop. Compilers are also free to do as they see fit, meaning they may not optimize the loop (e.g., a small number of iterations doing a simple calculation, like the below example). In general, compiler flags are required to activate possible parallelization for do concurrent loops. 153 | code: "program nested\r\n \r\n real, parameter :: pi = 3.14159265\r\n integer, parameter :: n = 10\r\n real :: result_sin(n)\r\n integer :: i\r\n \r\n do concurrent (i = 1:n) ! Careful, the syntax is slightly different\r\n result_sin(i) = sin(i * pi/4.)\r\n end do\r\n \r\n print *, result_sin\r\n\r\nend program nested" 154 | 155 | - title: Organising code structure 156 | content: | 157 | Fortran has two forms of procedure: 158 | 1. Subroutine: invoked by a call statement 159 | 2. Function: invoked within an expression or assignment to which it returns a value 160 | code: "" 161 | 162 | - title: Subroutines 163 | content: The subroutine input arguments, known as dummy arguments, are specified in parentheses after the subroutine name; the dummy argument types and attributes are declared within the body of the subroutine just like local variables. 164 | code: "subroutine print_matrix(n,m,A)\r\n implicit none\r\n integer, intent(in) :: n\r\n integer, intent(in) :: m\r\n real, intent(in) :: A(n, m)\r\n\r\n integer :: i\r\n\r\n do i = 1, n\r\n print *, A(i, 1:m)\r\n end do\r\n\r\nend subroutine print_matrix\r\n\r\nprogram call_sub\r\n implicit none\r\n\r\n real :: mat(10, 20)\r\n\r\n mat(:,:) = 0.0\r\n\r\n call print_matrix(10, 20, mat)\r\n\r\nend program call_sub\r\n" 165 | 166 | - title: Subroutines 167 | content: | 168 | Note the additional intent attribute when declaring the dummy arguments; this optional attribute signifies to the compiler whether the argument is read-only (intent(in)) or write-only (intent(out)) or read-write(intent(inout)) within the procedure. In this example, the subroutine does not modify its arguments, hence all arguments are intent(in). 169 | Tip: It is good practice to always specify the intent attribute for dummy arguments; this allows the compiler to check for unintentional errors and provides self-documentation. 170 | We can call this subroutine from a program using a call statement. 171 | code: "subroutine print_matrix(n,m,A)\r\n implicit none\r\n integer, intent(in) :: n\r\n integer, intent(in) :: m\r\n real, intent(in) :: A(n, m)\r\n\r\n integer :: i\r\n\r\n do i = 1, n\r\n print *, A(i, 1:m)\r\n end do\r\n\r\nend subroutine print_matrix\r\n\r\nprogram call_sub\r\n implicit none\r\n\r\n real :: mat(10, 20)\r\n\r\n mat(:,:) = 0.0\r\n\r\n call print_matrix(10, 20, mat)\r\n\r\nend program call_sub\r\n" 172 | 173 | - title: Functions 174 | content: | 175 | Tip: In production code, the intrinsic function norm2 should be used. 176 | Tip: It is good programming practice for functions not to modify their arguments that is, all function arguments should be intent(in). Such functions are known as pure functions. Use subroutines if your procedure needs to modify its arguments. 177 | code: "function vector_norm(n,vec) result(norm)\r\n implicit none\r\n integer, intent(in) :: n\r\n real, intent(in) :: vec(n)\r\n real :: norm\r\n\r\n norm = sqrt(sum(vec**2))\r\n\r\nend function vector_norm\r\n\r\nprogram run_fcn\r\n implicit none\r\n\r\n real :: v(9)\r\n real :: vector_norm\r\n\r\n v(:) = 9\r\n\r\n print *, 'Vector norm = ', vector_norm(9,v)\r\n\r\nend program run_fcn\r\n" 178 | 179 | 180 | -------------------------------------------------------------------------------- /backend/wsgi.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | if __name__=='__main__': 3 | app.run() -------------------------------------------------------------------------------- /frontend/.env: -------------------------------------------------------------------------------- 1 | REACT_APP_PLAYGROUND_API_URL=http://localhost:5000 -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.4", 7 | "@testing-library/react": "^13.3.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "ace-builds": "^1.6.0", 10 | "axios": "^0.27.2", 11 | "bootstrap": "^5.1.3", 12 | "react": "^18.2.0", 13 | "react-ace": "^10.1.0", 14 | "react-bootstrap": "^2.4.0", 15 | "react-bootstrap-icons": "^1.10.2", 16 | "react-dom": "^18.2.0", 17 | "react-scripts": "5.0.1", 18 | "web-vitals": "^2.1.4" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /frontend/public/fortran-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fortran-lang/playground/3d4d85309324765e13a25ce1ebb815513463979d/frontend/public/fortran-logo.png -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Playground 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Playground", 3 | "name": "Fortran Playground", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | 2 | .main-container{ 3 | width: 100%; 4 | height: auto; 5 | padding: 0.5rem; 6 | display: flex; 7 | justify-content: space-between; 8 | background-color: white; 9 | } 10 | 11 | .my-editor { 12 | width: 50%; 13 | overflow: auto; 14 | } 15 | 16 | .terminal{ 17 | width: 50%; 18 | border: 2px solid black; 19 | padding: 0.2rem; 20 | display: flex; 21 | flex-direction: column; 22 | justify-content: space-around; 23 | 24 | } 25 | 26 | .run-button{ 27 | height: 50px; 28 | width: 70px; 29 | padding-top: 15rem; 30 | } 31 | 32 | 33 | #execute{ 34 | height: 50px; 35 | width: 70px; 36 | } 37 | 38 | .linkText{ 39 | color:#FDFEFE 40 | } 41 | .linkText:hover{ 42 | color:#B3B6B7; 43 | 44 | } 45 | 46 | .options{ 47 | display: flex; 48 | flex-direction: column; 49 | width: 4em; 50 | height: 100%; 51 | margin: 0rem 0.5rem 0rem 0.5rem; 52 | } 53 | 54 | .potrait-options { 55 | display: flex; 56 | flex-direction: row; 57 | width: 100%; 58 | margin: 0.5rem 0rem 0.5rem 0rem; 59 | overflow-x: auto; 60 | /* justify-content: space-around; */ 61 | } 62 | 63 | .output-formmating{ 64 | white-space: pre-wrap; 65 | 66 | } 67 | 68 | .tutButton { 69 | display: flex; 70 | justify-content: space-between; 71 | } 72 | 73 | .selector{ 74 | display: flex; 75 | align-items: center; 76 | margin: 2px; 77 | } 78 | 79 | .selector img{ 80 | width : 24px; 81 | } 82 | 83 | /*Custom theming for bootstrap buttons*/ 84 | .btn-run { 85 | background-color: #009900 !important; 86 | color: white; 87 | } 88 | .btn-run:hover{ 89 | background-color: #018701 !important; 90 | color: white; 91 | } 92 | .btn-run:active:focus{ 93 | background-color: #018701 !important; 94 | color: white; 95 | } 96 | .btn-lib{ 97 | background-color: #FF8E00 !important; 98 | color: white; 99 | } 100 | .btn-lib:hover{ 101 | background-color: #FF8E00 !important; 102 | color: white; 103 | } 104 | .btn-lib:active:focus{ 105 | background-color: #ed8502 !important; 106 | color: white; 107 | } 108 | .btn-rotate { 109 | background-color: #734f96 !important; 110 | color: white; 111 | } 112 | .btn-rotate:hover{ 113 | background-color: #674886 !important; 114 | color: white; 115 | } 116 | .btn-rotate:active:focus{ 117 | background-color: #734f96 !important; 118 | color: white; 119 | } 120 | 121 | .btn-light { 122 | background-color: #F7F7F7 !important; 123 | /* color: white; */ 124 | } 125 | 126 | .btn-light:hover{ 127 | background-color: #eeeeee !important; 128 | color: white; 129 | } 130 | .btn-light:active:focus{ 131 | background-color: #dddddd !important; 132 | color: white; 133 | } -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | //Importing CSS, Ace Editor, Axios, Bootstrap and other components used in app 2 | import './App.css'; 3 | import Editor from './Editor'; 4 | import { useState } from 'react'; 5 | import axios from 'axios'; 6 | import Navigationbar from './Navbar'; 7 | import 'bootstrap/dist/css/bootstrap.min.css'; 8 | import Button from 'react-bootstrap/Button'; 9 | import Spinner from 'react-bootstrap/Spinner'; 10 | import Card from 'react-bootstrap/Card'; 11 | import InputBox from './InputBox'; 12 | import TutorialCard from './TutorialCard'; 13 | import Modal from 'react-bootstrap/Modal'; 14 | import Form from 'react-bootstrap/Form'; 15 | import Tutorial from './tutorial.json'; //Tutorial JSON 16 | import { 17 | ArrowClockwise, 18 | MoonStarsFill, 19 | PlayFill, 20 | Stack, 21 | SunFill 22 | } from 'react-bootstrap-icons'; 23 | //Function to push \n in string to new lines 24 | function NewlineText(props) { 25 | const text = props.text; 26 | return
{text}
; 27 | } 28 | 29 | var libs = []; 30 | 31 | function App() { 32 | const [text, setText] = useState(''); //State to store editor code 33 | const [output, setOutput] = useState(''); //State to store output 34 | const [isLoading, setIsLoading] = useState(false); //Loading animations 35 | const [input, setInput] = useState(''); // user input 36 | const [inputOn, setinputOn] = useState(false); // toggle for input button 37 | const [show, setShow] = useState(false); // library modal toggle 38 | const [height, setHeight] = useState('calc(100vh - 72px)'); 39 | const [potrait, setPotrait] = useState(false); // potrait view 40 | const handleClose = () => setShow(false); 41 | const handleShow = () => setShow(true); 42 | const [stdlibOn, setstdlibOn] = useState(false); // state to store package info 43 | const [exercise, setExercise] = useState(0); // Tutorial Exercise 44 | const [showTutorial, setshowTutorial] = useState(false); 45 | const [theme, setTheme] = useState('monokai'); 46 | const queryString = window.location.search; 47 | const urlParams = new URLSearchParams(queryString); 48 | 49 | // Handle tutorial buttons 50 | const goRight = () => { 51 | if (exercise < Tutorial.length - 1) { 52 | setExercise(exercise + 1); 53 | } 54 | 55 | tutfunc(Tutorial[exercise + 1].code); 56 | }; 57 | const goLeft = () => { 58 | if (exercise > 0) { 59 | setExercise(exercise - 1); 60 | } 61 | tutfunc(Tutorial[exercise - 1].code); 62 | }; 63 | 64 | // Switch toggle for stdlib 65 | const onSwitchAction = () => { 66 | setstdlibOn(!stdlibOn); 67 | if (!stdlibOn) { 68 | libs.push('stdlib'); 69 | } else { 70 | if (libs.includes('stdlib')) { 71 | libs = libs.filter((item) => item !== 'stdlib'); 72 | } 73 | } 74 | }; 75 | 76 | //Changing code inside editor for tutorial 77 | const tutfunc = (TutorialCode) => { 78 | setText(TutorialCode); 79 | }; 80 | //Tutorial Prompt 81 | const startTutorial = () => { 82 | setshowTutorial(true); 83 | tutfunc(Tutorial[exercise].code); 84 | }; 85 | const handleInputChange = (e) => { 86 | e.preventDefault(); // prevent the default action 87 | setInput(e.target.value); // set name to e.target.value (event) 88 | console.log(input); 89 | }; 90 | 91 | //inputbox toggle 92 | const handleInputBox = (e) => { 93 | inputOn ? setinputOn(false) : setinputOn(true); 94 | //setinputOn(true) 95 | }; 96 | //POST request to Flask server 97 | const handleClick = async () => { 98 | setOutput(''); 99 | setIsLoading(true); 100 | 101 | // POST request using axios inside useEffect React hook 102 | await axios 103 | .post(`${process.env.REACT_APP_PLAYGROUND_API_URL}/run`, { code: text, programInput: input, libs: libs }) 104 | .then((response) => { 105 | setOutput(response.data.executed); 106 | }); 107 | 108 | setIsLoading(false); 109 | }; 110 | 111 | const handleLayout = () => { 112 | const container = document.querySelector('.main-container'); 113 | const editor = document.querySelector('.my-editor'); 114 | const options = document.querySelector('.options'); 115 | const terminal = document.querySelector('.terminal'); 116 | 117 | if (potrait) { 118 | container.style.flexDirection = 'row'; 119 | options.classList.remove('potrait-options'); 120 | options.style.flexDirection = 'column'; 121 | terminal.classList.remove('w-100'); 122 | editor.classList.remove('w-100'); 123 | setHeight('calc(100vh - 72px)'); 124 | } else { 125 | container.style.flexDirection = 'column'; 126 | options.classList.add('potrait-options'); 127 | options.style.flexDirection = 'row'; 128 | terminal.classList.add('w-100'); 129 | editor.classList.add('w-100'); 130 | setHeight('calc((100vh - 132px) / 2)'); 131 | } 132 | setPotrait(!potrait); 133 | }; 134 | 135 | //reset code button 136 | const resetCode = () => { 137 | setText(''); 138 | }; 139 | 140 | const loadCode = () => { 141 | setText(urlParams.get('code')); 142 | }; 143 | 144 | const handleKeyDown = (event) => { 145 | if (event.ctrlKey && event.keyCode === 13) { 146 | handleClick(); 147 | } 148 | }; 149 | 150 | const toggleTheme = () => { 151 | if (theme === 'monokai') { 152 | setTheme('solarized_light'); 153 | } else { 154 | setTheme('monokai'); 155 | } 156 | }; 157 | 158 | const LayoutIcon = ({ portrait }) => { 159 | return ( 160 |
168 | 169 |
170 | ) 171 | } 172 | 173 | return ( 174 |
175 | {/*Navbar*/} 176 |
177 | 178 |
179 |
183 | {/*Editor Component*/} 184 |
185 | setText(value)} /> 186 |
187 | 188 | {/* Buttons */} 189 |
190 | 193 | 196 | 199 | 202 | 214 |
215 | 216 | {/*Card component to display output*/} 217 |
218 | 226 | Output 227 | 228 | 229 | {/* Spinning Animation While Request is processed */} 230 | {isLoading ? ( 231 |

232 | 233 |

234 | ) : null} 235 | 236 |
237 |
238 |
239 | {/*Tutorial Card Component */} 240 | {showTutorial ? ( 241 | <> 242 | 243 |
244 |   245 | 246 |
247 | 248 | ) : ( 249 | 257 | Welcome to the Fortran Playground 258 | 259 |

260 | Use the editor on the left to type your code, you can select your{' '} 261 | libraries 262 | and provide custom input for your code 263 | using the respective buttons. 264 |

265 |

266 | New to Fortran? 267 |

Try our Quickstart Tutorial

268 | 269 |

270 |
271 |
272 | )} 273 | {/* Input Box to provide input for program */} 274 |
275 | 276 | {inputOn ? : null} {/*toggle for input */} 277 | {/*Library selector pop-up modal */} 278 | 279 | 280 | Packages 281 | 282 | 283 | Please select the packages you want 284 |
285 | 292 | 293 |
294 | 295 | 298 | 301 | 302 |
303 |
304 |
305 |
306 | ); 307 | } 308 | 309 | export default App; 310 | -------------------------------------------------------------------------------- /frontend/src/Editor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AceEditor from 'react-ace'; 3 | 4 | import 'ace-builds/src-noconflict/mode-fortran'; 5 | import 'ace-builds/src-noconflict/theme-monokai'; 6 | import 'ace-builds/src-noconflict/theme-solarized_light'; 7 | import 'ace-builds/src-noconflict/ext-language_tools'; 8 | 9 | export default function Editor(props) { 10 | return ( 11 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/InputBox.js: -------------------------------------------------------------------------------- 1 | import Form from 'react-bootstrap/Form' 2 | 3 | export default function InputBox(props){ 4 | 5 | 6 | 7 | return( 8 | <> 9 |
10 | 11 | 12 | 13 |
14 | 15 | ) 16 | } -------------------------------------------------------------------------------- /frontend/src/Navbar.js: -------------------------------------------------------------------------------- 1 | import logo from "./fortran-logo.png" 2 | import Navbar from 'react-bootstrap/Navbar' 3 | import Nav from 'react-bootstrap/Nav' 4 | 5 | export default function Navigationbar(){ 6 | return( 7 | 8 | 9 | {' '} 16 | Fortran Playground 17 | 18 | 19 | 20 | 31 | 32 | 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /frontend/src/TutorialCard.js: -------------------------------------------------------------------------------- 1 | import Card from 'react-bootstrap/Card'; 2 | 3 | function NewlineText(props) { 4 | const text = props.text; 5 | return
{text}
; 6 | } 7 | 8 | function TutorialCard(props) { 9 | return ( 10 | 18 | 19 | {props.title} 20 | 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | } 28 | 29 | export default TutorialCard; 30 | -------------------------------------------------------------------------------- /frontend/src/fortran-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fortran-lang/playground/3d4d85309324765e13a25ce1ebb815513463979d/frontend/src/fortran-logo.png -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0.2rem; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/src/tutorial.json: -------------------------------------------------------------------------------- 1 | [{"title": "Hello World", "content": "Let us start with our first Hello World program", "code": "program hello\r\n ! This is a comment line; it is ignored by the compiler\r\n print *, 'Hello, World!'\r\nend program hello\r\n"}, {"title": "Declaring variables", "content": "Variable names must start with a letter and can consist of letters, numbers and underscores. In the following example we declare a variable for each of the built-in types.", "code": "program variables\r\n implicit none\r\n\r\n integer :: amount\r\n real :: pi\r\n complex :: frequency\r\n character :: initial\r\n logical :: isOkay\r\n\r\nend program variables"}, {"title": "Declaring variables", "content": "Once we have declared a variable, we can assign and reassign values to it using the assignment operator =.", "code": "program variables\r\n implicit none\r\n\r\n integer :: amount\r\n real :: pi\r\n complex :: frequency\r\n character :: initial\r\n logical :: isOkay\r\n\r\n amount = 10\r\n pi = 3.1415927\r\n frequency = (1.0, -0.5)\r\n initial = 'A'\r\n isOkay = .false.\r\nend program variables"}, {"title": "Standard output", "content": "We can use the print statement introduced earlier to print variable values to stdout", "code": "program variables\r\n implicit none\r\n\r\n integer :: amount\r\n real :: pi\r\n complex :: frequency\r\n character :: initial\r\n logical :: isOkay\r\n\r\n amount = 10\r\n pi = 3.1415927\r\n frequency = (1.0, -0.5)\r\n initial = 'A'\r\n isOkay = .false.\r\n \r\n print *, 'The value of amount (integer) is: ', amount\r\n print *, 'The value of pi (real) is: ', pi\r\n\r\nend program variables"}, {"title": "Standard input", "content": "In a similar way, we can read values from the command window using the read statement. Enter values in the box below.", "code": "program read_value\r\n implicit none\r\n integer :: age\r\n\r\n print *, 'Please enter your age: '\r\n read(*,*) age\r\n\r\n print *, 'Your age is: ', age\r\n\r\nend program read_value"}, {"title": "Expressions", "content": "The usual set of arithmetic operators are available, listed in order or precedence - \n ** - Exponent\n * - Multiplication\n / - Division\n + - Addition\n - - Subtraction\n \n", "code": "program arithmetic\r\n implicit none\r\n\r\n real :: pi\r\n real :: radius\r\n real :: height\r\n real :: area\r\n real :: volume\r\n\r\n pi = 3.1415927\r\n\r\n print *, 'Enter cylinder base radius:'\r\n read(*,*) radius\r\n\r\n print *, 'Enter cylinder height:'\r\n read(*,*) height\r\n\r\n area = pi * radius**2.0\r\n volume = area * height\r\n\r\n print *, 'Cylinder radius is: ', radius\r\n print *, 'Cylinder height is: ', height\r\n print *, 'Cylinder base area is: ', area\r\n print *, 'Cylinder volume is: ', volume\r\n\r\nend program arithmetic"}, {"title": "Floating-point precision", "content": "The desired floating-point precision can be explicitly declared using a kind parameter. The iso_fortran_env intrinsic module provides kind parameters for the common 32-bit and 64-bit floating-point types.", "code": "program float\r\n use, intrinsic :: iso_fortran_env, only: sp=>real32, dp=>real64\r\n implicit none\r\n\r\n real(sp) :: float32\r\n real(dp) :: float64\r\n\r\n float32 = 1.0_sp ! Explicit suffix for literal constants\r\n float64 = 1.0_dp\r\n\r\nend program float"}, {"title": "Arrays and strings", "content": "More often than not, we need to store and operate on long lists of numbers as opposed to just the single scalar variables that we have been using so far; in computer programming such lists are called arrays.\nArrays are multidimensional variables that contain more than one value where each value is accessed using one or more indices. \nThere are two common notations for declaring array variables using the dimension attribute or by appending the array dimensions in parentheses to the variable name.\n", "code": "program arrays\r\n implicit none\r\n\r\n ! 1D integer array\r\n integer, dimension(10) :: array1\r\n\r\n ! An equivalent array declaration\r\n integer :: array2(10)\r\n\r\n ! 2D real array\r\n real, dimension(10, 10) :: array3\r\n\r\n ! Custom lower and upper index bounds\r\n real :: array4(0:9)\r\n real :: array5(-5:5)\r\n\r\nend program arrays\r\n"}, {"title": "Array slicing", "content": "A powerful feature of the Fortran language is its built-in support for array operations; we can perform operations on all or part of an array using array slicing notation", "code": "program array_slice\r\n implicit none\r\n\r\n integer :: i\r\n integer :: array1(10) ! 1D integer array of 10 elements\r\n integer :: array2(10, 10) ! 2D integer array of 100 elements\r\n\r\n array1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ! Array constructor\r\n array1 = [(i, i = 1, 10)] ! Implied do loop constructor\r\n array1(:) = 0 ! Set all elements to zero\r\n array1(1:5) = 1 ! Set first five elements to one\r\n array1(6:) = 1 ! Set all elements after five to one\r\n\r\n print *, array1(1:10:2) ! Print out elements at odd indices\r\n print *, array2(:,1) ! Print out the first column in a 2D array\r\n print *, array1(10:1:-1) ! Print an array in reverse\r\n\r\nend program array_slice\r\n"}, {"title": "Allocatable (dynamic) arrays", "content": "So far we have specified the size of our array in our program code\u00e2\u20ac\u201dthis type of array is known as a static array since its size is fixed when we compile our program.\nQuite often, we do not know how big our array needs to be until we run our program, for example, if we are reading data from a file of unknown size.\nFor this problem, we need allocatable arrays. These are allocated while the program is running once we know how big the array needs to be.\n", "code": "program allocatable\r\n implicit none\r\n\r\n integer, allocatable :: array1(:)\r\n integer, allocatable :: array2(:,:)\r\n\r\n allocate(array1(10))\r\n allocate(array2(10,10))\r\n\r\n ! ...\r\n\r\n deallocate(array1)\r\n deallocate(array2)\r\n\r\nend program allocatable\r\n"}, {"title": "Character strings", "content": "Example - static character string", "code": "program string\r\n implicit none\r\n\r\n character(len=4) :: first_name\r\n character(len=5) :: last_name\r\n character(10) :: full_name\r\n\r\n first_name = 'John'\r\n last_name = 'Smith'\r\n\r\n ! String concatenation\r\n full_name = first_name//' '//last_name\r\n\r\n print *, full_name\r\n\r\nend program string\r\n"}, {"title": "Character strings", "content": "Example - allocatable character string", "code": "program allocatable_string\r\n implicit none\r\n\r\n character(:), allocatable :: first_name\r\n character(:), allocatable :: last_name\r\n\r\n ! Explicit allocation statement\r\n allocate(character(4) :: first_name)\r\n first_name = 'John'\r\n\r\n ! Allocation on assignment\r\n last_name = 'Smith'\r\n\r\n print *, first_name//' '//last_name\r\n\r\nend program allocatable_string\r\n"}, {"title": "Array of strings", "content": "An array of strings can be expressed in Fortran as an array of character variables. All elements in a character array have equal length. However, strings of varying lengths can be provided as input to the array constructor, as shown in the example below. They will be truncated or right-padded with spaces if they are longer or shorter, respectively, than the declared length of the character array. Finally, we use the intrinsic function trim to remove any excess spaces when printing the values to the standard output.", "code": "program string_array\r\n implicit none\r\n character(len=10), dimension(2) :: keys, vals\r\n\r\n keys = [character(len=10) :: \"user\", \"dbname\"]\r\n vals = [character(len=10) :: \"ben\", \"motivation\"]\r\n\r\n call show(keys, vals)\r\n\r\n contains\r\n\r\n subroutine show(akeys, avals)\r\n character(len=*), intent(in) :: akeys(:), avals(:)\r\n integer :: i\r\n\r\n do i = 1, size(akeys)\r\n print *, trim(akeys(i)), \": \", trim(avals(i))\r\n end do\r\n\r\n end subroutine show\r\n\r\nend program string_array\r\n"}, {"title": "Operators and flow control", "content": "One of the powerful advantages of computer algorithms, compared to simple mathematical formulae, comes in the form of program branching whereby the program can decide which instructions to execute next based on a logical condition.\nThere are two main forms of controlling program flow:\nConditional (if): choose program path based on a boolean (true or false) value\nLoop: repeat a portion of code multiple times\n", "code": ""}, {"title": "Relational operators", "content": "Before we use a conditional branching operator, we need to be able to form a logical expression.\nTo form a logical expression, the following set of relational operators are available:\nOperator - Alternative - Description\n== - .eq. - Tests for equality of two operands\n/= .ne. Test for inequality of two operands\n> .gt. Tests if left operand is strictly greater than right operand\n< .lt. Tests if left operand is strictly less than right operand\n>= .ge. Tests if left operand is greater than or equal to right operand\n<= .le. Tests if left operand is less than or equal to right operand\n", "code": ""}, {"title": "Logical Operators", "content": "There\u00e2\u20ac\u2122s the following logical operators:\n.and. TRUE if both left and right operands are TRUE\n.or. TRUE if either left or right or both operands are TRUE\n.not. TRUE if right operand is FALSE\n.eqv. TRUE if left operand has same logical value as right operand\n.neqv. TRUE if left operand has the opposite logical value as right operand\n", "code": ""}, {"title": "Conditional construct (if)", "content": "In the following examples, a conditional if construct is used to print out a message to describe the nature of the angle variable, In this first example, the code within the if construct is only executed if the test expression (angle < 90.0) is true. Tip - It is good practice to indent code within constructs such as if and do to make code more readable.", "code": "program conditional\r\n \r\n real :: angle\r\n angle = 60\r\n \r\n if (angle < 90.0) then\r\n print *, 'Angle is acute'\r\n end if\r\n \r\nend program conditional\r\n\r\n"}, {"title": "Conditional construct (if-else)", "content": "We can add an alternative branch to the construct using the else keyword", "code": "program conditional\r\n \r\n real :: angle\r\n angle = 120\r\n \r\n if (angle < 90.0) then\r\n print *, 'Angle is acute'\r\n else\r\n print *, 'Angle is obtuse'\r\n end if\r\n\r\nend program conditional\r\n\r\n"}, {"title": "Conditional construct (if)", "content": "Now there are two branches in the if construct, but only one branch is executed depending on the logical expression following the if keyword. We can actually add any number of branches using else if to specify more conditions When multiple conditional expressions are used, each conditional expression is tested only if none of the previous expressions have evaluated to true.", "code": "program conditional\r\n \r\n real :: angle\r\n angle = 200\r\n \r\n if (angle < 90.0) then\r\n print *, 'Angle is acute'\r\n else if (angle < 180.0) then\r\n print *, 'Angle is obtuse'\r\n else\r\n print *, 'Angle is reflex'\r\n end if\r\n\r\nend program conditional\r\n\r\n"}, {"title": "Loop constructs (do)", "content": "In this example, a do loop construct is used to print out the numbers in a sequence.\nThe do loop has an integer counter variable which is used to track which iteration\nof the loop is currently executing.\nIn this example we use a common name for this counter variable i.\nWhen we define the start of the do loop, we use our counter variable name followed by an equals (=) sign\nto specify the start value and final value of our counting variable.\n", "code": "program conditional\r\n \r\n integer :: i\r\n\r\n do i = 1, 10\r\n print *, i\r\n end do\r\n\r\nend program conditional\r\n\r\n"}, {"title": "Loop constructs (do)", "content": "Here's how to perform a do loop with skip", "code": "program conditional\r\n \r\n integer :: i\r\n \r\n do i = 1, 10, 2\r\n print *, i ! Print odd numbers\r\n end do\r\n\r\nend program conditional\r\n\r\n"}, {"title": "Conditional loop (do while)", "content": "A condition may be added to a do loop with the while keyword. The loop will be executed while the condition given in while() evaluates to .true..", "code": "program loop\r\n \r\n integer :: i\r\n \r\n i = 1\r\n do while (i < 11)\r\n print *, i\r\n i = i + 1\r\n end do\r\n ! Here i = 11\r\n\r\n\r\nend program loop"}, {"title": "Loop control statements (cycle)", "content": "Most often than not, loops need to be stopped if a condition is met. Fortran provides two executable statements to deal with such cases. exit is used to quit the loop prematurely. It is usually enclosed inside an if.", "code": "program loop\r\n \r\n integer :: i\r\n \r\n do i = 1, 100\r\n if (i > 10) then\r\n exit ! Stop printing numbers\r\n end if\r\n print *, i\r\n end do\r\n ! Here i = 11\r\n\r\nend program loop"}, {"title": "Loop control statements (cycle)", "content": "On the other hand, cycle skips whatever is left of the loop and goes into the next cycle. Note - When used within nested loops, the cycle and exit statements operate on the innermost loop.", "code": "program loop\r\n \r\n integer :: i\r\n \r\n do i = 1, 10\r\n if (mod(i, 2) == 0) then\r\n cycle ! Don't print even numbers\r\n end if\r\n print *, i\r\n end do\r\n\r\nend program loop"}, {"title": "Nested loop control - tags", "content": "A recurring case in any programming language is the use of nested loops. Nested loops refer to loops that exist within another loop. Fortran allows the programmer to tag or name each loop. If loops are tagged, there are two potential benefits:\n1. The readability of the code may be improved (when the naming is meaningful).\n2. exit and cycle may be used with tags, which allows for very fine-grained control of the loops.\n", "code": "program nested\r\n \r\n integer :: i, j\r\n \r\n outer_loop: do i = 1, 10\r\n inner_loop: do j = 1, 10\r\n if ((j + i) > 10) then ! Print only pairs of i and j that add up to 10\r\n cycle outer_loop ! Go to the next iteration of the outer loop\r\n end if\r\n print *, 'I=', i, ' J=', j, ' Sum=', j + i\r\n end do inner_loop\r\n end do outer_loop\r\n \r\n do i = 1, 10\r\n if (mod(i, 2) == 0) then\r\n cycle ! Don't print even numbers\r\n end if\r\n print *, i\r\n end do\r\n\r\nend program nested"}, {"title": "Parallelizable loop (do concurrent)", "content": "The do concurrent loop is used to explicitly specify that the inside of the loop has no interdependencies; this informs the compiler that it may use parallelization/SIMD to speed up execution of the loop and conveys programmer intention more clearly. More specifically, this means that any given loop iteration does not depend on the prior execution of other loop iterations. It is also necessary that any state changes that may occur must only happen within each do concurrent loop. These requirements place restrictions on what can be placed within the loop body.", "code": "program nested\r\n \r\n real, parameter :: pi = 3.14159265\r\n integer, parameter :: n = 10\r\n real :: result_sin(n)\r\n integer :: i\r\n \r\n do concurrent (i = 1:n) ! Careful, the syntax is slightly different\r\n result_sin(i) = sin(i * pi/4.)\r\n end do\r\n \r\n print *, result_sin\r\n\r\nend program nested"}, {"title": "Parallelizable loop (do concurrent)", "content": "Important - Simply replacing a do loop with a do concurrent does not guarantee parallel execution. The explanation given above does not detail all the requirements that need to be met in order to write a correct do concurrent loop. Compilers are also free to do as they see fit, meaning they may not optimize the loop (e.g., a small number of iterations doing a simple calculation, like the below example). In general, compiler flags are required to activate possible parallelization for do concurrent loops.", "code": "program nested\r\n \r\n real, parameter :: pi = 3.14159265\r\n integer, parameter :: n = 10\r\n real :: result_sin(n)\r\n integer :: i\r\n \r\n do concurrent (i = 1:n) ! Careful, the syntax is slightly different\r\n result_sin(i) = sin(i * pi/4.)\r\n end do\r\n \r\n print *, result_sin\r\n\r\nend program nested"}, {"title": "Organising code structure", "content": "Fortran has two forms of procedure:\n1. Subroutine: invoked by a call statement\n2. Function: invoked within an expression or assignment to which it returns a value\n", "code": ""}, {"title": "Subroutines", "content": "The subroutine input arguments, known as dummy arguments, are specified in parentheses after the subroutine name; the dummy argument types and attributes are declared within the body of the subroutine just like local variables.", "code": "subroutine print_matrix(n,m,A)\r\n implicit none\r\n integer, intent(in) :: n\r\n integer, intent(in) :: m\r\n real, intent(in) :: A(n, m)\r\n\r\n integer :: i\r\n\r\n do i = 1, n\r\n print *, A(i, 1:m)\r\n end do\r\n\r\nend subroutine print_matrix\r\n\r\nprogram call_sub\r\n implicit none\r\n\r\n real :: mat(10, 20)\r\n\r\n mat(:,:) = 0.0\r\n\r\n call print_matrix(10, 20, mat)\r\n\r\nend program call_sub\r\n"}, {"title": "Subroutines", "content": "Note the additional intent attribute when declaring the dummy arguments; this optional attribute signifies to the compiler whether the argument is read-only (intent(in)) or write-only (intent(out)) or read-write(intent(inout)) within the procedure. In this example, the subroutine does not modify its arguments, hence all arguments are intent(in).\nTip: It is good practice to always specify the intent attribute for dummy arguments; this allows the compiler to check for unintentional errors and provides self-documentation.\nWe can call this subroutine from a program using a call statement.\n", "code": "subroutine print_matrix(n,m,A)\r\n implicit none\r\n integer, intent(in) :: n\r\n integer, intent(in) :: m\r\n real, intent(in) :: A(n, m)\r\n\r\n integer :: i\r\n\r\n do i = 1, n\r\n print *, A(i, 1:m)\r\n end do\r\n\r\nend subroutine print_matrix\r\n\r\nprogram call_sub\r\n implicit none\r\n\r\n real :: mat(10, 20)\r\n\r\n mat(:,:) = 0.0\r\n\r\n call print_matrix(10, 20, mat)\r\n\r\nend program call_sub\r\n"}, {"title": "Functions", "content": "Tip: In production code, the intrinsic function norm2 should be used.\nTip: It is good programming practice for functions not to modify their arguments that is, all function arguments should be intent(in). Such functions are known as pure functions. Use subroutines if your procedure needs to modify its arguments.\n", "code": "function vector_norm(n,vec) result(norm)\r\n implicit none\r\n integer, intent(in) :: n\r\n real, intent(in) :: vec(n)\r\n real :: norm\r\n\r\n norm = sqrt(sum(vec**2))\r\n\r\nend function vector_norm\r\n\r\nprogram run_fcn\r\n implicit none\r\n\r\n real :: v(9)\r\n real :: vector_norm\r\n\r\n v(:) = 9\r\n\r\n print *, 'Vector norm = ', vector_norm(9,v)\r\n\r\nend program run_fcn\r\n"}] -------------------------------------------------------------------------------- /systemd/initialize-services.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This script initializes the systemd services. 4 | # This is useful to do when setting up the Fortran Playground background server 5 | # on a new machine or VM for the first time. 6 | # Run it as `sudo initialize-services.sh`. 7 | 8 | for service in playground-server playground-reverse-proxy; do 9 | echo Copying $service.service to /etc/systemd/system/. 10 | cp $service.service /etc/systemd/system/. 11 | echo Starting and enabling service $service 12 | systemctl start $service 13 | systemctl enable $service 14 | done 15 | -------------------------------------------------------------------------------- /systemd/playground-reverse-proxy.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Reverse proxy and SSL for the Fortran Playground server 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/opt/caddy-2.5.2/bin/caddy reverse-proxy --from play-api.fortran-lang.org --to localhost:5000 7 | WorkingDirectory=/home/ubuntu 8 | StandardOutput=file:/home/ubuntu/playground-reverse-proxy_stdout.log 9 | StandardError=file:/home/ubuntu/playground-reverse-proxy_stderr.log 10 | Restart=always 11 | User=root 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /systemd/playground-server.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Fortran Playground server 3 | 4 | [Service] 5 | ExecStart=/home/ubuntu/playground/backend/.venv/bin/gunicorn --workers 3 --timeout 600 --reload --access-logfile access.log --log-file error.log --bind 0.0.0.0:5000 wsgi:app 6 | WorkingDirectory=/home/ubuntu/playground/backend 7 | Restart=always 8 | StandardOutput=file:/home/ubuntu/playground-server_stdout.log 9 | StandardError=file:/home/ubuntu/playground-server_stderr.log 10 | User=ubuntu 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | --------------------------------------------------------------------------------