Skip to content

Commit 1ab8889

Browse files
committed
state machine examples
0 parents  commit 1ab8889

File tree

19 files changed

+2306
-0
lines changed

19 files changed

+2306
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.DS_Store
2+
python/.DS_Store
3+
python/__pycache__/
4+
python/test/__pycache__/
5+
node_modules

Makefile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
.PHONY: all run-rust run-bash run-python run-js run-ruby
2+
3+
all: run-rust run-bash run-python run-js run-ruby
4+
5+
run-rust:
6+
@echo ---
7+
@echo "Running Rust implementation..."
8+
cargo run --manifest-path rust/Cargo.toml
9+
10+
run-bash:
11+
@echo ---
12+
@echo "Running Bash implementation..."
13+
bash bash/main.sh
14+
15+
run-python:
16+
@echo ---
17+
@echo "Running Python implementation..."
18+
python3 python/main.py
19+
20+
run-js:
21+
@echo ---
22+
@echo "Running JavaScript implementation..."
23+
node javascript/src/main.js
24+
25+
run-ruby:
26+
@echo ---
27+
@echo "Running Ruby implementation..."
28+
ruby ruby/main.rb

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Coffee Machine State Machine
2+
3+
This project demonstrates the implementation of a coffee vending machine using state machines
4+
in different programming languages.
5+
6+
[![pflow](https://pflow.dev/img/zb2rhjeJ8bKSGRDHYv1e4Ap6wZrXvF6hqQkGYW7B1hk1KBsWa.svg)](https://pflow.dev/p/zb2rhjeJ8bKSGRDHYv1e4Ap6wZrXvF6hqQkGYW7B1hk1KBsWa/)
7+
8+
Click the image above to view the state machine in the [PFlow](https://pflow.dev) editor.
9+
10+
## Contributing
11+
12+
Contributions are welcome! Feel free to add new implementations in different languages or improve existing ones.
13+
14+
## State Machine Implementation
15+
16+
The state machine is implemented using the following components:
17+
- **States**: Represent the different states of the coffee machine (e.g., `BoiledWater`, `GroundCoffee`, `CoffeeInPot`).
18+
- **Transitions**: Represent the actions that cause state changes (e.g., `BrewCoffee`, `PourCoffee`).
19+
- **Arrows**: Define the allowed transitions between states.
20+
- **Guards**: Ensure that certain transitions only occur when specific conditions are met.
21+
22+
## Languages
23+
24+
The state machine is implemented in multiple programming languages to compare their behavior and ensure consistency.
25+
Each implementation follows the same Petri net model.
26+
27+
### Other Languages
28+
29+
Implementations in other languages (e.g., Python, Java, C++) will be added in their respective directories.
30+
Each implementation will follow the same structure and logic as the Rust implementation.
31+
32+
## Running the State Machine
33+
34+
To run all samples just use
35+
```
36+
make all
37+
```
38+
39+
To run a specific language use
40+
```
41+
make run-bash
42+
```

bash/main.sh

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
#!/bin/bash
2+
3+
states=(
4+
"Water:1"
5+
"BoiledWater:0"
6+
"CoffeeBeans:1"
7+
"GroundCoffee:0"
8+
"Filter:1"
9+
"CoffeeInPot:0"
10+
"Pending:1"
11+
"Sent:0"
12+
"Payment:0"
13+
"Cup:1"
14+
)
15+
16+
actions=(
17+
"BoilWater"
18+
"GrindBeans"
19+
"BrewCoffee"
20+
"PourCoffee"
21+
"Send"
22+
"Credit"
23+
)
24+
25+
arrows=(
26+
"Water > BoilWater"
27+
"BoilWater > BoiledWater"
28+
"CoffeeBeans > GrindBeans"
29+
"GrindBeans > GroundCoffee"
30+
"BoiledWater > BrewCoffee"
31+
"GroundCoffee > BrewCoffee"
32+
"Filter > BrewCoffee"
33+
"BrewCoffee > CoffeeInPot"
34+
"CoffeeInPot > PourCoffee"
35+
"Cup > PourCoffee"
36+
"Pending > Send"
37+
"Send > Sent"
38+
"Sent > Credit"
39+
"Credit > Payment"
40+
)
41+
42+
guards=(
43+
"Payment > PourCoffee"
44+
)
45+
46+
# Print the current state as a set
47+
function print_state() {
48+
local output="{"
49+
for state in "${states[@]}"; do
50+
IFS=":" read -r name value <<<"$state"
51+
if [[ $value == "1" ]]; then
52+
output+=" $name,"
53+
fi
54+
done
55+
output="${output%,} }"
56+
echo "$output"
57+
}
58+
59+
# Check if the element is present in the current state
60+
function has_state() {
61+
for state in "${states[@]}"; do
62+
IFS=":" read -r name value <<<"$state"
63+
if [[ $name == "$1" && $value == "1" ]]; then
64+
return 0
65+
fi
66+
done
67+
return 1
68+
}
69+
70+
# Set the state of a given element
71+
function set_state() {
72+
for i in "${!states[@]}"; do
73+
IFS=":" read -r name value <<<"${states[$i]}"
74+
if [[ $name == "$1" ]]; then
75+
states[$i]="$name:$2"
76+
fi
77+
done
78+
return 0
79+
}
80+
81+
function guard_fails() {
82+
for guard in "${guards[@]}"; do
83+
local from to
84+
IFS=" > " read -r from to <<<"$guard"
85+
if [[ "$from" == "$1" ]]; then
86+
if has_state "$to"; then
87+
return 0
88+
fi
89+
fi
90+
if [[ "$to" == "$1" ]]; then
91+
if ! has_state "$from"; then
92+
return 0
93+
fi
94+
fi
95+
96+
done
97+
return 1
98+
}
99+
100+
# Transform the state based on the action
101+
function transform() {
102+
if guard_fails "$1"; then
103+
return 1
104+
fi
105+
106+
local arrow
107+
108+
for arrow in "${arrows[@]}"; do
109+
IFS=" > " read -r from to <<<"$arrow"
110+
if [[ "$from" == "$1" ]]; then # action -> state
111+
if has_state "$to"; then
112+
return 1 # Invalid output
113+
else
114+
set_state $to 1
115+
fi
116+
fi
117+
if [[ $to == "$1" ]]; then # state -> action
118+
if has_state "$from"; then
119+
set_state "$from" 0
120+
else
121+
return 1 # Insufficient input
122+
fi
123+
fi
124+
done
125+
return 0
126+
}
127+
128+
# Check if the action can be performed based on the current state
129+
function can_transform() {
130+
if guard_fails "$1"; then
131+
return 1
132+
fi
133+
134+
local arrow
135+
for arrow in "${arrows[@]}"; do
136+
IFS=" > " read -r from to <<<"$arrow"
137+
if [[ $from == "$1" ]]; then # action -> state
138+
if has_state "$to"; then
139+
return 1 # Invalid output
140+
fi
141+
fi
142+
if [[ $to == "$1" ]]; then # state -> action
143+
if ! has_state "$from"; then
144+
return 1 # Insufficient input
145+
fi
146+
fi
147+
done
148+
}
149+
150+
# Main function - runs the process by executing the actions
151+
# for the current state until no more actions are possible
152+
function main() {
153+
local step=1
154+
echo "step #0: Initial state"
155+
print_state
156+
while true; do
157+
local transformed=false
158+
for action in "${actions[@]}"; do
159+
if can_transform "$action"; then
160+
transform "$action"
161+
echo "step #$step: $($action)"
162+
transformed=true
163+
step=$((step + 1))
164+
break
165+
fi
166+
done
167+
if ! $transformed; then
168+
break
169+
else
170+
print_state
171+
fi
172+
done
173+
}
174+
175+
function BoilWater {
176+
echo "Boiling Water"
177+
}
178+
179+
function GrindBeans {
180+
echo "Grinding Beans"
181+
}
182+
183+
function BrewCoffee {
184+
echo "Brewing Coffee"
185+
}
186+
187+
function PourCoffee {
188+
echo "Pouring Coffee"
189+
}
190+
191+
function Send {
192+
echo "Sending Payment"
193+
}
194+
195+
function Credit {
196+
echo "Crediting Payment"
197+
}
198+
199+
main

golang/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/stackdump/pflow-polyglot/golang
2+
3+
go 1.22.4

0 commit comments

Comments
 (0)