Lines_interpreter

This repository contains my version of the ATP assignment. The goal of the assignment was to create an interpreter using only functional programming.

My version implements the following should haves:
Advanced language features because I implemented more than the basic addition and substraction operators. In my programming language you can print and add comments as well. Creating your own programming language because this is a made up programming language


In this document I will try to explain how the program works and how to use it.

Contents
Basic info
Restrictions
How to use
How to run

Basic info

This programming language is based on describing the look of an operator in lines hence the name Lines. For example the assignment operator ('=') is translated to HORIZONTAL_LINES because it consists of 2 horizontal lines. The equal operator ('==') translates to DOUBLE_HORIZONTAL_LINES because it consists of 2 assignment operators. An in depth breakdown of all the operators and keywords can be found in the How to use section.

This program uses the lex, parse, run, structure to execute the given code. Before the lexing can start the input file needs to be prepared. The input gets split into lines ('\n'). Every line then gets further split into words (' '). Every line starting with a question mark ('?') gets removed from the list. Tabs ('\t') and blanklines also get removed.

Lexing

When the lexing process starts every word gets compared to a known list of different operators and keywords. The lexer returns a list with tokens containing a value that corresponds to the given operator or keyword. When a word is classified as a digit the lexer returns a token containing the value of the digit. When a word does not match any of the known operators or keywords and is not classified as a digit, the program assumes it is a variable and returns a token containing the variable name.

Parsing

The parsing process takes all the tokens from the lexer output and turns every token into the corresponding node. For example a token containing the value IF will return a If_node. Operator and keyword nodes store the operation that should be executed as well as their child nodes. When initially turning the tokens into nodes, the child nodes will be initialized as empty nodes. These nodes will be filled in the next step, where they will be turned into a AST.

In order to turn these nodes into a tree, the nodes should be handled in a specific sequence. This is done in order to adhere to mathematical priority rules.

Priority list with the top having the most priority:

Priority list
Multiplication, Division
Addition, Subtraction
Equals, Greater Than, Less Than
Assignment, Print, If, While

In order to turn the nodes into a tree, the program handles the operators and keywords in the order seen above. When handling the operator the parser looks for the given operator or keyword for instance the Addition Node. When the program finds the Addition Node it takes the adjacent nodes and moves them to the child nodes of the Addition Node. The program repeats this for every Addition Node And every other operator and keyword. The result is a tree on every line with a line only containing 1 root node.

Running

The program runs by executing the root node of every line. If the root node contains any child nodes with a keyword or operator in it, it will execute the child node(s) first before executing the root/parent node. The program keeps track of all variables and the program counter in a class object called program_state. This program_state gets passed on to every node, in case the node needs to request/modify a variable or change the program counter.

How to use

Restrictions

Using this interpreter comes with a few restrictions. First of all, every number will be handled as a float. When using integers the program will use the float variant of the integer. Printing 10 will restult in 10.0 being printed

Every word in the code file should be seperated by a space (' '), this means you can not do the following:

// Not supported in this interpreter
Variable=2+2

Every line of code should also be seperated by a newline character ('\n').

Every line must have an assignment, print, if, while, endif or endwhile, else the program will not work.

Operators

Operator Description C Equivalent
HORIZONTAL_AND_VERTICAL_LINES Adds the two operands +
HORIZONTAL_LINE Subtracts the right operand from the left operand -
DIAGONAL_LINES Multiplies the two operands *
DIAGONAL_LINE Divides the right operand from the left operand -
DOUBLE_HORIZONTAL_LINES Checks if the two operands are equal ==
BROKEN_LINE_LEFT Checks if the left operand is greater than the right operand >
BROKEN_LINE_RIGHT Checks if the left operand is less than the right operand <
HORIZONTAL_LINES Assigns the value from the right operand to the left operand =

Keywords

Keyword Description C Equivalent
IF If the condition after the keyword is True the lines leading up to the matching !IF will be executed. If the condition turns out false the lines leading up to the !IF will be skipped. if
!IF Signals the end of the matching IF. }
WHILE If the condition after the keyword is True the lines leading up to the matching !WHILE will be executed until the condition turns false. If the condition turns out false the lines leading up to the !WHILE will be skipped. while
!WHILE Signals the end of the matching WHILE. }
SHOW Prints a variable or result from an expression. printf
? This line will be ignored. //

Example code

? C style:
?
? float my_balance = 25;
? if(my_balance < 10 * 3){
?   printf("%f", my_balance);
? }
?
? Lines style:

my_balance HORIZONTAL_LINES 25
IF my_balance BROKEN_LINE_LEFT 10 DIAGONAL_LINES 3
    SHOW my_balance
!IF

? expected output:
? 25.0

? C style:
?
? float countdown = 10;
? while(countdown > -1){
?   printf("%f", countdown);
?   countdown = countdown - 1
? }
?
? Lines style:

countdown HORIZONTAL_LINES 10
WHILE countdown BROKEN_LINE_RIGHT -1
    SHOW countdown
    countdown HORIZONTAL_LINES countdown HORIZONTAL_LINE 1
!WHILE

? expected output:
? 10.0
? 9.0
? 8.0
? 7.0
? 6.0
? 5.0
? 4.0
? 3.0
? 2.0
? 1.0
? 0.0

How to run

The interpreter needs to be run using the command line.
To run the program use the following line:
python interpreter.py file_name

With file_name being the name of the code file you want to run. In the repository there is a test file called my_code.lines. So to run the test code you would replace file_name by my_code.lines.

The program also supports a few command line arguments to show specific statistics when running the program. To use these arguments, simply add them after the file_name.
For example:
python interpreter.py file_name -l -p

Argument Description
-l Shows the output from the lexer
-p Shows the output from the parser
-t Shows the amount of time it took to run the program
-h Shows all possible arguments