Online C Code Compiler using Flask


In this article, we have explored how we can develop an online C Programming language compiler using Flask (a Python web framework). It can be extended to support any programming language. The basic idea is to store the text form the textarea into a file on the server machine and compile and execute the file on the server machine and return the output/error (if any) to the user.

Flow of application

Now we will learn about how the dataflow occurs in the application. As you can see in the diagram given below the the data flow starts with the user providing it's input to the frontend of the application by providing data to the code, input and checkbox fields. This data is then transfered to the server when the user click on the submit button.

Flow of our online compiler application

The server gets the data by a POST request,which enables the server to get the data in the form.After receiving the data from the form the backend code stores the code into a ".c" file on the server machine and then compiles the file, which gives a ".exe" file.This ".exe" file is then executed with or without inputs as per the data received from the user.The ".exe" files returns the the output/error to the backend code.

The backend code then send this data to the output/error fields in the frontend for the user to see the result.

Important modules/functions.

In order to work on this project basic knowledge of flask framework is required and familiarity with the following modules in python:

  1. subprocess module
  2. os module

We will go through these basic knowledge first and then, dive directly into building our online compiler application in Flask.

subprocess module

The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes.Using this module we can start processes and run commands by using the python script.We will now learn about the function used in this project.

subprocess.run():

The subprocess.run() function is used to run a process/command and when the execution of the process/command is complete it returns a CompletedProcess instance and by using this function we can compile and execute our c program and gets it's output.

Syntax:subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None)

Parameters:The above syntax show many parameters for the function with their default values,the only parameter that is required to be passed by the user is the args parameter, the other parameters may or may not be used by the user .We will learn about some commonly used parameters.

  • args:args should be a string, or a sequence of program arguments(list of string argument). Providing a sequence of arguments is generally preferred, as it allows the module to take care of any required escaping and quoting of arguments.If passing a single string, either shell must be True or else the string must simply name the program to be executed without specifying any arguments.
  • stdin:It specifies the standard input of the executed program.The valid parameters values are PIPE, an existing file descriptor (a positive integer), an existing file object, and None.PIPE indicates that a new pipe to the child should be created.If stdin is set to any value other then none then input parameter cannot be used.
  • stdout:It specifies the standard output of the executed program.The valid parameters values are PIPE, an existing file descriptor (a positive integer), an existing file object, and None.PIPE indicates that a new pipe to the child should be created.
  • stderr:It specifies the standard error(if any) of the executed program.The valid parameters values are PIPE, an existing file descriptor (a positive integer), an existing file object, and None.PIPE indicates that a new pipe to the child should be created.
  • input:The input argument is passed to Popen.communicate() and thus to the subprocess stdin. If used it must be a byte sequence, or a string if encoding or errors is specified or text is true. When used, the internal Popen object is automatically created with stdin=PIPE, and the stdin argument may not be used as well.
  • shell:If shell is True, the specified command will be executed through the shell. This can be useful if you are using Python primarily for the enhanced control flow it offers over most system shells and still want convenient access to other shell features such as shell pipes, filename wildcards, environment variable expansion, and expansion of ~ to a user’s home directory.

Example:

import subprocess
from subprocess import PIPE
sr=subprocess.run("dir",stdout=PIPE,shell=True)
dirlist=sr.stdout.decode("utf-8")
print(dirlist)

Output:

Volume in drive H is Softwares
Volume Serial Number is B4E3-4A31
Directory of H:\Projects
04/12/2020  11:55 AM    <DIR>          .
04/12/2020  11:55 AM    <DIR>          ..
01/20/2020  10:37 PM    <DIR>          .metadata
04/02/2020  11:56 AM    <DIR>          CCompilerX
01/29/2020  07:59 PM    <DIR>          git_tutorial
03/18/2020  11:26 AM    <DIR>          OpenGenus
01/20/2020  10:37 PM    <DIR>          RemoteSystemsTempFiles
04/12/2020  11:58 AM               148 t.py
03/16/2020  08:55 PM    <DIR>          timecheckpy
1 File(s)            148 bytes
8 Dir(s)  35,971,387,392 bytes free

os module

We will be using various features of the os module to work with files,check whether file exist or not,etc.

os.path.exists():

This function is used to check whether the path provided to it is a valid path or not.
Syntax: os.path.exists(path)
parameter: It takes a path like object as a parameter.
Return type: It returns a boolean value,true if the path exists and false if the path does not exists.

os.open()

This function is used to open a file and set the requires flags and modes of the file.the default mode is 0o777(octal)

Syntax: open(file,flag,mode)

Parameters:

  • file :This parameter stores the path to a file or the name of the file.
  • flag :This parameter is used to set a flag for the file.
  • mode :This parameter allows the user to set and change the mode of a file.

Return type: Return a file descriptor of the opened file.

List of flags:

os.O_RDONLY βˆ’ open for reading only

os.O_WRONLY βˆ’ open for writing only

os.O_RDWR βˆ’ open for reading and writing

