use std::collections::HashMap;
use std::rc::{Rc, Weak};
use std::cell::RefCell;
#[derive(Debug)]
pub struct SourceLine {
pub definition: Option<Weak<RefCell<Symbol>>>,
pub references: Vec<Weak<RefCell<Symbol>>>,
pub is_dirty: bool,
pub line_offset: i32,
pub is_origin: bool,
pub data_bytes: Vec<u8>,
pub relative_offset: i32,
pub include_bytes_read: Option<usize>,
}
impl SourceLine {
pub fn new() -> Self {
Self {
definition: None,
references: Vec::new(),
is_dirty: false,
line_offset: 0,
is_origin: false,
data_bytes: Vec::new(),
relative_offset: -1,
include_bytes_read: None,
}
}
pub fn get_offset(&self) -> i32 {
self.line_offset
}
pub fn get_byte_count(&self) -> usize {
self.data_bytes.len()
}
pub fn set_data(&mut self, data: &[u8]) -> i32 {
let delta = data.len() as i32 - self.data_bytes.len() as i32;
self.data_bytes = data.to_vec();
delta
}
pub fn get_data(&self) -> &[u8] {
&self.data_bytes
}
pub fn set_offset(&mut self, byte_offset: i32, is_origin: bool) {
self.line_offset = byte_offset;
self.is_origin = is_origin;
}
pub fn adjust_offset(&mut self, offset: i32) {
self.set_offset(self.line_offset + offset, false);
}
pub fn mark_dirty(&mut self, is_dirty: bool) {
self.is_dirty = is_dirty;
}
pub fn is_dirty(&self) -> bool {
self.is_dirty
}
pub fn is_origin(&self) -> bool {
self.is_origin
}
pub fn set_relative(&mut self, offset: i32) {
self.relative_offset = offset;
}
pub fn get_relative_offset(&self) -> i32 {
self.relative_offset
}
pub fn reset_references(&mut self, self_ref: Weak<RefCell<SourceLine>>) {
for weak_symbol in &self.references {
if let Some(symbol) = weak_symbol.upgrade() {
symbol.borrow_mut().remove_reference(self_ref.clone());
}
}
self.references.clear();
}
pub fn add_reference(&mut self, symbol: Weak<RefCell<Symbol>>) {
self.references.push(symbol);
}
pub fn set_include_bytes_read(&mut self, bytes: usize) {
self.include_bytes_read = Some(bytes);
}
pub fn set_definition(&mut self, symbol: Option<Weak<RefCell<Symbol>>>, self_ref: Weak<RefCell<SourceLine>>) {
if let Some(old_def) = &self.definition {
if let Some(old_symbol) = old_def.upgrade() {
old_symbol.borrow_mut().remove_definition_for_line(self_ref.clone());
}
}
self.definition = symbol;
}
}
#[derive(Debug)]
pub struct Symbol {
pub value: i32,
pub definitions: u32,
pub is_keyword: bool,
pub defined_lines: Vec<Weak<RefCell<SourceLine>>>,
pub referenced_lines: Vec<Weak<RefCell<SourceLine>>>,
}
impl Symbol {
pub fn new() -> Self {
Self {
value: 0,
definitions: 0,
is_keyword: false,
defined_lines: Vec::new(),
referenced_lines: Vec::new(),
}
}
pub fn set_keyword(&mut self, value: i32) {
self.is_keyword = true;
self.value = value;
}
pub fn is_keyword(&self) -> bool {
self.is_keyword
}
pub fn is_referenced(&self) -> bool {
!self.referenced_lines.is_empty()
}
pub fn get_value(&self) -> i32 {
self.value
}
pub fn set_value(&mut self, value: i32) {
if value == self.value {
return;
}
self.value = value;
self.referenced_lines.retain(|weak_ref| {
if let Some(line) = weak_ref.upgrade() {
line.borrow_mut().mark_dirty(true);
true
} else {
false
}
});
}
pub fn add_definition(&mut self, source_line: Weak<RefCell<SourceLine>>) {
self.definitions += 1;
self.defined_lines.push(source_line);
}
pub fn add_reference(&mut self, source_line: Weak<RefCell<SourceLine>>) {
self.referenced_lines.push(source_line);
}
pub fn get_definition_count(&self) -> u32 {
self.definitions
}
pub fn remove_definition_for_line(&mut self, source_line: Weak<RefCell<SourceLine>>) {
self.defined_lines.retain(|weak_ref| {
if let (Some(existing), Some(target)) = (weak_ref.upgrade(), source_line.upgrade()) {
!Rc::ptr_eq(&existing, &target)
} else {
false }
});
self.definitions = self.defined_lines.len() as u32;
for weak_def in &self.defined_lines {
if let Some(def_line) = weak_def.upgrade() {
def_line.borrow_mut().mark_dirty(true);
}
}
}
pub fn remove_reference(&mut self, source_line: Weak<RefCell<SourceLine>>) {
self.referenced_lines.retain(|weak_ref| {
if let (Some(existing), Some(target)) = (weak_ref.upgrade(), source_line.upgrade()) {
!Rc::ptr_eq(&existing, &target)
} else {
false }
});
}
}
pub struct SymbolTable {
symbols: HashMap<String, Rc<RefCell<Symbol>>>,
}
impl SymbolTable {
pub fn new() -> Self {
Self {
symbols: HashMap::new(),
}
}
pub fn lookup_symbol(&mut self, name: &str) -> Rc<RefCell<Symbol>> {
let symbol_name = name.to_uppercase();
if let Some(symbol) = self.symbols.get(&symbol_name) {
symbol.clone()
} else {
let symbol = Rc::new(RefCell::new(Symbol::new()));
self.symbols.insert(symbol_name, symbol.clone());
symbol
}
}
pub fn lookup_symbol_with_reference(&mut self, name: &str, source_line: Weak<RefCell<SourceLine>>) -> Rc<RefCell<Symbol>> {
let symbol_name = name.to_uppercase();
let symbol = if let Some(existing) = self.symbols.get(&symbol_name) {
existing.clone()
} else {
let new_symbol = Rc::new(RefCell::new(Symbol::new()));
self.symbols.insert(symbol_name, new_symbol.clone());
new_symbol
};
if !symbol.borrow().is_keyword() {
symbol.borrow_mut().add_reference(source_line.clone());
if let Some(line) = source_line.upgrade() {
line.borrow_mut().add_reference(Rc::downgrade(&symbol));
}
}
symbol
}
pub fn lookup_keyword(&self, name: &str) -> Option<i32> {
let symbol_name = name.to_uppercase();
if let Some(symbol) = self.symbols.get(&symbol_name) {
let symbol_ref = symbol.borrow();
if symbol_ref.is_keyword() {
Some(symbol_ref.get_value())
} else {
None
}
} else {
None
}
}
pub fn define_symbol(&mut self, name: &str, source_line: Weak<RefCell<SourceLine>>, value: i32) -> Rc<RefCell<Symbol>> {
let symbol = self.lookup_symbol(name);
{
let mut symbol_ref = symbol.borrow_mut();
symbol_ref.set_value(value);
symbol_ref.add_definition(source_line.clone());
}
if let Some(line) = source_line.upgrade() {
line.borrow_mut().set_definition(Some(Rc::downgrade(&symbol)), source_line.clone());
}
symbol
}
pub fn define_keyword(&mut self, name: &str, value: i32) -> Rc<RefCell<Symbol>> {
let symbol_name = name.to_uppercase();
let symbol = if let Some(existing) = self.symbols.get(&symbol_name) {
existing.clone()
} else {
let new_symbol = Rc::new(RefCell::new(Symbol::new()));
self.symbols.insert(symbol_name, new_symbol.clone());
new_symbol
};
symbol.borrow_mut().set_keyword(value);
symbol
}
}