2022-10-10 20:55:50 +00:00
|
|
|
use crate::arguments;
|
2022-10-08 10:01:28 +00:00
|
|
|
use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind};
|
2022-10-12 21:21:21 +00:00
|
|
|
use std::io::{Write};
|
2022-10-08 20:11:13 +00:00
|
|
|
use std::{char, usize, vec};
|
2022-10-12 21:21:21 +00:00
|
|
|
use crate::bf_interpreter::cell::Cell;
|
2022-10-07 18:17:21 +00:00
|
|
|
|
2022-10-12 16:31:07 +00:00
|
|
|
pub struct Interpreter {
|
2022-10-12 21:21:21 +00:00
|
|
|
pub cells: Vec<Cell>,
|
2022-10-07 19:54:49 +00:00
|
|
|
pub pointer: usize,
|
2022-10-10 20:55:50 +00:00
|
|
|
pub bf_commands: Vec<BfCommand>,
|
2022-10-07 21:40:56 +00:00
|
|
|
brackets: Vec<BfCommand>,
|
2022-10-07 19:54:49 +00:00
|
|
|
pub features: Vec<arguments::Feature>,
|
2022-10-12 21:21:21 +00:00
|
|
|
term: console::Term,
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
|
2022-10-12 16:31:07 +00:00
|
|
|
impl Interpreter {
|
2022-10-12 21:21:21 +00:00
|
|
|
pub fn new(
|
|
|
|
array_size: usize,
|
|
|
|
features: Vec<arguments::Feature>,
|
|
|
|
term: &console::Term,
|
|
|
|
) -> Self {
|
2022-10-07 19:54:49 +00:00
|
|
|
Self {
|
2022-10-12 21:21:21 +00:00
|
|
|
cells: vec![Cell::default_cell(&features); array_size],
|
2022-10-07 19:54:49 +00:00
|
|
|
pointer: 0,
|
2022-10-10 20:55:50 +00:00
|
|
|
bf_commands: vec![],
|
2022-10-07 19:54:49 +00:00
|
|
|
brackets: Vec::new(),
|
|
|
|
features,
|
2022-10-12 21:21:21 +00:00
|
|
|
term: term.clone(),
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-10 20:55:50 +00:00
|
|
|
pub fn run(&mut self, bf_code: String) -> Result<i32, InterpreterError> {
|
|
|
|
self.bf_commands = to_bf_commands(bf_code.chars().collect())?;
|
2022-10-07 19:54:49 +00:00
|
|
|
|
2022-10-10 20:55:50 +00:00
|
|
|
match self.run_brainfuck_code(&self.bf_commands.clone()) {
|
2022-10-07 21:40:56 +00:00
|
|
|
Ok(_) => Ok(0),
|
2022-10-07 23:06:35 +00:00
|
|
|
Err(e) => Err(e),
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-07 19:54:49 +00:00
|
|
|
|
2022-10-07 21:40:56 +00:00
|
|
|
// +[>++<-]
|
2022-10-10 20:55:50 +00:00
|
|
|
fn iterate(&mut self, code: &Vec<BfCommand>) -> Result<(), InterpreterError> {
|
2022-10-08 20:11:13 +00:00
|
|
|
trace!("Iterate: {:?}", code);
|
2022-10-12 21:21:21 +00:00
|
|
|
while self.cells[self.pointer].get_value_utf8() != 0 {
|
2022-10-10 20:55:50 +00:00
|
|
|
self.run_brainfuck_code(code)?;
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-10-10 20:55:50 +00:00
|
|
|
fn run_brainfuck_code(&mut self, bf_code: &Vec<BfCommand>) -> Result<(), InterpreterError> {
|
|
|
|
for command in bf_code {
|
|
|
|
match command {
|
|
|
|
BfCommand::IncPtr => self.increment_pointer()?,
|
|
|
|
BfCommand::DecPtr => self.decrement_pointer()?,
|
|
|
|
BfCommand::IncVal => self.increment_value()?,
|
|
|
|
BfCommand::DecVal => self.decrement_value()?,
|
|
|
|
BfCommand::Print => self.output_value()?,
|
|
|
|
BfCommand::Read => self.input_value()?,
|
|
|
|
BfCommand::Loop(loop_body) => self.iterate(loop_body)?,
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-10 20:55:50 +00:00
|
|
|
|
2022-10-07 21:40:56 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-10-10 20:55:50 +00:00
|
|
|
fn increment_pointer(&mut self) -> Result<(), InterpreterError> {
|
|
|
|
trace!("Increment pointer");
|
|
|
|
self.pointer += 1;
|
2022-10-12 11:56:36 +00:00
|
|
|
if self.pointer >= self.cells.len() {
|
2022-10-10 20:55:50 +00:00
|
|
|
if self.features.contains(&arguments::Feature::ReversePointer) {
|
|
|
|
self.pointer = 0;
|
|
|
|
} else {
|
|
|
|
return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error());
|
2022-10-07 23:06:35 +00:00
|
|
|
}
|
2022-10-10 20:55:50 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn decrement_pointer(&mut self) -> Result<(), InterpreterError> {
|
|
|
|
trace!("Decrement pointer");
|
|
|
|
if self.pointer == 0 {
|
|
|
|
if self.features.contains(&arguments::Feature::ReversePointer) {
|
2022-10-12 11:56:36 +00:00
|
|
|
self.pointer = self.cells.len() - 1;
|
2022-10-10 20:55:50 +00:00
|
|
|
} else {
|
|
|
|
return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error());
|
2022-10-07 23:06:35 +00:00
|
|
|
}
|
2022-10-10 20:55:50 +00:00
|
|
|
} else {
|
|
|
|
self.pointer -= 1;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn increment_value(&mut self) -> Result<(), InterpreterError> {
|
|
|
|
trace!("Increment value");
|
2022-10-12 21:21:21 +00:00
|
|
|
self.cells[self.pointer].increment(
|
|
|
|
!self.features.contains(&arguments::Feature::NoReverseValue))?;
|
2022-10-10 20:55:50 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn decrement_value(&mut self) -> Result<(), InterpreterError> {
|
|
|
|
trace!("Decrement value");
|
2022-10-12 21:21:21 +00:00
|
|
|
self.cells[self.pointer].decrement(
|
|
|
|
!self.features.contains(&arguments::Feature::NoReverseValue))?;
|
2022-10-10 20:55:50 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn output_value(&mut self) -> Result<(), InterpreterError> {
|
|
|
|
trace!("Output value");
|
|
|
|
|
|
|
|
if self.features.contains(&arguments::Feature::AllowUtf8) {
|
2022-10-12 21:21:21 +00:00
|
|
|
let c = char::from_u32(self.cells[self.pointer].get_value_utf8());
|
2022-10-10 20:55:50 +00:00
|
|
|
match c {
|
|
|
|
Some(c) => print!("{}", c),
|
|
|
|
None => return Err(InterpreterErrorKind::InvalidUtf8.to_error()),
|
2022-10-07 23:06:35 +00:00
|
|
|
}
|
2022-10-10 20:55:50 +00:00
|
|
|
} else {
|
2022-10-12 21:21:21 +00:00
|
|
|
print!("{}", self.cells[self.pointer].get_value() as char);
|
2022-10-10 20:55:50 +00:00
|
|
|
}
|
|
|
|
match std::io::stdout().flush() {
|
|
|
|
Ok(_) => Ok(()),
|
|
|
|
Err(e) => Err(InterpreterErrorKind::FlushError(e).to_error()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn input_value(&mut self) -> Result<(), InterpreterError> {
|
|
|
|
trace!("Input value");
|
2022-10-12 21:21:21 +00:00
|
|
|
match self.term.read_char() {
|
|
|
|
Ok(ch) => {
|
|
|
|
if self.features.contains(&arguments::Feature::AllowUtf8) {
|
|
|
|
self.cells[self.pointer].set_value_utf8(ch);
|
|
|
|
} else {
|
|
|
|
self.cells[self.pointer].set_value(ch);
|
|
|
|
}
|
|
|
|
print!("{}", ch);
|
|
|
|
match std::io::stdout().flush() {
|
|
|
|
Ok(_) => Ok(()),
|
|
|
|
Err(e) => Err(InterpreterErrorKind::FlushError(e).to_error()),
|
|
|
|
}
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
2022-10-10 20:55:50 +00:00
|
|
|
Err(e) => Err(InterpreterErrorKind::IoError(e).to_error()),
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-07 22:33:44 +00:00
|
|
|
|
|
|
|
pub fn reset(&mut self) {
|
2022-10-12 21:21:21 +00:00
|
|
|
self.cells = vec![Cell::default_cell(&self.features); self.cells.len()];
|
2022-10-07 22:33:44 +00:00
|
|
|
self.pointer = 0;
|
|
|
|
self.brackets = Vec::new();
|
2022-10-10 20:55:50 +00:00
|
|
|
self.bf_commands = Vec::new();
|
2022-10-07 22:33:44 +00:00
|
|
|
}
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
|
|
|
|
2022-10-10 20:55:50 +00:00
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
|
|
pub enum BfCommand {
|
2022-10-07 19:54:49 +00:00
|
|
|
IncPtr,
|
|
|
|
DecPtr,
|
|
|
|
IncVal,
|
|
|
|
DecVal,
|
|
|
|
Print,
|
|
|
|
Read,
|
2022-10-10 20:55:50 +00:00
|
|
|
Loop(Vec<BfCommand>),
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_bf_commands(bf_code: Vec<char>) -> Result<Vec<BfCommand>, InterpreterError> {
|
|
|
|
let mut bf_commands = Vec::new();
|
|
|
|
let mut i = 0;
|
|
|
|
while i < bf_code.len() {
|
|
|
|
match bf_code[i] {
|
|
|
|
'[' => {
|
|
|
|
let mut bracket_count = 1;
|
|
|
|
let mut j = i + 1;
|
|
|
|
while j < bf_code.len() {
|
|
|
|
match bf_code[j] {
|
|
|
|
'[' => bracket_count += 1,
|
|
|
|
']' => bracket_count -= 1,
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
if bracket_count == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
j += 1;
|
|
|
|
}
|
|
|
|
if bracket_count != 0 {
|
|
|
|
return Err(InterpreterErrorKind::UnmatchedBracket.to_error());
|
|
|
|
}
|
|
|
|
bf_commands.push(BfCommand::Loop(to_bf_commands(bf_code[i + 1..j].to_vec())?));
|
|
|
|
i = j;
|
|
|
|
}
|
2022-10-12 16:59:12 +00:00
|
|
|
_ => match BfCommand::from(bf_code[i]) {
|
|
|
|
Some(command) => bf_commands.push(command),
|
|
|
|
None => (),
|
2022-10-10 20:55:50 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
Ok(bf_commands)
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl BfCommand {
|
2022-10-10 20:55:50 +00:00
|
|
|
fn from(c: char) -> Option<Self> {
|
2022-10-07 19:54:49 +00:00
|
|
|
match c {
|
|
|
|
'>' => Some(BfCommand::IncPtr),
|
|
|
|
'<' => Some(BfCommand::DecPtr),
|
|
|
|
'+' => Some(BfCommand::IncVal),
|
|
|
|
'-' => Some(BfCommand::DecVal),
|
|
|
|
'.' => Some(BfCommand::Print),
|
|
|
|
',' => Some(BfCommand::Read),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
2022-10-07 22:52:43 +00:00
|
|
|
|
2022-10-08 20:11:13 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2022-10-10 20:55:50 +00:00
|
|
|
use crate::utils;
|
2022-10-12 16:59:12 +00:00
|
|
|
use pretty_assertions::assert_eq; // for testing only
|
2022-10-08 20:11:13 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn print_h_combine_repl() {
|
2022-10-13 12:23:46 +00:00
|
|
|
let mut interpreter = Interpreter::new(30000, vec![],
|
2022-10-12 21:21:21 +00:00
|
|
|
&console::Term::stdout());
|
2022-10-08 20:11:13 +00:00
|
|
|
|
2022-10-12 16:59:12 +00:00
|
|
|
assert_eq!(
|
|
|
|
interpreter.run(String::from(">+++++++++[<++++ ++++>-]<.")),
|
|
|
|
Ok(0)
|
|
|
|
);
|
2022-10-08 20:11:13 +00:00
|
|
|
|
2022-10-10 20:55:50 +00:00
|
|
|
println!();
|
2022-10-08 20:11:13 +00:00
|
|
|
}
|
2022-10-10 20:55:50 +00:00
|
|
|
|
2022-10-08 20:11:13 +00:00
|
|
|
#[test]
|
|
|
|
fn print_h_repl() {
|
2022-10-13 12:23:46 +00:00
|
|
|
let mut interpreter = Interpreter::new(30000, vec![],
|
2022-10-12 21:21:21 +00:00
|
|
|
&console::Term::stdout());
|
2022-10-08 20:11:13 +00:00
|
|
|
|
2022-10-10 20:55:50 +00:00
|
|
|
assert_eq!(interpreter.run(String::from(">+++++++++")), Ok(0));
|
|
|
|
assert_eq!(interpreter.run(String::from("[<++++ ++++>-]<.")), Ok(0));
|
2022-10-08 20:11:13 +00:00
|
|
|
|
2022-10-10 20:55:50 +00:00
|
|
|
println!();
|
2022-10-08 20:11:13 +00:00
|
|
|
}
|
|
|
|
|
2022-10-10 20:55:50 +00:00
|
|
|
#[test]
|
|
|
|
fn nested_loop_level_1_combine() {
|
2022-10-13 12:23:46 +00:00
|
|
|
let mut interpreter = Interpreter::new(5, vec![],
|
2022-10-12 21:21:21 +00:00
|
|
|
&console::Term::stdout());
|
2022-10-10 20:55:50 +00:00
|
|
|
|
|
|
|
assert_eq!(interpreter.run(String::from("++[>++[>+<-]<-]")), Ok(0));
|
2022-10-12 21:21:21 +00:00
|
|
|
assert_eq!(interpreter.cells[2], Cell::new(4, &vec![]));
|
2022-10-10 20:55:50 +00:00
|
|
|
|
|
|
|
println!();
|
|
|
|
}
|
|
|
|
|
2022-10-08 20:11:13 +00:00
|
|
|
#[test]
|
|
|
|
fn execute_hello_world_from_file() {
|
2022-10-13 12:23:46 +00:00
|
|
|
let mut interpreter = Interpreter::new(30000, vec![],
|
2022-10-12 21:21:21 +00:00
|
|
|
&console::Term::stdout());
|
2022-10-10 20:55:50 +00:00
|
|
|
|
|
|
|
println!();
|
|
|
|
|
2022-10-12 16:59:12 +00:00
|
|
|
assert_eq!(
|
|
|
|
interpreter.run(utils::read_brainfuck_code(&String::from(
|
|
|
|
"test_code/hello_world.bf"
|
|
|
|
))),
|
|
|
|
Ok(0)
|
|
|
|
);
|
2022-10-08 20:11:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn execute_print_hi_from_file() {
|
2022-10-13 12:23:46 +00:00
|
|
|
let mut interpreter = Interpreter::new(30000, vec![],
|
2022-10-12 21:21:21 +00:00
|
|
|
&console::Term::stdout());
|
2022-10-08 20:11:13 +00:00
|
|
|
|
2022-10-10 20:55:50 +00:00
|
|
|
println!();
|
|
|
|
|
2022-10-12 16:59:12 +00:00
|
|
|
assert_eq!(
|
|
|
|
interpreter.run(utils::read_brainfuck_code(&String::from(
|
|
|
|
"test_code/print_hi.bf"
|
|
|
|
))),
|
|
|
|
Ok(0)
|
|
|
|
);
|
2022-10-08 20:11:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn execute_print_hi_yooo_from_file() {
|
2022-10-13 12:23:46 +00:00
|
|
|
let mut interpreter = Interpreter::new(30000, vec![],
|
2022-10-12 21:21:21 +00:00
|
|
|
&console::Term::stdout());
|
2022-10-08 20:11:13 +00:00
|
|
|
|
2022-10-10 20:55:50 +00:00
|
|
|
println!();
|
|
|
|
|
2022-10-12 16:59:12 +00:00
|
|
|
assert_eq!(
|
|
|
|
interpreter.run(utils::read_brainfuck_code(&String::from(
|
|
|
|
"test_code/print_hi_yooo.bf"
|
|
|
|
))),
|
|
|
|
Ok(0)
|
|
|
|
);
|
2022-10-10 20:55:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn execute_print_my_first_name_from_formatted_file() {
|
2022-10-13 12:23:46 +00:00
|
|
|
let mut interpreter = Interpreter::new(30000, vec![],
|
2022-10-12 21:21:21 +00:00
|
|
|
&console::Term::stdout());
|
2022-10-10 20:55:50 +00:00
|
|
|
|
|
|
|
println!();
|
|
|
|
|
2022-10-12 16:59:12 +00:00
|
|
|
assert_eq!(
|
|
|
|
interpreter.run(utils::read_brainfuck_code(&String::from(
|
|
|
|
"test_code/print_my_first_name_formatted.bf"
|
|
|
|
))),
|
|
|
|
Ok(0)
|
|
|
|
);
|
2022-10-10 20:55:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn execute_print_my_first_name_from_file() {
|
2022-10-13 12:23:46 +00:00
|
|
|
let mut interpreter = Interpreter::new(30000, vec![],
|
2022-10-12 21:21:21 +00:00
|
|
|
&console::Term::stdout());
|
2022-10-10 20:55:50 +00:00
|
|
|
|
|
|
|
println!();
|
|
|
|
|
2022-10-12 16:59:12 +00:00
|
|
|
assert_eq!(
|
|
|
|
interpreter.run(utils::read_brainfuck_code(&String::from(
|
|
|
|
"test_code/print_my_first_name.bf"
|
|
|
|
))),
|
|
|
|
Ok(0)
|
|
|
|
);
|
2022-10-13 12:23:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(interpreter.cells[0], Cell::default_cell(&vec![]));
|
|
|
|
assert_eq!(interpreter.cells[1], Cell::default_cell(&vec![]));
|
|
|
|
assert_eq!(interpreter.cells[2], Cell::new(115, &vec![]));
|
|
|
|
assert_eq!(interpreter.cells[3], Cell::new(96, &vec![]));
|
|
|
|
assert_eq!(interpreter.cells[4], Cell::new(112, &vec![]));
|
|
|
|
assert_eq!(interpreter.cells[5], Cell::new(32, &vec![]));
|
2022-10-10 20:55:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn execute_print_my_first_name_and_last_name_from_formatted_file() {
|
2022-10-13 12:23:46 +00:00
|
|
|
let mut interpreter = Interpreter::new(30000, vec![],
|
2022-10-12 21:21:21 +00:00
|
|
|
&console::Term::stdout());
|
2022-10-10 20:55:50 +00:00
|
|
|
|
|
|
|
println!();
|
|
|
|
|
2022-10-12 16:59:12 +00:00
|
|
|
assert_eq!(
|
|
|
|
interpreter.run(utils::read_brainfuck_code(&String::from(
|
|
|
|
"test_code/print_my_first_name_and_last_name_formatted.bf"
|
|
|
|
))),
|
|
|
|
Ok(0)
|
|
|
|
);
|
2022-10-10 20:55:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn execute_print_my_first_name_and_last_name_from_file() {
|
2022-10-13 12:23:46 +00:00
|
|
|
let mut interpreter = Interpreter::new(30000, vec![],
|
2022-10-12 21:21:21 +00:00
|
|
|
&console::Term::stdout());
|
2022-10-10 20:55:50 +00:00
|
|
|
|
|
|
|
println!();
|
|
|
|
|
2022-10-12 16:59:12 +00:00
|
|
|
assert_eq!(
|
|
|
|
interpreter.run(utils::read_brainfuck_code(&String::from(
|
|
|
|
"test_code/print_my_first_name_and_last_name.bf"
|
|
|
|
))),
|
|
|
|
Ok(0)
|
|
|
|
);
|
2022-10-08 20:11:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reset() {
|
2022-10-13 12:23:46 +00:00
|
|
|
let mut interpreter = Interpreter::new(30000, vec![],
|
2022-10-12 21:21:21 +00:00
|
|
|
&console::Term::stdout());
|
2022-10-08 20:11:13 +00:00
|
|
|
|
2022-10-10 20:55:50 +00:00
|
|
|
assert_eq!(interpreter.run(String::from(">++++")), Ok(0));
|
2022-10-08 20:11:13 +00:00
|
|
|
|
|
|
|
assert_eq!(interpreter.pointer, 1);
|
2022-10-12 21:21:21 +00:00
|
|
|
assert_eq!(interpreter.cells[0], Cell::new(0, &vec![]));
|
|
|
|
assert_eq!(interpreter.cells[1], Cell::new(4, &vec![]));
|
2022-10-10 20:55:50 +00:00
|
|
|
// assert_eq!(interpreter.commands, vec!['>', '+', '+', '+', '+']);
|
2022-10-08 20:11:13 +00:00
|
|
|
|
|
|
|
// reset
|
|
|
|
interpreter.reset();
|
|
|
|
|
|
|
|
assert_eq!(interpreter.pointer, 0);
|
2022-10-12 21:21:21 +00:00
|
|
|
assert_eq!(interpreter.cells[0], Cell::new(0, &vec![]));
|
|
|
|
assert_eq!(interpreter.cells[1], Cell::new(0, &vec![]));
|
2022-10-10 20:55:50 +00:00
|
|
|
assert_eq!(interpreter.bf_commands, Vec::<BfCommand>::new());
|
2022-10-08 20:11:13 +00:00
|
|
|
}
|
|
|
|
}
|