3 个不稳定版本
使用旧的Rust 2015
0.2.0 | 2017年10月4日 |
---|---|
0.1.1 | 2017年9月20日 |
0.1.0 | 2017年9月20日 |
1753 在 算法 中
31KB
360 行
Rust宏中的有限状态机生成器
概述
使用此宏,您可以轻松以声明性方式实现有限状态机。
状态机由以下组成
- 名称
- 初始状态
- 状态列表
- 命令列表
- 状态节点列表
每个 状态节点 包含
- 状态
- 上下文(可选)
- 命令反应列表
每个 命令反应 包含
- 要反应的命令
- 用户定义的反应代码(可选)
- 机器的下一个状态(可选)
开始的工作示例
假设我们想实现这样的机器
相应的代码将如下所示
#[macro_use] extern crate macro_machine;
declare_machine!(
MyMachine(A {counter: 0}) // Name and initial state with initial value
states[A,B] // List of states
commands[Next] // List of commands
(A context{counter: i16}: // State node and this state context description with name binding
>> { // Executed on state A enter
println!("Enter A: {:?}", context);
context.counter = context.counter + 1;
}
<< { // Executed on state A leave
println!("Leave A: {:?}", context);
context.counter = context.counter + 1;
}
Next {
println!("Next in A: {:?}", context);
context.counter = context.counter + 1;
} => B {counter: context.counter}; // Command Reaction. Now on command Next we add 1 to our context. Also we change state to B and init it with our counter value.
)
(B context{counter: i16}:
>> {
println!("Enter B: {:?}", context);
context.counter = context.counter + 1;
}
<< {
println!("Leave B: {:?}", context);
context.counter = context.counter + 1;
}
Next {
println!("Next in B: {:?}", context);
context.counter = context.counter + 1;
} => A {counter: context.counter};
)
);
fn main() {
use MyMachine::*;
let mut machine = MyMachine::new();
machine.execute(&MyMachine::Commands::Next).unwrap();
machine.execute(&MyMachine::Commands::Next).unwrap();
}
更详细的解释
最简单的状态机示例
#[macro_use] extern crate macro_machine;
declare_machine!(
Simple(A) // Name and initial State
states[A,B] // list of States
commands[Next] // list of Commands
(A: // State Node
Next => B; // Command Reaction. Just change state to B
)
(B:
Next => A; // And back to A
)
);
因此,现在您可以使用状态机
fn main() {
use Simple::*;
let mut machine = Simple::new();
machine.execute(&Simple::Commands::Next).unwrap();
machine.execute(&Simple::Commands::Next).unwrap();
}
您可以为机器添加一些智能。
每个状态可以保存一些数据。在状态改变时,您可以在状态之间传递一些数据。这就像您创建了一个带有一些字段初始化的结构体。
#[macro_use] extern crate macro_machine;
declare_machine!(
Simple(A{counter:0}) // Name and initial State with initial value
states[A,B] // list of States
commands[Next] // list of Commands
(A context{counter:i16}: // State Node and this state context description with binding name
Next {context.counter=context.counter+1}=> B{counter:context.counter}; // Command Reaction. Now on command Next we add 1 to our context. Also we change state to B and init it with our x value.
)
(B context{counter:i16}:
Next {context.counter=context.counter+1}=> A{counter:context.counter};
)
);
让我们检查我们的状态传递
fn main() {
use Simple::*;
let mut machine = Simple::new();
// We are in state A and have our initial value 0
assert!(match machine.get_current_state(){
States::A{context}=> if context.counter == 0 {true} else {false},
_=>false
});
machine.execute(&Simple::Commands::Next).unwrap();
// We are in state B and have counter == 1
assert!(match machine.get_current_state(){
States::B{context}=> if context.counter == 1 {true} else {false},
_=>false
});
machine.execute(&Simple::Commands::Next).unwrap();
// Again in state A and have counter == 2
assert!(match machine.get_current_state(){
States::A{context}=> if context.counter == 2 {true} else {false},
_=>false
});
}
还有在每个状态的进入和离开时的回调。
#[macro_use] extern crate macro_machine;
declare_machine!(
Simple(A{counter:0}) // Name and initial State with initial value
states[A,B] // list of States
commands[Next] // list of Commands
(A context{counter:i16}: // State Node and this state context description with binding name
>> {context.counter = context.counter+1;} // Execute when enter state A
<< {context.counter = context.counter+1;} // Execute when leave state A
Next {context.counter=context.counter+1;} => B{counter:context.counter}; // Command Reaction. Now on command Next we add 1 to our context. Also we change state to B and init it with our x value.
)
(B context{counter:i16}:
Next {context.counter=context.counter+1} => A{counter:context.counter};
)
);
fn main() {
use Simple::*;
let mut machine = Simple::new();
assert!(match machine.get_current_state(){
// We are in state A and have value 1. Because Enter State callback executed.
States::A{context}=> if context.counter == 1 {true} else {false},
_=>false
});
machine.execute(&Simple::Commands::Next).unwrap();
assert!(match machine.get_current_state(){
// We are in state B and have counter == 3. Increment happen on User Code execution and execution of Leave state callback.
States::B{context}=> {println!("context counter: {}", context.counter);if context.counter == 3 {true} else {false}},
_=>false
});
machine.execute(&Simple::Commands::Next).unwrap();
assert!(match machine.get_current_state(){
// Again in state A and have counter == 5. Increment happen on User Code execution and on state A enter.
States::A{context}=> if context.counter == 5 {true} else {false},
_=>false
});
}
机器作用域上下文的示例。此上下文存在于机器的生命周期中。
让我们计算机器的状态改变次数
#[macro_use] extern crate macro_machine;
declare_machine!(
Simple machine_context{counter: i16} (A) // Declare machine scoped context
states[A,B]
commands[Next]
(A :
>> {machine_context.counter=machine_context.counter+1;} // Add 1 when enter in state
Next => B; // Just switch to other state
)
(B :
>> {machine_context.counter=machine_context.counter+1;}
Next => A;
)
);
fn main() {
use Simple::*;
let mut machine = Simple::new(0); // Create machine and initiate machine context by 0
let context = machine.get_inner_context();
assert!(context.counter == 1);
machine.execute(&Simple::Commands::Next).unwrap();
let context = machine.get_inner_context();
assert!(context.counter == 2);
machine.execute(&Simple::Commands::Next).unwrap();
let context = machine.get_inner_context();
assert!(context.counter == 3);
}
变更日志
0.2.0
- 修改了Leave动作的行为。现在它在创建新的状态上下文之前执行。
- 添加了机器作用域上下文。它可以由机器内的所有回调使用。此上下文中的数据具有机器的生命周期。