Two-part homework for the Compilers course at the University of Athens (Department of Informatics & Telecommunications).
- Part 1 — Hand-write an LL(1) recursive-descent parser for an expression grammar, derived from the grammar's
FIRST/FOLLOWsets and a pre-built lookup table. - Part 2 — Build a complete scanner + parser + code generator for a small string-manipulation DSL using JFlex and JavaCUP: source → IR → Java. The generated Java is compiled and executed automatically as part of the pipeline.
Solo project.
Source DSL: arithmetic expressions over numbers (the original grammar lives in Part1/LL1_grammars/expressionGrammar; the LL(1)-rewritten version that removes left recursion and factors the grammar is in expressionGrammarLL1).
The Java code:
| File | Purpose |
|---|---|
Part1/evaluator/Main.java |
Reads an expression from stdin, hands it to the evaluator |
Part1/evaluator/ExpressionEvaluator.java |
Recursive-descent parser using the lookup table; evaluates as it parses |
Part1/evaluator/ParseError.java |
Custom exception for syntax errors |
Part1/LL1_grammars/FirstFollowSets |
Computed FIRST and FOLLOW sets for the grammar |
Part1/LL1_grammars/LookupTable |
The LL(1) parsing table — what production to apply for each (non-terminal, terminal) pair |
cd Part1
make
make execute # interactive: type an expression, get the valueThe source language is a small DSL for string manipulation. It supports:
- Function definitions (
name() { "John" },fullname(first, sep, last) { first + sep + last }) - String literals and concatenation (
"a" + "b") - Built-in operations:
prefix,suffix,reverse - Equality testing (
=) andif/else
A sample input (from inputs/input1.txt):
name() { "John" }
surname() { "Doe" }
fullname(first_name, sep, last_name) {
first_name + sep + last_name
}
name()
surname()
fullname(name(), " ", surname())
The pipeline:
input.txt
│
▼
JFlex scanner (scanner.flex) ──tokens──▶ JavaCUP parser (IRparser.cup)
│
▼
Translated.ir
│
▼
JavaCUP parser (JavaParser.cup)
│
▼
Translated.java
│
▼
javac + java
│
▼
program output
Two CUP grammars: one that produces IR from the source DSL, one that translates IR into Java source. Output files land in Part2/outputs/. The Makefile chains all of it — generate, compile, run.
cd Part2
make
make execute < inputs/input1.txt # drives the full pipeline on one input
./tests.sh # runs all 21 example.txt + 6 error.txt testsPart of a two-piece Compilers arc:
- compilers-hw1 (you are here) — LL(1) parser construction + JFlex/JavaCUP translator
- compilers-hw2 — MiniJava semantic analysis (JavaCC/JTB, two-visitor design)
MIT — applies to my own code in this repo. Vendored libraries (JavaCUP runtime jars under Part2/libs/) and assignment-distributed materials retain their original licenses.