nx_state_machine.nx source
↩ module page · 119 lines · 4492 B
1// nx_state_machine.nx -- generic state-transition primitive.
2//
3// Many primitives have sealed-state-machine shapes (nx_cell.state,
4// nx_yield.state, nx_treaty.state, nx_recovery.phase). This is the
5// shared substrate for that pattern: caller declares N states + a
6// transition table, gets atomic transitions + queries.
7//
8// Composes with anything that has lifecycle states; useful for app-
9// level state machines beyond the substrate primitives.
10
11import "nx_syscalls.nx"
12import "nx_tier.nx"
13
14const NX_SM_OK: nx_int = 0
15const NX_SM_ERR_BAD_FROM: nx_int = 1
16const NX_SM_ERR_BAD_TO: nx_int = 2
17const NX_SM_ERR_BAD_TRANSITION: nx_int = 3
18const NX_SM_ERR_TABLE_FULL: nx_int = 4
19
20// ===== Struct: NxStateMachine ======================================
21//
22// states_count: how many sealed states this machine has (1..63 typical)
23// transition_table: bitmask matrix; bit (from*states_count + to) is
24// set if the transition is allowed
25// current_state: where we are now
26// transition_count: monotonic across all moves
27
28struct NxStateMachine {
29 states_count: nx_int,
30 current_state: nx_int,
31 initial_state: nx_int,
32 transition_count: nx_int,
33 transition_table: *u8, // states_count * states_count bytes (1 byte per cell)
34 table_bytes: nx_size,
35}
36
37func nx_state_machine_new(states_count: nx_int, initial_state: nx_int) -> *NxStateMachine {
38 if states_count <= 0 { return (0 as i64) as *NxStateMachine }
39 if states_count > 64 { return (0 as i64) as *NxStateMachine }
40 if initial_state < 0 { return (0 as i64) as *NxStateMachine }
41 if initial_state >= states_count { return (0 as i64) as *NxStateMachine }
42 let sm: *NxStateMachine = (sys_mmap(40)) as *NxStateMachine
43 sm.states_count = states_count
44 sm.current_state = initial_state
45 sm.initial_state = initial_state
46 sm.transition_count = 0
47 sm.table_bytes = (states_count as nx_size) * (states_count as nx_size)
48 sm.transition_table = (sys_mmap(sm.table_bytes)) as *u8
49 // Initialize: all transitions disallowed by default
50 var i: nx_size = 0
51 while i < sm.table_bytes {
52 sm.transition_table[i] = 0 as u8
53 i = i + 1
54 }
55 // Self-transition always allowed (idempotent)
56 var k: nx_int = 0
57 while k < states_count {
58 let idx: nx_size = (k as nx_size) * (states_count as nx_size) + (k as nx_size)
59 sm.transition_table[idx] = 1 as u8
60 k = k + 1
61 }
62 return sm
63}
64
65// ===== nx_state_machine_allow ======================================
66//
67// Allow transition from -> to. Caller builds the table once at
68// instantiation; runtime transitions just consult it.
69
70func nx_state_machine_allow(sm: *NxStateMachine, from: nx_int, to: nx_int) -> nx_int {
71 if from < 0 { return NX_SM_ERR_BAD_FROM }
72 if from >= sm.states_count { return NX_SM_ERR_BAD_FROM }
73 if to < 0 { return NX_SM_ERR_BAD_TO }
74 if to >= sm.states_count { return NX_SM_ERR_BAD_TO }
75 let idx: nx_size = (from as nx_size) * (sm.states_count as nx_size) + (to as nx_size)
76 sm.transition_table[idx] = 1 as u8
77 return NX_SM_OK
78}
79
80// ===== nx_state_machine_is_allowed =================================
81
82func nx_state_machine_is_allowed(sm: *NxStateMachine, from: nx_int, to: nx_int) -> nx_int {
83 if from < 0 { return 0 }
84 if from >= sm.states_count { return 0 }
85 if to < 0 { return 0 }
86 if to >= sm.states_count { return 0 }
87 let idx: nx_size = (from as nx_size) * (sm.states_count as nx_size) + (to as nx_size)
88 if (sm.transition_table[idx] as i64) & 255 != 0 { return 1 }
89 return 0
90}
91
92// ===== nx_state_machine_transition ================================
93//
94// Atomic state change. Returns NX_SM_OK or NX_SM_ERR_BAD_TRANSITION.
95// current_state -> to_state must be in the allow-table.
96
97func nx_state_machine_transition(sm: *NxStateMachine, to_state: nx_int) -> nx_int {
98 if nx_state_machine_is_allowed(sm, sm.current_state, to_state) == 0 {
99 return NX_SM_ERR_BAD_TRANSITION
100 }
101 sm.current_state = to_state
102 sm.transition_count = sm.transition_count + 1
103 return NX_SM_OK
104}
105
106func nx_state_machine_current(sm: *NxStateMachine) -> nx_int {
107 return sm.current_state
108}
109
110func nx_state_machine_transitions(sm: *NxStateMachine) -> nx_int {
111 return sm.transition_count
112}
113
114// ===== nx_state_machine_reset ======================================
115
116func nx_state_machine_reset(sm: *NxStateMachine) -> nx_int {
117 sm.current_state = sm.initial_state
118 return NX_SM_OK
119}