diff --git a/demo/demo.rpy b/demo/demo.rpy index d22c671..59e0d74 100644 --- a/demo/demo.rpy +++ b/demo/demo.rpy @@ -1,13 +1,9 @@ # Control testing -if True: - let x = "3" - "Bob will be here in [x] seconds." - let x = "2" - "Bob will be here in [x] seconds." - if True: - let x = "213" - let x = "1" - "Bob will be here in [x] seconds." +$ x = "3" +"Bob will be here in [x] seconds." +$ x = "2" +"Bob will be here in [x] seconds." +$ x = "1" "Bob will be here in [x] seconds." "Bob" "I will not say anything, [foo]" define foo = "bar" diff --git a/renrs-gui/src/lib.rs b/renrs-gui/src/lib.rs index c41e8d3..13d50ae 100644 --- a/renrs-gui/src/lib.rs +++ b/renrs-gui/src/lib.rs @@ -163,10 +163,10 @@ impl App { } let Vector2f { x, y } = shape.position(); - if !(0.0..WIDTH - SIZE).contains(&x) { + if x >= WIDTH - SIZE || x < 0.0 { velocity.x *= -1.0; } - if !(0.0..HEIGHT - SIZE).contains(&y) { + if y >= HEIGHT - SIZE || y < 0.0 { velocity.y *= -1.0; } shape.set_position(Vector2f::new(x + velocity.x, y + velocity.y)); diff --git a/renrs/src/lib.rs b/renrs/src/lib.rs index 4824cf7..5be83e7 100644 --- a/renrs/src/lib.rs +++ b/renrs/src/lib.rs @@ -18,19 +18,17 @@ impl State { } } - fn next_command(&mut self) -> Option { - self.script.borrow_mut().next(&self.script) - } -} -impl Iterator for State { - type Item = Event; - fn next(&mut self) -> Option { + pub fn next(&mut self) -> Option { while let Some(command) = self.next_command() { let event = command.execute(); - if event.is_some() { + if let Some(_) = event { return event; } } None } + + fn next_command(&mut self) -> Option { + self.script.borrow_mut().next(&self.script) + } } diff --git a/renrs/src/parser/block.rs b/renrs/src/parser/block.rs index e355b25..f79ffd4 100644 --- a/renrs/src/parser/block.rs +++ b/renrs/src/parser/block.rs @@ -2,7 +2,7 @@ use debug_cell::RefCell; use std::{collections::HashMap, rc::Rc}; use super::{ - command::{parse_command, AssignType, Command}, + command::{parse_command, Command}, control::{parse_control, Control}, event::Event, token::Token, @@ -14,7 +14,7 @@ pub struct CommandBlock { control: Option, elements: Vec, next: Option, - pub variables: Rc>>, + pub variables: Option>>>, } pub struct CommandContext { @@ -24,7 +24,6 @@ pub struct CommandContext { impl CommandContext { pub fn execute(&self) -> Option { - use AssignType::*; use Command::*; Some( match &self.command { @@ -32,48 +31,26 @@ impl CommandContext { 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; + Define { .. } => panic!("define command should not be executed at runtime!"), + Assign { variable, value } => { + if let Token::Keyword(_) = value { + todo!("assignment variable interpolation isn't implemented"); } - 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") - } - }, + 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() + .unwrap() + .borrow_mut() + .insert(variable.clone(), value.clone()); + return None; + } Eat { .. } => return None, } .process(&self.context), @@ -82,7 +59,7 @@ impl CommandContext { } impl CommandBlock { - pub fn next(&mut self, self_rc: &Rc>) -> Option { + pub fn next<'a>(&mut self, self_rc: &Rc>) -> Option { let mut next = match self.next { Some(next) => next, None => return None, @@ -102,7 +79,7 @@ impl CommandBlock { next += 1; break; } - BlockElement::Block(block) => match block.borrow_mut().next(block) { + BlockElement::Block(block) => match block.borrow_mut().next(&block) { Some(context_command) => { result = Some(context_command); break; @@ -125,19 +102,24 @@ impl CommandBlock { } pub fn get_variables(&self) -> HashMap { - 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 + match &self.variables { + Some(variables) => { + let variables = 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 + } + } + None => HashMap::new(), } } pub fn get_root(&self, self_rc: &Rc>) -> Rc> { if let Some(parent) = &self.parent { - parent.borrow().get_root(parent) + parent.borrow().get_root(&parent) } else { self_rc.clone() } @@ -151,7 +133,7 @@ impl Default for CommandBlock { control: None, elements: Vec::new(), next: Some(0), - variables: Rc::new(RefCell::new(HashMap::new())), + variables: None, } } } @@ -170,9 +152,9 @@ pub fn parse_block( 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() + Some(definitions.clone()) } else { - Default::default() + None }, ..Default::default() })); @@ -191,11 +173,7 @@ pub fn parse_block( continue; } Rule::Line => BlockElement::Command(match parse_command(pair) { - Command::Assign { - variable, - value, - assign_type: AssignType::Define, - } => { + Command::Define { variable, value } => { let mut value = value; if let Token::Keyword(keyword) = value { value = definitions @@ -213,7 +191,13 @@ pub fn parse_block( let subblock_rc = parse_block(pair, Some(definitions.clone())); { let mut subblock = subblock_rc.borrow_mut(); - if control.is_none() { + if let Some(control) = control.as_ref() { + if control.has_variable_scope() { + // TODO: Sublock-scoped variables + subblock.variables = + Some(Rc::new(RefCell::new(HashMap::new()))); + } + } else { panic!("block should have control"); } subblock.parent = Some(block_rc.clone()); @@ -229,7 +213,7 @@ pub fn parse_block( elements }; if is_root { - block.variables = definitions; + block.variables = Some(definitions); } } block_rc diff --git a/renrs/src/parser/command.rs b/renrs/src/parser/command.rs index 0c3cba6..16af242 100644 --- a/renrs/src/parser/command.rs +++ b/renrs/src/parser/command.rs @@ -1,5 +1,3 @@ -use std::str::FromStr; - use super::{ token::Token, utils::{describe_line, parse_line}, @@ -9,41 +7,10 @@ use super::{ #[derive(Debug, Clone)] #[allow(dead_code)] pub enum Command { - Say { - name: Option, - text: String, - }, - Eat { - food: String, - politely: bool, - }, - Assign { - assign_type: AssignType, - variable: String, - value: Token, - }, -} - -#[derive(Debug, Clone)] -pub enum AssignType { - Define, - GlobalAssign, - Let, - Assign, -} - -impl FromStr for AssignType { - type Err = (); - - fn from_str(s: &str) -> Result { - use AssignType::*; - Ok(match s { - "define" => Define, - "$" => GlobalAssign, - "let" => Let, - _ => return Err(()), - }) - } + Say { name: Option, text: String }, + Eat { food: String, politely: bool }, + Define { variable: String, value: Token }, + Assign { variable: String, value: Token }, } pub fn parse_command(pair: Pair) -> Command { @@ -64,15 +31,22 @@ pub fn parse_command(pair: Pair) -> Command { name: Some(name.clone()), text: text.clone(), }, - // https://github.com/rust-lang/rust/issues/51114 - [Keyword(keyword), Keyword(variable), Keyword(equals), value] if equals.eq("=") => Assign { - assign_type: match keyword.parse() { - Ok(assign_type) => assign_type, - Err(_) => unknown!(), - }, - variable: variable.clone(), - value: value.clone(), - }, + [Keyword(keyword), Keyword(variable), Keyword(equals), value] + if keyword.eq("define") && equals.eq("=") => + { + Define { + variable: variable.clone(), + value: value.clone(), + } + } + [Keyword(keyword), Keyword(variable), Keyword(equals), value] + if keyword.eq("$") && equals.eq("=") => + { + Assign { + variable: variable.clone(), + value: value.clone(), + } + } [Keyword(keyword), Str(food), tail @ ..] if keyword.eq("eat") => Eat { food: food.to_owned(), politely: match tail { diff --git a/renrs/src/parser/control.rs b/renrs/src/parser/control.rs index 81811d2..deccae2 100644 --- a/renrs/src/parser/control.rs +++ b/renrs/src/parser/control.rs @@ -10,6 +10,14 @@ pub enum Control { If { condition: bool }, } +impl Control { + pub fn has_variable_scope(&self) -> bool { + match self { + _ => false, + } + } +} + pub fn parse_control(pair: Pair) -> Control { use Control::*; use Token::*; diff --git a/renrs/src/parser/event.rs b/renrs/src/parser/event.rs index 5cfbf90..53abb11 100644 --- a/renrs/src/parser/event.rs +++ b/renrs/src/parser/event.rs @@ -19,8 +19,8 @@ impl Event { let variables = context.get_variables(); *name = name .as_deref() - .map(|name| interpolate_string(name, &variables)); - *text = interpolate_string(text, &variables); + .map(|name| interpolate_string(&name, &variables)); + *text = interpolate_string(&text, &variables); } } self @@ -34,7 +34,7 @@ fn interpolate_string(input: &str, variables: &HashMap) -> String if let Some(value) = variables.get(var_name) { value.to_string() } else { - panic!("undefined variable `{var_name}`"); + format!("[{}]", var_name) } }); diff --git a/renrs/src/parser/token.rs b/renrs/src/parser/token.rs index ee58af4..32660f5 100644 --- a/renrs/src/parser/token.rs +++ b/renrs/src/parser/token.rs @@ -78,6 +78,6 @@ pub fn parse_token(pair: Pair) -> Token { }), Rule::Number => Token::Number(pair.as_str().parse().unwrap()), Rule::Keyword => Token::Keyword(pair.as_str().to_owned()), - _ => unreachable!(), + __ => unreachable!(), } } diff --git a/renrs/src/parser/utils.rs b/renrs/src/parser/utils.rs index 0f230d0..395257c 100644 --- a/renrs/src/parser/utils.rs +++ b/renrs/src/parser/utils.rs @@ -4,7 +4,7 @@ use super::{ }; pub fn parse_line(pair: Pair) -> Vec { - pair.into_inner().map(parse_token).collect() + pair.into_inner().map(|pair| parse_token(pair)).collect() } // Line description e.g. [String, Keyword, Array] @@ -12,10 +12,10 @@ pub fn parse_line(pair: Pair) -> Vec { pub fn describe_line(line: &[Token]) -> String { let mut description = "[".to_owned(); let mut iter = line.iter(); - description.push_str(iter.next().unwrap().print()); + description.push_str(&format!("{}", iter.next().unwrap().print())); for token in iter { description.push_str(&format!(", {}", token.print())); } - description.push(']'); + description.push_str("]"); description }