Skip to main content

assembler/
eval.rs

1//! Turn the symbolic program into a sequence of words in binary.
2//!
3//! We evaluate (i.e. generate binary from) high-level parts of the
4//! abstract syntax tree, including assignment of default values.
5//! Some of the evaluation of individual parts of the abstract
6//! representation is performed in [`super::ast`].
7use std::collections::BTreeMap;
8use std::fmt::{self, Debug, Display, Formatter};
9use std::ops::Shl;
10
11use tracing::{Level, event};
12
13use base::{
14    charset::Script,
15    prelude::{Address, IndexBy, Unsigned18Bit, Unsigned36Bit},
16    subword,
17};
18#[cfg(test)]
19use base::{u18, u36};
20
21use super::ast::{Equality, Origin, RcUpdater};
22use super::collections::OneOrMore;
23use super::memorymap::{
24    BlockPosition, MemoryMap, RcAllocator, RcWordAllocationFailure, RcWordSource,
25};
26use super::source::Source;
27#[cfg(test)]
28use super::span::{OrderableSpan, span};
29use super::span::{Span, Spanned};
30#[cfg(test)]
31use super::symbol::AddressUse;
32use super::symbol::{ConfigUse, IndexUse, OriginUse, SymbolName};
33
34use super::types::{AssemblerFailure, BlockIdentifier, MachineLimitExceededFailure, ProgramError};
35use crate::symbol::SymbolContext;
36use crate::symtab::{
37    ExplicitDefinition, ExplicitSymbolTable, FinalSymbolDefinition, FinalSymbolTable,
38    FinalSymbolType, ImplicitDefinition, ImplicitSymbolTable, IndexRegisterAssigner,
39    LookupOperation, TagDefinition, record_undefined_symbol_or_return_failure,
40};
41
42/// We ran out of index registers when assigning a default value.
43///
44/// Symbols used only in an index context but which have no definition
45/// are assigned the next available index register. This error
46/// indicates that we couldn't do that because we ran out.
47#[derive(Debug, PartialEq, Eq, Clone, Hash)]
48pub(crate) struct ExhaustedIndexRegisters {
49    /// Location of the symbol we were trying to default-assign
50    span: Span,
51    /// Name of the symbol we were trying to default-assign.
52    name: SymbolName,
53}
54
55/// We failed while evaluating the value of a word (typically, the
56/// value of a word in the final assembled output).
57#[derive(Debug, PartialEq, Eq)]
58pub(crate) enum EvaluationFailure {
59    /// Evaluation failed because there was a loop in the definition
60    /// of a symbol (i.e. in one of the equaities we needed to
61    /// evaluate to determine the final result).
62    SymbolDefinitionLoop {
63        span: Span,
64        deps_in_order: OneOrMore<SymbolName>,
65    },
66    /// A block of the program is too large.
67    BlockTooLarge(Span, MachineLimitExceededFailure),
68    /// More index values needed to be default-assigned than there are
69    /// index registers in the TX-2 architecture.
70    FailedToAssignIndexRegister(ExhaustedIndexRegisters),
71
72    /// We could not evaluate something because `#` was used in a
73    /// context in which it was not allowed (an origin value).
74    HereIsNotAllowedHere(Span),
75}
76
77impl Spanned for EvaluationFailure {
78    fn span(&self) -> Span {
79        match self {
80            EvaluationFailure::HereIsNotAllowedHere(span)
81            | EvaluationFailure::FailedToAssignIndexRegister(ExhaustedIndexRegisters {
82                span,
83                ..
84            })
85            | EvaluationFailure::SymbolDefinitionLoop { span, .. }
86            | EvaluationFailure::BlockTooLarge(span, _) => *span,
87        }
88    }
89}
90
91impl Display for EvaluationFailure {
92    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
93        match self {
94            EvaluationFailure::SymbolDefinitionLoop {
95                deps_in_order,
96                span: _,
97            } => {
98                let names: Vec<String> = deps_in_order.iter().map(ToString::to_string).collect();
99                write!(
100                    f,
101                    "definition of {} has a dependency loop ({})",
102                    deps_in_order.first(),
103                    names.join("->")
104                )
105            }
106            EvaluationFailure::FailedToAssignIndexRegister(ExhaustedIndexRegisters {
107                name,
108                ..
109            }) => {
110                write!(
111                    f,
112                    "unable to assign index register as the default value for symbol {name} because there are not enough index registers"
113                )
114            }
115            EvaluationFailure::BlockTooLarge(_span, mle) => {
116                write!(f, "program block too large: {mle}")
117            }
118            EvaluationFailure::HereIsNotAllowedHere(_) => {
119                f.write_str("'#' (representing the current address) is not allowed here")
120            }
121        }
122    }
123}
124
125impl EvaluationFailure {
126    /// Convert an instance of `EvaluationFailure` (which describes
127    /// why an evaluation failed) into an instance of [`ProgramError`]
128    /// (which describes why the user's program cannot be assembled).
129    pub(crate) fn into_program_error(self) -> ProgramError {
130        match self {
131            EvaluationFailure::FailedToAssignIndexRegister(ExhaustedIndexRegisters {
132                span,
133                name,
134            }) => ProgramError::FailedToAssignIndexRegister(span, name),
135            EvaluationFailure::BlockTooLarge(span, mle) => ProgramError::BlockTooLong(span, mle),
136            EvaluationFailure::SymbolDefinitionLoop {
137                deps_in_order,
138                span,
139            } => ProgramError::SymbolDefinitionLoop {
140                symbol_names: deps_in_order,
141                span,
142            },
143            EvaluationFailure::HereIsNotAllowedHere(span) => ProgramError::SyntaxError {
144                span,
145                msg: "# is not allowed in this context".to_string(),
146            },
147        }
148    }
149}
150
151impl std::error::Error for EvaluationFailure {}
152
153/// `HereValue` specifies the value used for `#`.  A `#` always
154/// signifies the address of the word we are trying to assemble.
155#[derive(Debug, PartialEq, Eq, Clone, Copy)]
156pub(crate) enum HereValue {
157    /// '#' refers to an address
158    Address(Address),
159    /// `NotAllowed` is for when '#' is not allowed (this is used
160    /// when evaluating an origin).
161    NotAllowed,
162}
163
164impl HereValue {
165    pub(crate) fn get_address(self, span: &Span) -> Result<Address, EvaluationFailure> {
166        match self {
167            HereValue::Address(addr) => Ok(addr),
168            HereValue::NotAllowed => Err(EvaluationFailure::HereIsNotAllowedHere(*span)),
169        }
170    }
171}
172
173/// Assign a default value for a symbol, using the rules from
174/// [section 6-2.2 of the Users Handbook, "SYMEX DEFINITON - TAGS - EQUALITIES - AUTOMATIC ASSIGNMENT"](https://archive.org/details/tx-2-users-handbook-nov-63/page/n157/mode/1up).
175fn assign_default_value(
176    index_register_assigner: &mut IndexRegisterAssigner,
177    name: &SymbolName,
178    contexts_used: &SymbolContext,
179) -> Result<Unsigned36Bit, ExhaustedIndexRegisters> {
180    event!(
181        Level::DEBUG,
182        "assigning default value for {name} used in contexts {contexts_used:?}"
183    );
184    let span: Span = *contexts_used.any_span();
185    match &contexts_used.origin {
186        OriginUse::IncludesOrigin(_block, _origin) => {
187            unreachable!(
188                "assign_default_value should not be called for origin specifications; attempted to assign default value for {name} (used in contexts: {contexts_used:?}"
189            )
190        }
191        OriginUse::NotOrigin { config, index } => match (config, index) {
192            (ConfigUse::NotConfig, IndexUse::NotIndex) => {
193                if contexts_used.is_address() {
194                    // Values which refer to addresses (and which
195                    // therefore should point to a zero-initialised
196                    // RC-word) should already have a default value
197                    // assigned.
198                    unreachable!(
199                        "default assignments for address-context symexes should be assigned before evaluation starts"
200                    )
201                }
202                unreachable!(
203                    "attempted to assign a default value for a symbol {name} which appears not to be used in any context at all"
204                )
205            }
206            (ConfigUse::IncludesConfig, _) => Ok(Unsigned36Bit::ZERO),
207            (ConfigUse::NotConfig, IndexUse::IncludesIndex) => {
208                // Index but not also configuration. Assign the next
209                // index register.
210                match index_register_assigner.assign_index_register() {
211                    Some(n) => Ok(n.into()),
212                    None => Err(ExhaustedIndexRegisters {
213                        span,
214                        name: name.clone(),
215                    }),
216                }
217            }
218        },
219    }
220}
221
222/// Context data used by symbol lookup and word value evaluation.
223///
224/// The word-value evaluation process does not modify the memory map
225/// or the symbol table (so those structs are shared references).  But
226/// it does modify the "implicit symbol table" which records
227/// default-assignments for symbols that don't have an explicit
228/// definition.  It can also update RC-words in-place.
229#[derive(Debug)]
230pub(crate) struct EvaluationContext<'s, R: RcUpdater> {
231    /// Defines how to evaluate `#`.
232    pub(crate) here: HereValue,
233    /// Definitions of tags, origins and equalities.
234    pub(crate) explicit_symtab: &'s ExplicitSymbolTable,
235    /// Implicit symbol definitions and information about how these
236    /// symbols have been used.
237    pub(crate) implicit_symtab: &'s mut ImplicitSymbolTable,
238    /// Assigns default values to symbols used in an index context.
239    pub(crate) index_register_assigner: &'s mut IndexRegisterAssigner,
240    /// Memory locations of the program's blocks.
241    pub(crate) memory_map: &'s MemoryMap,
242    /// Performs in-place updates of the values of RC-words.
243    pub(crate) rc_updater: &'s mut R,
244    /// The current lookup operation; this is used to detect loops.
245    pub(crate) lookup_operation: LookupOperation,
246}
247
248impl<R: RcUpdater> EvaluationContext<'_, R> {
249    /// For a symbol lacking an explicit definition, look up the
250    /// default value we assigned to it, or if we didn't assign a
251    /// value yet, assign a default value now and record it.
252    pub(super) fn fetch_or_assign_default(
253        &mut self,
254        name: &SymbolName,
255    ) -> Result<Unsigned36Bit, EvaluationFailure> {
256        let existing_def: &mut ImplicitDefinition = match self.implicit_symtab.get_mut(name) {
257            Some(def) => def,
258            None => {
259                // In order to assign a default value for a symbol, we
260                // need to know the contexts in which it is used (see
261                // [`assign_default_value()`]) and so not having
262                // recorded that information is an internal error.
263                panic!("attempted to assign a default value for an entirely unknown symbol {name}");
264            }
265        };
266
267        match existing_def {
268            ImplicitDefinition::DefaultAssigned(value, _) => Ok(*value),
269            ImplicitDefinition::Undefined(_) => {
270                let saved_def = existing_def.clone();
271                let context: SymbolContext = existing_def.context().clone();
272                let span: Span = *context.any_span();
273
274                // Special case assigning origin addresses, because
275                // the rest of the work is carried out with
276                // index_register_assigner only.
277                if let Some((block_identifier, _origin)) = context.get_origin() {
278                    if let Some(block_position) = self.memory_map.get(&block_identifier).cloned() {
279                        // If we simply try to pass the block's origin
280                        // to evaluate() we will loop and diagnose
281                        // this as an undefined symbol.  While the
282                        // symbol has no definition via assignment, we
283                        // can determine the position of the block by
284                        // appending it to the previous block.  So we
285                        // evaluate the block's position as if it had
286                        // no origin specification.
287                        let position_without_origin = BlockPosition {
288                            origin: None,
289                            ..block_position
290                        };
291                        match position_without_origin.evaluate(self, ScopeIdentifier::global()) {
292                            Ok(value) => {
293                                let address: Address = subword::right_half(value).into();
294                                match self.implicit_symtab.record_deduced_origin_value(
295                                    name,
296                                    address,
297                                    block_identifier,
298                                    span,
299                                ) {
300                                    Ok(()) => Ok(value),
301                                    Err(e) => {
302                                        dbg!(saved_def);
303                                        panic!(
304                                            "got a bad symbol definition error ({e:?}) on a previously undefined origin symbol {name}"
305                                        );
306                                    }
307                                }
308                            }
309                            Err(e) => Err(e),
310                        }
311                    } else {
312                        unreachable!(
313                            "symbol {name} was used as an origin for block {block_identifier} but this is not a known block"
314                        );
315                    }
316                } else {
317                    match assign_default_value(self.index_register_assigner, name, &context) {
318                        Ok(value) => {
319                            *existing_def = ImplicitDefinition::DefaultAssigned(value, context);
320                            Ok(value)
321                        }
322                        Err(e) => Err(EvaluationFailure::FailedToAssignIndexRegister(e)),
323                    }
324                }
325            }
326        }
327    }
328}
329
330#[test]
331fn can_deduce_address_of_origin_with_previous_forward_reference() {
332    // GIVEN a program containing two blocks, in whicht the second block has a
333    // symbolic origin and where there is a forward reference within the first
334    // block to the address of the second block
335    const BLOCK0_SIZE: Unsigned18Bit = u18!(4);
336    const BLOCK1_SIZE: Unsigned18Bit = u18!(3);
337    let origin_name = SymbolName::from("INPUT");
338    let origin_def = Origin::Symbolic(span(2455..2461), origin_name.clone());
339    let block0_pos = BlockPosition {
340        block_identifier: BlockIdentifier::from(0),
341        // The first reference to block 1 is inside block 1, so the
342        // span of block1 includes the location of the first reference
343        // to origin_name.  This is not a necessary setup for our test
344        // scenario, but it is realistic.
345        span: span(0..2400),
346        origin: None,
347        block_address: None,
348        block_size: BLOCK0_SIZE,
349    };
350    let block1_pos = BlockPosition {
351        block_identifier: BlockIdentifier::from(1),
352        span: span(2455..2800),
353        origin: None,
354        block_address: None,
355        block_size: BLOCK1_SIZE,
356    };
357
358    let mut implicit_symtab = ImplicitSymbolTable::default();
359    implicit_symtab.load_test_definitions([(
360        origin_name.clone(),
361        ImplicitDefinition::Undefined(SymbolContext {
362            address: AddressUse::IncludesAddress,
363            origin: OriginUse::IncludesOrigin(block1_pos.block_identifier, origin_def.clone()),
364            uses: [
365                // The first reference is inside block 0.
366                OrderableSpan(span(1188..1193)),
367                // The second reference is in the origin definition of
368                // block 1 (which is the thing we're trying to assign
369                // a default value to).
370                OrderableSpan(span(block1_pos.span.start..(block1_pos.span.start + 6))),
371                // There is a third reference (but this is just from
372                // our motivating example, it is not necessary to the
373                // test).
374                OrderableSpan(span(3059..3064)),
375            ]
376            .into_iter()
377            .collect(),
378        }),
379    )]);
380
381    let explicit_symtab = ExplicitSymbolTable::default();
382    let mut register_assigner = IndexRegisterAssigner::default();
383    let mut memory_map = MemoryMap::new([
384        (span(0..10), None, BLOCK0_SIZE),
385        (
386            span(2455..2461),
387            Some(Origin::Symbolic(span(2455..2461), origin_name.clone())),
388            BLOCK1_SIZE,
389        ),
390    ]);
391    let mut rc_block = RcBlock::default();
392    let mut context = EvaluationContext {
393        here: HereValue::Address(Address::from(u18!(0o100))),
394        explicit_symtab: &explicit_symtab,
395        implicit_symtab: &mut implicit_symtab,
396        index_register_assigner: &mut register_assigner,
397        memory_map: &memory_map,
398        rc_updater: &mut rc_block,
399        lookup_operation: LookupOperation::default(),
400    };
401    let scope = ScopeIdentifier::global();
402
403    // Assign an address for block 0 as part of the scenario setup.
404    let block0_addr = block0_pos
405        .evaluate(&mut context, scope)
406        .expect("should be able to assign an address to the first block");
407    drop(context); // no longer want an immutable borrow on memory_map.
408    memory_map.set_block_position(
409        block0_pos.block_identifier,
410        subword::right_half(block0_addr).into(),
411    );
412
413    // WHEN we try to assign an address to the second block (block 1).
414    let mut context = EvaluationContext {
415        here: HereValue::Address(Address::from(u18!(0o100))),
416        explicit_symtab: &explicit_symtab,
417        implicit_symtab: &mut implicit_symtab,
418        index_register_assigner: &mut register_assigner,
419        memory_map: &memory_map,
420        rc_updater: &mut rc_block,
421        lookup_operation: LookupOperation::default(),
422    };
423    let eval_result = context.fetch_or_assign_default(&origin_name);
424
425    // THEN we should assign an address without failing.
426    match eval_result {
427        Err(e) => {
428            panic!("should not fail to assign a start address to a block: {e}");
429        }
430        Ok(default_value) => {
431            let expected: Unsigned36Bit =
432                u36!(0o200_000).wrapping_add(Unsigned36Bit::from(BLOCK0_SIZE));
433            assert_eq!(default_value, expected);
434        }
435    }
436}
437
438impl<R: RcUpdater> EvaluationContext<'_, R> {
439    pub(super) fn for_target_address<F, T>(&mut self, new_here: HereValue, mut closure: F) -> T
440    where
441        F: FnMut(&mut EvaluationContext<'_, R>) -> T,
442    {
443        let old_here = self.here;
444        self.here = new_here;
445        let output = closure(self);
446        self.here = old_here;
447        output
448    }
449}
450
451/// A scope in which a tag name can be looked up.
452///
453/// Macro bodies can contain "local" tags and so in general a tag's
454/// definition will be local to the scope in which the definition is
455/// made.  However, this is not yet implemented and so for the moment,
456/// `ScopeIdentifier` just contains a unit value.
457#[derive(Debug, PartialEq, Eq, Clone, Hash, Ord, PartialOrd, Copy)]
458pub(super) struct ScopeIdentifier(());
459
460impl ScopeIdentifier {
461    pub(super) fn global() -> ScopeIdentifier {
462        ScopeIdentifier(())
463    }
464}
465
466/// The `Evaluate` trait is implemented by any element of the program
467/// that can evaluate to a 36-bit word value.
468///
469/// This includes octal constants, full instructions and also parts of
470/// instructions (e.g. the index value, configuration value,
471/// instruction opcodes, `#`, RC-word references and so forth.
472pub(super) trait Evaluate: Spanned {
473    // By separating the RcUpdater and RcAllocator traits we ensure
474    // that evaluation cannot allocate more RC words.
475    fn evaluate<R: RcUpdater>(
476        &self,
477        ctx: &mut EvaluationContext<R>,
478        scope: ScopeIdentifier,
479    ) -> Result<Unsigned36Bit, EvaluationFailure>;
480}
481
482/// Represents the RC-block.
483///
484/// See [section 6-2.6 of the Users Handbook](https://archive.org/details/tx-2-users-handbook-nov-63/page/n161/mode/1up).
485#[derive(Debug, Default, Clone)]
486pub(crate) struct RcBlock {
487    /// The address of the RC-block.
488    pub(crate) address: Address,
489    /// The contents of the RC-block, along with information about why
490    /// each of the RC-words was allocated.
491    pub(crate) words: Vec<(RcWordSource, Unsigned36Bit)>,
492}
493
494impl RcBlock {
495    fn end(&self) -> Option<Address> {
496        match Unsigned18Bit::try_from(self.words.len()) {
497            Ok(offset) => Some(self.address.index_by(offset)),
498            Err(_) => None,
499        }
500    }
501}
502
503impl RcAllocator for RcBlock {
504    fn allocate(
505        &mut self,
506        source: RcWordSource,
507        value: Unsigned36Bit,
508    ) -> Result<Address, RcWordAllocationFailure> {
509        if let Some(addr) = self.end() {
510            self.words.push((source, value));
511            Ok(addr)
512        } else {
513            Err(RcWordAllocationFailure::RcBlockTooBig {
514                source,
515                rc_block_len: self.words.len(),
516            })
517        }
518    }
519}
520
521impl RcUpdater for RcBlock {
522    fn update(&mut self, address: Address, value: Unsigned36Bit) {
523        assert!(
524            address >= self.address,
525            "out of range access to address {address} of RC-block starting at {}",
526            self.address
527        );
528        match Unsigned18Bit::from(address).checked_sub(Unsigned18Bit::from(self.address)) {
529            Some(offset) => match self.words.get_mut(usize::from(offset)) {
530                Some((_source, spot)) => {
531                    *spot = value;
532                }
533                None => {
534                    panic!(
535                        "out of range access to offset {offset} of RC-block having length {}",
536                        self.words.len()
537                    );
538                }
539            },
540            None => {
541                // The checks above should ensure that address >= self.address.
542                panic!(
543                    "inconsistent checks; {address:o} should be greater than {:o}",
544                    self.address
545                );
546            }
547        }
548    }
549}
550
551#[cfg(test)]
552pub(crate) fn make_empty_rc_block_for_test(location: Address) -> RcBlock {
553    RcBlock {
554        address: location,
555        words: Vec::new(),
556    }
557}
558
559/// Compute the value of a symbol appearing in normal script.
560fn evaluate_normal_symbol<R: RcUpdater>(
561    name: &SymbolName,
562    span: Span,
563    ctx: &mut EvaluationContext<R>,
564    scope: ScopeIdentifier,
565) -> Result<Unsigned36Bit, EvaluationFailure> {
566    ctx.lookup_operation.deps_in_order.push(name.clone());
567    if !ctx.lookup_operation.depends_on.insert(name.clone()) {
568        // `name` was already in `depends_on`; in other words,
569        // we have a loop.
570        if let Ok(deps_in_order) =
571            OneOrMore::try_from_vec(ctx.lookup_operation.deps_in_order.clone())
572        {
573            return Err(EvaluationFailure::SymbolDefinitionLoop {
574                span,
575                deps_in_order,
576            });
577        }
578        panic!("we know deps_in_order is non-empty because we just appended an item to it");
579    }
580
581    let result = if let Some(def) = ctx.explicit_symtab.get(name) {
582        let what: (&Span, &SymbolName, &ExplicitDefinition) = (&span, name, def);
583        what.evaluate(ctx, scope)
584    } else {
585        ctx.fetch_or_assign_default(name)
586    };
587
588    ctx.lookup_operation.deps_in_order.pop();
589    ctx.lookup_operation.depends_on.remove(name);
590    result
591}
592
593/// Compute the value of a symbol in super/sub/normal script.
594///
595/// This factors out behaviour common to the evaluation of both
596/// symbolic origins and general symbols.
597pub(crate) fn evaluate_elevated_symbol<R: RcUpdater>(
598    name: &SymbolName,
599    elevation: Script,
600    span: Span,
601    ctx: &mut EvaluationContext<R>,
602    scope: ScopeIdentifier,
603) -> Result<Unsigned36Bit, EvaluationFailure> {
604    evaluate_normal_symbol(name, span, ctx, scope).map(|value| value.shl(elevation.shift()))
605}
606
607/// Determine the numerical value of all equalities, where they have
608/// one.
609///
610/// We do this in order to identify problems with symbol definitons
611/// and to generate information that we may need to emit into the
612/// ouptut listing.
613///
614/// This function isn't actually needed to generate the output binary.
615#[allow(clippy::too_many_arguments)]
616pub(super) fn extract_final_equalities<R: RcUpdater>(
617    equalities: &[Equality],
618    body: &Source,
619    explicit_symbols: &ExplicitSymbolTable,
620    implicit_symbols: &mut ImplicitSymbolTable,
621    memory_map: &MemoryMap,
622    index_register_assigner: &mut IndexRegisterAssigner,
623    rc_allocator: &mut R,
624    final_symbols: &mut FinalSymbolTable,
625    bad_symbol_definitions: &mut BTreeMap<SymbolName, ProgramError>,
626) -> Result<(), AssemblerFailure> {
627    for eq in equalities {
628        let mut ctx = EvaluationContext {
629            explicit_symtab: explicit_symbols,
630            implicit_symtab: implicit_symbols,
631            memory_map,
632            here: HereValue::NotAllowed,
633            index_register_assigner,
634            rc_updater: rc_allocator,
635            lookup_operation: Default::default(),
636        };
637        let scope = ScopeIdentifier::global();
638
639        match eq.value.evaluate(&mut ctx, scope) {
640            Ok(value) => {
641                final_symbols.define(
642                    eq.name.clone(),
643                    FinalSymbolType::Equality,
644                    body.extract(eq.span.start..eq.span.end).to_string(),
645                    FinalSymbolDefinition::PositionIndependent(value),
646                );
647            }
648            Err(EvaluationFailure::HereIsNotAllowedHere(_)) => {
649                // The value of this equality would depend on the
650                // address at which it is evaluated, so it has no
651                // single final value.  This is OK.
652                final_symbols.define(
653                    eq.name.clone(),
654                    FinalSymbolType::Equality,
655                    body.extract(eq.span.start..eq.span.end).to_string(),
656                    FinalSymbolDefinition::PositionDependent,
657                );
658            }
659            Err(e) => {
660                record_undefined_symbol_or_return_failure(body, e, bad_symbol_definitions)?;
661            }
662        }
663    }
664    Ok(())
665}
666
667impl Spanned for (&BlockIdentifier, &Option<Origin>, &Span) {
668    fn span(&self) -> Span {
669        *self.2
670    }
671}
672
673/// Represents an overflow in adding an index value to an address.
674#[derive(Debug)]
675struct AddressOverflow(pub(crate) Address, pub(crate) Unsigned18Bit);
676
677impl std::error::Error for AddressOverflow {}
678
679impl Display for AddressOverflow {
680    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
681        write!(
682            f,
683            "Adding {:o} to {:o} would generate a result which doesn't fit into an 18-bit address",
684            self.0, self.1
685        )
686    }
687}
688
689/// Compute sum of an address and an unsigned offset.
690fn offset_from_origin(origin: Address, offset: Unsigned18Bit) -> Result<Address, AddressOverflow> {
691    let (physical, _mark) = origin.split();
692    match physical.checked_add(offset) {
693        Some(total) => Ok(Address::from(total)),
694        None => Err(AddressOverflow(origin, offset)),
695    }
696}
697
698impl Evaluate for BlockPosition {
699    fn evaluate<R: RcUpdater>(
700        &self,
701        ctx: &mut EvaluationContext<R>,
702        scope: ScopeIdentifier,
703    ) -> Result<Unsigned36Bit, EvaluationFailure> {
704        // Resolve the address of this block by evaluating its origin
705        // specification if it has one.
706        if let Some(origin) = self.origin.as_ref() {
707            return origin.evaluate(ctx, scope);
708        }
709
710        // If it has no origin specification, we can deduce the
711        // address of this block by placing it immediately after the
712        // previous block, or (for the first block) using the default
713        // block address.
714        let previous_block_id: BlockIdentifier = match self.block_identifier.previous_block() {
715            None => {
716                // This is the first block.
717                return Ok(Origin::default_address().into());
718            }
719            Some(id) => id,
720        };
721
722        // Get the location of the previous block and place this block after it.
723        let (prev_addr, prev_size, prev_span): (Address, Unsigned18Bit, Span) =
724            if let Some(previous_block) = ctx.memory_map.get(&previous_block_id).cloned() {
725                // The previous block should have already been
726                // assigned an address, because we assign them
727                // in block-number order, ascending.
728                let address_of_previous_block: Address = previous_block
729                    .block_address
730                    .expect("blocks should be assigned addresses in ascending block order");
731                (
732                    address_of_previous_block,
733                    previous_block.block_size,
734                    previous_block.span,
735                )
736            } else {
737                unreachable!("unknown block {previous_block_id}")
738            };
739
740        // Add the size of the previous block to its address, yielding
741        // the address of the location following it; this is the
742        // address at which we will locate the current block.
743        match offset_from_origin(prev_addr, prev_size) {
744            Ok(addr) => Ok(addr.into()),
745            Err(_) => {
746                // The address of this block would be outside
747                // physical memory, so it is the combination
748                // of the address of the previous block and
749                // the size of the previous block which is the
750                // problem.
751                Err(EvaluationFailure::BlockTooLarge(
752                    prev_span,
753                    MachineLimitExceededFailure::BlockTooLarge {
754                        span: prev_span,
755                        block_id: previous_block_id,
756                        offset: prev_size.into(),
757                    },
758                ))
759            }
760        }
761    }
762}
763
764impl Evaluate for (&Span, &SymbolName, &ExplicitDefinition) {
765    fn evaluate<R: RcUpdater>(
766        &self,
767        ctx: &mut EvaluationContext<R>,
768        scope: ScopeIdentifier,
769    ) -> Result<Unsigned36Bit, EvaluationFailure> {
770        let (_span, name, def): (&Span, &SymbolName, &ExplicitDefinition) = *self;
771        match def {
772            ExplicitDefinition::Origin(Origin::Symbolic(_span, name), _block_id) => {
773                unreachable!("symbolic origin {name} should already have been deduced")
774            }
775            ExplicitDefinition::Origin(Origin::Deduced(_span, _name, address), _block_id) => {
776                Ok((*address).into())
777            }
778            ExplicitDefinition::Origin(Origin::Literal(_span, address), _block_id) => {
779                Ok((*address).into())
780            }
781            ExplicitDefinition::Tag(TagDefinition::Resolved { span: _, address }) => {
782                Ok(address.into())
783            }
784            ExplicitDefinition::Tag(TagDefinition::Unresolved {
785                block_id,
786                block_offset,
787                span,
788            }) => {
789                if let Some(block_position) = ctx.memory_map.get(block_id).cloned() {
790                    let block_origin: Address =
791                        subword::right_half(block_position.evaluate(ctx, scope)?).into();
792                    match offset_from_origin(block_origin, *block_offset) {
793                        Ok(computed_address) => Ok(computed_address.into()),
794                        Err(_overflow_error) => Err(EvaluationFailure::BlockTooLarge(
795                            *span,
796                            MachineLimitExceededFailure::BlockTooLarge {
797                                span: *span,
798                                block_id: *block_id,
799                                offset: (*block_offset).into(),
800                            },
801                        )),
802                    }
803                } else {
804                    panic!(
805                        "Tag named {name} at {span:?} refers to unknown block {block_id}: {def:?}"
806                    );
807                }
808            }
809            ExplicitDefinition::Equality(expression) => expression.evaluate(ctx, scope),
810        }
811    }
812}