Loading repository data…
Loading repository data…
eswar-7116 / repository
Flux is a simple interpreted language developed in Java
A transparent discovery signal based on current public GitHub metadata.
This score does not audit code, security, maintainers, documentation quality, or suitability. Verify the repository and its current documentation before adoption.
A tree-walking interpreted language built in Java.
Flux has arithmetic, mutable and immutable variables, and a REPL for interactive use. Source code is parsed by a recursive descent parser, optimized via constant folding/propagation and algebraic simplifications, then executed by a tree-walking interpreter that uses pattern matching as a visitor-like dispatch.
Everything is a double (IEEE 754 double-precision float).
42
3.14
Standard infix operators with the usual precedence rules:
| Operator | Description | Example | Result |
|---|---|---|---|
+ | Addition | 1 + 2 | 3.0 |
- | Subtraction | 10 - 3 | 7.0 |
* | Multiplication | 4 * 5 | 20.0 |
/ | Division | 10 / 4 | 2.5 |
% | Modulo | 10 % 3 | 1.0 |
Precedence, highest to lowest:
+, -*, /, %+, -= (assignment, right-to-left)Parentheses override precedence:
1 + 2 * 3 # 7.0
(1 + 2) * 3 # 9.0
let - MutableDeclare a variable with let. You can reassign it later.
let x = 10
x = 20
print x # 20.0
const - ImmutableDeclare a constant with const. Trying to reassign it is a runtime error.
const pi = 3.14
print pi # 3.14
pi = 0 # Error: Cannot reassign const variable: pi
del - DeleteRemove a variable from scope. You can re-declare it after deletion.
let x = 1
del x
let x = 99 # fine, x was deleted
printEvaluates an expression and prints the result.
print 1 + 2 # 3.0
print x * 2
exitStops the program. You can optionally pass an exit code (defaults to 0).
exit # exits with code 0
exit 1 # exits with code 1
# starts a comment. Everything after it on that line is ignored.
# this is a full-line comment
print 42 # this is an inline comment
Assignments return the assigned value, so you can chain them:
let a = 1
let b = 2
a = b = 10 # both a and b are now 10.0
# operator precedence demo
const a = 1 + 2 * 3
const b = (1 + 2) * 3
print a
print b
# variables
let x = 10
let y = x + 5
print y
x = 100
print x
Output:
7.0
9.0
15.0
100.0
./gradlew build
Pass the source file path as an argument:
java -cp build/classes/java/main Main program.flux
There's a sample program at src/main/resources/testcode/code.flux you can try:
java -cp build/classes/java/main Main src/main/resources/testcode/code.flux
java -cp build/classes/java/main repl.REPL
>>> let x = 5
>>> print x + 10
15.0
>>> const pi = 3.14
>>> print pi * 2
6.28
./gradlew test
210 tests across the lexer, parser, interpreter, and optimizer.
flowchart TD
A["Source Code (.flux)"] --> B["Lexer - Converts source text into tokens (line-by-line)"]
B -->|List<Token>| C["Parser - Builds AST (Expr/Stmt) using recursive descent"]
C -->|Stmt| D["Optimizer - Constant folding, propagation, simplifications"]
D -->|Stmt optimized| E["Interpreter - Executes AST and maintains symbol table"]
The lexer reads source code line by line and produces a flat list of Token objects. Each token carries its type, lexeme text, and source position (line and column). It handles single-character operators, multi-character numbers (integers and floats), identifiers, keywords, comments (#), and whitespace.
The parser is a hand-written recursive descent parser with precedence climbing. Each grammar rule maps to a method: statement() handles keywords like let, const, del, print, and exit; expression() dispatches to assignment(), which calls addition(), then term(), then unary(), then primary(). This gives the correct operator precedence without needing a table-driven approach. Assignment is right-associative; arithmetic is left-associative.
Before the interpreter runs, the optimizer walks the AST and applies three transformations:
1 + 2 * 3 becomes 7.0)let x = 5 was seen earlier, a later print x + 1 gets rewritten to print 5 + 1 (and then folded to 6.0)x + 0, x * 1, and x * 0The optimizer maintains its own constants map, updating it as it processes let, const, del, and assignment statements.
The interpreter is a tree-walking evaluator that uses Java's pattern matching on sealed-style records as a visitor-like dispatch. Instead of a traditional Visitor interface with accept/visit methods, the interpreter uses switch expressions with record patterns to destructure each Stmt and Expr type. This gives the same separation of concerns as the visitor pattern but with less boilerplate. It maintains a symbol table (Map<String, Variable>) that tracks values and mutability.
| Package | What's in it |
|---|---|
token | Token record and TokenType enum |
lexer | Lexer - tokenizer with line/column tracking |
expr | Expression AST nodes: LiteralExpr, BinaryExpr, VariableExpr, AssignExpr |
stmt | Statement AST nodes: LetStmt, ConstStmt, DelStmt, PrintStmt, ExitStmt, ExprStmt |
parser | Recursive descent parser with precedence climbing |
optimizer | Constant folding, propagation, and algebraic simplifications |
interpreter | Tree-walking evaluator using pattern matching dispatch |
repl | Interactive read-eval-print loop |
benchmark | Benchmarking and metrics related code |
A stress test benchmark (benchmark.flux) with 10,000 lines of complex arithmetic operations, variable assignments, and constant definitions was executed on the Flux runtime to evaluate the efficiency of the pipeline stages.
| Metric | Result | Description |
|---|---|---|
| Parsing Time | ~24.0 ms | Time to build the initial AST from 10,000 tokenized lines |
| Optimization Time | ~11.2 ms | Time for the Optimizer to process and rewrite the AST |
| Execution Time | ~7.0 ms | Time for the Interpreter to evaluate the optimized AST |
| Optimization Delta | 67.24% | Percentage of AST nodes removed/simplified by the Optimizer |
The Optimizer successfully folded and propagated constants, reducing the AST size from 115,982 nodes to 37,996 nodes before execution.
src/
├── main/java/
│ ├── Main.java # Entry point, takes a source file path
│ ├── token/ # Token, TokenType
│ ├── lexer/ # Lexer, LexerException
│ ├── expr/ # Expression AST nodes
│ ├── stmt/ # Statement AST nodes
│ ├── parser/ # Parser, ParserException
│ ├── optimizer/ # Optimizer
│ ├── interpreter/ # Interpreter, Variable, InterpreterException
│ └── repl/ # REPL
│ └── benchmark/ # Benchmarking and metrics related code
└── test/java/
├── lexer/ # Lexer tests
├── parser/ # Parser tests
├── interpreter/ # Interpreter tests
└── optimizer/ # Optimizer tests
git checkout -b feature/my-feature)./gradlew test)Roughly, you'd touch these layers in order:
TokenType and teach Lexer to recognize itExpr or Stmt recordMIT. See LICENSE.