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