sandwich

A funny programming language written in Rust
git clone https://tilde.team/~karx/sandwich.git
Log | Files | Refs | README | LICENSE

main.rs (9342B)


      1 use std::collections::HashMap;
      2 use std::env;
      3 use std::fmt;
      4 use std::fs;
      5 use std::usize;
      6 
      7 mod eval;
      8 
      9 struct Program {
     10     data: Vec<String>,
     11     pc: usize,
     12     vars: HashMap<char, String>,
     13     funcs: HashMap<char, String>
     14 }
     15 
     16 impl Program {
     17     fn from_string(program: String) -> Program {
     18         let mut op_list: Vec<String> = Vec::new();
     19 
     20         for opcode in program.split("\n").collect::<Vec<&str>>() {
     21             let new_op = opcode.to_owned();
     22 
     23             if new_op.len() != 0 {
     24                 op_list.push(new_op.to_owned());
     25             }
     26         }
     27 
     28         return Program {
     29             data: op_list,
     30             pc: 0,
     31             vars: HashMap::new(),
     32             funcs: HashMap::new()
     33         };
     34     }
     35 
     36     // Reads the arguments passed to an opcode, and inserts variables where necessary
     37     fn args_or_vars(&self, arguments: &str) -> String {
     38         let mut builder = String::from(""); // Empty string that will be rebuilt based on the loop
     39         let argument_vec: Vec<char> = arguments.chars().collect(); // Deconstructed arguments
     40 
     41         // Iterate through each char
     42         for index in 0..argument_vec.len() {
     43             let current_char = argument_vec[index];
     44             let str_to_push: String;
     45 
     46             if index > 0 {
     47                 // Only test for the dollar sign if it's not the first character
     48                 // This is because there can't be anything before the first character, otherwise it's not the first
     49                 if argument_vec[index - 1] == '$' {
     50                     // If the previous character is a dollar sign, we can skip this iteration because we know the variable has already been handled
     51                     continue;
     52                 }
     53             }
     54 
     55             if current_char == '$' {
     56                 // If the current char is a $, we know that the next char should be a variable
     57                 let variable = argument_vec[index + 1];
     58 
     59                 let key = self.vars.get(&variable);
     60                 match key {
     61                     Some(value) => str_to_push = value.to_string(),
     62                     None => panic!("NotFoundError: Variable {} has not been defined", variable),
     63                 }
     64             } else {
     65                 // If there's no variable, then just push the char that was already there
     66                 str_to_push = current_char.to_string();
     67             }
     68 
     69             builder.push_str(&str_to_push);
     70         }
     71 
     72         builder
     73     }
     74 
     75     fn add_var(&mut self, arguments: &str) {
     76         let argument_vec: Vec<char> = arguments.chars().collect();
     77         let name = argument_vec[0];
     78         let mut value: String = argument_vec[1..].into_iter().collect();
     79         value = self.args_or_funcs(&value);
     80 
     81         self.vars.insert(name, value);
     82     }
     83 
     84     fn add_func(&mut self, arguments: &str) {
     85         let argument_vec: Vec<char> = arguments.chars().collect();
     86         let name = argument_vec[0];
     87         let body: String = argument_vec[1..].into_iter().collect();
     88 
     89         self.funcs.insert(name, body);
     90     }
     91 
     92     fn parse_funcs(&mut self, instruction: &String) -> u32 {
     93         // Opcode is the first character, arguments are everything after the first char
     94         let opcode = instruction.chars().collect::<Vec<char>>()[0];
     95         let arguments = &instruction[1..];
     96 
     97         // Only a subset of opcodes, because the others don't make sense in a function
     98         match opcode {
     99             'a' => eval::do_math(self.args_or_funcs(&self.args_or_vars(arguments)), '+'),
    100             's' => eval::do_math(self.args_or_funcs(&self.args_or_vars(arguments)), '-'),
    101             'm' => eval::do_math(self.args_or_funcs(&self.args_or_vars(arguments)), '*'),
    102             'd' => eval::do_math(self.args_or_funcs(&self.args_or_vars(arguments)), '/'),
    103             'l' => {self.add_var(arguments);0}
    104             _ => panic!("SyntaxError: No such opcode: {}", self.pc),
    105         }
    106     }
    107 
    108     fn args_or_funcs(&mut self, arguments: &str) -> String {
    109         let mut builder = String::from("");
    110         let argument_vec: Vec<char> = arguments.chars().collect();
    111 
    112         for index in 0..argument_vec.len() {
    113             let current_char = argument_vec[index];
    114             let str_to_push: String;
    115 
    116             if index > 0 {
    117                 if argument_vec[index-1] == '*' {
    118                     continue;
    119                 }
    120             }
    121 
    122             if current_char == '*' {
    123                 let func_name = argument_vec[index+1];
    124                 let body: String;
    125 
    126                 let key = (self).funcs.get(&func_name);
    127                 match key {
    128                     Some(content) => body = content.to_owned(),
    129                     None => panic!("ValueError: function {} has not been defined yet!", func_name)
    130                 }
    131 
    132                 str_to_push = self.parse_funcs(&body).to_string();
    133             } else {
    134                 str_to_push = current_char.to_string();
    135             }
    136 
    137             builder.push_str(&str_to_push);
    138         }
    139 
    140         builder
    141     }
    142 
    143     fn run_external(&mut self, arguments: String) {
    144         // Split arguments by -
    145         let argument_vec: Vec<&str> = arguments.split("-").collect();
    146         println!("{}", argument_vec.len());
    147         let filename = argument_vec[0];
    148 
    149         // Read contents of the provided file and construct a symbolic Program from it
    150         let contents = fs::read_to_string(filename).expect("Something went wrong reading the file");
    151         let mut prog = Program::from_string(contents);
    152         prog.run();
    153 
    154         if argument_vec.len() > 1 {
    155             // Start from the second element
    156             for name in argument_vec[1..].iter() {
    157                 let kind = match name.chars().nth(0) {
    158                     Some(content) => content,
    159                     None => panic!("SyntaxError: {}: Invalid syntax", arguments)
    160                 };
    161                 let name_to_import = match name.chars().nth(1) {
    162                     Some(content) => content,
    163                     None => panic!("SyntaxError: {}: Invalid syntax", arguments)
    164                 };
    165                 if kind == 'v' {
    166                     let key = prog.vars.get(&name_to_import);
    167                     match key {
    168                         Some(value) => self.vars.insert(name_to_import, value.to_string()),
    169                         None => panic!("ValueError: variable {} has not been defined in {}!", name_to_import, filename)
    170                     };
    171                 } else if kind == 'f' {
    172                     let key = prog.funcs.get(&name_to_import);
    173                     match key {
    174                         Some(value) => {self.funcs.insert(name_to_import, value.to_string());()},
    175                         None => panic!("ValueError: function {} has not been defined in {}!", name_to_import, filename)
    176                     }
    177                 } else {
    178                     // Skip unknown types
    179                     continue;
    180                 }
    181             }
    182         } else {
    183             // implied catch-all, might be unintuitive
    184             for (key, value) in prog.vars.iter() {
    185                 self.vars.insert(*key, value.to_string());
    186             }
    187 
    188             for (key, value) in prog.funcs.iter() {
    189                 self.funcs.insert(*key, value.to_string());
    190             }
    191         }
    192     }
    193 
    194     fn parse(&mut self, instruction: &String) {
    195         // Opcode is the first character, arguments are everything after the first char
    196         let opcode = instruction.chars().collect::<Vec<char>>()[0];
    197         let arguments = eval::args_or_comments(&instruction[1..]);
    198 
    199         if opcode != '#' {
    200             match opcode {
    201                 'p' => println!("{}", self.args_or_funcs(&self.args_or_vars(&arguments))),
    202                 'a' => println!("{}", eval::do_math(self.args_or_vars(&arguments), '+')),
    203                 's' => println!("{}", eval::do_math(self.args_or_vars(&arguments), '-')),
    204                 'm' => println!("{}", eval::do_math(self.args_or_vars(&arguments), '*')),
    205                 'd' => println!("{}", eval::do_math(self.args_or_vars(&arguments), '/')),
    206                 'l' => self.add_var(&arguments),
    207                 'f' => self.add_func(&arguments),
    208                 'i' => self.run_external(arguments),
    209                 _ => panic!("SyntaxError at opcode {}: Unknown opcode {}", self.pc, opcode),
    210             }
    211         }
    212     }
    213 
    214     fn run(&mut self) {
    215         println!("{}", self);
    216         while self.pc < self.data.len() {
    217             // Grab instruction from op list and parse the instruction
    218             let instruction = self.data[self.pc].to_owned();
    219 
    220             self.parse(&instruction);
    221 
    222             self.pc += 1;
    223         }
    224     }
    225 }
    226 
    227 impl fmt::Display for Program {
    228     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    229         write!(f, "Program ({:?})", self.data)
    230     }
    231 }
    232 
    233 fn main() {
    234     // Grab args and a filename
    235     let args: Vec<String> = env::args().collect();
    236     if args.len() == 1 {
    237         // Args will always have at least 1 argument, which is the name of the executable.
    238         // That's why we're checking index 1, not index 0.
    239         panic!("You must provide a filename!");
    240     }
    241 
    242     let filename = &args[1];
    243 
    244     // Read contents of the provided file and construct a symbolic Program from it
    245     let contents = fs::read_to_string(filename).expect("Something went wrong reading the file");
    246     let mut prog = Program::from_string(contents);
    247     prog.run();
    248 }
    249 
    250 #[cfg(test)]
    251 mod tests;