A domain-specific language for defining and executing finite state machines.
- Python 3.8 or higher
- No external dependencies — standard library only
fsmlang/
├── main.py # Entry point — run this
├── lexer.py # Tokenizer
├── ast_nodes.py # AST node definitions + printer
├── recursive_descent_parser.py # Recursive descent parser
├── example_programs/ # Valid example programs
│ ├── program1_traffic.fsm
│ ├── program2_vending.fsm
│ └── program3_gameai.fsm
└── malformed_programs/ # Programs with intentional errors
├── malformed1_no_initial.fsm
├── malformed2_bad_arrow.fsm
├── malformed3_no_machine_kw.fsm
├── malformed4_unclosed_brace.fsm
└── malformed5_invalid_token.fsm
# Parse a program and check for errors
python main.py <source_file>
# Parse and print the AST
python main.py <source_file> --dump-ast
# Show help
python main.py --help# Parse the traffic light example
python main.py examples/program1_traffic.fsm
# Dump its AST
python main.py examples/program1_traffic.fsm --dump-ast
# See a parse error
python main.py malformed/malformed1_no_initial.fsmFSMLang programs consist of global variable declarations, machine definitions, and run statements.
// Global variable — accessible by all machines
int threshold = 10
machine MyMachine {
// Local variable — accessible only within this machine
int counter = 0
state Idle {
on_enter: output "Entering Idle"
on_step: counter = counter + 1
on_exit: output "Leaving Idle"
}
state Active {
on_enter: output "Now Active"
}
initial: Idle
transition Idle -> Active if counter > threshold
}
// Run for a fixed number of steps
run MyMachine for 20 steps
// Or run until Ctrl+C
// run MyMachine forever
| Hook | When it runs |
|---|---|
on_enter |
Once, when the machine enters this state |
on_step |
Every step while this state is active (optional) |
on_exit |
Once, when the machine leaves this state |
Transitions fire when their guard expression evaluates to true. If no guard is given, the transition fires unconditionally on the next step.
transition StateA -> StateB if counter > 10
transition StateA -> StateB // unconditional
run MyMachine for 100 steps // runs exactly 100 steps
run MyMachine forever // runs until Ctrl+C
| Type | Example |
|---|---|
int |
int x = 0 |
float |
float rate = 0.5 |
bool |
bool active = true |
string |
string label = "hello" |
| Category | Operators |
|---|---|
| Arithmetic | + - * / |
| Comparison | == != < > <= >= |
| Logical | and or not |
| Unary | - (negation) |
The lexer and parser produce informative error messages with line and column numbers.
[Lexer Error] Line 5, Col 12: Unexpected character '@'
[Parse Error] Line 8, Col 9: Machine 'A' has no 'initial' declaration
[Parse Error] Line 12, Col 20: Expected '->' but got '-'