1use std::collections::BTreeMap;
3use std::error::Error;
4use std::fmt::{self, Display, Formatter};
5
6use base::Unsigned18Bit;
7use base::Unsigned36Bit;
8use base::prelude::Address;
9use base::prelude::IndexBy;
10
11use super::ast::InstructionSequence;
12use super::ast::Origin;
13use super::ast::RcUpdater;
14use super::listing::Listing;
15use super::source::Source;
16use super::span::{Span, Spanned};
17use super::symbol::SymbolName;
18use super::symtab::{
19 ExplicitSymbolTable, FinalSymbolTable, ImplicitSymbolTable, IndexRegisterAssigner,
20};
21use super::types::AssemblerFailure;
22use super::types::BlockIdentifier;
23use super::types::ProgramError;
24
25#[derive(Debug, PartialEq, Eq, Clone)]
26pub enum RcWordKind {
27 PipeConstruct,
29 Braces,
30 DefaultAssignment,
31}
32
33#[derive(Debug, PartialEq, Eq, Clone)]
35pub struct RcWordSource {
36 pub span: Span,
37 pub kind: RcWordKind,
38}
39
40impl Spanned for RcWordSource {
41 fn span(&self) -> Span {
42 self.span
43 }
44}
45
46impl Display for RcWordKind {
47 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
48 f.write_str(match self {
49 RcWordKind::PipeConstruct => "pipe construct",
50 RcWordKind::Braces => "RC-word",
51 RcWordKind::DefaultAssignment => "default-assignment",
52 })
53 }
54}
55
56#[derive(Debug, PartialEq, Eq, Clone)]
57pub(crate) enum RcWordAllocationFailure {
58 RcBlockTooBig {
59 source: RcWordSource,
60 rc_block_len: usize,
61 },
62 InconsistentTag {
63 tag_name: SymbolName,
64 span: Span,
65 explanation: String,
66 },
67}
68
69impl Display for RcWordAllocationFailure {
70 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
71 match self {
72 RcWordAllocationFailure::RcBlockTooBig {
73 source: RcWordSource { kind, .. },
74 rc_block_len,
75 } => {
76 write!(
77 f,
78 "failed to allocate RC word for {kind}; RC block is already {rc_block_len} words long"
79 )
80 }
81 RcWordAllocationFailure::InconsistentTag {
82 tag_name,
83 span: _,
84 explanation,
85 } => {
86 write!(f, "failed to define tag {tag_name}: {explanation}")
87 }
88 }
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub(super) struct BlockPosition {
94 pub(super) block_identifier: BlockIdentifier,
95 pub(super) span: Span,
98 pub(super) origin: Option<Origin>,
99 pub(super) block_address: Option<Address>,
100 pub(super) block_size: Unsigned18Bit,
101}
102
103impl Spanned for BlockPosition {
104 fn span(&self) -> Span {
105 self.span
106 }
107}
108
109#[derive(Debug, Default, Clone, PartialEq, Eq)]
110pub(crate) struct MemoryMap {
111 blocks: Vec<BlockPosition>,
112}
113
114impl MemoryMap {
115 pub(crate) fn get(&self, block: &BlockIdentifier) -> Option<&BlockPosition> {
116 #![allow(clippy::trivially_copy_pass_by_ref)]
119 let pos = usize::from(*block);
120 self.blocks.get(pos)
121 }
122
123 pub(crate) fn set_block_position(&mut self, block: BlockIdentifier, location: Address) {
124 match self.blocks.get_mut(usize::from(block)) {
125 Some(pos) => pos.block_address = Some(location),
126 None => {
127 unreachable!("attempted to set location of nonexistent block {block}");
128 }
129 }
130 }
131
132 pub(crate) fn iter(&self) -> impl Iterator<Item = &BlockPosition> {
133 self.blocks.iter()
134 }
135
136 pub(crate) fn new<I>(block_sizes: I) -> MemoryMap
137 where
138 I: IntoIterator<Item = (Span, Option<Origin>, Unsigned18Bit)>,
139 {
140 let blocks = block_sizes
141 .into_iter()
142 .enumerate()
143 .map(|(i, (span, maybe_origin, block_size))| {
144 if i > 0 {
145 assert!(maybe_origin.is_some());
150 }
151 BlockPosition {
152 block_identifier: BlockIdentifier::from(i),
153 span,
154 origin: maybe_origin,
155 block_address: None,
156 block_size,
157 }
158 })
159 .collect();
160 MemoryMap { blocks }
161 }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
165pub(crate) struct LocatedBlock {
166 pub(crate) origin: Option<Origin>,
167 pub(crate) location: Address,
168 pub(crate) sequences: Vec<InstructionSequence>,
169}
170
171impl LocatedBlock {
172 pub(super) fn emitted_word_count(&self) -> Unsigned18Bit {
173 self.sequences
174 .iter()
175 .map(InstructionSequence::emitted_word_count)
176 .sum()
177 }
178
179 pub(super) fn following_addr(&self) -> Address {
180 self.location.index_by(self.emitted_word_count())
181 }
182
183 pub(super) fn allocate_rc_words<R: RcAllocator>(
184 &mut self,
185 explicit_symtab: &mut ExplicitSymbolTable,
186 implicit_symtab: &mut ImplicitSymbolTable,
187 rc_allocator: &mut R,
188 ) -> Result<(), RcWordAllocationFailure> {
189 for seq in &mut self.sequences {
190 seq.allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)?;
191 }
192 Ok(())
193 }
194
195 #[allow(clippy::too_many_arguments)]
196 pub(super) fn build_binary_block<R: RcUpdater>(
197 &self,
198 location: Address,
199 explicit_symtab: &ExplicitSymbolTable,
200 implicit_symtab: &mut ImplicitSymbolTable,
201 memory_map: &MemoryMap,
202 index_register_assigner: &mut IndexRegisterAssigner,
203 rc_allocator: &mut R,
204 final_symbols: &mut FinalSymbolTable,
205 body: &Source,
206 listing: &mut Listing,
207 undefined_symbols: &mut BTreeMap<SymbolName, ProgramError>,
208 ) -> Result<Vec<Unsigned36Bit>, AssemblerFailure> {
209 let word_count: usize = self
210 .sequences
211 .iter()
212 .map(|seq| usize::from(seq.emitted_word_count()))
213 .sum();
214 let mut result: Vec<Unsigned36Bit> = Vec::with_capacity(word_count);
215 for seq in &self.sequences {
216 let current_block_len: Unsigned18Bit = result
217 .len()
218 .try_into()
219 .expect("assembled code block should fit within physical memory");
220 let mut words: Vec<Unsigned36Bit> = seq.build_binary_block(
221 location,
222 current_block_len,
223 explicit_symtab,
224 implicit_symtab,
225 memory_map,
226 index_register_assigner,
227 rc_allocator,
228 final_symbols,
229 body,
230 listing,
231 undefined_symbols,
232 )?;
233 result.append(&mut words);
234 }
235 Ok(result)
236 }
237}
238
239impl Error for RcWordAllocationFailure {}
240
241pub(crate) trait RcAllocator {
242 fn allocate(
243 &mut self,
244 source: RcWordSource,
245 value: Unsigned36Bit,
246 ) -> Result<Address, RcWordAllocationFailure>;
247}