Website Summary Files Commits Branches @commit @diff @stats
path: root / assembler / src
diff options
context:
space:
mode:
Commit1395b9eac37187227874c2bf4e768680c7dffd32 (patch)
Author
 Syndamia <kamen@syndamia.com>
2025-02-13 21:53:10 +0200
Committer
 Syndamia <kamen@syndamia.com>
2025-02-13 21:53:10 +0200
Files50ad9f0c614e1ff07e20964a170b1f27116069ea /assembler/src
tonkobali-1395b9eac37187227874c2bf4e768680c7dffd32.tar
tonkobali-1395b9eac37187227874c2bf4e768680c7dffd32.tar.gz
tonkobali-1395b9eac37187227874c2bf4e768680c7dffd32.zip
Parent3ea01bbd75cc252453cd21074f56eb9cc6cff317 (diff)
feat(assembler): Implement first step of parser
4 files changed, 140 insertions, 8 deletions
-rw-r--r--assembler/src/lexer/mod.rs32
-rw-r--r--assembler/src/main.rs12
-rw-r--r--assembler/src/parser/instruction.rs0
-rw-r--r--assembler/src/parser/mod.rs104
diff --git a/assembler/src/lexer/mod.rs b/assembler/src/lexer/mod.rs
index 617aca1..679df0f 100644
--- a/assembler/src/lexer/mod.rs
+++ b/assembler/src/lexer/mod.rs
@@ -1,9 +1,13 @@
+use crate::Word;
+
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum Token {
Label(String),
Operator(String),
Symbol(String),
- Number(i64),
+ SymbolDef(String),
+ Number(Word),
+ Address(Word),
String(String),
}
@@ -16,6 +20,7 @@ pub fn split(mut line: String) -> Vec<Token> {
let mut has_label = false;
let mut string_started = false;
let mut skip_next = false;
+ let mut address = false;
for c in line.chars() {
buffer.push(c);
@@ -52,31 +57,42 @@ pub fn split(mut line: String) -> Vec<Token> {
}
string_started = !string_started;
},
- ',' | '\n' => {
+ ',' | '\n' | ';' => {
buffer.pop();
if buffer.is_empty() { continue; }
- if let Some(num) = buffer.parse::<i64>().ok() {
- res.push(Token::Number(num));
+ if let Some(num) = buffer.parse::<Word>().ok() {
+ if address {
+ address = false;
+ res.push(Token::Address(num));
+ }
+ else {
+ res.push(Token::Number(num));
+ }
}
else {
res.push(Token::Symbol(buffer.clone()));
}
buffer.clear();
+
+ if c == ';' { break; }
},
- '\\' => skip_next = true,
- ';' => break,
'=' => if buffer.is_empty() {
panic!("Cannot begin the line with an =")
}
else if let Token::Operator(name) = &res[0] {
- res[0] = Token::Symbol(name.clone());
+ res[0] = Token::SymbolDef(name.clone());
buffer.clear();
}
else {
panic!("Before an = you may only have a symbol name!");
},
- 'A'..='Z' | 'a'..='z' | '0'..='9' | '_' | '-' | '.' | '$' | '?'
+ '\\' => skip_next = true,
+ '%' => address = true,
+ '-' => if address {
+ panic!("Addresses cannot be negative!");
+ },
+ 'A'..='Z' | 'a'..='z' | '0'..='9' | '_' | '.' | '$' | '?'
=> {},
_ => panic!("Found invalid character '{}'", c),
}
diff --git a/assembler/src/main.rs b/assembler/src/main.rs
index 0cda74e..ebd8177 100644
--- a/assembler/src/main.rs
+++ b/assembler/src/main.rs
@@ -1,18 +1,30 @@
mod lexer;
+mod parser;
use std::fs::File;
use std::io::{BufRead, BufReader};
+use parser::Program;
+
+pub(crate) type Word = i64;
+
fn main() {
let file_path = "./example.s";
+ /* Read */
let file = File::open(file_path).unwrap();
let lines = BufReader::new(file).lines();
+ /* Lex */
let mut tokens = Vec::new();
for line in lines.map_while(Result::ok) {
tokens.extend(lexer::split(line));
}
println!("{:#?}", tokens);
+
+ /* Parse */
+ let prog = Program::new(tokens);
+
+ println!("{:#?}", prog);
}
diff --git a/assembler/src/parser/instruction.rs b/assembler/src/parser/instruction.rs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/assembler/src/parser/instruction.rs
diff --git a/assembler/src/parser/mod.rs b/assembler/src/parser/mod.rs
new file mode 100644
index 0000000..7061286
--- /dev/null
+++ b/assembler/src/parser/mod.rs
@@ -0,0 +1,104 @@
+use std::collections::HashMap;
+use crate::{ Word, lexer::Token };
+
+#[derive(Debug, Default)]
+pub(crate) struct Instruction {
+ name: String,
+ args: Vec<Address>,
+}
+
+#[derive(Debug, Default, PartialEq, Eq, Clone)]
+pub(crate) enum Address {
+ Memory(usize),
+ Constants(usize),
+ Definitions(String),
+ Instruction(usize),
+ #[default]
+ None,
+}
+
+impl Address {
+ fn take(&mut self) -> Address {
+ let val = self.clone();
+ *self = Address::None;
+ val
+ }
+}
+
+#[derive(Debug, Default)]
+pub(crate) struct Program {
+ definitions: HashMap<String, Address>,
+ constants: Vec<Word>,
+ instructions: Vec<Instruction>,
+}
+
+impl Program {
+ pub(crate) fn new(tokens: Vec<Token>) -> Program {
+ let mut prog = Program {..Default::default() };
+
+ let mut op_name = String::new();
+ let mut op_args = Vec::new();
+ let mut value = Address::None;
+ let mut symbol_def = String::new();
+
+ for token in tokens {
+ match token {
+ /* Names (lvalue) */
+ Token::Operator(name) => {
+ if ! op_name.is_empty() {
+ prog.instructions.push(Instruction {
+ name: op_name,
+ args: op_args.clone(),
+ });
+ }
+ op_name = name;
+ op_args.clear();
+ },
+ Token::Label(name) => {
+ // Predicting the index
+ let index = prog.instructions.len() + !op_name.is_empty() as usize;
+ prog.definitions.insert(name, Address::Instruction(index));
+ },
+ Token::SymbolDef(name) => symbol_def = name,
+
+ /* Values (rvalue) */
+ Token::Address(num) => {
+ value = Address::Memory(num as usize);
+ },
+ Token::Number(num) => {
+ prog.constants.push(num);
+ value = Address::Constants(prog.constants.len() - 1);
+ },
+ Token::String(str) => {
+ let index = prog.constants.len();
+ for c in str.chars() {
+ prog.constants.push(c as Word);
+ }
+ value = Address::Constants(index);
+ },
+ Token::Symbol(name) => {
+ value = Address::Definitions(name);
+ },
+ }
+
+ if value != Address::None {
+ if ! symbol_def.is_empty() {
+ prog.definitions.insert(symbol_def.clone(), value.take());
+ symbol_def.clear();
+ }
+ else {
+ op_args.push(value.take());
+ }
+ }
+ }
+
+ if ! op_name.is_empty() {
+ prog.instructions.push(Instruction {
+ name: op_name,
+ args: op_args.clone(),
+ });
+ }
+
+ prog
+ }
+}