solilang 0.21.1

A statically-typed, class-based OOP language with pipeline operators
Documentation
// StateMachine Example - Pure Soli Implementation
// This demonstrates managing complex workflows entirely in Soli lang

class StateMachine {
    states: Array;
    transitions: Array;
    _current_state: String;
    _history: Array;
    _last_transition: Hash;
    valid_events: Hash;
    context: Hash;

    new(initial_state: String, states: Array, transitions: Array) {
        this.states = states;
        this.transitions = transitions;
        this._current_state = initial_state;
        this._history = [];
        this._last_transition = null;
        this.context = {};
        
        // Build valid events map
        this.valid_events = {};
        let t_idx = 0;
        while t_idx < len(transitions) {
            let transition = transitions[t_idx];
            let event = transition["event"];
            let sources = transition["from"];
            if type(sources) == "String" {
                sources = [sources];
            }
            if !has_key(this.valid_events, event) {
                this.valid_events[event] = sources;
            } else {
                let current = this.valid_events[event];
                let new_sources = [];
                let s_idx = 0;
                while s_idx < len(sources) {
                    let s = sources[s_idx];
                    // Use Array.find() to check if source already exists
                    let existing = current.find(fn(x) x == s);
                    if existing == null {
                        new_sources = [...new_sources, s];
                    }
                    s_idx = s_idx + 1;
                }
                if len(new_sources) > 0 {
                    this.valid_events[event] = [...current, ...new_sources];
                }
            }
            t_idx = t_idx + 1;
        }
    }

    fn current_state() -> String {
        return this._current_state;
    }

    fn is(state: String) -> Bool {
        return this._current_state == state;
    }

    fn is_in(states: Array) -> Bool {
        // Use Array.find() to check if current state is in list
        return states.find(fn(x) x == this._current_state) != null;
    }

    fn can(event: String) -> Bool {
        if !has_key(this.valid_events, event) {
            return false;
        }
        // Use Array.find() to check if current state can trigger event
        return this.valid_events[event].find(fn(x) x == this._current_state) != null;
    }

    fn available_events() -> Array {
        let all_events = keys(this.valid_events);
        let result = [];
        let e_idx = 0;
        while e_idx < len(all_events) {
            let event = all_events[e_idx];
            // Use Array.find() to check if event is available from current state
            if this.valid_events[event].find(fn(x) x == this._current_state) != null {
                result = [...result, event];
            }
            e_idx = e_idx + 1;
        }
        return result;
    }

    fn transition(event: String) -> Hash {
        let idx = 0;
        while idx < len(this.transitions) {
            let transition = this.transitions[idx];
            if transition["event"] == event {
                let sources = transition["from"];
                let is_valid = false;
                if type(sources) == "String" {
                    if sources == this._current_state {
                        is_valid = true;
                    }
                } else {
                    // Use Array.find() to check if current state is a valid source
                    if sources.find(fn(x) x == this._current_state) != null {
                        is_valid = true;
                    }
                }
                if is_valid {
                    let from_state = this._current_state;
                    let to_state = transition["to"];
                    this._current_state = to_state;
                    this._history = [...this._history, {
                        "from": from_state,
                        "to": to_state,
                        "event": event
                    }];
                    this._last_transition = {
                        "from": from_state,
                        "to": to_state,
                        "event": event
                    };
                    return {
                        "success": true,
                        "from": from_state,
                        "to": to_state,
                        "event": event
                    };
                }
            }
            idx = idx + 1;
        }
        
        return {
            "success": false,
            "error": "invalid_transition",
            "reason": "Cannot transition '" + event + "' from state '" + this._current_state + "'"
        };
    }

    fn set(key: String, value: Any) {
        this.context[key] = value;
    }

    fn get(key: String) -> Any {
        if has_key(this.context, key) {
            return this.context[key];
        }
        return null;
    }

    fn history() -> Array {
        return this._history;
    }

    fn last_transition() -> Hash {
        return this._last_transition;
    }
}

