|
|
|
@ -20,6 +20,7 @@ enum Token {
|
|
|
|
|
|
|
|
|
|
impl Token {
|
|
|
|
|
fn print(&self) -> &str {
|
|
|
|
|
use Token::*;
|
|
|
|
|
match &self {
|
|
|
|
|
Keyword(keyword) => keyword,
|
|
|
|
|
Str(_) => "String",
|
|
|
|
@ -30,11 +31,10 @@ impl Token {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
use Token::*;
|
|
|
|
|
|
|
|
|
|
// Indented command block
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct CommandBlock {
|
|
|
|
|
control: Option<Control>,
|
|
|
|
|
elements: Vec<BlockElement>,
|
|
|
|
|
next: Option<usize>,
|
|
|
|
|
}
|
|
|
|
@ -45,6 +45,9 @@ impl CommandBlock {
|
|
|
|
|
Some(next) => next,
|
|
|
|
|
None => return None,
|
|
|
|
|
};
|
|
|
|
|
if !self.evaluate_control() {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
let mut result = None;
|
|
|
|
|
let count = self.elements.len();
|
|
|
|
|
for element in &mut self.elements[next..] {
|
|
|
|
@ -74,11 +77,19 @@ impl CommandBlock {
|
|
|
|
|
};
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn evaluate_control(&self) -> bool {
|
|
|
|
|
use Control::*;
|
|
|
|
|
self.control.as_ref().map_or(true, |control| match control {
|
|
|
|
|
If { condition } => *condition,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for CommandBlock {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
control: None,
|
|
|
|
|
elements: Vec::new(),
|
|
|
|
|
next: Some(0),
|
|
|
|
|
}
|
|
|
|
@ -93,6 +104,12 @@ enum BlockElement {
|
|
|
|
|
|
|
|
|
|
type Script = CommandBlock;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
enum Control {
|
|
|
|
|
If { condition: bool },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parsed script commands
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
@ -101,10 +118,9 @@ enum Command {
|
|
|
|
|
Eat { food: String, politely: bool },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
use Command::*;
|
|
|
|
|
|
|
|
|
|
impl Command {
|
|
|
|
|
fn is_blocking(&self) -> bool {
|
|
|
|
|
use Command::*;
|
|
|
|
|
match self {
|
|
|
|
|
Say { .. } => true,
|
|
|
|
|
_ => false,
|
|
|
|
@ -139,22 +155,66 @@ fn parse(script: &str) -> CommandBlock {
|
|
|
|
|
|
|
|
|
|
fn parse_block(block: Pair) -> CommandBlock {
|
|
|
|
|
CommandBlock {
|
|
|
|
|
elements: block.into_inner().filter_map(|pair| match pair.as_rule() {
|
|
|
|
|
Rule::Line => Some(BlockElement::Command(parse_command(pair))),
|
|
|
|
|
Rule::Block => Some(BlockElement::Block(parse_block(pair))),
|
|
|
|
|
Rule::EOI => None, // end
|
|
|
|
|
elements: {
|
|
|
|
|
let mut control = None;
|
|
|
|
|
let mut elements = Vec::new();
|
|
|
|
|
for pair in block.into_inner() {
|
|
|
|
|
elements.push(match pair.as_rule() {
|
|
|
|
|
Rule::Control => {
|
|
|
|
|
if control.is_some() {
|
|
|
|
|
panic!("control statement should not be empty");
|
|
|
|
|
}
|
|
|
|
|
control = Some(parse_control(pair));
|
|
|
|
|
continue;
|
|
|
|
|
},
|
|
|
|
|
Rule::Line => BlockElement::Command(parse_command(pair)),
|
|
|
|
|
Rule::Block => BlockElement::Block({
|
|
|
|
|
let mut block = parse_block(pair);
|
|
|
|
|
if control.is_none() {
|
|
|
|
|
panic!("block should have control");
|
|
|
|
|
}
|
|
|
|
|
block.control = control;
|
|
|
|
|
control = None;
|
|
|
|
|
block
|
|
|
|
|
}),
|
|
|
|
|
Rule::EOI => break, // end
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
|
}).collect(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
elements
|
|
|
|
|
},
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_command(pair: Pair) -> Command {
|
|
|
|
|
use Token::*;
|
|
|
|
|
let line: Vec<Token> = pair
|
|
|
|
|
fn parse_line(pair: Pair) -> Vec<Token> {
|
|
|
|
|
pair
|
|
|
|
|
.into_inner()
|
|
|
|
|
.map(|pair| parse_token(pair))
|
|
|
|
|
.collect();
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_control(pair: Pair) -> Control {
|
|
|
|
|
use Control::*;
|
|
|
|
|
use Token::*;
|
|
|
|
|
let line = parse_line(pair);
|
|
|
|
|
macro_rules! unknown {
|
|
|
|
|
() => {
|
|
|
|
|
panic!("Unknown control {}", describe_line(&line))
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
match line.as_slice() {
|
|
|
|
|
[Keyword(control), Boolean(condition)] if control.eq("if") => If {
|
|
|
|
|
condition: *condition,
|
|
|
|
|
},
|
|
|
|
|
_ => unknown!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_command(pair: Pair) -> Command {
|
|
|
|
|
use Command::*;
|
|
|
|
|
use Token::*;
|
|
|
|
|
let line = parse_line(pair);
|
|
|
|
|
macro_rules! unknown {
|
|
|
|
|
() => {
|
|
|
|
|
panic!("Unknown command {}", describe_line(&line))
|
|
|
|
@ -247,7 +307,7 @@ impl State {
|
|
|
|
|
while let Some(command) = self.next_command() {
|
|
|
|
|
if command.is_blocking() {
|
|
|
|
|
return Some(match command {
|
|
|
|
|
Say { name, text } => Event::Say {
|
|
|
|
|
Command::Say { name, text } => Event::Say {
|
|
|
|
|
name: name.clone(),
|
|
|
|
|
text: text.clone()
|
|
|
|
|
},
|
|
|
|
|