M++ is a simple programming language compiler similar to C++ built using Lex and Yacc.
For more information about M++, please refer to this link.
M++ comes with a very simple IDE that enables us view, edit, and compile mpp
files, and display
the resulting quadruples and symbol table.
For more information on how to build and use, please refer to the gui branch.
Download and install Lex & Yacc compiler generating packages.
This compiler is built on:
Package | Version |
---|---|
Lex | Flex v2.6.4 |
Yacc | Bison v3.3.1 |
Download links: Windows, Linux, Mac
Run make build
from the repository's main folder to build the compiler.
Run make run
to run the recently built compiler.
Note: You can change the input file from the Makefile
.
Syntax:
M++ [-h|--help] [-v|--version] [-w|--warn] [-o|--output <output_file>] [-s|--sym_table <filename>] <input_file>
Command | Description |
---|---|
-h or --help |
Print help menu and exit. |
-o or --output <filename> |
Specify the output filename. |
-s or --sym_table <filename> |
Output the symbol table to the given file |
-v or --version |
Print the installed version number and exit. |
-w or --warn |
Show warning messages. |
In this section, we are going to give a brief descriptions and examples for the syntax and semantics allowed by M++. As we said, it is almost identical to C-language but with less features.
In M++, we support the basic data types but unfortunately, we do not support arrays or pointers. The supported types:
void
: is only valid as a function return type to tell that it has no value to return.int
: is an integer numeric value data type.float
: is a real numeric value data type.char
: is a character value data type.bool
: is a Boolean value data type that accepts eithertrue
orfalse
.
In M++, we support scoped variables and constants declaration. Each variable or constant has its own scope, and multiple variable/constants can be declared with the same identifier only if they are in different scopes. As in C-language, constants must be initialized while being declared.
e.g.
const float PI = 3.14;
const float EPS = 1e-9;
int x;
char c = 'c';
bool flag = true;
int a = 0, b, MAX = 100;
We support if-else control statement in almost the exact same way as in C-language. If the if-condition evaluates to a non-zero value, then the if-body will be executed. Otherwise, the else-body will be executed if exists. If-body and else-body can either be one statement, or can be multiple statements enclosed by a block.
e.g.
if (x) {
if (y > 0)
/* if-body */
else if (z & 1)
/* else-if-body */
else
/* else-body */
}
Like if-statement, we support switch-statement in almost the exact same way as in C-language. The switch-expression must be of integer value, and the case-expression must be a constant integer value. Also, multiple case-expressions that evaluate to the same value is not allowed. Like C, the code of the matched case will be executed and the execution will continue to the below code of other cases until a break-statement is found.
e.g.
switch (state) {
case 1:
case 2:
/* do something */
case RUNNING: // RUNNING must be defined as constant
/* do something */
break;
default:
/* default */
}
M++ supports loops in almost the exact same way as in C-language. We support for-loops, while-loops, and do-while loops. Break-statements and continue-statements are supported within the scope of a loop, and they function like in C-language, they break or continue the execution of the inner most loop.
e.g.
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
while (i < j) // do something
continue;
}
}
do {
if (cond)
break;
// do something
} while (true);
M++ supports functions but with limited functionalities than that of the C-language. We do not support default parameters. We do not support neither function prototyping nor function overloading. Return-statements are allowed within the scope of a function. And functions can only be defined in the global scope.
e.g.
int fibonacci(int n) {
return fibonacci(n - 1) + fibonacci(n - 2);
}
In M++, we support complex expressions similar to those of C-language. We support almost the entire set of operators supported by C-language with the same precedence and associativity.
e.g.
(((++x) = y++) = (8 * 7 - MAX) ^ (1 << i)) = (z = 3);
M++ supports the same comment styles as in C-language. The comments can either be:
- Line comment
// This is a line comment
- Block comment (multi-line comment)
/**
* This is a block comment
* that can span
* multiple lines
*/
- Code blocks or statements (other than variable, constants, and function declaration/definition) in the global scope.
continue
-statement outsidefor
,while
, ordo-while
scopes.break
-statement outsidefor
,while
,do-while
, orswitch
scopes.return
-statement outside function scope.case
anddefault
labels outside switch scope.
- Variable or constant declared with type
void
. - Constant declaration without initialization.
- Identifier re-declaration in the same scope.
- Undeclared identifier access.
- Constant assignment after declaration.
- Invalid operand types. (i.e. operands of type
void
or pointer to function). - Branch condition not Boolean convertible.
- Float operand to modulus operator.
- Float operand to bitwise operators.
- Use of uninitialized variable.
- Increment and decrement operators with
rvalue
operand.
switch
andcase
statements with non integer expression.case
-statement with non-constant expression.- Multiple
default
-labels inswitch
scope. - Multiple
case
-labels with the same constant expression inswitch
scope. - Cross variables initialization in
switch
-statement.
- Value returned in
void
function and vice-versa. - Void returned in value-typed function.
- Variable or constant call as a function.
- Function call with more/less arguments than its parameters.
- Function call with invalid argument type (i.e. argument of type
void
or pointer to function). - Function parameter with default value.
To generate the lexer code using Flex, type the following command:
flex -o <output>.c <input>.l
Where <input>.l
is the input lex specifications filename, and <output>.c
is the name of the generated lexer C file.
To compiler the generated lexer program, type the following command:
gcc <output>.c -o <lexer_name>.exe
Where <output>.c
is the name of the generated lexer C file, and <lexer_name>.exe
is the name of the compiled lexer program.
For more information about lex, please refer to this link.