This project is a fully functional compiler and interpreter for "minic", a small, statically-typed programming language. It was built using Java and compiler construction tools like JFlex (for lexical analysis) and BYACC/J (for parsing).
The compiler parses source code written in minic, performs semantic analysis to catch errors, and then directly interprets the program to execute it.
The "minic" language supports a variety of modern programming constructs like...
- Data Types:
num
(for floating-point numbers) andbool
(fortrue
/false
). - Variables: Declare variables using the
var
keyword. - Arrays: Create and use arrays of
num
orbool
. - Functions: Define functions with parameters and return values using the
func
keyword. - Control Flow:
- Conditional logic with
if-then-else-end
blocks. - Looping with
while-begin-end
blocks.
- Conditional logic with
- Expressions:
- Arithmetic:
+
,-
,*
,/
,%
- Relational:
=
,<>
,!=
,<
,<=
,>
,>=
- Logical:
and
,or
,not
- Arithmetic:
- Built-in Functions:
print
: Prints a value to the console..size
: Gets the size of an array.
- Comments: Supports
//
for single-line comments.
minic-SemanticChecker & Executor/
├── src/
│ ├── Compiler.java # Main compiler driver
│ ├── Grammar.txt # Language grammar and token definitions
│ ├── Lexer.java # Lexical analyzer (generated by JFlex)
│ ├── Parser.java # Parser (generated by BYACC/J)
│ ├── ParserImpl.java # Semantic actions and tree construction logic
│ ├── ParseTree.java # Defines the AST nodes and interpreter logic
│ ├── ParseTreeInfo.java # Helper classes for storing info in the AST
│ ├── Env.java # Symbol table for managing scope and types
│ ├── Token.java # Represents a single lexical token
│ ├── Program.java # Main entry point for running the compiler
│ └── ...
└── samples/
├── succ_01.minc # Example of a valid minic program
└── fail_01a.minc # Example of an invalid minic program
The compilation and execution process follows these steps:
- Lexical Analysis:
Lexer.java
reads the.minc
file and converts it into a stream of tokens based on the rules inGrammar.txt
. - Parsing:
Parser.java
consumes the tokens and, with the help of the grammar rules, builds a Parse Tree. The code to actually build the tree is inParserImpl.java
. - Semantic Analysis: As the tree is built, the code in
ParserImpl.java
performs semantic checks. It usesEnv.java
to keep track of variables and functions in scope to detect errors like type mismatches, undeclared variables, or incorrect function calls. - Interpretation: If no errors are found, the
Compiler
calls theExec()
method on the root of theParseTree
. This triggers a walk down the tree, where each node executes its specific logic (e.g., performing arithmetic, assigning values, calling functions).
- Prerequisites: You need to have a Java Development Kit (JDK) installed.
- Compile: Navigate to the
src
directory and compile all the Java files.cd "minic-SemanticChecker & Executor/src" javac *.java
- Run: Execute the
Program
class, passing the path to a.minc
sample file as an argument.You can try any of the files in thejava Program ../samples/succ_01.minc
samples
directory to see the compiler in action.
Example of success test - succ_04.minc
func main :: num ()
begin
var i :: num ;
var x :: num ;
var y :: bool[];
var z :: num [];
x := 1;
y := new bool[3];
y[0] := true;
y[1] := true and false;
y[2] := y[1] or true;
print y;
i := 5;
z := new num[i];
i := 0 ; z[i] := 0;
i := i + 1; z[i] := z[i-1] + i;
i := i + 1; z[i] := z[i-1] + i;
i := i + 1; z[i] := z[i-1] + i;
i := i + 1; z[i] := z[i-1] + i;
print z;
return 1 ;
end
the output of my program is...
Success: no semantic error is found.
================================================================================
Code with indentations:
func main::num()
begin
var num::i;
var num::x;
var bool[]::y;
var num[]::z;
x := 1.0;
y := new bool[3.0];
y[0.0] := true;
y[1.0] := true and false;
y[2.0] := y[1.0] or true;
print y;
i := 5.0;
z := new num[i];
i := 0.0;
z[i] := 0.0;
i := i + 1.0;
z[i] := z[i - 1.0] + i;
i := i + 1.0;
z[i] := z[i - 1.0] + i;
i := i + 1.0;
z[i] := z[i - 1.0] + i;
i := i + 1.0;
z[i] := z[i - 1.0] + i;
print z;
return 1.0;
end
================================================================================
Code with indentations and comments for running environment:
func main::num()
begin
var num::i; // relative address of local variable i from this func call base pointer is 1
var num::x; // relative address of local variable x from this func call base pointer is 2
var bool[]::y; // relative address of local variable y from this func call base pointer is 3
var num[]::z; // relative address of local variable z from this func call base pointer is 4
x{addr:2} := 1.0;
y{addr:3} := new bool[3.0];
y{addr:3}[0.0] := true;
y{addr:3}[1.0] := true and false;
y{addr:3}[2.0] := y[1.0] or true;
print y{addr:3};
i{addr:1} := 5.0;
z{addr:4} := new num[i{addr:1}];
i{addr:1} := 0.0;
z{addr:4}[i{addr:1}] := 0.0;
i{addr:1} := i{addr:1} + 1.0;
z{addr:4}[i{addr:1}] := z[i{addr:1} - 1.0] + i{addr:1};
i{addr:1} := i{addr:1} + 1.0;
z{addr:4}[i{addr:1}] := z[i{addr:1} - 1.0] + i{addr:1};
i{addr:1} := i{addr:1} + 1.0;
z{addr:4}[i{addr:1}] := z[i{addr:1} - 1.0] + i{addr:1};
i{addr:1} := i{addr:1} + 1.0;
z{addr:4}[i{addr:1}] := z[i{addr:1} - 1.0] + i{addr:1};
print z{addr:4};
return 1.0;
end
================================================================================
Execute:
[true, false, true]
[0.0, 1.0, 3.0, 6.0, 10.0]
Returned value by main: 1.0
================================================================================
Example of success test - succ_01.minc
func main :: num ()
begin
// there is no error in this code.
{
The block comment should be excluded in the parse tree
}
return 3.142592;
end
the output of my program is...
================================================================================
Code with indentations:
func main::num()
begin
return 3.142592;
end
================================================================================
Code with indentations and comments for running environment:
func main::num()
begin
return 3.142592;
end
================================================================================
Execute:
Returned value by main: 3.142592
================================================================================
end
Example of Fail test - fail_01d.minc
func main :: num ()
begin
// there is no error in this code.
{
The block comment should be excluded in the parse tree
}
return not 3.142592;
end
the output of my program is...
Error: there is semantic error(s).
[Error] Unary operation not cannot be used with num value.
Example of Fail test - fail_04a.minc
func main :: num ()
begin
var i :: num ;
var x :: num ;
var y :: bool[];
var z :: num [];
x := 1;
y := new bool[3];
y[true] := true;
y[1] := true and false;
y[2] := y[1] or true;
print y;
i := 5;
z := new num[i];
i := 0 ; z[i] := 0;
i := i + 1; z[i] := z[i-1] + i;
i := i + 1; z[i] := z[i-1] + i;
i := i + 1; z[i] := z[i-1] + i;
i := i + 1; z[i] := z[i-1] + i;
print z;
return 1 ;
end
the output of my program is...
Error: there is semantic error(s).
[Error] Array index must be num value.
Rohan Mankame