summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortyropro <[email protected]>2026-05-22 21:46:18 +0100
committertyropro <[email protected]>2026-05-22 21:46:18 +0100
commite853164a0cd623fc2f643689883442ddfb3622c0 (patch)
tree58c6e4a619b15bc56c3fed1ecf342f3b60e58000
parent6cd94ed60f143d5c69ef5b39da4472852243381a (diff)
feature: finished interpreter
-rw-r--r--lmc_code/bubble_sort_invalid.lmc83
-rw-r--r--lmc_code/max.lmc4
-rw-r--r--src/interpreter.rs125
-rw-r--r--src/main.rs132
-rw-r--r--src/parser.rs288
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);
+ }
+}