-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsymbol_table.rs
More file actions
118 lines (104 loc) · 3.4 KB
/
symbol_table.rs
File metadata and controls
118 lines (104 loc) · 3.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use std::collections::HashMap;
use tiny_elf::asm::{Operand, Program};
/// The symbol table
///
/// Note there is no parent table because we don't need it in TinyBASIC, everything is global.
#[derive(Default, Debug)]
pub struct SymbolTable {
symbols: HashMap<String, Symbol>,
/// Current address on the stack
///
/// This is basically the compiler's `rsp`
current_address: u32,
}
impl SymbolTable {
/// Creates a new symbol in the table
pub fn insert(&mut self, name: &str, ty: Type) {
// This basically implements shadowing
// Meaning you can declare a variable with the same name
if self.get(name).is_some() {
return;
}
self.symbols
.insert(name.into(), Symbol::new(ty, self.current_address));
self.current_address += 8;
}
/// Gets a symbol by name
pub fn get(&self, name: &str) -> Option<&Symbol> {
self.symbols.get(name)
}
/// The size to allocate on the stack for every symbol to fit
///
/// Note that string are note on the stack but in the data section.
pub fn size(&self) -> u32 {
self.symbols
.values()
.filter(|s| s.ty != Type::String)
.fold(0, |acc, s| acc + s.ty.size())
}
/// Puts the variable with the given name in the [`Rbx`](tiny_elf::asm::Register::Rbx) register
///
/// This is not usually part of a symbol table. However, this is convenient since stack access
/// is so complicated because pointer arithmetic is not implemented.
pub fn access(&self, name: &str, program: Program) -> Program {
use tiny_elf::asm::{Mnemonic::*, Register::*};
let addr = self.get(name).unwrap().end_addr() as i32;
program
.add(Mov(R14, Rsp.into()))
.add(Mov(Rsp, R15.into()))
.add(Sub(Rsp, addr.into()))
.add(Pop(Rbx))
.add(Mov(Rsp, R14.into()))
}
/// [`Mov`](tiny_elf::asm::Mnemonic::Mov) the given operand into the variable with the given
/// name
///
/// This is not usually part of a symbol table. However, this is convenient since stack access
/// is so complicated because pointer arithmetic is not implemented.
pub fn write(&self, name: &str, value: Operand, program: Program) -> Program {
use tiny_elf::asm::{Mnemonic::*, Register::*};
let addr = self.get(name).unwrap().st_addr() as i32;
program
.add(Mov(R14, Rsp.into()))
.add(Mov(Rsp, R15.into()))
.add(Sub(Rsp, addr.into()))
.add(Push(value))
.add(Mov(Rsp, R14.into()))
}
}
#[derive(Default, Debug)]
pub struct Symbol {
ty: Type,
/// Its address on the stack
address: u32,
}
impl Symbol {
fn new(ty: Type, address: u32) -> Self {
Self { ty, address }
}
/// Gets the start address of this symbol
///
/// Useful for writing to it.
pub fn st_addr(&self) -> u32 {
self.address
}
/// Gets the end address of this symbol
///
/// Useful for reading it.
pub fn end_addr(&self) -> u32 {
self.address + self.ty.size()
}
}
#[derive(Default, Debug, PartialEq)]
pub enum Type {
#[default]
Int,
/// Strings are currently not used in this implementation because they cannot be in a variable
String,
}
impl Type {
/// Returns the size in memory of this data type
pub fn size(&self) -> u32 {
8
}
}