Implement rudimentary control flow

main
Elnu 2 years ago
parent c0c8153cbe
commit abc17c7c3c

@ -1,13 +1,14 @@
# 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."
"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!"

@ -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…
Cancel
Save