generated from ElnuDev/rust-project
Implement rudimentary control flow
This commit is contained in:
parent
c0c8153cbe
commit
abc17c7c3c
3 changed files with 93 additions and 30 deletions
|
@ -1,13 +1,14 @@
|
|||
"Bob sat on the bench."
|
||||
"Bob" "Good morning!"
|
||||
"Bob" "I am in a block now!"
|
||||
"Bob" "Isn't this cool?"
|
||||
"Bob" "And now we're back in normal indentation."
|
||||
"Bob" "We can even go multiple levels in!"
|
||||
"Bob" "How far does the rabbit hole go?"
|
||||
"Bob" "rabbit hole go?"
|
||||
"Bob" "go?"
|
||||
"Bob" "Not sure what came of me there."
|
||||
"Bob" "I suppose I should eat a potato."
|
||||
eat "potato" True
|
||||
"Bob" "Yum!"
|
||||
# Control testing
|
||||
"Bob" "I will not say anything"
|
||||
if False:
|
||||
"Bob" "W-what? Why am I saying this?"
|
||||
if True:
|
||||
"Testing 123"
|
||||
"..."
|
||||
"Bob" "Good."
|
||||
"Bob" "Now I will tell you my favorite food..."
|
||||
if True:
|
||||
"Bob" "...cereal!"
|
||||
if False:
|
||||
"Bob" "But I actually hate it."
|
||||
"Bob sat on the bench."
|
|
@ -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
|
||||
_ => unreachable!(),
|
||||
}).collect(),
|
||||
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!(),
|
||||
});
|
||||
}
|
||||
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()
|
||||
},
|
||||
|
|
|
@ -43,12 +43,14 @@ Number = @{
|
|||
|
||||
// comments are a # followed by
|
||||
// any Number of non-newline characters
|
||||
// TODO: Inline comments broken by no implicit whitespace
|
||||
COMMENT = _{ "#" ~ char* }
|
||||
|
||||
Colon = { ":" }
|
||||
|
||||
// lines are comprised of a statement
|
||||
Line = @{ (token ~ whitespace+)* ~ token ~ Colon? }
|
||||
Line = @{ (token ~ whitespace+)* ~ token }
|
||||
Control = @{ Line ~ Colon }
|
||||
|
||||
File = { SOI ~ NEWLINE* ~ block_content* ~ NEWLINE* ~ EOI }
|
||||
|
||||
|
@ -66,5 +68,5 @@ Block = {
|
|||
whitespace = _{ " " }
|
||||
|
||||
block_content = _{
|
||||
Line ~ (whitespace+ ~ Line)* ~ (NEWLINE | EOI) ~ Block*
|
||||
(Control | Line) ~ (whitespace+ ~ Line)* ~ (NEWLINE | EOI) ~ Block*
|
||||
}
|
Loading…
Add table
Reference in a new issue