fn create_state_machine(initial_state: String, states: Array, transitions: Array) -> StateMachine {
    return new StateMachine(initial_state, states, transitions);
}

class StateMachineBuilder {
    initial_state: String;
    states: Array;
    transitions: Array;

    new() {
        this.initial_state = "";
        this.states = [];
        this.transitions = [];
    }

    fn initial(state: String) -> StateMachineBuilder {
        this.initial_state = state;
        return this;
    }

    fn states_list(states: Array) -> StateMachineBuilder {
        this.states = states;
        return this;
    }

    fn transition(event: String, from_state: Any, to: String) -> StateMachineBuilder {
        let sources = from_state;
        if type(from_state) == "String" {
            sources = [from_state];
        }
        this.transitions = [...this.transitions, {
            "event": event,
            "from": sources,
            "to": to
        }];
        return this;
    }

    fn build() -> StateMachine {
        return create_state_machine(this.initial_state, this.states, this.transitions);
    }
}

fn state_machine() -> StateMachineBuilder {
    return new StateMachineBuilder();
}

// Example 1: Order Processing Workflow
print("=== Order Processing State Machine ===");

let order_states = ["pending", "confirmed", "processing", "shipped", "delivered", "cancelled"];
let order_transitions = [
    {"event": "confirm", "from": "pending", "to": "confirmed"},
    {"event": "process", "from": "confirmed", "to": "processing"},
    {"event": "ship", "from": "processing", "to": "shipped"},
    {"event": "deliver", "from": "shipped", "to": "delivered"},
    {"event": "cancel", "from": "pending", "to": "cancelled"}
];

let order = create_state_machine("pending", order_states, order_transitions);

print("Initial state: ", order.current_state());
print("Available events: ", order.available_events());

order.transition("confirm");
print("After confirm: ", order.current_state());

order.transition("process");
print("After process: ", order.current_state());

order.set("tracking_number", "TRK-12345");
order.transition("ship");
print("After ship - tracking: ", order.get("tracking_number"));

order.transition("deliver");
print("Final state: ", order.current_state());

// Example 2: Payment State Machine with Multiple Source States
print("");
print("=== Payment State Machine ===");

let payment = create_state_machine("pending", 
    ["pending", "authorized", "captured", "failed", "refunded"],
    [
        {"event": "authorize", "from": "pending", "to": "authorized"},
        {"event": "capture", "from": "authorized", "to": "captured"},
        {"event": "fail", "from": ["pending", "authorized"], "to": "failed"},
        {"event": "refund", "from": ["captured", "failed"], "to": "refunded"}
    ]
);

payment.set("amount", 100.00);
print("Payment amount: ", payment.get("amount"));

payment.transition("authorize");
print("After authorize: ", payment.current_state());

payment.transition("capture");
print("After capture: ", payment.current_state());

// Example 3: Using StateMachineBuilder
print("");
print("=== StateMachineBuilder ===");

let article = state_machine()
    .initial("draft")
    .states_list(["draft", "review", "approved", "published"])
    .transition("submit", "draft", "review")
    .transition("approve", "review", "approved")
    .transition("publish", "approved", "published")
    .build();

print("Article state: ", article.current_state());
article.transition("submit");
print("After submit: ", article.current_state());

// Example 4: Query Methods
print("");
print("=== Query Methods ===");

let sm = create_state_machine("a", ["a", "b", "c"], [
    {"event": "to_b", "from": "a", "to": "b"},
    {"event": "to_c", "from": "b", "to": "c"}
]);

print("Can go to b? ", sm.can("to_b"));
print("Can go to c? ", sm.can("to_c"));
print("Available events: ", sm.available_events());

sm.transition("to_b");
print("After to_b - Can still go to b? ", sm.can("to_b"));
print("Can go to c now? ", sm.can("to_c"));
print("Last transition: ", sm.last_transition());
print("History: ", sm.history());

print("");
print("All examples completed successfully!");