generated from ElnuDev/rust-project
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
237 lines
8.0 KiB
237 lines
8.0 KiB
use debug_cell::RefCell;
|
|
use std::{collections::HashMap, rc::Rc};
|
|
|
|
use super::{
|
|
command::{parse_command, AssignType, Command},
|
|
control::{parse_control, Control},
|
|
event::Event,
|
|
token::Token,
|
|
Pair, Rule,
|
|
};
|
|
|
|
pub struct CommandBlock {
|
|
parent: Option<Rc<RefCell<CommandBlock>>>,
|
|
control: Option<Control>,
|
|
elements: Vec<BlockElement>,
|
|
next: Option<usize>,
|
|
pub variables: Rc<RefCell<HashMap<String, Token>>>,
|
|
}
|
|
|
|
pub struct CommandContext {
|
|
pub context: Rc<RefCell<CommandBlock>>,
|
|
pub command: Command,
|
|
}
|
|
|
|
impl CommandContext {
|
|
pub fn execute(&self) -> Option<Event> {
|
|
use AssignType::*;
|
|
use Command::*;
|
|
Some(
|
|
match &self.command {
|
|
Say { name, text } => Event::Say {
|
|
name: name.clone(),
|
|
text: text.clone(),
|
|
},
|
|
Command::Assign {
|
|
assign_type,
|
|
variable,
|
|
value,
|
|
} => match assign_type {
|
|
Define => panic!("define command should not be executed at runtime!"),
|
|
GlobalAssign => {
|
|
if let Token::Keyword(_) = value {
|
|
todo!("assignment variable interpolation isn't implemented");
|
|
}
|
|
let root = self.context.borrow().get_root(&self.context);
|
|
// Don't want to re-borrow if root is self
|
|
let root = if Rc::ptr_eq(&self.context, &root) {
|
|
&self.context
|
|
} else {
|
|
&root
|
|
};
|
|
root.borrow_mut()
|
|
.variables
|
|
.as_ref()
|
|
.borrow_mut()
|
|
.insert(variable.clone(), value.clone());
|
|
return None;
|
|
}
|
|
Let => {
|
|
if let Token::Keyword(_) = value {
|
|
todo!("assignment variable interpolation isn't implemented");
|
|
}
|
|
self.context
|
|
.borrow()
|
|
.variables
|
|
.borrow_mut()
|
|
.insert(variable.clone(), value.clone());
|
|
return None;
|
|
}
|
|
Assign => {
|
|
if let Token::Keyword(_) = value {
|
|
todo!("assignment variable interpolation isn't implemented");
|
|
}
|
|
todo!("local variable assignment isn't implemented")
|
|
}
|
|
},
|
|
Eat { .. } => return None,
|
|
}
|
|
.process(&self.context),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl CommandBlock {
|
|
pub fn next(&mut self, self_rc: &Rc<RefCell<Self>>) -> Option<CommandContext> {
|
|
let mut next = match self.next {
|
|
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..] {
|
|
match element {
|
|
BlockElement::Command(command) => {
|
|
result = Some(CommandContext {
|
|
context: self_rc.clone(),
|
|
command: command.clone(),
|
|
});
|
|
next += 1;
|
|
break;
|
|
}
|
|
BlockElement::Block(block) => match block.borrow_mut().next(block) {
|
|
Some(context_command) => {
|
|
result = Some(context_command);
|
|
break;
|
|
}
|
|
None => {
|
|
next += 1;
|
|
}
|
|
},
|
|
};
|
|
}
|
|
self.next = if count >= next { Some(next) } else { None };
|
|
result
|
|
}
|
|
|
|
fn evaluate_control(&self) -> bool {
|
|
use Control::*;
|
|
self.control.as_ref().map_or(true, |control| match control {
|
|
If { condition } => *condition,
|
|
})
|
|
}
|
|
|
|
pub fn get_variables(&self) -> HashMap<String, Token> {
|
|
let variables = self.variables.borrow().clone();
|
|
if let Some(parent) = &self.parent {
|
|
let mut parent_variables = parent.borrow().get_variables();
|
|
parent_variables.extend(variables.into_iter());
|
|
parent_variables
|
|
} else {
|
|
variables
|
|
}
|
|
}
|
|
|
|
pub fn get_root(&self, self_rc: &Rc<RefCell<Self>>) -> Rc<RefCell<Self>> {
|
|
if let Some(parent) = &self.parent {
|
|
parent.borrow().get_root(parent)
|
|
} else {
|
|
self_rc.clone()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for CommandBlock {
|
|
fn default() -> Self {
|
|
Self {
|
|
parent: None,
|
|
control: None,
|
|
elements: Vec::new(),
|
|
next: Some(0),
|
|
variables: Rc::new(RefCell::new(HashMap::new())),
|
|
}
|
|
}
|
|
}
|
|
|
|
enum BlockElement {
|
|
Command(Command),
|
|
Block(Rc<RefCell<CommandBlock>>),
|
|
}
|
|
|
|
pub fn parse_block(
|
|
pair: Pair,
|
|
definitions: Option<Rc<RefCell<HashMap<String, Token>>>>,
|
|
) -> Rc<RefCell<CommandBlock>> {
|
|
//let variables: HashMap<String, Token> = HashMap::new();
|
|
let is_root = definitions.is_none();
|
|
let definitions = definitions.unwrap_or(Rc::new(RefCell::new(HashMap::new())));
|
|
let block_rc = Rc::new(RefCell::new(CommandBlock {
|
|
variables: if is_root {
|
|
definitions.clone()
|
|
} else {
|
|
Default::default()
|
|
},
|
|
..Default::default()
|
|
}));
|
|
{
|
|
let mut block = block_rc.borrow_mut();
|
|
block.elements = {
|
|
let mut control = None;
|
|
let mut elements = Vec::new();
|
|
for pair in pair.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(match parse_command(pair) {
|
|
Command::Assign {
|
|
variable,
|
|
value,
|
|
assign_type: AssignType::Define,
|
|
} => {
|
|
let mut value = value;
|
|
if let Token::Keyword(keyword) = value {
|
|
value = definitions
|
|
.borrow()
|
|
.get(&keyword)
|
|
.unwrap_or_else(|| panic!("undefined variable `{keyword}`"))
|
|
.clone();
|
|
}
|
|
definitions.borrow_mut().insert(variable, value);
|
|
continue;
|
|
}
|
|
command => command,
|
|
}),
|
|
Rule::Block => BlockElement::Block({
|
|
let subblock_rc = parse_block(pair, Some(definitions.clone()));
|
|
{
|
|
let mut subblock = subblock_rc.borrow_mut();
|
|
if control.is_none() {
|
|
panic!("block should have control");
|
|
}
|
|
subblock.parent = Some(block_rc.clone());
|
|
subblock.control = control;
|
|
control = None;
|
|
}
|
|
subblock_rc
|
|
}),
|
|
Rule::EOI => break, // end
|
|
_ => unreachable!(),
|
|
});
|
|
}
|
|
elements
|
|
};
|
|
if is_root {
|
|
block.variables = definitions;
|
|
}
|
|
}
|
|
block_rc
|
|
}
|