MinJ, short for minimalistic Java, is a lightweight interpreter designed to bring a concise, statically typed scripting language to the JVM. Built from the ground up in Java with ANTLRv4 for lexical and syntactic analysis, MinJ provides an accessible platform for experimenting with language design and interpreter implementation. Its clean grammar and modular visitor‑based execution model ensure fast parsing and straightforward extensibility.
MinJ currently supports variable declarations (var and val), arithmetic and comparison operations, conditional statements (if/elseif/else), loops (while, for, foreach), list manipulation, and printing literals or variables, enabling you to write programs like FizzBuzz, iterate over collections, and perform calculations with ease.
MinJ is built using Gradle with the ANTLR plugin and ShadowJar for packaging a standalone fat‑JAR. To use the language yourself: simply download the release and execute your scripts via java -jar minjc-<VERSION>.jar <yourfile>.mj. A comprehensive CI/CD pipeline on GitHub Actions ensures that every change is validated, tested, and packaged automatically for reliable releases.
Key Features
int, String, boolean, etc., plus dynamic variables via var and immutable constants via val.&&/and, ||/or, ^/xor, !/not).if/elseif/else, while, for … to … [step …], foreach … in ….[1,2,3] and iteration with foreach.func/method, instance methods, new‑based object creation, return.print(...) and input(...) for interactive prompts.val) violations, undefined‑name errors..g4 grammar with clearly layered rules (declarations, statements, expressions, primary).MinJ ships as a standalone “fat‑JAR” built with Gradle and the ANTLR plugin. Every commit runs through a CI/CD pipeline (GitHub Actions) that regenerates the parser, runs the full test suite (unit tests, self‑test scripts), and publishes versioned releases.
Run the interpreter:
java -jar minjc-<VERSION>.jar <yourfile>.mj
Replace <VERSION> with the actual version number and <yourfile> with your MinJ script.
Explore the examples in the examples directory to see MinJ in action.
For grammar development and live parse tree visualization, it’s recommended to use IntelliJ IDEA Ultimate with the ANTLR v4 plugin. This setup provides:
Build fat-JAR (includes ANTLR runtime):
./gradlew clean shadowJar
Run interpreter:
java -jar build/libs/minjc-<VERSION>.jar <PROGRAM_NAME>.mj
Below is an in-depth look at the MinJ grammar defined in src/main/antlr/MinJ.g4, annotated with extensive explanations. Wherever a code‑block would normally begin or end with triple backticks (), you'll see the marker **** instead.
Grammar snippet
program
: (topLevelDecl? NEWLINE)*
topLevelDecl?
EOF
;
topLevelDecl? can be empty.Grammar snippet
topLevelDecl
: classDecl
| methodDecl
| statement
;
Grammar snippet
varDecl
: (type | VAR | VAL) idList (ASSIGN expr)?
;
idList
: ID (COMMA ID)*
;
type
: INT_TYPE | FLOAT_TYPE | DOUBLE_TYPE | BOOLEAN_TYPE | CHAR_TYPE | STRING_TYPE
;
int, String, bool).var x, y = 3.Grammar snippet
assign
: idList ASSIGN expr
;
printStmt
: PRINT expr
;
Grammar snippet
ifStmt
: IF expr THEN COLON block
(ELSEIF expr THEN COLON block)*
(ELSE COLON block)?
END
;
block
: (statement? NEWLINE)*
;
While
whileStmt
: WHILE expr DO COLON block END
;
For
forStmt
: FOR (varDecl | assign) TO expr (STEP assign)? DO COLON block END
;
Foreach
foreachStmt
: FOREACH ID IN expr DO COLON block END
;
step, then a body.Operator precedence (highest → lowest):
! (NOT), - (negation)* / %+ -< > <= >= == !=&&/and, ||/or, ^/xorGrammar snippet
expr
: NOT expr // unary NOT
| SUB expr // unary minus
| expr op=(MUL|DIV|MOD) expr // *,/, %
| expr op=(ADD|SUB) expr // +, -
| expr op=(LT|GT|LE|GE|EQ|NE) expr // comparisons
| expr op=(AND|OR|XOR) expr // boolean ops
| primary // literals, names, calls
;
Grammar snippet
primary
: NEW ID LPAREN RPAREN # NewExpr
| ID LPAREN argList? RPAREN # CallExprPrimary
| primary DOT ID LPAREN argList? RPAREN # DotCallExpr
| INT # IntLiteral
| FLOAT_LIT # FloatLiteral
| DOUBLE_LIT # DoubleLiteral
| STRING # StringLiteral
| CHAR # CharLiteral
| BOOL_LIT # BoolLiteral
| ID # VarReference
| LPAREN expr RPAREN # ParenExpr
| listLiteral # ListExpr
;
listLiteral
: '[' (expr (COMMA expr)*)? ']'
;
argList
: expr (COMMA expr)*
;
List<Object>.Grammar snippet
NEWLINE : '\r'? '\n' ;
WS : [ \t]+ -> skip ;
LINE_COMMENT : '//' ~[\r\n]* -> skip ;
BLOCK_COMMENT: '/*' .*? '*/' -> skip ;
// Keywords
IF : 'if' ; FOR : 'for' ; VAR : 'var' ; VAL : 'val' ; …
// Operators/punctuation
ASSIGN : '=' ; LT : '<' ; EQ : '==' ; AND : '&&' | 'and' ; OR : '||' | 'or' ; …
// Literals
INT : [0-9]+ ;
FLOAT_LIT : [0-9]+ '.' [0-9]+ [fF] ;
STRING : '"' (~["\\\r\n] | '\\' .)* '"' ;
BOOL_LIT : 'true' | 'false' ;
ID : [a-zA-Z_] [a-zA-Z_0-9]* ;
var any = 10
any = "now a String!" // OK: var is dynamic
int n = 5
// n = "oops" // ERROR: type mismatch
val PI = 3.14
// PI = 3.0 // ERROR: reassign val
Copy
Edit
var a = true and false
var b = true xor true
var c = a or b
var d = !a && !b
print a // false
print b // false
print c // false
print d // true
class Counter:
var count = 0
method inc():
count = count + 1
end
method get():
return count
end
end
var c = new Counter()
c.inc()
print c.get() // 1
func factorial(n):
if n <= 1 then:
return 1
end
return n * factorial(n - 1)
end
func factLoop(n):
var r = 1
for i = 1 to n do:
r = r * i
end
return r
end
print factorial(5) // 120
print factLoop(5) // 120
var nums = [1, 2, 3, 4, 5]
print "Numbers:"
foreach n in nums do:
print n
end
var names = ["Alice", "Bob", "Charlie"]
print "Names:"
foreach name in names do:
print name
end
var sum = 0
while sum < 10 do:
if sum % 2 == 0 then:
print sum
end
sum = sum + 1
end
The interpreter is packaged as a fat-JAR in the release section, including all dependencies. You can run it directly from the command line:
cd Downloads/MinJ-<VERSION>
java -jar minjc-<VERSION>.jar <PROGRAM_NAME>.mj
ANTLR Generation
antlr plugin reads MinJ.g4 and generates MinJLexer.java, MinJParser.java, MinJBaseVisitor.java, etc.Parsing (Main.java)
CharStreams.fromPath()MinJLexer → CommonTokenStream → MinJParser.program() → ParseTreeEvaluation (EvalVisitor.java)
MinJBaseVisitor<Object>Overrides:
visitVarDecl → allocate in env (mark val immutable)visitAssign → update env (error if immutable)visitPrintStmt → print values, explicitly showing '\0' and "" for empty char/stringvisitIfStmt → evaluate conditions in order, execute matching blockvisitExpr → perform arithmetic & comparisons via ctx.op.getText()visitPrimary → parse literals, look up variables, handle parenthesesvisitor.visit(tree) walks the AST and executes statementsAdd Grammar Rule Example: how the while loop has been added to the language:
in MinJ.g4:
whileStmt rule:whileStmt: 'while' expr 'do' ':' block ;
expr is the condition, block is the body.
Add whileStmt to the statement rule:
statement: varDecl | assign | printStmt | ifStmt | whileStmt ;
WHILE, DO, and END keywords to the lexer rules:WHILE: 'while' ;
DO: 'do' ;
END: 'end' ;
Implement Visitor Logic
EvalVisitor.java, implement the visitWhileStmt method: @Override
public Object visitWhileStmt(MinJParser.WhileStmtContext ctx) {
while ((Boolean) visit(ctx.expr())) {
visitBlock(ctx.block());
}
return null;
}
3Rebuild
./gradlew clean generateGrammarSource compileJava shadowJar
4Implement Visitor java @Override public Object visitWhileStmt(MinJParser.WhileStmtContext ctx) { while ((Boolean) visit(ctx.expr())) { visitBlock(ctx.block()); } return null; }
5Test
.mj file using while, run java -jar build/libs/minjc-0.1.0.jar yourLoop.mj, and verify the loop executes as expected.GitHub Pages automatically serves the project documentation from the master branch (configured in repository settings). A GitHub Actions workflow triggers on pushes to main, builds the site by copying README.md into a static site, and deploys to gh-pages. The site is available at https://Conava.github.io/MinJ.
The CI/CD pipeline is defined in .github/workflows/ci.yml. On each push or pull request to release:
actions/setup-java./gradlew clean generateGrammerSource shadowJar to build the fat‑JAR including all dependencies./gradlew test for unit testsbuild/libs/minjc-*.jar artifactMinJ leverages several tools to automate and streamline language development, parsing, and distribution:
MinJBaseVisitor to walk this tree, interpreting each node.generateGrammarSource phase. The generated Java files reside in build/generated-src/antlr/main.build.gradle, automating grammar generation, compilation, testing, and packaging.These tools together create a robust, reproducible development workflow, from grammar changes to production-ready interpreters.