Решение на Basic BASIC от Владимир Радев

Обратно към всички решения

Към профила на Владимир Радев

Резултати

  • 19 точки от тестове
  • 0 бонус точки
  • 19 точки общо
  • 14 успешни тест(а)
  • 1 неуспешни тест(а)

Код

use std::{
collections::{HashMap},
io::{self, BufReader, Read, Write, BufRead}
};
#[derive(Debug)]
pub enum InterpreterError {
IoError(io::Error),
UnknownVariable { name: String },
NotANumber { value: String },
SyntaxError { code: String },
RuntimeError { line_number: u16, message: String },
}
// ВАЖНО: тук ще трябва да се добави lifetime анотация!
pub struct Interpreter<'a, R: Read, W: Write> {
// Тези полета не са публични, така че може да си ги промените, ако искате:
pub output: &'a mut W,
pub reader: BufReader<R>,
pub lines: HashMap<u16, Vec<String>>, // Каквито други полета ви трябват
pub vars: HashMap<String,u16>
}
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Конструира нов интерпретатор, който чете вход от `input` чрез `READ` командата и пише в
/// `output` чрез `PRINT`.
///
pub fn new(input: R, output: &'a mut W) -> Self {
Self {
output: output,
reader: BufReader::new(input),
lines: HashMap::new(),
vars : HashMap::new()
}
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Тази функция добавя ред код към интерпретатора. Този ред се очаква да започва с 16-битово
/// unsigned число, последвано от някаква команда и нейните параметри. Всички компоненти на
/// реда ще бъдат разделени точно с един интервал -- или поне така ще ги тестваме.
///
/// Примерни редове:
///
/// 10 IF 2 > 1 GOTO 30
/// 20 GOTO 10
/// 30 READ V
/// 40 PRINT V
///
/// В случай, че реда не е валидна команда (детайли по-долу), очакваме да върнете
/// `InterpreterError::SyntaxError` с кода, който е бил подаден.
///
pub fn add(&mut self, code: &str) -> Result<(), InterpreterError> {
if code.is_empty() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
// line {
// 0 1 2 3 4 5 6
// 10 IF <стойност> <оператор> <стойност> GOTO <номер на ред>
// 20 PRINT <стойност>
// 30 READ <име на променлива>
// 40 GOTO <номер на ред>
let mut line = code
.split_whitespace()
.map(|x| x.to_string())
.collect::<Vec<String>>();
let mut num;
match &line[0].parse::<u16>() {
Ok(x) => {
num = x;
if line.len()==1
{
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
},
Err(_) => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
match line[1].as_str() {
"IF" => {
if line.len() != 7 {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
}
"PRINT" => {
if line.len() != 3 {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
}
"READ" => {
if line.len() != 3 {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
let mut var = line[2].chars().next().unwrap();
if !var.is_uppercase() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
}
"GOTO" => {
if line.len() != 3 {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
match line[2].parse::<u16>() {
Ok(_) => (),
Err(_) => return Err(InterpreterError::SyntaxError {
code: code.to_string(),
}),
};
}
_ => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
self.lines.insert(line[0].parse::<u16>().unwrap(), line[1..].to_vec());
Ok(())
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Оценява `value` като стойност в контекста на интерпретатора:
///
/// - Ако `value` е низ, който започва с главна буква (съгласно `char::is_uppercase`), търсим
/// дефинирана променлива с това име и връщаме нейната стойност.
/// -> Ако няма такава, връщаме `InterpreterError::UnknownVariable` с това име.
/// - Ако `value` е валидно u16 число, връщаме числото
/// -> Иначе, връщаме `InterpreterError::NotANumber`vp с тази стойност
///
pub fn eval_value(&self, value: &str) -> Result<u16, InterpreterError> {
let firstLetter= value.chars().next().unwrap();
if firstLetter.is_uppercase() {
if !self.vars.contains_key(&value.to_string()) {
return Err(InterpreterError::UnknownVariable{ name: value.to_string()})
}
return Ok(*self.vars.get(&value.to_string()).unwrap());
}
match value.parse::<u16>() {
Ok(x) => return Ok(x),
Err(_) => return Err(InterpreterError::NotANumber{value: value.to_string()}),
};
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Функцията започва да изпълнява редовете на програмата в нарастващ ред. Ако стигне до GOTO,
/// скача на съответния ред и продължава от него в нарастващ ред. Когато вече няма ред с
/// по-голямо число, функцията свършва и връща `Ok(())`.
///
/// Вижте по-долу за отделните команди и какви грешки могат да върнат.
///
pub fn run(&mut self) -> Result<(), InterpreterError> {
//# zashto ne napravat prev() na nqkoq struktura (pone ne namerih nikude imashe nqkuv
// range za binary tree map ama toi pa vzima excluded na lqva granica) , ta trqbva da prava tova :(
let mut sorted_keys:Vec<u16>=Vec::new();
for key in self.lines.keys() {
sorted_keys.push(*key);
}
sorted_keys.sort();
let mut keys_indexes : HashMap<u16,u16> = HashMap::new();
for i in 0..sorted_keys.len() {
keys_indexes.insert(sorted_keys[i], i as u16);
}
// for (key,value) in &keys_indexes{
// println!("{} {} ", key,value);
// }
let mut curr:usize=0;
while curr < sorted_keys.len() {
// command_and_arguments {
// 0 1 2 3 4 5
// IF <стойност> <оператор> <стойност> GOTO <номер на ред>
// PRINT <стойност>
// READ <име на променлива>
// GOTO <номер на ред>
//print!("while");
let line_num :&u16 = &sorted_keys[curr];
let command_and_arguments = self.lines.get(line_num).unwrap().clone();
//println!("{}",command_and_arguments.len());
match command_and_arguments[0].as_str() {
"PRINT" => {
//println!("print");
if command_and_arguments[1].chars().next().unwrap().is_uppercase() {
if !self.vars.contains_key(&command_and_arguments[1]) {
return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() });
}
match self.output.write(format!("{}\n",self.vars.get(&command_and_arguments[1]).unwrap().to_string()).as_bytes()){
Ok(_) => { curr += 1 ;
continue;
}
Err(mess) => return Err(InterpreterError::IoError(mess)),
};
}
// println!("here");
match self.output.write(format!("{}\n",command_and_arguments[1]).as_bytes()){
Ok(_) => {
curr += 1 ;
continue;
},
Err(mess) => return Err(InterpreterError::IoError(mess)),
};
}
"READ" => {
let mut buf:Vec<u8> = Vec::new();
match self.reader.read_until('\n' as u8,&mut buf){
Ok(_) => (),
Err(mess) => return Err(InterpreterError::IoError(mess)),
}
let mut n = buf.len()-1;
while n >= 0 {
if buf[n].is_ascii_control(){
buf.pop();
n-=1;
continue;
}
break;
}
// println!("{}",buf.len());
for i in &buf {
if !(*i >= '0' as u8 && *i<='9' as u8) {
return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() });
}
}
let mut number=0;
let mut out:String=String::new();
for i in &buf {
out+=&format!("{}",*i as char)[..];
}
match out.parse::<u16>(){
Ok(x) => {
number=x;
},
Err(_) => return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() }),
};
//println!("{}",number);
// match u16::from_str_radix(&buf, 16) {
// Ok(_) => () ,
// Err(_) => return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() }),
// };
// let mut number = u16::from_str_radix(&buf, 16).unwrap();
// println!();
// println!();
// println!();
// println!();
// println!("{}",buf.len());
// println!("{}",num);
// match buf.parse::<u16>() {
// Ok(_num) => { num= _num; } ,
// Err(_) => return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() }),
// }
self.vars.insert(command_and_arguments[1].clone(), number);
// println!("read");
curr+=1;
continue;
},
"GOTO" => {
if !keys_indexes.contains_key(&command_and_arguments[1].parse::<u16>().unwrap()) {
return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() })
}
curr=*keys_indexes.get(&command_and_arguments[1].parse::<u16>().unwrap()).unwrap() as usize;
continue;
},
"IF" => {
let mut valueLhs: u16=0;
let mut valueRhs:u16=0;
match self.eval_value(command_and_arguments[1].as_str()) {
Ok(_num) => { valueLhs =_num },
Err(_) => return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() }),
}
match self.eval_value(command_and_arguments[3].as_str()) {
Ok(_num) => { valueRhs =_num },
Err(_) => return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() }),
}
match command_and_arguments[2].as_str() {
"<" => {
if valueLhs < valueRhs {
if !keys_indexes.contains_key(&command_and_arguments[5].parse::<u16>().unwrap()) {
return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() })
}
curr=*keys_indexes.get(&command_and_arguments[5].parse::<u16>().unwrap()).unwrap() as usize;
continue;
}
else {
curr+=1;
continue;
}
}
">" => {
if valueLhs > valueRhs {
if !keys_indexes.contains_key(&command_and_arguments[5].parse::<u16>().unwrap()) {
return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() })
}
curr=*keys_indexes.get(&command_and_arguments[5].parse::<u16>().unwrap()).unwrap() as usize;
continue;
}
else {
curr+=1;
continue;
}
}
"=" => {
if valueLhs == valueRhs {
if !keys_indexes.contains_key(&command_and_arguments[5].parse::<u16>().unwrap()) {
return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() })
}
curr=*keys_indexes.get(&command_and_arguments[5].parse::<u16>().unwrap()).unwrap() as usize;
continue;
}
else {
curr+=1;
continue;
}
}
_ => ()
};
},
_ => ()
}
}
Ok(())
}
}
macro_rules! assert_match {
($expr:expr, $pat:pat) => {
let result = $expr;
if let $pat = result {
// all good
} else {
assert!(false, "Expression {:?} does not match the pattern {:?}", result, stringify!($pat));
}
}
}
#[test]
fn test_basic_1() {
// Забележете `b""` -- низ от байтове
let input: &[u8] = b"1\n2\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 READ A").unwrap();
interpreter.add("20 READ B").unwrap();
interpreter.add("30 PRINT A").unwrap();
interpreter.add("40 PRINT B").unwrap();
interpreter.run().unwrap();
assert_eq!(interpreter.eval_value("A").unwrap(), 1_u16);
assert_eq!(String::from_utf8(output).unwrap(), "1\n2\n");
}
#[test]
fn test_basic_2() {
let input: &[u8] = b"";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
assert_match!(interpreter.add("10 PRINT"), Err(InterpreterError::SyntaxError { .. }));
}
pub struct NotBuffered {}
impl std::io::Read for NotBuffered {
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
Ok(0)
}
}
#[test]
fn test_not_buffered() {
let input = NotBuffered {};
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 PRINT 10").unwrap();
interpreter.run().unwrap();
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20230111-3772066-18j437b/solution)
warning: unused macro definition: `assert_match`
   --> src/lib.rs:368:14
    |
