1use parse;
2use std::cmp::PartialEq;
3use std::collections::HashMap;
4use std::ops::{Add, Sub, Mul, Div};
5
6#[derive(Clone, Debug, PartialEq, Eq, Hash)]
9pub struct NodeName(pub String);
10#[derive(Clone, Debug, PartialEq, Eq, Hash)]
11pub struct VariableName(pub String);
12
13struct Variables(HashMap<VariableName, Value>);
14impl Variables {
15 fn set(&mut self, name: VariableName, value: Value) {
16 self.0.insert(name, value);
17 }
18}
19
20#[derive(Debug, PartialEq)]
21pub(crate) struct Choice {
22 text: String,
23 kind: ChoiceKind,
24}
25
26impl Choice {
27 pub(crate) fn external(text: String, name: NodeName) -> Choice {
28 Choice {
29 text: text,
30 kind: ChoiceKind::External(name),
31 }
32 }
33
34 pub(crate) fn inline(text: String, steps: Vec<Step>, condition: Option<Expr>) -> Choice {
35 Choice {
36 text: text,
37 kind: ChoiceKind::Inline(steps, condition),
38 }
39 }
40}
41
42#[derive(Debug, PartialEq)]
43enum ChoiceKind {
44 External(NodeName),
45 Inline(Vec<Step>, Option<Expr>),
46}
47
48#[derive(Debug, PartialEq)]
49pub(crate) enum Step {
50 Dialogue(String, Vec<Choice>),
51 Command(String),
52 Assign(VariableName, Expr),
53 Conditional(Expr, Vec<Step>, Vec<(Expr, Vec<Step>)>, Vec<Step>),
54 Jump(NodeName),
55}
56
57#[derive(Debug, PartialEq)]
58pub(crate) enum Expr {
59 Unary(UnaryOp, Box<Expr>),
60 Binary(BinaryOp, Box<Expr>, Box<Expr>),
61 Term(Term),
62 Parentheses(Box<Expr>),
63}
64
65#[derive(Debug, PartialEq)]
66pub(crate) enum UnaryOp {
67 Not,
68 Negate,
69}
70
71#[derive(Debug, PartialEq)]
72pub(crate) enum BinaryOp {
73 And,
74 Or,
75 Plus,
76 Minus,
77 Multiply,
78 Divide,
79 Equals,
80 NotEquals,
81 GreaterThan,
82 LessThan,
83 GreaterThanEqual,
84 LessThanEqual,
85}
86
87#[derive(Debug, PartialEq)]
88pub(crate) enum Term {
89 Number(f32),
90 Boolean(bool),
91 String(String),
92 Variable(VariableName),
93 Function(String, Vec<Expr>),
94}
95
96#[derive(Debug, PartialEq)]
97pub(crate) struct Node {
98 pub title: NodeName,
99 pub extra: HashMap<String, String>,
100 pub steps: Vec<Step>,
101 pub visited: bool,
102}
103
104struct Conversation {
105 node: NodeName,
106 base_index: usize,
107 indexes: Vec<StepIndex>,
108}
109
110impl Conversation {
111 fn new(node: NodeName) -> Conversation {
112 Conversation {
113 node,
114 base_index: 0,
115 indexes: vec![],
116 }
117 }
118
119 fn reset(&mut self, name: NodeName) {
120 *self = Conversation::new(name);
121 }
122}
123
124#[derive(Copy, Clone)]
125enum StepIndex {
126 Dialogue(usize, usize),
127 If(usize),
128 ElseIf(usize, usize),
129 Else(usize),
130}
131
132impl StepIndex {
133 fn advance(&mut self) {
134 let idx = match *self {
135 StepIndex::Dialogue(_, ref mut idx) |
136 StepIndex::If(ref mut idx) |
137 StepIndex::ElseIf(_, ref mut idx) |
138 StepIndex::Else(ref mut idx) => idx,
139 };
140 *idx += 1;
141 }
142}
143
144#[derive(PartialEq)]
145enum ExecutionStatus {
146 Continue,
147 Halt,
148}
149
150#[derive(Clone)]
152pub enum Value {
153 String(String),
155 Number(f32),
157 Boolean(bool),
159 }
161
162impl PartialEq for Value {
163 fn eq(&self, other: &Value) -> bool {
164 match (self, other) {
165 (&Value::String(ref s1), ref v) => s1 == &v.as_string(),
166 (ref v, &Value::String(ref s2)) => s2 == &v.as_string(),
167 (&Value::Number(f1), ref v) => f1 == v.as_num(),
168 (ref v, &Value::Number(f2)) => f2 == v.as_num(),
169 (&Value::Boolean(b1), &Value::Boolean(b2)) => b1 == b2,
170 }
171 }
172}
173
174impl Add for Value {
175 type Output = Value;
176 fn add(self, other: Value) -> Value {
177 match (self, other) {
178 (Value::String(s1), v) =>
179 Value::String(format!("{}{}", s1, v.as_string())),
180 (v, Value::String(s2)) =>
181 Value::String(format!("{}{}", v.as_string(), s2)),
182 (Value::Number(f1), v) =>
183 Value::Number(f1 + v.as_num()),
184 (v, Value::Number(f2)) =>
185 Value::Number(v.as_num() + f2),
186 (v1, v2) =>
187 Value::Number(v1.as_num() + v2.as_num()),
188 }
189 }
190}
191
192impl Sub for Value {
193 type Output = Value;
194 fn sub(self, other: Value) -> Value {
195 Value::Number(self.as_num() - other.as_num())
196 }
197}
198
199impl Mul for Value {
200 type Output = Value;
201 fn mul(self, other: Value) -> Value {
202 Value::Number(self.as_num() * other.as_num())
203 }
204}
205
206impl Div for Value {
207 type Output = Value;
208 fn div(self, other: Value) -> Value {
209 Value::Number(self.as_num() / other.as_num())
210 }
211}
212
213impl Value {
214 pub fn as_string(&self) -> String {
216 match *self {
217 Value::Boolean(b) => b.to_string(),
218 Value::String(ref s) => (*s).clone(),
219 Value::Number(f) => f.to_string(),
220 }
221 }
222
223 fn as_bool(&self) -> bool {
226 match *self {
227 Value::Boolean(b) => b,
228 Value::String(ref s) => !s.is_empty(),
229 Value::Number(f) => f != 0.0,
230 }
231 }
232
233 fn as_num(&self) -> f32 {
236 match *self {
237 Value::Boolean(b) => b as isize as f32,
238 Value::String(ref _s) => 0.,
239 Value::Number(f) => f,
240 }
241 }
242}
243
244struct Function {
245 num_args: usize,
246 callback: Box<FunctionCallback>,
247}
248
249pub type FunctionCallback = Fn(Vec<Value>, &Nodes) -> Result<Value, ()>;
251
252pub struct YarnEngine {
254 handler: Box<YarnHandler>,
255 state: NodeState,
256 engine_state: EngineState,
257}
258
259struct EngineState {
260 variables: Variables,
261 functions: HashMap<String, Function>,
262}
263
264impl EngineState {
265 fn evaluate(&self, expr: &Expr, state: &Nodes) -> Result<Value, ()> {
266 match expr {
267 Expr::Parentheses(expr) => self.evaluate(expr, state),
268 Expr::Term(Term::Number(f)) => Ok(Value::Number(*f)),
269 Expr::Term(Term::Boolean(b)) => Ok(Value::Boolean(*b)),
270 Expr::Term(Term::String(ref s)) => Ok(Value::String((*s).clone())),
271 Expr::Term(Term::Variable(ref n)) => {
272 self.variables.0.get(n).cloned().ok_or(())
273 }
274 Expr::Term(Term::Function(ref name, ref args)) => {
275 let mut eval_args = vec![];
276 for arg in args {
277 let v = self.evaluate(arg, state)?;
278 eval_args.push(v);
279 }
280 let f = self.functions.get(name).ok_or(())?;
281 if f.num_args != args.len() {
282 return Err(());
283 }
284 (f.callback)(eval_args, state)
285 }
286
287 Expr::Unary(UnaryOp::Not, expr) =>
288 self.evaluate(expr, state).map(|v| Value::Boolean(!v.as_bool())),
289 Expr::Unary(UnaryOp::Negate, expr) =>
290 self.evaluate(expr, state).map(|v| Value::Number(-v.as_num())),
291
292 Expr::Binary(BinaryOp::And, left, right) => {
293 let left = self.evaluate(left, state)?.as_bool();
294 let right = self.evaluate(right, state)?.as_bool();
295 Ok(Value::Boolean(left && right))
296 }
297 Expr::Binary(BinaryOp::Or, left, right) => {
298 let left = self.evaluate(left, state)?.as_bool();
299 let right = self.evaluate(right, state)?.as_bool();
300 Ok(Value::Boolean(left || right))
301 }
302
303 Expr::Binary(BinaryOp::Plus, left, right) => {
304 let left = self.evaluate(left, state)?;
305 let right = self.evaluate(right, state)?;
306 Ok(left + right)
307 }
308 Expr::Binary(BinaryOp::Minus, left, right) => {
309 let left = self.evaluate(left, state)?;
310 let right = self.evaluate(right, state)?;
311 Ok(left - right)
312 }
313 Expr::Binary(BinaryOp::Multiply, left, right) => {
314 let left = self.evaluate(left, state)?;
315 let right = self.evaluate(right, state)?;
316 Ok(left * right)
317 }
318 Expr::Binary(BinaryOp::Divide, left, right) => {
319 let left = self.evaluate(left, state)?;
320 let right = self.evaluate(right, state)?;
321 Ok(left / right)
322 }
323
324 Expr::Binary(BinaryOp::Equals, left, right) => {
325 let left = self.evaluate(left, state)?;
326 let right = self.evaluate(right, state)?;
327 Ok(Value::Boolean(left == right))
328 }
329 Expr::Binary(BinaryOp::NotEquals, left, right) => {
330 let left = self.evaluate(left, state)?;
331 let right = self.evaluate(right, state)?;
332 Ok(Value::Boolean(!(left == right)))
333 }
334
335 Expr::Binary(BinaryOp::GreaterThan, left, right) => {
336 let left = self.evaluate(left, state)?;
337 let right = self.evaluate(right, state)?;
338 Ok(Value::Boolean(left.as_num() > right.as_num()))
339 }
340 Expr::Binary(BinaryOp::GreaterThanEqual, left, right) => {
341 let left = self.evaluate(left, state)?;
342 let right = self.evaluate(right, state)?;
343 Ok(Value::Boolean(left.as_num() >= right.as_num()))
344 }
345 Expr::Binary(BinaryOp::LessThan, left, right) => {
346 let left = self.evaluate(left, state)?;
347 let right = self.evaluate(right, state)?;
348 Ok(Value::Boolean(left.as_num() < right.as_num()))
349 }
350 Expr::Binary(BinaryOp::LessThanEqual, left, right) => {
351 let left = self.evaluate(left, state)?;
352 let right = self.evaluate(right, state)?;
353 Ok(Value::Boolean(left.as_num() <= right.as_num()))
354 }
355 }
356 }
357}
358
359pub struct Nodes(HashMap<NodeName, Node>);
361
362struct NodeState {
363 nodes: Nodes,
364 conversation: Option<Conversation>,
365}
366
367impl NodeState {
368 fn take_conversation(&mut self) -> Conversation {
369 self.conversation.take().expect("missing conversation")
370 }
371
372 fn get_current_step(&self, conversation: &Conversation) -> (&Vec<Step>, usize) {
373 let mut steps = {
374 let current = self.nodes.0.get(&conversation.node).expect("missing node");
375 ¤t.steps
376 };
377 let mut current_step_index = conversation.base_index;
378
379 for index in conversation.indexes.iter() {
380 match (&steps[current_step_index], *index) {
381 (&Step::Dialogue(_, ref choices), StepIndex::Dialogue(choice, step_index)) => {
382 let choice = &choices[choice];
383 match choice.kind {
384 ChoiceKind::Inline(ref choice_steps, _) => {
385 steps = choice_steps;
386 current_step_index = step_index;
387 }
388 ChoiceKind::External(..) => unreachable!(),
389 }
390 }
391 (&Step::Conditional(_, ref if_steps, ..), StepIndex::If(step_index)) => {
392 steps = if_steps;
393 current_step_index = step_index;
394 }
395 (&Step::Conditional(_, _, ref else_ifs, ..), StepIndex::ElseIf(index, step_index)) => {
396 steps = &else_ifs[index].1;
397 current_step_index = step_index;
398 }
399 (&Step::Conditional(_, _, _, ref else_steps), StepIndex::Else(step_index)) => {
400 steps = else_steps;
401 current_step_index = step_index;
402 }
403 _ => unreachable!(),
404 }
405 }
406
407 (steps, current_step_index)
408 }
409}
410
411impl YarnEngine {
412 pub fn new(handler: Box<YarnHandler>) -> YarnEngine {
414 let mut engine = YarnEngine {
415 state: NodeState {
416 nodes: Nodes(HashMap::new()),
417 conversation: None,
418 },
419 engine_state: EngineState {
420 variables: Variables(HashMap::new()),
421 functions: HashMap::new(),
422 },
423 handler,
424 };
425
426 engine.register_function("visited".to_string(), 1, Box::new(|args, state| {
428 match args[0] {
429 Value::String(ref s) => {
430 state
431 .0
432 .get(&NodeName(s.to_string()))
433 .map(|node| Value::Boolean(node.visited)).ok_or(())
434 }
435 _ => return Err(())
436 }
437 }));
438
439 engine
440 }
441
442 pub fn load_from_string(&mut self, s: &str) -> Result<(), ()> {
445 let nodes = parse::parse_nodes_from_string(s)?;
446 for node in nodes {
447 self.state.nodes.0.insert(node.title.clone(), node);
448 }
449 Ok(())
450 }
451
452 pub fn register_function(
454 &mut self,
455 name: String,
456 num_args: usize,
457 callback: Box<FunctionCallback>,
458 ) {
459 self.engine_state.functions.insert(name, Function {
460 num_args: num_args,
461 callback,
462 });
463 }
464
465 pub fn set_variable(
468 &mut self,
469 name: VariableName,
470 value: Value
471 ) {
472 self.engine_state.variables.set(name, value);
473 }
474
475 pub fn activate(&mut self, node: NodeName) {
477 self.state.conversation = Some(Conversation::new(node));
479 self.proceed();
480 }
481
482 pub fn choose(&mut self, choice: usize) {
485 let conversation = {
486 let mut conversation = self.state.take_conversation();
487 let (steps, current_step_index) = self.state.get_current_step(&conversation);
488 match &steps[current_step_index] {
489 &Step::Dialogue(_, ref choices) => {
490 match choices[choice].kind {
491 ChoiceKind::External(ref node) => conversation.reset((*node).clone()),
492 ChoiceKind::Inline(..) => {
493 conversation.indexes.push(StepIndex::Dialogue(choice, 0));
494 }
495 }
496 }
497 &Step::Command(..) | &Step::Assign(..) | &Step::Conditional(..) | &Step::Jump(..) =>
498 unreachable!(),
499 }
500 conversation
501 };
502 self.state.conversation = Some(conversation);
503 self.proceed();
504 }
505
506 pub fn proceed(&mut self) {
508 while self.proceed_one_step() == ExecutionStatus::Continue {
509 }
510 }
511
512 fn do_proceed_one_step(&mut self) -> (Conversation, ExecutionStatus) {
513 let mut conversation = self.state.take_conversation();
514 let (steps, current_step_index) = self.state.get_current_step(&conversation);
515
516 if current_step_index >= steps.len() {
517 self.handler.end_conversation();
518 return (conversation, ExecutionStatus::Halt);
519 }
520
521 let (advance, execution_status) = match steps[current_step_index] {
522 Step::Dialogue(ref text, ref choices) => {
523 if choices.is_empty() {
524 self.handler.say(text.clone());
525 (true, ExecutionStatus::Halt)
526 } else {
527 self.handler.choose(text.clone(), choices.iter().map(|c| c.text.clone()).collect());
529 (false, ExecutionStatus::Halt)
530 }
531 }
532 Step::Command(ref command) => {
533 self.handler.command(command.clone()).unwrap();
534 (true, ExecutionStatus::Continue)
535 }
536 Step::Assign(ref name, ref expr) => {
537 let value = self.engine_state.evaluate(expr, &self.state.nodes).unwrap();
538 self.engine_state.variables.set((*name).clone(), value);
539 (true, ExecutionStatus::Continue)
540 }
541 Step::Conditional(ref expr, ref _if_steps, ref else_ifs, ref _else_steps) => {
542 let value = self.engine_state.evaluate(expr, &self.state.nodes).unwrap();
543 if value.as_bool() {
544 conversation.indexes.push(StepIndex::If(0));
545 } else {
546 let mut matched = false;
547 for (else_if_index, else_ifs) in else_ifs.iter().enumerate() {
548 let value = self.engine_state.evaluate(&else_ifs.0, &self.state.nodes).unwrap();
549 if value.as_bool() {
550 conversation.indexes.push(StepIndex::ElseIf(else_if_index, 0));
551 matched = true;
552 break;
553 }
554 }
555 if !matched {
556 conversation.indexes.push(StepIndex::Else(0));
557 }
558 }
559 (false, ExecutionStatus::Continue)
560 }
561 Step::Jump(ref name) => {
562 conversation.reset((*name).clone());
564 (false, ExecutionStatus::Continue)
565 }
566 };
567
568 if advance {
569 match conversation.indexes.last_mut() {
570 Some(index) => index.advance(),
571 None => conversation.base_index += 1,
572 }
573 }
574
575 (conversation, execution_status)
576 }
577
578 fn proceed_one_step(&mut self) -> ExecutionStatus {
579 let (conversation, execution_status) = self.do_proceed_one_step();
580
581 self.state.conversation = Some(conversation);
582
583 execution_status
584 }
585}
586
587pub trait YarnHandler {
591 fn say(&mut self, text: String);
594 fn choose(&mut self, text: String, choices: Vec<String>);
597 fn command(&mut self, action: String) -> Result<(), ()>;
600 fn end_conversation(&mut self);
603}