os.O_NONBLOCK βˆ’ do not block on open

os.O_APPEND βˆ’ append on each write

os.O_CREAT βˆ’ create file if it does not exist

os.O_TRUNC βˆ’ truncate size to 0

os.O_EXCL βˆ’ error if create and file exists

os.O_SHLOCK βˆ’ atomically obtain a shared lock

os.O_EXLOCK βˆ’ atomically obtain an exclusive lock

os.O_DIRECT βˆ’ eliminate or reduce cache effects

os.O_FSYNC βˆ’ synchronous writes

os.O_NOFOLLOW βˆ’ do not follow symlinks

close()

This function is used to close the file descriptor.

Syntax: close(fd)

Parameter:
fd: fd denotes the file descriptor that was created using the open function.

Return type: No return value.

os.write()

Apart from reading from a file descriptor we can also write into it using the write function.

Syntax: write(fd,text)

Parameter:
fd: fd represents the file descriptor.
text: It contains the text that is to be written in the file descriptor.

Return type: It returns the number of bytes written in the file descriptor.

Example:

import os
file="Your file path or file name."
#Creating file descriptor in read and write mode.
fd=os.open(file,os.O_RDWR)
text="Your text here"
#Converting text to bytes
fileadd=str.encode(text)
num=os.write(fd,fileadd)
print(num," bytes are added to the file")
os.close(fd)

os.truncate()

This function is used to truncate the contents of the file to a specified length.

Syntax: os.truncate(fd,length)

Parameters:
fd: fd represents the file descriptor.
length: It represent the length to which the content is to be truncated.

Return type: No return type.

Stepwise breakdown of the project

  1. Creating a home route for the web page.
  2. Creating the main html template page for the project.
  3. Creating another route to display the output/error.
  4. Defining a function that would contain the logic for C program compilation and execution.
  5. Finished project.

Creating a home route for the web page.

First we need to create a home route for the application that would load the html template for our application.Creating a route is very easy just copy the code given below into your python file.

Code:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'
    
if __name__=='__main__':
    app.run(debug=True)

With this you have a minimal flask application ready.

Creating the main html template page for the project

Now we need to create a html template for our application which we would render
from the main route that we created in the previous step.We need a from containing 3 text-areas to get the code,input for the program and to print the output, a checkbox to check whether the user is providing any input for the program or not and a button to submit the form.You need to create a template folder as save this html file inside the folder.
The html code is given below.

<!DOCTYPE html>
<html>
<head>
	<title>Online C Compiler</title>
</head>
<body>
	<form action="/submit" method="post">
		<label for="code">Type your code here:</label>&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;<label for="output">This is the output.</label><br>

		<textarea id="code" name="code" rows="30" cols="50" style="resize:none" required placeholder="//Enter your code here.">{{code}}</textarea>
		<textarea id="output" name="output" rows="30" cols="50" style="resize:none" placeholder="//Your output here.">{{output}}</textarea><br>
		
		<label for="input">Enter your input:</label><br>
		<textarea id="input" name="input" rows="5" cols="50" style="resize:none" placeholder="//Your Input here.">{{input}}</textarea>
		<input type="checkbox" name="check" id="GFG" 
                    value="1" {{check}}>Enable input<br> 
		
		<button type="submit">Run Code</button>
</form>
</body>
</html>

Now we need to render that template into our main route for this we need the render_template function of the flask module.We make the necessary changes in the python file.

Python code

from flask import Flask,render_template
app = Flask(__name__)

#route for the main page.
@app.route('/')
def Compiler():
	check=''
	return render_template('home.html',check=check)
    
if __name__=='__main__':
    app.run(debug=True)

Note:Here we have used a variable check='' which is used to set the checked property of the checkbox,for now it is empty so checkbox is unchecked.

Creating another route to display the output/error

You might have noticed that we have used <form action="/submit" method="post"> but we have not created any route leading to /submit so in this step we will create a route to /submit and accept the inputs from the form.

To create a /submit route we follow the procedure in step 1 the only change is the route name :@app.route('/submit').
Now we need to define the functionality of this route.We will breakdown this task into several step to make is easier to understand.

  • Getting the values of the form.
  • Creating a function that would compile and execute the c program using the inputs from the form and would return the output/error for the program.
  • Rendering the previously built template with the new values.

Getting the values of the form.

In order to access the values that the user has submitted we need to use the HTTP POST method.This method is used to send data to a server to create/update a resource.We have already mentioned in the html form that we are using the POST methods using this <form action="/submit" method="post"> .In this line method="post" indicates that the form is using POST method to send data to the server.

Now we need to add some code in the backend to retrieve the data,for this we need to use the request module to to send HTTP requests using Python.We can use the following code to retrieve the data from the form using the name of the element whose data we want.

We will now see how we can retrieve data from the textarea named code and inputand the checkbox named check in our html template.

Code:

#route for the submit page to show the output/error of the c program.
@app.route('/submit',methods=['GET','POST'])
def submit():
	if request.method=='POST':

		#Getting input(code and input for program) and checkbox value from the form.
		code=request.form['code']
		inp=request.form['input']
		chk=request.form.get('check')

