From b882ef44c0efa15a9b7e0680ddc5a09e73ca0a2a Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Tue, 30 May 2023 11:23:14 -0700 Subject: [PATCH 1/5] Working let statements --- demo/demo.rpy | 14 +++++++---- renrs/src/parser/block.rs | 49 ++++++++++++++++++------------------- renrs/src/parser/command.rs | 9 +++++++ renrs/src/parser/control.rs | 8 ------ 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/demo/demo.rpy b/demo/demo.rpy index 59e0d74..d22c671 100644 --- a/demo/demo.rpy +++ b/demo/demo.rpy @@ -1,9 +1,13 @@ # Control testing -$ x = "3" -"Bob will be here in [x] seconds." -$ x = "2" -"Bob will be here in [x] seconds." -$ x = "1" +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." "Bob will be here in [x] seconds." "Bob" "I will not say anything, [foo]" define foo = "bar" diff --git a/renrs/src/parser/block.rs b/renrs/src/parser/block.rs index f79ffd4..3ff889d 100644 --- a/renrs/src/parser/block.rs +++ b/renrs/src/parser/block.rs @@ -14,7 +14,7 @@ pub struct CommandBlock { control: Option, elements: Vec, next: Option, - pub variables: Option>>>, + pub variables: Rc>>, } pub struct CommandContext { @@ -46,7 +46,17 @@ impl CommandContext { root.borrow_mut() .variables .as_ref() - .unwrap() + .borrow_mut() + .insert(variable.clone(), value.clone()); + return None; + }, + Let { variable, value } => { + 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; @@ -102,18 +112,13 @@ impl CommandBlock { } pub fn get_variables(&self) -> HashMap { - 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(), + 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 } } @@ -133,7 +138,7 @@ impl Default for CommandBlock { control: None, elements: Vec::new(), next: Some(0), - variables: None, + variables: Rc::new(RefCell::new(HashMap::new())), } } } @@ -152,9 +157,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 { - Some(definitions.clone()) + definitions.clone() } else { - None + Default::default() }, ..Default::default() })); @@ -191,13 +196,7 @@ pub fn parse_block( let subblock_rc = parse_block(pair, Some(definitions.clone())); { let mut subblock = subblock_rc.borrow_mut(); - 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 { + if control.is_none() { panic!("block should have control"); } subblock.parent = Some(block_rc.clone()); @@ -213,7 +212,7 @@ pub fn parse_block( elements }; if is_root { - block.variables = Some(definitions); + block.variables = definitions; } } block_rc diff --git a/renrs/src/parser/command.rs b/renrs/src/parser/command.rs index 16af242..4026d45 100644 --- a/renrs/src/parser/command.rs +++ b/renrs/src/parser/command.rs @@ -11,6 +11,7 @@ pub enum Command { Eat { food: String, politely: bool }, Define { variable: String, value: Token }, Assign { variable: String, value: Token }, + Let { variable: String, value: Token }, } pub fn parse_command(pair: Pair) -> Command { @@ -47,6 +48,14 @@ pub fn parse_command(pair: Pair) -> Command { value: value.clone(), } } + [Keyword(keyword), Keyword(variable), Keyword(equals), value] + if keyword.eq("let") && equals.eq("=") => + { + Let { + 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 deccae2..81811d2 100644 --- a/renrs/src/parser/control.rs +++ b/renrs/src/parser/control.rs @@ -10,14 +10,6 @@ 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::*; From 9686884c6216aaf8c4f11ec1274db70f42eec4cb Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Tue, 30 May 2023 11:25:00 -0700 Subject: [PATCH 2/5] panic for undefined variables --- renrs/src/parser/event.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renrs/src/parser/event.rs b/renrs/src/parser/event.rs index 53abb11..b2344d9 100644 --- a/renrs/src/parser/event.rs +++ b/renrs/src/parser/event.rs @@ -34,7 +34,7 @@ fn interpolate_string(input: &str, variables: &HashMap) -> String if let Some(value) = variables.get(var_name) { value.to_string() } else { - format!("[{}]", var_name) + panic!("undefined variable `{var_name}`"); } }); From c72bd9429178d0b4e304dd863f60827a5ddf5ac4 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Tue, 30 May 2023 12:27:54 -0700 Subject: [PATCH 3/5] Restructure assignment commands into one command --- renrs/src/parser/block.rs | 71 +++++++++++++++++++++---------------- renrs/src/parser/command.rs | 65 +++++++++++++++++++++------------ renrs/src/parser/token.rs | 2 +- 3 files changed, 84 insertions(+), 54 deletions(-) diff --git a/renrs/src/parser/block.rs b/renrs/src/parser/block.rs index 3ff889d..0f14df5 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, Command}, + command::{parse_command, Command, AssignType}, control::{parse_control, Control}, event::Event, token::Token, @@ -25,41 +25,50 @@ pub struct CommandContext { impl CommandContext { pub fn execute(&self) -> Option { use Command::*; + use AssignType::*; Some( match &self.command { Say { name, text } => Event::Say { name: name.clone(), text: text.clone(), }, - 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 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 { variable, value } => { - 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; + 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, } @@ -178,7 +187,7 @@ pub fn parse_block( continue; } Rule::Line => BlockElement::Command(match parse_command(pair) { - Command::Define { variable, value } => { + Command::Assign { variable, value, assign_type: AssignType::Define } => { let mut value = value; if let Token::Keyword(keyword) = value { value = definitions diff --git a/renrs/src/parser/command.rs b/renrs/src/parser/command.rs index 4026d45..99c886e 100644 --- a/renrs/src/parser/command.rs +++ b/renrs/src/parser/command.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use super::{ token::Token, utils::{describe_line, parse_line}, @@ -7,11 +9,41 @@ use super::{ #[derive(Debug, Clone)] #[allow(dead_code)] pub enum Command { - Say { name: Option, text: String }, - Eat { food: String, politely: bool }, - Define { variable: String, value: Token }, - Assign { variable: String, value: Token }, - Let { variable: String, value: Token }, + 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(()), + }) + } } pub fn parse_command(pair: Pair) -> Command { @@ -32,26 +64,15 @@ 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 keyword.eq("define") && equals.eq("=") => - { - Define { - variable: variable.clone(), - value: value.clone(), - } - } - [Keyword(keyword), Keyword(variable), Keyword(equals), value] - if keyword.eq("$") && equals.eq("=") => + if equals.eq("=") => { Assign { - variable: variable.clone(), - value: value.clone(), - } - } - [Keyword(keyword), Keyword(variable), Keyword(equals), value] - if keyword.eq("let") && equals.eq("=") => - { - Let { + assign_type: match keyword.parse() { + Ok(assign_type) => assign_type, + Err(_) => unknown!(), + }, variable: variable.clone(), value: value.clone(), } diff --git a/renrs/src/parser/token.rs b/renrs/src/parser/token.rs index 32660f5..ee58af4 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!(), } } From 683917dd7484e3e505deacbc0ff05f43e087cc2c Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Tue, 30 May 2023 12:34:43 -0700 Subject: [PATCH 4/5] Clear clippy warnings --- renrs-gui/src/lib.rs | 4 ++-- renrs/src/lib.rs | 16 +++++++++------- renrs/src/parser/block.rs | 6 +++--- renrs/src/parser/event.rs | 4 ++-- renrs/src/parser/utils.rs | 6 +++--- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/renrs-gui/src/lib.rs b/renrs-gui/src/lib.rs index 13d50ae..c41e8d3 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 x >= WIDTH - SIZE || x < 0.0 { + if !(0.0..WIDTH - SIZE).contains(&x) { velocity.x *= -1.0; } - if y >= HEIGHT - SIZE || y < 0.0 { + if !(0.0..HEIGHT - SIZE).contains(&y) { 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 5be83e7..ffff5ec 100644 --- a/renrs/src/lib.rs +++ b/renrs/src/lib.rs @@ -18,17 +18,19 @@ impl State { } } - pub fn next(&mut self) -> Option { + 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 { while let Some(command) = self.next_command() { let event = command.execute(); - if let Some(_) = event { + if event.is_some() { return event; } } None } - - fn next_command(&mut self) -> Option { - self.script.borrow_mut().next(&self.script) - } -} +} \ No newline at end of file diff --git a/renrs/src/parser/block.rs b/renrs/src/parser/block.rs index 0f14df5..5dab512 100644 --- a/renrs/src/parser/block.rs +++ b/renrs/src/parser/block.rs @@ -78,7 +78,7 @@ impl CommandContext { } impl CommandBlock { - pub fn next<'a>(&mut self, self_rc: &Rc>) -> Option { + pub fn next(&mut self, self_rc: &Rc>) -> Option { let mut next = match self.next { Some(next) => next, None => return None, @@ -98,7 +98,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; @@ -133,7 +133,7 @@ impl CommandBlock { 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() } diff --git a/renrs/src/parser/event.rs b/renrs/src/parser/event.rs index b2344d9..5cfbf90 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 diff --git a/renrs/src/parser/utils.rs b/renrs/src/parser/utils.rs index 395257c..0f230d0 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(|pair| parse_token(pair)).collect() + pair.into_inner().map(parse_token).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(&format!("{}", iter.next().unwrap().print())); + description.push_str(iter.next().unwrap().print()); for token in iter { description.push_str(&format!(", {}", token.print())); } - description.push_str("]"); + description.push(']'); description } From 149842c121851ad6bc9e005ade264ccc37b6ddbf Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Tue, 30 May 2023 12:34:50 -0700 Subject: [PATCH 5/5] cargo fmt --- renrs/src/lib.rs | 2 +- renrs/src/parser/block.rs | 24 ++++++++++++++++-------- renrs/src/parser/command.rs | 22 +++++++++------------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/renrs/src/lib.rs b/renrs/src/lib.rs index ffff5ec..4824cf7 100644 --- a/renrs/src/lib.rs +++ b/renrs/src/lib.rs @@ -33,4 +33,4 @@ impl Iterator for State { } None } -} \ No newline at end of file +} diff --git a/renrs/src/parser/block.rs b/renrs/src/parser/block.rs index 5dab512..e355b25 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, Command, AssignType}, + command::{parse_command, AssignType, Command}, control::{parse_control, Control}, event::Event, token::Token, @@ -24,15 +24,19 @@ pub struct CommandContext { impl CommandContext { pub fn execute(&self) -> Option { - use Command::*; 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 { + 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 { @@ -51,7 +55,7 @@ impl CommandContext { .borrow_mut() .insert(variable.clone(), value.clone()); return None; - }, + } Let => { if let Token::Keyword(_) = value { todo!("assignment variable interpolation isn't implemented"); @@ -62,14 +66,14 @@ impl CommandContext { .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), @@ -187,7 +191,11 @@ pub fn parse_block( continue; } Rule::Line => BlockElement::Command(match parse_command(pair) { - Command::Assign { variable, value, assign_type: AssignType::Define } => { + Command::Assign { + variable, + value, + assign_type: AssignType::Define, + } => { let mut value = value; if let Token::Keyword(keyword) = value { value = definitions diff --git a/renrs/src/parser/command.rs b/renrs/src/parser/command.rs index 99c886e..0c3cba6 100644 --- a/renrs/src/parser/command.rs +++ b/renrs/src/parser/command.rs @@ -64,19 +64,15 @@ 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(), - } - } + // 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), Str(food), tail @ ..] if keyword.eq("eat") => Eat { food: food.to_owned(), politely: match tail {