sandwich/src/main.rs

294 lines
10 KiB
Rust
Raw Permalink Normal View History

2021-02-08 14:52:12 +00:00
use std::collections::HashMap;
2021-02-03 21:19:11 +00:00
use std::env;
2021-02-03 21:48:11 +00:00
use std::fmt;
2021-02-08 14:52:12 +00:00
use std::fs;
2021-02-03 21:54:13 +00:00
use std::usize;
2021-02-03 21:48:11 +00:00
2021-04-08 20:27:19 +00:00
use regex::Regex;
2021-02-04 19:47:55 +00:00
mod eval;
2021-02-03 21:48:11 +00:00
struct Program {
data: Vec<String>,
pc: usize,
2021-02-08 14:52:12 +00:00
vars: HashMap<char, String>,
2021-02-08 14:59:55 +00:00
funcs: HashMap<char, String>
2021-02-03 21:48:11 +00:00
}
macro_rules! create_program {
($filename:expr, $prog_name:ident) => {
let contents = fs::read_to_string($filename).expect("Something went wrong reading the file");
let mut $prog_name = Program::from_string(contents);
};
}
2021-02-03 21:48:11 +00:00
impl Program {
fn from_string(program: String) -> Program {
let mut op_list: Vec<String> = Vec::new();
2021-02-08 14:52:12 +00:00
for opcode in program.split("\n").collect::<Vec<&str>>() {
let new_op = opcode.to_owned();
2021-02-03 21:48:11 +00:00
if new_op.len() != 0 {
op_list.push(new_op.to_owned());
}
}
2021-02-08 14:52:12 +00:00
return Program {
data: op_list,
pc: 0,
vars: HashMap::new(),
2021-02-08 14:59:55 +00:00
funcs: HashMap::new()
2021-02-08 14:52:12 +00:00
};
}
2021-02-07 18:10:56 +00:00
// Reads the arguments passed to an opcode, and inserts variables where necessary
2021-02-07 17:18:15 +00:00
fn args_or_vars(&self, arguments: &str) -> String {
2021-02-07 18:10:56 +00:00
let mut builder = String::from(""); // Empty string that will be rebuilt based on the loop
let argument_vec: Vec<char> = arguments.chars().collect(); // Deconstructed arguments
2021-02-07 18:10:56 +00:00
// Iterate through each char
2021-02-07 17:18:15 +00:00
for index in 0..argument_vec.len() {
let current_char = argument_vec[index];
let str_to_push: String;
2021-02-07 00:10:34 +00:00
2021-02-07 17:18:15 +00:00
if index > 0 {
2021-02-07 18:10:56 +00:00
// Only test for the dollar sign if it's not the first character
// This is because there can't be anything before the first character, otherwise it's not the first
2021-02-08 14:52:12 +00:00
if argument_vec[index - 1] == '$' {
2021-02-07 18:10:56 +00:00
// If the previous character is a dollar sign, we can skip this iteration because we know the variable has already been handled
2021-02-07 17:18:15 +00:00
continue;
}
}
2021-02-08 14:52:12 +00:00
2021-02-07 17:18:15 +00:00
if current_char == '$' {
2021-02-07 18:10:56 +00:00
// If the current char is a $, we know that the next char should be a variable
2021-02-08 14:52:12 +00:00
let variable = argument_vec[index + 1];
2021-02-07 17:18:15 +00:00
let key = self.vars.get(&variable);
match key {
Some(value) => str_to_push = value.to_string(),
2021-02-08 14:52:12 +00:00
None => panic!("NotFoundError: Variable {} has not been defined", variable),
2021-02-07 17:18:15 +00:00
}
} else {
2021-02-07 18:10:56 +00:00
// If there's no variable, then just push the char that was already there
2021-02-07 17:18:15 +00:00
str_to_push = current_char.to_string();
}
2021-02-07 17:18:15 +00:00
builder.push_str(&str_to_push);
}
2021-02-07 17:18:15 +00:00
builder
}
fn add_var(&mut self, arguments: &str) {
2021-02-07 00:10:34 +00:00
let argument_vec: Vec<char> = arguments.chars().collect();
2021-02-07 17:24:53 +00:00
let name = argument_vec[0];
2021-02-08 15:45:13 +00:00
let mut value: String = argument_vec[1..].into_iter().collect();
value = self.args_or_funcs(&value);
2021-04-07 22:01:32 +00:00
value = eval::inp_or_normal(&value);
2021-02-07 17:24:53 +00:00
self.vars.insert(name, value);
2021-02-03 21:48:11 +00:00
}
2021-02-08 14:59:55 +00:00
fn add_func(&mut self, arguments: &str) {
let argument_vec: Vec<char> = arguments.chars().collect();
let name = argument_vec[0];
let body: String = argument_vec[1..].into_iter().collect();
self.funcs.insert(name, body);
}
2021-02-08 15:30:20 +00:00
fn parse_funcs(&mut self, instruction: &String) -> u32 {
// Opcode is the first character, arguments are everything after the first char
let opcode = instruction.chars().collect::<Vec<char>>()[0];
let arguments = &instruction[1..];
// Only a subset of opcodes, because the others don't make sense in a function
match opcode {
2021-02-08 15:48:22 +00:00
'a' => eval::do_math(self.args_or_funcs(&self.args_or_vars(arguments)), '+'),
's' => eval::do_math(self.args_or_funcs(&self.args_or_vars(arguments)), '-'),
'm' => eval::do_math(self.args_or_funcs(&self.args_or_vars(arguments)), '*'),
'd' => eval::do_math(self.args_or_funcs(&self.args_or_vars(arguments)), '/'),
2021-02-08 15:30:20 +00:00
'l' => {self.add_var(arguments);0}
_ => panic!("SyntaxError: No such opcode: {}", self.pc),
}
}
fn args_or_funcs(&mut self, arguments: &str) -> String {
let mut builder = String::from("");
let argument_vec: Vec<char> = arguments.chars().collect();
for index in 0..argument_vec.len() {
let current_char = argument_vec[index];
let str_to_push: String;
2021-02-08 15:45:13 +00:00
if index > 0 {
if argument_vec[index-1] == '*' {
continue;
}
}
2021-02-08 15:30:20 +00:00
if current_char == '*' {
let func_name = argument_vec[index+1];
let body: String;
let key = (self).funcs.get(&func_name);
match key {
Some(content) => body = content.to_owned(),
None => panic!("ValueError: function {} has not been defined yet!", func_name)
}
str_to_push = self.parse_funcs(&body).to_string();
} else {
str_to_push = current_char.to_string();
}
builder.push_str(&str_to_push);
}
builder
}
2021-03-16 17:38:36 +00:00
fn run_external(&mut self, arguments: String) {
2021-03-16 17:47:39 +00:00
// Split arguments by -
2021-03-16 17:38:36 +00:00
let argument_vec: Vec<&str> = arguments.split("-").collect();
println!("{}", argument_vec.len());
let filename = argument_vec[0];
// Read contents of the provided file and construct a symbolic Program from it
create_program!(filename, prog);
prog.run();
2021-03-16 17:41:50 +00:00
if argument_vec.len() > 1 {
2021-03-16 17:47:39 +00:00
// Start from the second element
2021-03-16 17:41:50 +00:00
for name in argument_vec[1..].iter() {
2021-03-16 17:43:19 +00:00
let kind = match name.chars().nth(0) {
Some(content) => content,
None => panic!("SyntaxError: {}: Invalid syntax", arguments)
};
let name_to_import = match name.chars().nth(1) {
Some(content) => content,
None => panic!("SyntaxError: {}: Invalid syntax", arguments)
};
2021-03-16 17:41:50 +00:00
if kind == 'v' {
let key = prog.vars.get(&name_to_import);
match key {
Some(value) => self.vars.insert(name_to_import, value.to_string()),
None => panic!("ValueError: variable {} has not been defined in {}!", name_to_import, filename)
};
} else if kind == 'f' {
let key = prog.funcs.get(&name_to_import);
match key {
Some(value) => {self.funcs.insert(name_to_import, value.to_string());()},
None => panic!("ValueError: function {} has not been defined in {}!", name_to_import, filename)
}
} else {
2021-03-16 17:47:39 +00:00
// Skip unknown types
2021-03-16 17:41:50 +00:00
continue;
}
}
} else {
2021-03-16 17:47:39 +00:00
// implied catch-all, might be unintuitive
2021-03-16 17:41:50 +00:00
for (key, value) in prog.vars.iter() {
self.vars.insert(*key, value.to_string());
}
for (key, value) in prog.funcs.iter() {
self.funcs.insert(*key, value.to_string());
}
}
}
2021-04-08 20:27:19 +00:00
fn handle_if_statement(&mut self, arguments: String) {
let argument_vec: Vec<&str> = arguments.split(":").collect();
let condition = argument_vec[0];
let success = argument_vec[1];
let failure = argument_vec[2];
let re = Regex::new(r"(?P<num1>\d+)(?P<operator>==|!=|>|<|>=|<=)(?P<num2>\d+)").unwrap();
let caps = re.captures(condition).unwrap();
let num1 = &caps["num1"];
let operator = &caps["operator"];
let num2 = &caps["num2"];
let meets_condition: bool;
match operator {
"==" => meets_condition = num1 == num2,
"!=" => meets_condition = num1 != num2,
">" => meets_condition = num1 > num2,
"<" => meets_condition = num1 < num2,
">=" => meets_condition = num1 >= num2,
"<=" => meets_condition = num1 <= num2,
_ => panic!("SyntaxError: Unknown operator at opcode {}: {}", self.pc, operator)
};
if meets_condition {
self.parse(&(success.to_string()));
} else {
self.parse(&(failure.to_string()));
}
}
2021-02-07 00:33:41 +00:00
fn parse(&mut self, instruction: &String) {
2021-02-07 18:10:56 +00:00
// Opcode is the first character, arguments are everything after the first char
2021-02-04 19:47:55 +00:00
let opcode = instruction.chars().collect::<Vec<char>>()[0];
2021-02-16 18:23:40 +00:00
if opcode != '#' {
let arguments = self.args_or_funcs(&self.args_or_vars(&eval::args_or_comments(&instruction[1..])));
2021-02-16 18:23:40 +00:00
match opcode {
'p' => println!("{}", arguments),
'a' => println!("{}", eval::do_math(arguments, '+')),
's' => println!("{}", eval::do_math(arguments, '-')),
'm' => println!("{}", eval::do_math(arguments, '*')),
'd' => println!("{}", eval::do_math(arguments, '/')),
2021-02-16 18:23:40 +00:00
'l' => self.add_var(&arguments),
'f' => self.add_func(&arguments),
'i' => self.run_external(arguments),
2021-04-08 20:27:19 +00:00
'?' => self.handle_if_statement(arguments),
2021-02-16 18:23:40 +00:00
_ => panic!("SyntaxError at opcode {}: Unknown opcode {}", self.pc, opcode),
}
2021-02-04 17:45:53 +00:00
}
}
2021-02-03 21:54:13 +00:00
fn run(&mut self) {
2021-02-03 21:48:11 +00:00
println!("{}", self);
2021-02-03 21:54:13 +00:00
while self.pc < self.data.len() {
2021-02-07 18:10:56 +00:00
// Grab instruction from op list and parse the instruction
let instruction = self.data[self.pc].to_owned();
2021-02-03 21:54:13 +00:00
2021-02-07 00:33:41 +00:00
self.parse(&instruction);
2021-02-03 21:54:13 +00:00
2021-02-07 18:10:56 +00:00
self.pc += 1;
2021-02-03 21:54:13 +00:00
}
2021-02-03 21:48:11 +00:00
}
}
impl fmt::Display for Program {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Program ({:?})", self.data)
}
}
2021-02-03 21:19:11 +00:00
fn main() {
2021-02-07 18:10:56 +00:00
// Grab args and a filename
2021-02-03 21:19:11 +00:00
let args: Vec<String> = env::args().collect();
if args.len() == 1 {
2021-02-08 14:52:12 +00:00
// Args will always have at least 1 argument, which is the name of the executable.
2021-02-07 18:10:56 +00:00
// That's why we're checking index 1, not index 0.
2021-02-07 00:33:41 +00:00
panic!("You must provide a filename!");
2021-02-03 21:19:11 +00:00
}
let filename = &args[1];
2021-02-03 21:48:11 +00:00
2021-02-07 18:10:56 +00:00
// Read contents of the provided file and construct a symbolic Program from it
create_program!(filename, prog);
2021-02-03 21:48:11 +00:00
prog.run();
2021-02-03 21:19:11 +00:00
}
2021-02-07 19:35:00 +00:00
#[cfg(test)]
2021-03-09 17:25:47 +00:00
mod tests;