Creating a function to compile and execute the c program

Now that we have retrieved the data from the form we need to save the code into a file and compile the file and then execute the executable file that will be created after the compilation.In order to do all this we will create a function that would take the data retrieved from the form as an input and return the output/error in the program.

Lets break down the task into small steps to make it easier to understand:

Define a function to accept code,input and chk(value returned by checkbox of form).

def complier_output(code,inp,chk):

Check whether the file you want to write to exists or not if not then create a new file with that name.In this article we will use the name 'try.c'.

if not os.path.exists('Try.c'):
	os.open('Try.c',os.O_CREAT)

The user might work on different programs so we need to clear the old content of the file each time the function is called.

fd=os.open("Try.c",os.O_WRONLY)
	#truncate the content of the file to 0 bytes so that there is no overwriting in any way using the write operation.
	os.truncate(fd,0)

Encode the string into bytes and then write it to the file.

	#encode the string into bytes.
	fileadd=str.encode(code)
	#write to the file.
	os.write(fd,fileadd)
	#close the file descriptor.
	os.close(fd)

Compile the c file using the subprocess module we discussed earlier and store the completeprocess instance returned by the run() function into a variable.

#Compiling the c program file and retrieving the error if any. 
s=subprocess.run(['gcc','-o','new','Try.c'],stderr=PIPE,)

Now we need to check whether the program compiled successfully or not.

#storing the value returned by return code.
check=s.returncode

If not compiled successfully we return the error.

return s.stderr.decode("utf-8")

If compiled successfully we check the 'chk' parameter whether input is enabled or not.

#checking whether program compiled succesfully or not.
if check==0:

If chk==0(input is enabled),then execute the file using run() function with input parameter.

r=subprocess.run(["new.exe"],input=inp.encode(),stdout=PIPE)

Else (input is disabled),then execute the file using run() function without input parameter.

r=subprocess.run(["new.exe"],stdout=PIPE)

Return the output of the program

return r.stdout.decode("utf-8")

Final Code of online compiler

After completing the above steps the final code will be:

from flask import Flask,render_template,request
import subprocess,os
from subprocess import PIPE

app = Flask(__name__)

#route for the main page.
@app.route('/')
def Compiler():
	check=''
	return render_template('home.html',check=check)

#route for the submit page to show the output/error of the c program.
@app.route('/submit',methods=['GET','POST'])
def submit():
	if request.method=='POST':

		#Getting input(code and input for program) and checkbox value from the form.
		code=request.form['code']
		inp=request.form['input']
		chk=request.form.get('check')

		#Checking if the checkbox is checked or not.
		if  not chk=='1':
			#If checkbox was not ckecked then the input field will be empty and checkbox will be unchecked. 
			inp=""
			check=''
		else:
			##If checkbox was ckecked then the input field will stay the same and checkbox will be checked.
			check='checked'	

		#calling the function to compile and execute the c program.	
		output=complier_output(code,inp,chk)
	#return render_tempelate to 	
	return render_template('home.html',code=code,input=inp,output=output,check=check)

def complier_output(code,inp,chk):
	#checking if a file already exists or not in no the create one.
	if not os.path.exists('Try.c'):
		os.open('Try.c',os.O_CREAT)
	#creating a file descriptor to write in to the file.	
	fd=os.open("Try.c",os.O_WRONLY)
	#truncate the content of the file to 0 bytes so that there is no overwriting in any way using the write operation.
	os.truncate(fd,0)
	#encode the string into bytes.
	fileadd=str.encode(code)
	#write to the file.
	os.write(fd,fileadd)
	#close the file descriptor.
	os.close(fd)
	#Compiling the c program file and retrieving the error if any. 
	s=subprocess.run(['gcc','-o','new','Try.c'],stderr=PIPE,)
	#storing the value returned by return code.
	check=s.returncode
	#checking whether program compiled succesfully or not.
	if check==0:
		#cheking whether input for program is enabled or not.
		if chk=='1':
			#executing the program with input.
			r=subprocess.run(["new.exe"],input=inp.encode(),stdout=PIPE)
		else:
			#executing the program without input.
			r=subprocess.run(["new.exe"],stdout=PIPE)
		#return the output of the program.	
		return r.stdout.decode("utf-8")
	else:
		#return the error if the program did not compile successfully
		return s.stderr.decode("utf-8")


if __name__=='__main__':
	app.run(debug=True)

Running the application

Now we just need to run the application and open the url.
We will get a frontend like shown in the picture below.

Frontend of our online compiler application

Now we can write a simple program to test the online compiler.
We use the following c program to test the compiler.

#include<stdio.h>
int main()
{
    printf("Hello World!!");
    return 0;
}

The output will be:

Output from our online compiler application for a simple C code

The complete code of this application is available at: OpenGenus repository at GitHub

With this article at OpenGenus, you must have the complete idea of developing your own online C compiler application in Flask. Enjoy.