diff options
| author | tyropro <[email protected]> | 2026-05-22 21:46:18 +0100 |
|---|---|---|
| committer | tyropro <[email protected]> | 2026-05-22 21:46:18 +0100 |
| commit | e853164a0cd623fc2f643689883442ddfb3622c0 (patch) | |
| tree | 58c6e4a619b15bc56c3fed1ecf342f3b60e58000 | |
| parent | 6cd94ed60f143d5c69ef5b39da4472852243381a (diff) | |
feature: finished interpreter
| -rw-r--r-- | lmc_code/bubble_sort_invalid.lmc | 83 | ||||
| -rw-r--r-- | lmc_code/max.lmc | 4 | ||||
| -rw-r--r-- | src/interpreter.rs | 125 | ||||
| -rw-r--r-- | src/main.rs | 132 | ||||
| -rw-r--r-- | src/parser.rs | 288 |
5 files changed, 503 insertions, 129 deletions
diff --git a/lmc_code/bubble_sort_invalid.lmc b/lmc_code/bubble_sort_invalid.lmc new file mode 100644 index 0000000..4f3c082 --- /dev/null +++ b/lmc_code/bubble_sort_invalid.lmc @@ -0,0 +1,83 @@ + INP + STA num1 + INP + STA num2 + INP + STA num3 + INP + STA num4 + INP + STA num5 +start LDA zero + STA swaps + LDA num2 + SUB num1 + BRP num2_b + LDA swaps + ADD one + STA swaps + LDA num2 + STA temp + LDA num1 + STA num2 + LDA temp + STA num1 +num2_b LDA num3 + SUB num2 + BRP num3_b + LDA swaps + ADD one + STA swaps + LDA num3 + STA temp + LDA num2 + STA num3 + LDA temp + STA num2 +num3_b LDA num4 + SUB num3 + BRP num4_b + LDA swaps + ADD one + STA swaps + LDA num4 + STA temp + LDA num3 + STA num4 + LDA temp + STA num3 +num4_b LDA num5 + SUB num4 + BRP num5_b + LDA swaps + ADD one + STA swaps + LDA num5 + STA temp + LDA num4 + STA num5 + LDA temp + STA num4 +num5_b LDA swaps + BRZ sort_complete + BRA start +sort_complete LDA num1 + OUT + LDA num2 + OUT + LDA num3 + OUT + LDA num4 + OUT + LDA num5 + OUT + HLT +num1 DAT 0 +num2 DAT 0 +num3 DAT 0 +num4 DAT 0 +num5 DAT 0 +temp DAT 0 +swaps DAT 0 +one DAT 1 +zero DAT 0 diff --git a/lmc_code/max.lmc b/lmc_code/max.lmc index 008b970..e637a00 100644 --- a/lmc_code/max.lmc +++ b/lmc_code/max.lmc @@ -1,7 +1,7 @@ INP -SDA num1 +STA num1 INP -SDA num2 +STA num2 LDA num1 SUB num2 BRP num1_larger diff --git a/src/interpreter.rs b/src/interpreter.rs new file mode 100644 index 0000000..ef7d3e3 --- /dev/null +++ b/src/interpreter.rs @@ -0,0 +1,125 @@ +use std::io::{self, Write};
+
+use crate::parser::{Instruction, is_number};
+
+const MEMORY_SIZE: usize = 100;
+
+pub fn interpret(instructions: &Vec<Instruction>) -> Result<(), ()> {
+ let mut acc: i16 = 0;
+ let mut pc: u16 = 0;
+ let mut instruction_reg: u16 = 0;
+ let mut memory_add_reg: u16 = 0;
+
+ let mut memory: Vec<String> =
+ load_instructions(instructions).expect("ERR: Instructions larger than memory size");
+
+ loop {
+ fetch(&memory, &mut pc, &mut instruction_reg, &mut memory_add_reg);
+
+ // decode (don't ask why we encode the instructions to deocode them, just don't)
+ let instruction: Instruction = match instruction_reg {
+ 5 => Instruction::Load(memory_add_reg),
+ 3 => Instruction::Save(memory_add_reg),
+ 1 => Instruction::Add(memory_add_reg),
+ 2 => Instruction::Subtract(memory_add_reg),
+ 6 => Instruction::BranchAlways(memory_add_reg),
+ 8 => Instruction::BranchPositive(memory_add_reg),
+ 7 => Instruction::BranchZero(memory_add_reg),
+ 0 => Instruction::Halt,
+ 9 => match memory_add_reg {
+ 1 => Instruction::Input,
+ 2 => Instruction::Output,
+ _ => Instruction::Halt,
+ },
+ _ => Instruction::Halt,
+ };
+
+ // execute
+ match instruction {
+ Instruction::Load(param) => acc = memory[param as usize].parse().unwrap(),
+ Instruction::Save(param) => memory[param as usize] = format!("{:03}", acc),
+ Instruction::Add(param) => acc += memory[param as usize].parse::<i16>().unwrap(),
+ Instruction::Subtract(param) => acc -= memory[param as usize].parse::<i16>().unwrap(),
+
+ Instruction::BranchAlways(param) => pc = param,
+ Instruction::BranchPositive(param) => {
+ if acc >= 0 {
+ pc = param
+ }
+ }
+ Instruction::BranchZero(param) => {
+ if acc == 0 {
+ pc = param
+ }
+ }
+
+ Instruction::Halt => break,
+
+ Instruction::Input => loop {
+ print!("INPUT: ");
+ io::stdout().flush().unwrap();
+
+ let mut input = String::new();
+ io::stdin().read_line(&mut input).unwrap();
+ input = input.trim().to_string();
+
+ if !is_number(input.as_str()) {
+ println!("ERR: Expected unsigned integer.");
+ continue;
+ }
+
+ acc = input.parse().unwrap();
+ break;
+ },
+ Instruction::Output => println!("{}", acc),
+
+ _ => (),
+ }
+ }
+
+ Ok(())
+}
+
+fn fetch(memory: &Vec<String>, pc: &mut u16, instruction_reg: &mut u16, memory_add_reg: &mut u16) {
+ let instruction: Vec<char> = memory[*pc as usize].chars().collect();
+
+ *pc += 1;
+
+ *instruction_reg = instruction[0].to_string().parse().unwrap();
+ *memory_add_reg = format!("{}{}", instruction[1], instruction[2])
+ .parse()
+ .unwrap();
+}
+
+fn load_instructions(instructions: &Vec<Instruction>) -> Result<Vec<String>, ()> {
+ if instructions.len() > MEMORY_SIZE {
+ return Err(());
+ }
+
+ let mut memory: Vec<String> = Vec::new();
+
+ for instruction in instructions.iter() {
+ let instruction_int: String = match instruction {
+ Instruction::Load(param) => format!("5{:02}", param),
+ Instruction::Save(param) => format!("3{:02}", param),
+ Instruction::Add(param) => format!("1{:02}", param),
+ Instruction::Subtract(param) => format!("2{:02}", param),
+ Instruction::Dat(param) => format!("{:03}", param),
+ Instruction::BranchAlways(param) => format!("6{:02}", param),
+ Instruction::BranchPositive(param) => format!("8{:02}", param),
+ Instruction::BranchZero(param) => format!("7{:02}", param),
+ Instruction::Halt => "000".to_string(),
+ Instruction::Input => "901".to_string(),
+ Instruction::Output => "902".to_string(),
+ };
+
+ memory.push(instruction_int);
+ }
+
+ if instructions.len() != MEMORY_SIZE {
+ let mut padding = vec!["000".to_string(); MEMORY_SIZE - instructions.len()];
+ memory.append(&mut padding);
+ }
+
+ Ok(memory)
+}
diff --git a/src/main.rs b/src/main.rs index 4945402..1629b26 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,139 +1,17 @@ +mod interpreter; +mod parser; + use std::env; -use std::error::Error; use std::fs; // const MEMORY_SIZE: usize = 100; -#[derive(Debug)] -enum Mnemonic { - Load, - Save, - Halt, - Add, - Subtract, - Dat, - BranchPositive, - BranchZero, - BranchAlways, - Input, - Output, -} - -#[derive(Debug)] -enum Token { - Mnemonic(Mnemonic), - Literal(u16), - Label(String), -} - -struct Instruction { - mnemonic: Mnemonic, - label: Option<String>, -} - -impl Instruction { - fn new(mnemonic: Mnemonic, label: Option<String>) -> Self { - Self { mnemonic, label } - } -} - fn main() { let file_name = env::args().nth(1).expect("ERR: No file provided"); let file_contents = fs::read_to_string(file_name).expect("ERR: File does not exist"); - println!("{}", file_contents); - - let tokens = parse_file(file_contents.trim().to_string()); - - println!("{:#?}", tokens); - - // let mut acc = 0; - // let mut program_counter = 0; - - // let mut memory = vec![0; MEMORY_SIZE]; -} - -fn parse_file(file_contents: String) -> Vec<Token> { - let lines: Vec<&str> = file_contents.split("\n").collect(); - - let tokens: Vec<Token> = tokenise(lines); - - tokens -} - -fn tokenise(lines: Vec<&str>) -> Vec<Token> { - let mut tokens: Vec<Token> = Vec::new(); - - for line in lines { - let words = line.split(" "); - - for word in words { - match word { - "LDA" => tokens.push(Token::Mnemonic(Mnemonic::Load)), - "STA" => tokens.push(Token::Mnemonic(Mnemonic::Save)), - "HLT" => tokens.push(Token::Mnemonic(Mnemonic::Halt)), - "ADD" => tokens.push(Token::Mnemonic(Mnemonic::Add)), - "SUB" => tokens.push(Token::Mnemonic(Mnemonic::Subtract)), - "DAT" => tokens.push(Token::Mnemonic(Mnemonic::Dat)), - "BRP" => tokens.push(Token::Mnemonic(Mnemonic::BranchPositive)), - "BRZ" => tokens.push(Token::Mnemonic(Mnemonic::BranchZero)), - "BRA" => tokens.push(Token::Mnemonic(Mnemonic::BranchAlways)), - "INP" => tokens.push(Token::Mnemonic(Mnemonic::Input)), - "OUT" => tokens.push(Token::Mnemonic(Mnemonic::Output)), - _ => { - if is_number(word) { - println!("{}", word); - let number: u16 = word.trim().parse().unwrap(); - tokens.push(Token::Literal(number)); - } else { - tokens.push(Token::Label(word.trim().to_string())) - } - } - } - } - } - - tokens -} - -fn syntax_analysis(tokens: Vec<Token>) -> bool { - for index in 0..tokens.len() { - match &tokens[index] { - Token::Mnemonic(mnemonic) => match mnemonic { - Mnemonic::Add - | Mnemonic::Subtract - | Mnemonic::BranchAlways - | Mnemonic::BranchPositive - | Mnemonic::BranchZero - | Mnemonic::Dat - | Mnemonic::Load - | Mnemonic::Save => match &tokens[index + 1] { - Token::Label(_) => (), - _ => return false, - }, - Mnemonic::Halt | Mnemonic::Input | Mnemonic::Output => match &tokens[index + 1] { - Token::Mnemonic(_) => (), - _ => return false, - }, - }, - Token::Label(_) => {} - Token::Literal(_) => {} - } - } - - true -} - -fn translate_to_opcodes(mnemonics: Vec<Mnemonic>) -> Vec<u16> { - todo!(); -} + let instructions = parser::parse_file(file_contents.trim().to_string()); -fn is_number(s: &str) -> bool { - for c in s.chars() { - if !c.is_numeric() { - return false; - } - } - true + interpreter::interpret(&instructions).unwrap(); } diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..544e3ed --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,288 @@ +#[derive(Debug, Clone)]
+pub enum Mnemonic {
+ Load,
+ Save,
+ Halt,
+ Add,
+ Subtract,
+ Dat,
+ BranchPositive,
+ BranchZero,
+ BranchAlways,
+ Input,
+ Output,
+}
+
+#[derive(Debug, Clone)]
+pub enum Parameter {
+ Literal(u16),
+ Label(String),
+}
+
+#[derive(Debug, Clone)]
+pub struct Label {
+ name: String,
+ value: u16,
+}
+
+impl Label {
+ fn new(name: String, value: u16) -> Self {
+ Self { name, value }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum Token {
+ Mnemonic(Mnemonic),
+ Parameter(Parameter),
+ Label(Label),
+ LineBreak,
+}
+
+#[derive(Debug, Clone)]
+pub enum Instruction {
+ Load(u16),
+ Save(u16),
+ Add(u16),
+ Subtract(u16),
+ Dat(u16),
+ BranchPositive(u16),
+ BranchZero(u16),
+ BranchAlways(u16),
+ Halt,
+ Input,
+ Output,
+}
+
+pub fn parse_file(file_contents: String) -> Vec<Instruction> {
+ let lines: Vec<&str> = file_contents.split("\n").collect();
+
+ let tokens: Vec<Token> = tokenise(lines);
+ syntax_analysis(&tokens).expect("Syntax is invalid");
+
+ let instructions = translate_to_instructions(&tokens);
+
+ instructions
+}
+
+fn tokenise(lines: Vec<&str>) -> Vec<Token> {
+ let mut tokens: Vec<Token> = Vec::new();
+
+ for i in 0..lines.len() {
+ let words: Vec<&str> = lines[i].split(" ").collect();
+
+ for j in 0..words.len() {
+ let word = words[j].trim();
+ match word.to_uppercase().as_str() {
+ "LDA" => tokens.push(Token::Mnemonic(Mnemonic::Load)),
+ "STA" => tokens.push(Token::Mnemonic(Mnemonic::Save)),
+ "HLT" => tokens.push(Token::Mnemonic(Mnemonic::Halt)),
+ "ADD" => tokens.push(Token::Mnemonic(Mnemonic::Add)),
+ "SUB" => tokens.push(Token::Mnemonic(Mnemonic::Subtract)),
+ "DAT" => tokens.push(Token::Mnemonic(Mnemonic::Dat)),
+ "BRP" => tokens.push(Token::Mnemonic(Mnemonic::BranchPositive)),
+ "BRZ" => tokens.push(Token::Mnemonic(Mnemonic::BranchZero)),
+ "BRA" => tokens.push(Token::Mnemonic(Mnemonic::BranchAlways)),
+ "INP" => tokens.push(Token::Mnemonic(Mnemonic::Input)),
+ "OUT" => tokens.push(Token::Mnemonic(Mnemonic::Output)),
+ "" => (),
+ _ => {
+ if is_number(word) {
+ let number: u16 = word.parse().unwrap();
+ tokens.push(Token::Parameter(Parameter::Literal(number)));
+ } else {
+ if j == 0 {
+ tokens.push(Token::Label(Label::new(word.to_string(), i as u16)))
+ } else {
+ tokens.push(Token::Parameter(Parameter::Label(word.to_string())))
+ }
+ }
+ }
+ }
+ }
+
+ tokens.push(Token::LineBreak)
+ }
+
+ tokens
+}
+
+fn get_labels(tokens: &Vec<Token>) -> Result<Vec<&str>, &'static str> {
+ let mut labels: Vec<&str> = Vec::new();
+
+ for token in tokens {
+ match token {
+ Token::Label(label) => {
+ if labels.contains(&label.name.as_str()) {
+ return Err("Label already used");
+ } else {
+ labels.push(&label.name);
+ }
+ }
+ _ => (),
+ }
+ }
+
+ Ok(labels)
+}
+
+fn syntax_analysis(tokens: &Vec<Token>) -> Result<(), ()> {
+ let labels = get_labels(&tokens).expect("Expected one label declaration");
+
+ for i in 0..tokens.len() {
+ match &tokens[i] {
+ Token::Mnemonic(mnemonic) => match mnemonic {
+ Mnemonic::Add
+ | Mnemonic::Subtract
+ | Mnemonic::BranchAlways
+ | Mnemonic::BranchPositive
+ | Mnemonic::BranchZero
+ | Mnemonic::Load
+ | Mnemonic::Save => {
+ if i + 1 < tokens.len() {
+ match tokens[i + 1] {
+ Token::Parameter(_) => (),
+ _ => return Err(()),
+ }
+ }
+ }
+ Mnemonic::Dat => {
+ if i + 1 < tokens.len() {
+ match &tokens[i + 1] {
+ Token::Parameter(param) => match param {
+ Parameter::Literal(_) => (),
+ Parameter::Label(_) => return Err(()),
+ },
+ _ => return Err(()),
+ }
+ }
+ }
+ Mnemonic::Halt | Mnemonic::Input | Mnemonic::Output => {
+ if i + 1 < tokens.len() {
+ match tokens[i + 1] {
+ Token::LineBreak => (),
+ _ => return Err(()),
+ }
+ }
+ }
+ },
+ Token::Label(_) => {
+ if i + 1 < tokens.len() {
+ match tokens[i + 1] {
+ Token::Mnemonic(_) => (),
+ _ => return Err(()),
+ }
+ }
+ }
+ Token::Parameter(param) => {
+ match param {
+ Parameter::Label(label) => {
+ if !labels.contains(&label.as_str()) {
+ return Err(());
+ }
+ }
+ Parameter::Literal(_) => {}
+ }
+
+ if i + 1 < tokens.len() {
+ match tokens[i + 1] {
+ Token::LineBreak => (),
+ _ => return Err(()),
+ }
+ }
+ }
+ Token::LineBreak => {
+ if i + 1 < tokens.len() {
+ match tokens[i + 1] {
+ Token::Mnemonic(_) => (),
+ Token::Label(_) => (),
+ _ => return Err(()),
+ }
+ }
+ }
+ }
+ }
+
+ Ok(())
+}
+
+fn translate_to_instructions(tokens: &Vec<Token>) -> Vec<Instruction> {
+ let mut instructions: Vec<Instruction> = Vec::new();
+
+ let mut labels: Vec<Label> = Vec::new();
+
+ for token in tokens {
+ match token {
+ Token::Label(label) => labels.push(label.clone()),
+ _ => (),
+ }
+ }
+
+ let mut mnemonic: Mnemonic = Mnemonic::Halt;
+ let mut parameter: u16 = 0;
+
+ for token in tokens.iter() {
+ match token {
+ Token::Mnemonic(mnem) => mnemonic = mnem.clone(),
+ Token::Parameter(param) => match param {
+ Parameter::Label(label_name) => {
+ for label in &labels {
+ if &label.name == label_name {
+ parameter = label.value;
+ }
+ }
+ }
+ Parameter::Literal(literal) => parameter = *literal,
+ },
+ Token::LineBreak => instructions.push(match mnemonic {
+ Mnemonic::Load => Instruction::Load(parameter),
+ Mnemonic::Save => Instruction::Save(parameter),
+ Mnemonic::Add => Instruction::Add(parameter),
+ Mnemonic::Subtract => Instruction::Subtract(parameter),
+ Mnemonic::Dat => Instruction::Dat(parameter),
+ Mnemonic::BranchAlways => Instruction::BranchAlways(parameter),
+ Mnemonic::BranchPositive => Instruction::BranchPositive(parameter),
+ Mnemonic::BranchZero => Instruction::BranchZero(parameter),
+ Mnemonic::Halt => Instruction::Halt,
+ Mnemonic::Input => Instruction::Input,
+ Mnemonic::Output => Instruction::Output,
+ }),
+ Token::Label(_) => (),
+ }
+ }
+
+ instructions
+}
+
+pub fn is_number(s: &str) -> bool {
+ for c in s.chars() {
+ if !c.is_numeric() {
+ return false;
+ }
+ }
+ true
+}
+
+pub fn print_opcodes(instructions: &Vec<Instruction>) {
+ for i in 0..instructions.len() {
+ let instruction = instructions[i].clone();
+
+ let instruction_string = match instruction {
+ Instruction::Load(param) => format!("LDA {:02}", param),
+ Instruction::Save(param) => format!("STA {:02}", param),
+ Instruction::Add(param) => format!("ADD {:02}", param),
+ Instruction::Subtract(param) => format!("SUB {:02}", param),
+ Instruction::Dat(param) => format!("DAT {:02}", param),
+ Instruction::BranchAlways(param) => format!("BRA {:02}", param),
+ Instruction::BranchPositive(param) => format!("BRP {:02}", param),
+ Instruction::BranchZero(param) => format!("BRZ {:02}", param),
+ Instruction::Halt => "HLT".to_string(),
+ Instruction::Input => "INP".to_string(),
+ Instruction::Output => "OUT".to_string(),
+ };
+
+ let output_string = format!("{:02} {}", i, instruction_string);
+
+ println!("{}", output_string);
+ }
+}
|