368 | macro_rules! assert_match {
    |              ^^^^^^^^^^^^
    |
    = note: `#[warn(unused_macros)]` on by default

warning: variable `num` is assigned to, but never used
  --> src/lib.rs:72:17
   |
72 |         let mut num;
   |                 ^^^
   |
   = note: consider using `_num` instead
   = note: `#[warn(unused_variables)]` on by default

warning: value assigned to `num` is never read
  --> src/lib.rs:75:17
   |
75 |                 num = x;
   |                 ^^^
   |
   = help: maybe it is overwritten before being read?
   = note: `#[warn(unused_assignments)]` on by default

warning: value assigned to `number` is never read
   --> src/lib.rs:258:27
    |
258 |                   let mut number=0;
    |                           ^^^^^^
    |
    = help: maybe it is overwritten before being read?

warning: value assigned to `valueLhs` is never read
   --> src/lib.rs:302:29
    |
302 |                     let mut valueLhs: u16=0;
    |                             ^^^^^^^^
    |
    = help: maybe it is overwritten before being read?

warning: value assigned to `valueRhs` is never read
   --> src/lib.rs:303:29
    |
303 |                     let mut valueRhs:u16=0;
    |                             ^^^^^^^^
    |
    = help: maybe it is overwritten before being read?

warning: variable does not need to be mutable
  --> src/lib.rs:68:13
   |
68 |         let mut line = code
   |             ----^^^^
   |             |
   |             help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

warning: variable does not need to be mutable
  --> src/lib.rs:72:13
   |
72 |         let mut num;
   |             ----^^^
   |             |
   |             help: remove this `mut`

warning: variable does not need to be mutable
   --> src/lib.rs:113:21
    |
113 |                 let mut var = line[2].chars().next().unwrap();
    |                     ----^^^
    |                     |
    |                     help: remove this `mut`

warning: variable `firstLetter` should have a snake case name
   --> src/lib.rs:155:13
    |
155 |         let firstLetter= value.chars().next().unwrap();
    |             ^^^^^^^^^^^ help: convert the identifier to snake case: `first_letter`
    |
    = note: `#[warn(non_snake_case)]` on by default

warning: comparison is useless due to type limits
   --> src/lib.rs:244:25
    |
244 |                   while n >= 0 {
    |                         ^^^^^^
    |
    = note: `#[warn(unused_comparisons)]` on by default

warning: variable `valueLhs` should have a snake case name
   --> src/lib.rs:302:29
    |
302 |                     let mut valueLhs: u16=0;
    |                             ^^^^^^^^ help: convert the identifier to snake case: `value_lhs`

warning: variable `valueRhs` should have a snake case name
   --> src/lib.rs:303:29
    |
303 |                     let mut valueRhs:u16=0;
    |                             ^^^^^^^^ help: convert the identifier to snake case: `value_rhs`

warning: `solution` (lib) generated 13 warnings
    Finished test [unoptimized + debuginfo] target(s) in 1.53s
     Running tests/solution_test.rs (target/debug/deps/solution_test-0edbea2040daef01)

running 15 tests
test solution_test::test_basic_if ... ok
test solution_test::test_basic_goto ... ok
test solution_test::test_basic_input ... ok
test solution_test::test_basic_print ... ok
test solution_test::test_basic_read ... ok
test solution_test::test_erroring_goto ... ok
test solution_test::test_io_error_read ... ok
test solution_test::test_full_program ... ok
test solution_test::test_io_error_write ... ok
test solution_test::test_line_order_and_overwriting ... ok
test solution_test::test_print_vars_and_strings ... ok
test solution_test::test_print_cyrillic ... ok
test solution_test::test_runtime_errors ... ok
test solution_test::test_syntax_errors_1 ... FAILED
test solution_test::test_syntax_errors_2 ... ok

failures:

---- solution_test::test_syntax_errors_1 stdout ----
thread '<unnamed>' panicked at 'Expression Ok(()) does not match the pattern "Err(InterpreterError::SyntaxError { .. })"', tests/solution_test.rs:267:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'solution_test::test_syntax_errors_1' panicked at 'Expression Ok(()) does not match the pattern "Err(InterpreterError::SyntaxError { .. })"', tests/solution_test.rs:254:5


failures:
    solution_test::test_syntax_errors_1

test result: FAILED. 14 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--test solution_test`

История (2 версии и 0 коментара)

Владимир качи първо решение на 06.01.2023 22:51 (преди над 2 години)

Владимир качи решение на 07.01.2023 00:12 (преди над 2 години)

use std::{
collections::{HashMap},
io::{self, BufReader, Read, Write, BufRead}
};
#[derive(Debug)]
pub enum InterpreterError {
IoError(io::Error),
UnknownVariable { name: String },
NotANumber { value: String },
SyntaxError { code: String },
RuntimeError { line_number: u16, message: String },
}
// ВАЖНО: тук ще трябва да се добави lifetime анотация!
pub struct Interpreter<'a, R: Read, W: Write> {
// Тези полета не са публични, така че може да си ги промените, ако искате:
pub output: &'a mut W,
pub reader: BufReader<R>,
pub lines: HashMap<u16, Vec<String>>, // Каквито други полета ви трябват
pub vars: HashMap<String,u16>
}
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Конструира нов интерпретатор, който чете вход от `input` чрез `READ` командата и пише в
/// `output` чрез `PRINT`.
///
pub fn new(input: R, output: &'a mut W) -> Self {
Self {
output: output,
reader: BufReader::new(input),
lines: HashMap::new(),
vars : HashMap::new()
}
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Тази функция добавя ред код към интерпретатора. Този ред се очаква да започва с 16-битово
/// unsigned число, последвано от някаква команда и нейните параметри. Всички компоненти на
/// реда ще бъдат разделени точно с един интервал -- или поне така ще ги тестваме.
///
/// Примерни редове:
///
/// 10 IF 2 > 1 GOTO 30
/// 20 GOTO 10
/// 30 READ V
/// 40 PRINT V
///
/// В случай, че реда не е валидна команда (детайли по-долу), очакваме да върнете
/// `InterpreterError::SyntaxError` с кода, който е бил подаден.
///
pub fn add(&mut self, code: &str) -> Result<(), InterpreterError> {
if code.is_empty() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
// line {
// 0 1 2 3 4 5 6
// 10 IF <стойност> <оператор> <стойност> GOTO <номер на ред>
// 20 PRINT <стойност>
// 30 READ <име на променлива>
// 40 GOTO <номер на ред>
let mut line = code
.split_whitespace()
.map(|x| x.to_string())
.collect::<Vec<String>>();
let mut num;
match &line[0].parse::<u16>() {
Ok(x) => {
num = x;
if line.len()==1
{
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
},
Err(_) => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
match line[1].as_str() {
"IF" => {
if line.len() != 7 {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
}
"PRINT" => {
if line.len() != 3 {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
}
"READ" => {
if line.len() != 3 {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
let mut var = line[2].chars().next().unwrap();
if !var.is_uppercase() {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
}
"GOTO" => {
if line.len() != 3 {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
});
}
match line[2].parse::<u16>() {
Ok(_) => (),
Err(_) => return Err(InterpreterError::SyntaxError {
code: code.to_string(),
}),
};
}
_ => {
return Err(InterpreterError::SyntaxError {
code: code.to_string(),
})
}
};
self.lines.insert(line[0].parse::<u16>().unwrap(), line[1..].to_vec());
Ok(())
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Оценява `value` като стойност в контекста на интерпретатора:
///
/// - Ако `value` е низ, който започва с главна буква (съгласно `char::is_uppercase`), търсим
/// дефинирана променлива с това име и връщаме нейната стойност.
/// -> Ако няма такава, връщаме `InterpreterError::UnknownVariable` с това име.
/// - Ако `value` е валидно u16 число, връщаме числото
/// -> Иначе, връщаме `InterpreterError::NotANumber`vp с тази стойност
///
pub fn eval_value(&self, value: &str) -> Result<u16, InterpreterError> {
let firstLetter= value.chars().next().unwrap();
if firstLetter.is_uppercase() {
if !self.vars.contains_key(&value.to_string()) {
return Err(InterpreterError::UnknownVariable{ name: value.to_string()})
}
return Ok(*self.vars.get(&value.to_string()).unwrap());
}
match value.parse::<u16>() {
Ok(x) => return Ok(x),
Err(_) => return Err(InterpreterError::NotANumber{value: value.to_string()}),
};
}
}
// Не забравяйте lifetime анотацията
impl<'a, R: Read, W: Write> Interpreter<'a, R, W> {
/// Функцията започва да изпълнява редовете на програмата в нарастващ ред. Ако стигне до GOTO,
/// скача на съответния ред и продължава от него в нарастващ ред. Когато вече няма ред с
/// по-голямо число, функцията свършва и връща `Ok(())`.
///
/// Вижте по-долу за отделните команди и какви грешки могат да върнат.
///
pub fn run(&mut self) -> Result<(), InterpreterError> {
//# zashto ne napravat prev() na nqkoq struktura (pone ne namerih nikude imashe nqkuv
// range za binary tree map ama toi pa vzima excluded na lqva granica) , ta trqbva da prava tova :(
let mut sorted_keys:Vec<u16>=Vec::new();
for key in self.lines.keys() {
sorted_keys.push(*key);
}
sorted_keys.sort();
let mut keys_indexes : HashMap<u16,u16> = HashMap::new();
for i in 0..sorted_keys.len() {
keys_indexes.insert(sorted_keys[i], i as u16);
}
// for (key,value) in &keys_indexes{
// println!("{} {} ", key,value);
// }
let mut curr:usize=0;
while curr < sorted_keys.len() {
// command_and_arguments {
// 0 1 2 3 4 5
// IF <стойност> <оператор> <стойност> GOTO <номер на ред>
// PRINT <стойност>
// READ <име на променлива>
// GOTO <номер на ред>
//print!("while");
let line_num :&u16 = &sorted_keys[curr];
let command_and_arguments = self.lines.get(line_num).unwrap().clone();
//println!("{}",command_and_arguments.len());
match command_and_arguments[0].as_str() {
"PRINT" => {
//println!("print");
if command_and_arguments[1].chars().next().unwrap().is_uppercase() {
if !self.vars.contains_key(&command_and_arguments[1]) {
return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() });
}
match self.output.write(format!("{}\n",self.vars.get(&command_and_arguments[1]).unwrap().to_string()).as_bytes()){
Ok(_) => { curr += 1 ;
continue;
}
Err(mess) => return Err(InterpreterError::IoError(mess)),
};
}
// println!("here");
match self.output.write(format!("{}\n",command_and_arguments[1]).as_bytes()){
Ok(_) => {
curr += 1 ;
continue;
},
Err(mess) => return Err(InterpreterError::IoError(mess)),
};
}
"READ" => {
let mut buf:Vec<u8> = Vec::new();
match self.reader.read_until('\n' as u8,&mut buf){
Ok(_) => (),
Err(mess) => return Err(InterpreterError::IoError(mess)),
}
let mut n = buf.len()-1;
while n >= 0 {
if buf[n].is_ascii_control(){
buf.pop();
n-=1;
continue;
}
break;
}
// println!("{}",buf.len());
for i in &buf {
if !(*i >= '0' as u8 && *i<='9' as u8) {
return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() });
}
}
let mut number=0;
let mut out:String=String::new();
for i in &buf {
out+=&format!("{}",*i as char)[..];
}
- number=out.parse::<u16>().unwrap();
+ match out.parse::<u16>(){
+ Ok(x) => {
+ number=x;
+ },
+ Err(_) => return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() }),
+ };
//println!("{}",number);
// match u16::from_str_radix(&buf, 16) {
// Ok(_) => () ,
// Err(_) => return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() }),
// };
// let mut number = u16::from_str_radix(&buf, 16).unwrap();
// println!();
// println!();
// println!();
// println!();
// println!("{}",buf.len());
// println!("{}",num);
// match buf.parse::<u16>() {
// Ok(_num) => { num= _num; } ,
// Err(_) => return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() }),
// }
self.vars.insert(command_and_arguments[1].clone(), number);
// println!("read");
curr+=1;
continue;
},
"GOTO" => {
if !keys_indexes.contains_key(&command_and_arguments[1].parse::<u16>().unwrap()) {
return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() })
}
curr=*keys_indexes.get(&command_and_arguments[1].parse::<u16>().unwrap()).unwrap() as usize;
continue;
},
"IF" => {
let mut valueLhs: u16=0;
let mut valueRhs:u16=0;
match self.eval_value(command_and_arguments[1].as_str()) {
Ok(_num) => { valueLhs =_num },
Err(_) => return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() }),
}
match self.eval_value(command_and_arguments[3].as_str()) {
Ok(_num) => { valueRhs =_num },
Err(_) => return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() }),
}
match command_and_arguments[2].as_str() {
"<" => {
if valueLhs < valueRhs {
if !keys_indexes.contains_key(&command_and_arguments[5].parse::<u16>().unwrap()) {
return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() })
}
curr=*keys_indexes.get(&command_and_arguments[5].parse::<u16>().unwrap()).unwrap() as usize;
continue;
}
else {
curr+=1;
continue;
}
}
">" => {
if valueLhs > valueRhs {
if !keys_indexes.contains_key(&command_and_arguments[5].parse::<u16>().unwrap()) {
return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() })
}
curr=*keys_indexes.get(&command_and_arguments[5].parse::<u16>().unwrap()).unwrap() as usize;
continue;
}
else {
curr+=1;
continue;
}
}
"=" => {
if valueLhs == valueRhs {
if !keys_indexes.contains_key(&command_and_arguments[5].parse::<u16>().unwrap()) {
return Err(InterpreterError::RuntimeError{ line_number: *line_num, message: "nevalidna komanda tuk".to_string() })
}
curr=*keys_indexes.get(&command_and_arguments[5].parse::<u16>().unwrap()).unwrap() as usize;
continue;
}
else {
curr+=1;
continue;
}
}
_ => ()
};
},
_ => ()
}
}
Ok(())
}
}
macro_rules! assert_match {
($expr:expr, $pat:pat) => {
let result = $expr;
if let $pat = result {
// all good
} else {
assert!(false, "Expression {:?} does not match the pattern {:?}", result, stringify!($pat));
}
}
}
#[test]
fn test_basic_1() {
// Забележете `b""` -- низ от байтове
let input: &[u8] = b"1\n2\n";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 READ A").unwrap();
interpreter.add("20 READ B").unwrap();
interpreter.add("30 PRINT A").unwrap();
interpreter.add("40 PRINT B").unwrap();
interpreter.run().unwrap();
assert_eq!(interpreter.eval_value("A").unwrap(), 1_u16);
assert_eq!(String::from_utf8(output).unwrap(), "1\n2\n");
}
#[test]
fn test_basic_2() {
let input: &[u8] = b"";
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
assert_match!(interpreter.add("10 PRINT"), Err(InterpreterError::SyntaxError { .. }));
}
-struct NotBuffered {}
+pub struct NotBuffered {}
impl std::io::Read for NotBuffered {
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
Ok(0)
}
}
#[test]
fn test_not_buffered() {
let input = NotBuffered {};
let mut output = Vec::<u8>::new();
let mut interpreter = Interpreter::new(input, &mut output);
interpreter.add("10 PRINT 10").unwrap();
interpreter.run().unwrap();
}