Skip to content

Commit

Permalink
state machine examples
Browse files Browse the repository at this point in the history
  • Loading branch information
stackdump committed Dec 1, 2024
0 parents commit 1ab8889
Show file tree
Hide file tree
Showing 19 changed files with 2,306 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
python/.DS_Store
python/__pycache__/
python/test/__pycache__/
node_modules
28 changes: 28 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.PHONY: all run-rust run-bash run-python run-js run-ruby

all: run-rust run-bash run-python run-js run-ruby

run-rust:
@echo ---
@echo "Running Rust implementation..."
cargo run --manifest-path rust/Cargo.toml

run-bash:
@echo ---
@echo "Running Bash implementation..."
bash bash/main.sh

run-python:
@echo ---
@echo "Running Python implementation..."
python3 python/main.py

run-js:
@echo ---
@echo "Running JavaScript implementation..."
node javascript/src/main.js

run-ruby:
@echo ---
@echo "Running Ruby implementation..."
ruby ruby/main.rb
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Coffee Machine State Machine

This project demonstrates the implementation of a coffee vending machine using state machines
in different programming languages.

[![pflow](https://pflow.dev/img/zb2rhjeJ8bKSGRDHYv1e4Ap6wZrXvF6hqQkGYW7B1hk1KBsWa.svg)](https://pflow.dev/p/zb2rhjeJ8bKSGRDHYv1e4Ap6wZrXvF6hqQkGYW7B1hk1KBsWa/)

Click the image above to view the state machine in the [PFlow](https://pflow.dev) editor.

## Contributing

Contributions are welcome! Feel free to add new implementations in different languages or improve existing ones.

## State Machine Implementation

The state machine is implemented using the following components:
- **States**: Represent the different states of the coffee machine (e.g., `BoiledWater`, `GroundCoffee`, `CoffeeInPot`).
- **Transitions**: Represent the actions that cause state changes (e.g., `BrewCoffee`, `PourCoffee`).
- **Arrows**: Define the allowed transitions between states.
- **Guards**: Ensure that certain transitions only occur when specific conditions are met.

## Languages

The state machine is implemented in multiple programming languages to compare their behavior and ensure consistency.
Each implementation follows the same Petri net model.

### Other Languages

Implementations in other languages (e.g., Python, Java, C++) will be added in their respective directories.
Each implementation will follow the same structure and logic as the Rust implementation.

## Running the State Machine

To run all samples just use
```
make all
```

To run a specific language use
```
make run-bash
```
199 changes: 199 additions & 0 deletions bash/main.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#!/bin/bash

states=(
"Water:1"
"BoiledWater:0"
"CoffeeBeans:1"
"GroundCoffee:0"
"Filter:1"
"CoffeeInPot:0"
"Pending:1"
"Sent:0"
"Payment:0"
"Cup:1"
)

actions=(
"BoilWater"
"GrindBeans"
"BrewCoffee"
"PourCoffee"
"Send"
"Credit"
)

arrows=(
"Water > BoilWater"
"BoilWater > BoiledWater"
"CoffeeBeans > GrindBeans"
"GrindBeans > GroundCoffee"
"BoiledWater > BrewCoffee"
"GroundCoffee > BrewCoffee"
"Filter > BrewCoffee"
"BrewCoffee > CoffeeInPot"
"CoffeeInPot > PourCoffee"
"Cup > PourCoffee"
"Pending > Send"
"Send > Sent"
"Sent > Credit"
"Credit > Payment"
)

guards=(
"Payment > PourCoffee"
)

# Print the current state as a set
function print_state() {
local output="{"
for state in "${states[@]}"; do
IFS=":" read -r name value <<<"$state"
if [[ $value == "1" ]]; then
output+=" $name,"
fi
done
output="${output%,} }"
echo "$output"
}

# Check if the element is present in the current state
function has_state() {
for state in "${states[@]}"; do
IFS=":" read -r name value <<<"$state"
if [[ $name == "$1" && $value == "1" ]]; then
return 0
fi
done
return 1
}

# Set the state of a given element
function set_state() {
for i in "${!states[@]}"; do
IFS=":" read -r name value <<<"${states[$i]}"
if [[ $name == "$1" ]]; then
states[$i]="$name:$2"
fi
done
return 0
}

function guard_fails() {
for guard in "${guards[@]}"; do
local from to
IFS=" > " read -r from to <<<"$guard"
if [[ "$from" == "$1" ]]; then
if has_state "$to"; then
return 0
fi
fi
if [[ "$to" == "$1" ]]; then
if ! has_state "$from"; then
return 0
fi
fi

done
return 1
}

# Transform the state based on the action
function transform() {
if guard_fails "$1"; then
return 1
fi

local arrow

for arrow in "${arrows[@]}"; do
IFS=" > " read -r from to <<<"$arrow"
if [[ "$from" == "$1" ]]; then # action -> state
if has_state "$to"; then
return 1 # Invalid output
else
set_state $to 1
fi
fi
if [[ $to == "$1" ]]; then # state -> action
if has_state "$from"; then
set_state "$from" 0
else
return 1 # Insufficient input
fi
fi
done
return 0
}

# Check if the action can be performed based on the current state
function can_transform() {
if guard_fails "$1"; then
return 1
fi

local arrow
for arrow in "${arrows[@]}"; do
IFS=" > " read -r from to <<<"$arrow"
if [[ $from == "$1" ]]; then # action -> state
if has_state "$to"; then
return 1 # Invalid output
fi
fi
if [[ $to == "$1" ]]; then # state -> action
if ! has_state "$from"; then
return 1 # Insufficient input
fi
fi
done
}

# Main function - runs the process by executing the actions
# for the current state until no more actions are possible
function main() {
local step=1
echo "step #0: Initial state"
print_state
while true; do
local transformed=false
for action in "${actions[@]}"; do
if can_transform "$action"; then
transform "$action"
echo "step #$step: $($action)"
transformed=true
step=$((step + 1))
break
fi
done
if ! $transformed; then
break
else
print_state
fi
done
}

function BoilWater {
echo "Boiling Water"
}

function GrindBeans {
echo "Grinding Beans"
}

function BrewCoffee {
echo "Brewing Coffee"
}

function PourCoffee {
echo "Pouring Coffee"
}

function Send {
echo "Sending Payment"
}

function Credit {
echo "Crediting Payment"
}

main
3 changes: 3 additions & 0 deletions golang/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/stackdump/pflow-polyglot/golang

go 1.22.4
Loading

0 comments on commit 1ab8889

Please sign in to comment.