1use std::borrow::Cow;
15use std::cmp::Ordering;
16use std::collections::BTreeMap;
17use std::error::Error;
18use std::fmt::{self, Display, Formatter, Octal, Write};
19use std::hash::Hash;
20use std::ops::{Shl, Shr};
21
22use tracing::{Level, event};
23
24use base::charset::{Script, subscript_char, superscript_char};
25use base::prelude::*;
26use base::u18;
27
28use super::collections::OneOrMore;
29use super::eval::{
30 Evaluate, EvaluationContext, EvaluationFailure, HereValue, ScopeIdentifier,
31 evaluate_elevated_symbol,
32};
33use super::glyph;
34use super::listing::{Listing, ListingLine};
35use super::manuscript::{MacroDefinition, MacroParameterBindings, MacroParameterValue};
36use super::memorymap::MemoryMap;
37use super::memorymap::RcAllocator;
38use super::memorymap::RcWordAllocationFailure;
39use super::memorymap::{RcWordKind, RcWordSource};
40use super::source::Source;
41use super::span::{Span, Spanned, span};
42use super::symbol::{InconsistentSymbolUse, SymbolContext, SymbolName};
43use super::symtab::{
44 BadSymbolDefinition, ExplicitDefinition, ExplicitSymbolTable, FinalSymbolDefinition,
45 FinalSymbolTable, FinalSymbolType, ImplicitSymbolTable, IndexRegisterAssigner, TagDefinition,
46 record_undefined_symbol_or_return_failure,
47};
48use super::types::{AssemblerFailure, BlockIdentifier, ProgramError};
49mod asteval;
50
51#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
54pub(crate) enum OnUnboundMacroParameter {
55 ElideReference,
56 SubstituteZero,
57}
58
59#[derive(Debug, PartialEq, Eq, Clone)]
61pub(crate) enum SymbolUse {
62 Reference(SymbolContext),
63 Definition(ExplicitDefinition),
64}
65
66pub(crate) trait RcUpdater {
71 fn update(&mut self, address: Address, value: Unsigned36Bit);
72}
73
74#[derive(Debug, Clone, PartialEq, Eq)]
76pub(crate) struct LiteralValue {
77 span: Span,
78 elevation: Script,
79 value: Unsigned36Bit,
80}
81
82impl LiteralValue {
83 pub(super) fn value(&self) -> Unsigned36Bit {
84 self.value << self.elevation.shift()
85 }
86
87 #[cfg(test)]
88 pub(super) fn unshifted_value(&self) -> Unsigned36Bit {
89 self.value
90 }
91}
92
93impl Spanned for LiteralValue {
94 fn span(&self) -> Span {
95 self.span
96 }
97}
98
99impl From<(Span, Script, Unsigned36Bit)> for LiteralValue {
100 fn from((span, elevation, value): (Span, Script, Unsigned36Bit)) -> LiteralValue {
101 LiteralValue {
102 span,
103 elevation,
104 value,
105 }
106 }
107}
108
109impl std::fmt::Display for LiteralValue {
110 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
111 let s = self.value.to_string();
112 format_elevated_chars(f, self.elevation, &s)
113 }
114}
115
116fn write_glyph_name(f: &mut Formatter<'_>, elevation: Script, ch: char) -> fmt::Result {
119 let prefix: &'static str = match elevation {
120 Script::Sub => "sub_",
121 Script::Super => "sup_",
122 Script::Normal => "",
123 };
124 let name: &'static str = match glyph::name_from_glyph(ch) {
125 Some(n) => n,
126 None => {
127 panic!("There is no glyph name for character '{ch}'");
128 }
129 };
130 write!(f, "@{prefix}{name}@")
131}
132
133fn elevated_string(s: &str, elevation: Script) -> Cow<'_, str> {
135 match elevation {
136 Script::Normal => Cow::Borrowed(s),
137 Script::Super => Cow::Owned(
138 s.chars()
139 .map(|ch| superscript_char(ch).unwrap_or(ch))
140 .collect(),
141 ),
142 Script::Sub => Cow::Owned(
143 s.chars()
144 .map(|ch| subscript_char(ch).unwrap_or(ch))
145 .collect(),
146 ),
147 }
148}
149fn format_elevated_chars(f: &mut Formatter<'_>, elevation: Script, s: &str) -> fmt::Result {
151 match elevation {
153 Script::Normal => {
154 f.write_str(s)?;
155 }
156 Script::Super => {
157 for ch in s.chars() {
158 match superscript_char(ch) {
159 Ok(superchar) => {
160 f.write_char(superchar)?;
161 }
162 Err(_) => {
163 write_glyph_name(f, elevation, ch)?;
164 }
165 }
166 }
167 }
168 Script::Sub => {
169 for ch in s.chars() {
170 match subscript_char(ch) {
171 Ok(sub) => {
172 f.write_char(sub)?;
173 }
174 Err(_) => {
175 write_glyph_name(f, elevation, ch)?;
176 }
177 }
178 }
179 }
180 }
181 Ok(())
182}
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq)]
188pub(crate) enum Operator {
189 Add,
190 LogicalAnd,
191 LogicalOr, Multiply,
193 Subtract,
194 Divide,
195}
196
197impl std::fmt::Display for Operator {
198 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
199 f.write_char(match self {
200 Operator::Add => '+',
201 Operator::LogicalAnd => '∧',
202 Operator::LogicalOr => '∨',
203 Operator::Multiply => '\u{00D7}',
204 Operator::Subtract => '-',
205 Operator::Divide => '/',
206 })
207 }
208}
209
210#[derive(Debug, Clone, PartialEq, Eq)]
211pub(crate) struct SignedAtom {
212 pub(super) negated: bool,
213 pub(super) span: Span,
214 pub(super) magnitude: Atom,
215}
216
217impl From<Atom> for SignedAtom {
218 fn from(magnitude: Atom) -> Self {
219 Self {
220 negated: false,
221 span: magnitude.span(),
222 magnitude,
223 }
224 }
225}
226
227impl SignedAtom {
228 fn symbol_uses(
229 &self,
230 block_id: BlockIdentifier,
231 block_offset: Unsigned18Bit,
232 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<>
233 {
234 self.magnitude.symbol_uses(block_id, block_offset)
235 }
236
237 fn substitute_macro_parameters(
238 &self,
239 param_values: &MacroParameterBindings,
240 on_missing: OnUnboundMacroParameter,
241 macros: &BTreeMap<SymbolName, MacroDefinition>,
242 ) -> Option<SignedAtom> {
243 self.magnitude
244 .substitute_macro_parameters(param_values, on_missing, macros)
245 .map(|magnitude| SignedAtom {
246 magnitude,
247 ..self.clone()
248 })
249 }
250
251 fn allocate_rc_words<R: RcAllocator>(
252 &mut self,
253 explicit_symtab: &mut ExplicitSymbolTable,
254 implicit_symtab: &mut ImplicitSymbolTable,
255 rc_allocator: &mut R,
256 ) -> Result<(), RcWordAllocationFailure> {
257 self.magnitude
258 .allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)
259 }
260}
261
262impl Spanned for SignedAtom {
263 fn span(&self) -> Span {
264 self.span
265 }
266}
267
268impl std::fmt::Display for SignedAtom {
269 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
270 if self.negated {
271 write!(f, "-{}", self.magnitude)
272 } else {
273 write!(f, "{}", self.magnitude)
274 }
275 }
276}
277
278#[derive(Debug, Clone, PartialEq, Eq)]
284pub(crate) struct ArithmeticExpression {
285 pub(crate) first: SignedAtom,
286 pub(crate) tail: Vec<(Operator, SignedAtom)>,
287}
288
289impl std::fmt::Display for ArithmeticExpression {
290 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
291 write!(f, "{}", self.first)?;
292 for (op, atom) in &self.tail {
293 write!(f, "{op}{atom}")?;
294 }
295 Ok(())
296 }
297}
298
299impl From<SignedAtom> for ArithmeticExpression {
300 fn from(a: SignedAtom) -> ArithmeticExpression {
301 ArithmeticExpression {
302 first: a,
303 tail: Vec::new(),
304 }
305 }
306}
307
308impl From<Atom> for ArithmeticExpression {
309 fn from(a: Atom) -> ArithmeticExpression {
310 ArithmeticExpression::from(SignedAtom::from(a))
311 }
312}
313
314impl From<SymbolOrLiteral> for ArithmeticExpression {
315 fn from(value: SymbolOrLiteral) -> Self {
316 ArithmeticExpression::from(Atom::from(value))
317 }
318}
319
320impl Spanned for ArithmeticExpression {
321 fn span(&self) -> Span {
322 let start = self.first.span().start;
323 let end = self
324 .tail
325 .last()
326 .map_or(self.first.span().end, |(_op, atom)| atom.span().end);
327 span(start..end)
328 }
329}
330
331impl ArithmeticExpression {
332 pub(super) fn with_tail(
333 first: SignedAtom,
334 tail: Vec<(Operator, SignedAtom)>,
335 ) -> ArithmeticExpression {
336 ArithmeticExpression { first, tail }
337 }
338
339 fn symbol_uses(
340 &self,
341 block_id: BlockIdentifier,
342 block_offset: Unsigned18Bit,
343 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<>
344 {
345 let mut result = Vec::with_capacity(1 + self.tail.len());
346 result.extend(self.first.symbol_uses(block_id, block_offset));
347 result.extend(
348 self.tail
349 .iter()
350 .flat_map(|(_op, x)| x.symbol_uses(block_id, block_offset)),
351 );
352 result.into_iter()
353 }
354
355 fn eval_binop(left: Unsigned36Bit, binop: Operator, right: Unsigned36Bit) -> Unsigned36Bit {
356 match binop {
357 Operator::Add => match left.checked_add(right) {
358 Some(result) => result,
359 None => {
360 todo!(
370 "{left:>012o}+{right:>012o} overflowed; please fix https://github.com/TX-2/TX-2-simulator/issues/146"
371 )
372 }
373 },
374 Operator::Subtract => match left.checked_sub(right) {
375 Some(result) => result,
376 None => {
377 todo!(
378 "{left:>012o}-{right:>012o} overflowed; please fix https://github.com/TX-2/TX-2-simulator/issues/146"
379 )
380 }
381 },
382 Operator::Multiply => match left.checked_mul(right) {
383 Some(result) => result,
384 None => {
385 todo!("multiplication overflow occurred but this is not implemented")
386 }
387 },
388 Operator::Divide => {
389 let sleft: Signed36Bit = left.reinterpret_as_signed();
390 let sright: Signed36Bit = right.reinterpret_as_signed();
391 match sleft.checked_div(sright) {
392 Some(result) => result.reinterpret_as_unsigned(),
393 None => {
394 if sright.is_positive_zero() {
395 !left
396 } else if sright.is_negative_zero() {
397 left
398 } else {
399 unreachable!("division overflow occurred but RHS is not zero")
400 }
401 }
402 }
403 }
404 Operator::LogicalAnd => left.and(right.into()),
405 Operator::LogicalOr => left.bitor(right.into()),
406 }
407 }
408
409 fn substitute_macro_parameters(
410 &self,
411 param_values: &MacroParameterBindings,
412 on_missing: OnUnboundMacroParameter,
413 macros: &BTreeMap<SymbolName, MacroDefinition>,
414 ) -> Option<ArithmeticExpression> {
415 match self
416 .first
417 .substitute_macro_parameters(param_values, on_missing, macros)
418 {
419 None => None,
420 Some(first) => {
421 let mut tail: Vec<(Operator, SignedAtom)> = Vec::with_capacity(self.tail.len());
422 for (op, atom) in &self.tail {
423 match atom.substitute_macro_parameters(param_values, on_missing, macros) {
424 Some(atom) => {
425 tail.push((*op, atom));
426 }
427 None => {
428 return None;
429 }
430 }
431 }
432 Some(ArithmeticExpression { first, tail })
433 }
434 }
435 }
436
437 fn allocate_rc_words<R: RcAllocator>(
438 &mut self,
439 explicit_symtab: &mut ExplicitSymbolTable,
440 implicit_symtab: &mut ImplicitSymbolTable,
441 rc_allocator: &mut R,
442 ) -> Result<(), RcWordAllocationFailure> {
443 self.first
444 .allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)?;
445 for (_op, atom) in &mut self.tail {
446 atom.allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)?;
447 }
448 Ok(())
449 }
450}
451
452#[derive(Debug, Clone, PartialEq, Eq)]
468pub(crate) struct ConfigValue {
469 pub(crate) already_superscript: bool,
471 pub(crate) expr: ArithmeticExpression,
473}
474
475impl ConfigValue {
476 fn symbol_uses(
477 &self,
478 block_id: BlockIdentifier,
479 block_offset: Unsigned18Bit,
480 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<>
481 {
482 self.expr
483 .symbol_uses(block_id, block_offset)
484 .map(|r| match r {
485 Ok((name, span, _ignore_symbol_use)) => Ok((
486 name,
487 span,
488 SymbolUse::Reference(SymbolContext::configuration(span)),
489 )),
490 Err(e) => Err(e),
491 })
492 }
493
494 fn substitute_macro_parameters(
495 &self,
496 param_values: &MacroParameterBindings,
497 on_missing: OnUnboundMacroParameter,
498 macros: &BTreeMap<SymbolName, MacroDefinition>,
499 ) -> Option<ConfigValue> {
500 self.expr
501 .substitute_macro_parameters(param_values, on_missing, macros)
502 .map(|expr| ConfigValue {
503 expr,
504 already_superscript: self.already_superscript,
505 })
506 }
507
508 fn allocate_rc_words<R: RcAllocator>(
509 &mut self,
510 explicit_symtab: &mut ExplicitSymbolTable,
511 implicit_symtab: &mut ImplicitSymbolTable,
512 rc_allocator: &mut R,
513 ) -> Result<(), RcWordAllocationFailure> {
514 self.expr
515 .allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)
516 }
517}
518
519impl Spanned for ConfigValue {
520 fn span(&self) -> Span {
521 self.expr.span()
522 }
523}
524
525#[derive(Debug, Clone, PartialEq, Eq)]
526pub(crate) struct RegistersContaining(OneOrMore<RegisterContaining>);
527
528impl RegistersContaining {
529 pub(super) fn from_words(words: OneOrMore<RegisterContaining>) -> RegistersContaining {
530 Self(words)
531 }
532
533 pub(super) fn words(&self) -> impl Iterator<Item = &RegisterContaining> {
534 self.0.iter()
535 }
536
537 pub(crate) fn words_mut(&mut self) -> impl Iterator<Item = &mut RegisterContaining> {
538 self.0.iter_mut()
539 }
540
541 fn symbol_uses(
542 &self,
543 block_id: BlockIdentifier,
544 block_offset: Unsigned18Bit,
545 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<'_>
546 {
547 self.0
548 .iter()
549 .flat_map(move |rc| rc.symbol_uses(block_id, block_offset))
550 }
551
552 fn substitute_macro_parameters(
553 &self,
554 param_values: &MacroParameterBindings,
555 on_missing: OnUnboundMacroParameter,
556 macros: &BTreeMap<SymbolName, MacroDefinition>,
557 ) -> Option<RegistersContaining> {
558 let tmp_rc: OneOrMore<Option<RegisterContaining>> = self
576 .0
577 .map(|rc| rc.substitute_macro_parameters(param_values, on_missing, macros));
578 if tmp_rc.iter().all(Option::is_some) {
579 Some(RegistersContaining(tmp_rc.into_map(|maybe_rc| {
580 maybe_rc.expect("we already checked this wasn't None")
581 })))
582 } else {
583 None
584 }
585 }
586
587 fn allocate_rc_words<R: RcAllocator>(
588 &mut self,
589 span: Span,
590 explicit_symtab: &mut ExplicitSymbolTable,
591 implicit_symtab: &mut ImplicitSymbolTable,
592 rc_allocator: &mut R,
593 ) -> Result<(), RcWordAllocationFailure> {
594 let source = RcWordSource {
595 span,
596 kind: RcWordKind::Braces,
597 };
598 for rc in self.words_mut() {
599 *rc = rc.clone().assign_rc_word(
600 source.clone(),
601 explicit_symtab,
602 implicit_symtab,
603 rc_allocator,
604 )?;
605 }
606 Ok(())
607 }
608}
609
610impl Spanned for RegistersContaining {
611 fn span(&self) -> Span {
612 use chumsky::span::Span;
613 let mut it = self.0.iter();
614 match it.next() {
615 Some(rc) => it.fold(rc.span(), |acc, rc| acc.union(rc.span())),
616 None => {
617 unreachable!(
618 "invariant broken: RegistersContaining contains no RegisterContaining instances"
619 )
620 }
621 }
622 }
623}
624
625#[derive(Debug, Clone, PartialEq, Eq)]
633pub(crate) enum RegisterContaining {
634 Unallocated(Box<TaggedProgramInstruction>),
635 Allocated(Address, Box<TaggedProgramInstruction>),
636}
637
638impl From<TaggedProgramInstruction> for RegisterContaining {
639 fn from(inst: TaggedProgramInstruction) -> Self {
640 RegisterContaining::Unallocated(Box::new(inst))
641 }
642}
643
644impl Spanned for RegisterContaining {
645 fn span(&self) -> Span {
646 match self {
647 RegisterContaining::Unallocated(b) | RegisterContaining::Allocated(_, b) => b.span(),
648 }
649 }
650}
651
652impl RegisterContaining {
653 fn instruction(&self) -> &TaggedProgramInstruction {
654 match self {
655 RegisterContaining::Unallocated(tpi) | RegisterContaining::Allocated(_, tpi) => tpi,
656 }
657 }
658
659 fn symbol_uses(
660 &self,
661 block_id: BlockIdentifier,
662 block_offset: Unsigned18Bit,
663 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<'_>
664 {
665 let mut result: Vec<Result<_, _>> = Vec::new();
666 for r in self.instruction().symbol_uses(block_id, block_offset) {
667 match r {
668 Ok((name, span, symbol_definition)) => {
669 match symbol_definition {
670 def @ SymbolUse::Reference(_) => {
671 result.push(Ok((name, span, def)));
672 }
673 SymbolUse::Definition(ExplicitDefinition::Tag { .. }) => {
674 }
684 SymbolUse::Definition(ExplicitDefinition::Origin(_, _)) => {
685 unreachable!(
686 "Found origin {name} inside an RC-word; the parser should have rejected this."
687 );
688 }
689 SymbolUse::Definition(_) => {
690 panic!(
705 "Found unexpected definition of {name} inside RC-word reference at {span:?}"
706 );
707 }
708 }
709 }
710 Err(e) => {
711 result.push(Err(e));
712 }
713 }
714 }
715 result.into_iter()
716 }
717
718 fn substitute_macro_parameters(
719 &self,
720 param_values: &MacroParameterBindings,
721 on_missing: OnUnboundMacroParameter,
722 macros: &BTreeMap<SymbolName, MacroDefinition>,
723 ) -> Option<RegisterContaining> {
724 match self {
725 RegisterContaining::Unallocated(tagged_program_instruction) => {
726 tagged_program_instruction
727 .substitute_macro_parameters(param_values, on_missing, macros)
728 .map(|tagged_program_instruction| {
729 RegisterContaining::Unallocated(Box::new(tagged_program_instruction))
730 })
731 }
732 RegisterContaining::Allocated(_address, _tagged_program_instruction) => {
733 unreachable!(
745 "macro expansion must be completed before any RC-block addresses are allocated"
746 )
747 }
748 }
749 }
750
751 fn assign_rc_word<R: RcAllocator>(
752 self,
753 source: RcWordSource,
754 explicit_symtab: &mut ExplicitSymbolTable,
755 implicit_symtab: &mut ImplicitSymbolTable,
756 rc_allocator: &mut R,
757 ) -> Result<RegisterContaining, RcWordAllocationFailure> {
758 match self {
759 RegisterContaining::Unallocated(mut tpibox) => {
760 let address: Address = rc_allocator.allocate(source, Unsigned36Bit::ZERO)?;
761 for tag in &tpibox.tags {
762 eprintln!(
763 "assigning RC-word at address {address} serves as defnition of tag {}",
764 &tag.name
765 );
766 implicit_symtab.remove(&tag.name);
767 let new_tag_definition = TagDefinition::Resolved {
768 span: tag.span,
769 address,
770 };
771 match explicit_symtab.define(
772 tag.name.clone(),
773 ExplicitDefinition::Tag(new_tag_definition.clone()),
774 ) {
775 Ok(()) => (),
776 Err(BadSymbolDefinition {
777 symbol_name,
778 span,
779 existing,
780 proposed: _,
781 }) => {
782 return Err(RcWordAllocationFailure::InconsistentTag {
783 tag_name: symbol_name,
784 span,
785 explanation: format!(
786 "previous definition {existing} is incompatible with new definition {new_tag_definition}"
787 ),
788 });
789 }
790 }
791 }
792 tpibox.allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)?;
793 let tpi: Box<TaggedProgramInstruction> = tpibox;
794 Ok(RegisterContaining::Allocated(address, tpi))
795 }
796 other @ RegisterContaining::Allocated(..) => Ok(other),
797 }
798 }
799}
800
801#[derive(Debug, Clone, PartialEq, Eq)]
806pub(crate) enum Atom {
807 SymbolOrLiteral(SymbolOrLiteral),
808 Parens(Span, Script, Box<ArithmeticExpression>),
809 RcRef(Span, RegistersContaining),
810}
811
812impl From<(Span, Script, SymbolName)> for Atom {
813 fn from((span, script, name): (Span, Script, SymbolName)) -> Self {
814 Atom::SymbolOrLiteral(SymbolOrLiteral::Symbol(script, name, span))
815 }
816}
817
818impl From<SymbolOrLiteral> for Atom {
819 fn from(value: SymbolOrLiteral) -> Self {
820 Atom::SymbolOrLiteral(value)
821 }
822}
823
824impl Atom {
825 fn symbol_uses(
826 &self,
827 block_id: BlockIdentifier,
828 block_offset: Unsigned18Bit,
829 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<>
830 {
831 let mut result: Vec<Result<_, _>> = Vec::with_capacity(1);
832 match self {
833 Atom::SymbolOrLiteral(SymbolOrLiteral::Symbol(script, name, span)) => {
834 result.push(Ok((
835 name.clone(),
836 *span,
837 SymbolUse::Reference(SymbolContext::from((script, *span))),
838 )));
839 }
840 Atom::SymbolOrLiteral(SymbolOrLiteral::Literal(_) | SymbolOrLiteral::Here(_, _)) => (),
841 Atom::Parens(_span, _script, expr) => {
842 result.extend(expr.symbol_uses(block_id, block_offset));
843 }
844 Atom::RcRef(_span, rc_words) => {
845 result.extend(rc_words.symbol_uses(block_id, block_offset));
846 }
847 }
848 result.into_iter()
849 }
850
851 fn substitute_macro_parameters(
852 &self,
853 param_values: &MacroParameterBindings,
854 on_missing: OnUnboundMacroParameter,
855 macros: &BTreeMap<SymbolName, MacroDefinition>,
856 ) -> Option<Atom> {
857 match self {
858 Atom::SymbolOrLiteral(symbol_or_literal) => {
859 match symbol_or_literal.substitute_macro_parameters(param_values, on_missing) {
860 SymbolSubstitution::AsIs(symbol_or_literal) => {
861 Some(Atom::SymbolOrLiteral(symbol_or_literal))
862 }
863 SymbolSubstitution::Hit(span, script, arithmetic_expression) => Some(
864 Atom::Parens(span, script, Box::new(arithmetic_expression.clone())),
865 ),
866 SymbolSubstitution::Omit => {
867 None
873 }
874 SymbolSubstitution::Zero(span) => {
875 Some(Atom::SymbolOrLiteral(SymbolOrLiteral::Literal(
876 LiteralValue {
877 span,
878 elevation: Script::Normal,
880 value: Unsigned36Bit::ZERO,
881 },
882 )))
883 }
884 }
885 }
886 Atom::Parens(span, script, arithmetic_expression) => arithmetic_expression
887 .substitute_macro_parameters(param_values, on_missing, macros)
888 .map(|arithmetic_expression| {
889 Atom::Parens(*span, *script, Box::new(arithmetic_expression))
890 }),
891 Atom::RcRef(span, registers_containing) => registers_containing
892 .substitute_macro_parameters(param_values, on_missing, macros)
893 .map(|registers_containing| Atom::RcRef(*span, registers_containing)),
894 }
895 }
896
897 fn allocate_rc_words<R: RcAllocator>(
898 &mut self,
899 explicit_symtab: &mut ExplicitSymbolTable,
900 implicit_symtab: &mut ImplicitSymbolTable,
901 rc_allocator: &mut R,
902 ) -> Result<(), RcWordAllocationFailure> {
903 match self {
904 Atom::SymbolOrLiteral(thing) => {
905 thing.allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)
906 }
907 Atom::Parens(_, _, expr) => {
908 expr.allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)
909 }
910 Atom::RcRef(span, rc) => {
911 rc.allocate_rc_words(*span, explicit_symtab, implicit_symtab, rc_allocator)
912 }
913 }
914 }
915}
916
917impl Spanned for Atom {
918 fn span(&self) -> Span {
919 match self {
920 Atom::SymbolOrLiteral(value) => value.span(),
921 Atom::Parens(span, _script, _bae) => *span,
922 Atom::RcRef(span, _) => *span,
923 }
924 }
925}
926
927impl From<LiteralValue> for Atom {
928 fn from(literal: LiteralValue) -> Atom {
929 Atom::SymbolOrLiteral(SymbolOrLiteral::Literal(literal))
930 }
931}
932
933impl From<(Span, Script, Unsigned36Bit)> for Atom {
934 fn from((span, script, v): (Span, Script, Unsigned36Bit)) -> Atom {
935 Atom::from(LiteralValue::from((span, script, v)))
936 }
937}
938
939impl std::fmt::Display for Atom {
940 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
941 match self {
942 Atom::SymbolOrLiteral(value) => write!(f, "{value}"),
943 Atom::Parens(_span, script, expr) => elevated_string(&expr.to_string(), *script).fmt(f),
944 Atom::RcRef(_span, _rc_reference) => {
945 write!(f, "{{...}}")
948 }
949 }
950 }
951}
952
953#[derive(Debug, Clone, PartialEq, Eq)]
955pub(crate) enum SymbolSubstitution<T> {
956 AsIs(T),
957 Hit(Span, Script, ArithmeticExpression),
958 Omit,
959 Zero(Span),
960}
961
962#[derive(Debug, Clone, PartialEq, Eq)]
967pub(crate) enum SymbolOrLiteral {
968 Symbol(Script, SymbolName, Span),
969 Literal(LiteralValue),
970 Here(Script, Span),
971}
972
973impl SymbolOrLiteral {
974 fn symbol_uses(
975 &self,
976 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<>
977 {
978 let mut result = Vec::with_capacity(1);
979 match self {
980 SymbolOrLiteral::Here(_, _) | SymbolOrLiteral::Literal(_) => (),
981 SymbolOrLiteral::Symbol(script, name, span) => {
982 let context: SymbolContext = (script, *span).into();
983 let sym_use: SymbolUse = SymbolUse::Reference(context);
984 result.push(Ok((name.clone(), *span, sym_use)));
985 }
986 }
987 result.into_iter()
988 }
989
990 fn substitute_macro_parameters(
991 &self,
992 param_values: &MacroParameterBindings,
993 on_missing: OnUnboundMacroParameter,
994 ) -> SymbolSubstitution<SymbolOrLiteral> {
995 match self {
996 SymbolOrLiteral::Symbol(_script, symbol_name, _span) => {
997 match param_values.get(symbol_name) {
998 Some((
999 span,
1000 Some(MacroParameterValue::Value(script, arithmetic_expression)),
1001 )) => {
1002 SymbolSubstitution::Hit(*span, *script, arithmetic_expression.clone())
1006 }
1007 Some((span, None)) => {
1008 match on_missing {
1013 OnUnboundMacroParameter::ElideReference => SymbolSubstitution::Omit,
1014 OnUnboundMacroParameter::SubstituteZero => {
1015 SymbolSubstitution::Zero(*span)
1016 }
1017 }
1018 }
1019 None => {
1020 SymbolSubstitution::AsIs(self.clone())
1022 }
1023 }
1024 }
1025 SymbolOrLiteral::Literal(literal) => {
1026 SymbolSubstitution::AsIs(SymbolOrLiteral::Literal(literal.clone()))
1027 }
1028 SymbolOrLiteral::Here(script, span) => {
1029 SymbolSubstitution::AsIs(SymbolOrLiteral::Here(*script, *span))
1030 }
1031 }
1032 }
1033
1034 fn allocate_rc_words<R: RcAllocator>(
1035 &mut self,
1036 _explicit_symtab: &ExplicitSymbolTable,
1037 _implicit_symtab: &mut ImplicitSymbolTable,
1038 _rc_allocator: &mut R,
1039 ) -> Result<(), RcWordAllocationFailure> {
1040 #![allow(clippy::unnecessary_wraps)]
1043 let _ = self; Ok(())
1047 }
1048}
1049
1050impl Spanned for SymbolOrLiteral {
1051 fn span(&self) -> Span {
1052 match self {
1053 SymbolOrLiteral::Literal(literal_value) => literal_value.span,
1054 SymbolOrLiteral::Symbol(_, _, span) | SymbolOrLiteral::Here(_, span) => *span,
1055 }
1056 }
1057}
1058
1059impl Display for SymbolOrLiteral {
1060 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1061 match self {
1062 SymbolOrLiteral::Here(script, _span) => match script {
1063 Script::Super => f.write_str("@super_hash@"),
1064 Script::Normal => f.write_char('#'),
1065 Script::Sub => f.write_str("@sub_hash@"),
1066 },
1067 SymbolOrLiteral::Symbol(script, name, _) => {
1068 elevated_string(&name.to_string(), *script).fmt(f)
1069 }
1070 SymbolOrLiteral::Literal(value) => value.fmt(f),
1071 }
1072 }
1073}
1074
1075#[derive(Debug, PartialEq, Eq, Clone)]
1076pub(crate) struct SpannedSymbolOrLiteral {
1077 pub(crate) item: SymbolOrLiteral,
1078 pub(crate) span: Span,
1079}
1080
1081impl SpannedSymbolOrLiteral {
1082 fn substitute_macro_parameters(
1083 &self,
1084 param_values: &MacroParameterBindings,
1085 on_missing: OnUnboundMacroParameter,
1086 ) -> SymbolSubstitution<SpannedSymbolOrLiteral> {
1087 match self
1088 .item
1089 .substitute_macro_parameters(param_values, on_missing)
1090 {
1091 SymbolSubstitution::AsIs(item) => SymbolSubstitution::AsIs(SpannedSymbolOrLiteral {
1092 item,
1093 span: self.span,
1094 }),
1095 SymbolSubstitution::Hit(span, script, arithmetic_expression) => {
1096 SymbolSubstitution::Hit(span, script, arithmetic_expression)
1097 }
1098 SymbolSubstitution::Omit => SymbolSubstitution::Omit,
1099 SymbolSubstitution::Zero(span) => SymbolSubstitution::Zero(span),
1100 }
1101 }
1102}
1103
1104#[derive(Debug, Clone, PartialEq, Eq)]
1109pub(crate) enum InstructionFragment {
1110 Arithmetic(ArithmeticExpression),
1114 DeferredAddressing(Span),
1122 Config(ConfigValue),
1124 PipeConstruct {
1126 index: SpannedSymbolOrLiteral,
1127 rc_word_span: Span,
1128 rc_word_value: RegisterContaining,
1129 },
1130 Null(Span),
1137}
1138
1139impl Spanned for InstructionFragment {
1140 fn span(&self) -> Span {
1141 match self {
1142 InstructionFragment::Arithmetic(arithmetic_expression) => arithmetic_expression.span(),
1143 InstructionFragment::Config(config_value) => config_value.span(),
1144 InstructionFragment::PipeConstruct {
1145 index,
1146 rc_word_span,
1147 rc_word_value: _,
1148 } => {
1149 let start = index.span.start;
1150 let end = rc_word_span.end;
1151 span(start..end)
1152 }
1153 InstructionFragment::DeferredAddressing(span) | InstructionFragment::Null(span) => {
1154 *span
1155 }
1156 }
1157 }
1158}
1159
1160impl InstructionFragment {
1161 fn symbol_uses(
1162 &self,
1163 block_id: BlockIdentifier,
1164 block_offset: Unsigned18Bit,
1165 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<>
1166 {
1167 let mut uses: Vec<Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> =
1168 Vec::new();
1169 match self {
1170 InstructionFragment::Arithmetic(expr) => {
1171 uses.extend(expr.symbol_uses(block_id, block_offset));
1172 }
1173 InstructionFragment::Null(_) | InstructionFragment::DeferredAddressing(_) => (),
1174 InstructionFragment::Config(value) => {
1175 uses.extend(value.symbol_uses(block_id, block_offset));
1176 }
1177 InstructionFragment::PipeConstruct {
1178 index,
1179 rc_word_span: _,
1180 rc_word_value,
1181 } => {
1182 for r in index.item.symbol_uses() {
1183 match r {
1184 Ok((name, span, mut symbol_use)) => {
1185 if let SymbolUse::Reference(context) = &mut symbol_use {
1186 assert!(!context.is_address());
1187 if let Err(e) = context.also_set_index(&name, span) {
1188 uses.push(Err(e));
1189 } else {
1190 uses.push(Ok((name, span, symbol_use)));
1191 }
1192 } else {
1193 uses.push(Ok((name, span, symbol_use)));
1194 }
1195 }
1196 Err(e) => {
1197 uses.push(Err(e));
1198 }
1199 }
1200 }
1201 uses.extend(rc_word_value.symbol_uses(block_id, block_offset));
1202 }
1203 }
1204 uses.into_iter()
1205 }
1206
1207 fn substitute_macro_parameters(
1208 &self,
1209 param_values: &MacroParameterBindings,
1210 on_missing: OnUnboundMacroParameter,
1211 macros: &BTreeMap<SymbolName, MacroDefinition>,
1212 ) -> Option<InstructionFragment> {
1213 match self {
1214 InstructionFragment::Arithmetic(arithmetic_expression) => arithmetic_expression
1215 .substitute_macro_parameters(param_values, on_missing, macros)
1216 .map(InstructionFragment::Arithmetic),
1217 InstructionFragment::DeferredAddressing(span) => {
1218 Some(InstructionFragment::DeferredAddressing(*span))
1219 }
1220 InstructionFragment::Config(config_value) => config_value
1221 .substitute_macro_parameters(param_values, on_missing, macros)
1222 .map(InstructionFragment::Config),
1223 InstructionFragment::PipeConstruct {
1224 index,
1225 rc_word_span,
1226 rc_word_value,
1227 } => match index.substitute_macro_parameters(param_values, on_missing) {
1228 SymbolSubstitution::AsIs(index) => rc_word_value
1229 .substitute_macro_parameters(param_values, on_missing, macros)
1230 .map(|rc_word_value| InstructionFragment::PipeConstruct {
1231 index,
1232 rc_word_span: *rc_word_span,
1233 rc_word_value,
1234 }),
1235 SymbolSubstitution::Hit(_span, _script, _arithmetic_expression) => {
1236 todo!(
1237 "macro parameter expansion is not yet fully supported in the index part of pipe constructs"
1238 )
1239 }
1240 SymbolSubstitution::Omit => None,
1241 SymbolSubstitution::Zero(span) => Some(InstructionFragment::Null(span)),
1242 },
1243 InstructionFragment::Null(span) => Some(InstructionFragment::Null(*span)),
1244 }
1245 }
1246
1247 fn allocate_rc_words<R: RcAllocator>(
1248 &mut self,
1249 explicit_symtab: &mut ExplicitSymbolTable,
1250 implicit_symtab: &mut ImplicitSymbolTable,
1251 rc_allocator: &mut R,
1252 ) -> Result<(), RcWordAllocationFailure> {
1253 match self {
1254 InstructionFragment::Null(_) | InstructionFragment::DeferredAddressing(_) => Ok(()),
1255 InstructionFragment::Arithmetic(expr) => {
1256 expr.allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)
1257 }
1258 InstructionFragment::Config(cfg) => {
1259 cfg.allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)
1260 }
1261 InstructionFragment::PipeConstruct {
1262 index: _,
1263 rc_word_span,
1264 rc_word_value,
1265 } => {
1266 let span: Span = *rc_word_span;
1267 let w = rc_word_value.clone();
1268 *rc_word_value = w.assign_rc_word(
1269 RcWordSource {
1270 span,
1271 kind: RcWordKind::PipeConstruct,
1272 },
1273 explicit_symtab,
1274 implicit_symtab,
1275 rc_allocator,
1276 )?;
1277 Ok(())
1278 }
1279 }
1280 }
1281}
1282
1283impl From<(Span, Script, Unsigned36Bit)> for InstructionFragment {
1284 fn from((span, script, v): (Span, Script, Unsigned36Bit)) -> InstructionFragment {
1285 InstructionFragment::Arithmetic(ArithmeticExpression::from(Atom::from((span, script, v))))
1287 }
1288}
1289
1290impl From<ArithmeticExpression> for InstructionFragment {
1291 fn from(expr: ArithmeticExpression) -> Self {
1292 InstructionFragment::Arithmetic(expr)
1293 }
1294}
1295
1296#[derive(Debug, Clone, Eq, PartialEq, Hash)]
1298pub(crate) enum Origin {
1299 Literal(Span, Address),
1301 Symbolic(Span, SymbolName),
1304 Deduced(Span, SymbolName, Address),
1308}
1309
1310impl Display for Origin {
1311 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
1312 match self {
1313 Origin::Literal(_span, addr) => fmt::Display::fmt(&addr, f),
1314 Origin::Symbolic(_span, sym) => fmt::Display::fmt(&sym, f),
1315 Origin::Deduced(_span, name, addr) => {
1316 write!(f, "{name} (deduced to be at address {addr:o})")
1317 }
1318 }
1319 }
1320}
1321
1322impl Origin {
1323 pub(super) fn default_address() -> Address {
1324 Address::new(u18!(0o200_000))
1329 }
1330
1331 pub(super) fn symbol_uses(
1332 &self,
1333 block_id: BlockIdentifier,
1334 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<>
1335 {
1336 let mut result = Vec::with_capacity(1);
1337 match self {
1338 Origin::Literal(_span, _) => (),
1339 org @ Origin::Deduced(span, name, _) => {
1340 event!(
1344 Level::WARN,
1345 "unexpectedly saw a deduced value for origin {name} in symbol_uses"
1346 );
1347 result.push(Ok((
1348 name.clone(),
1349 *span,
1350 SymbolUse::Definition(ExplicitDefinition::Origin(org.clone(), block_id)),
1351 )));
1352 }
1353 org @ Origin::Symbolic(span, name) => {
1354 result.push(Ok((
1355 name.clone(),
1356 *span,
1357 SymbolUse::Definition(ExplicitDefinition::Origin(org.clone(), block_id)),
1358 )));
1359 }
1360 }
1361 result.into_iter()
1362 }
1363}
1364
1365impl Spanned for Origin {
1366 fn span(&self) -> Span {
1367 match self {
1368 Origin::Deduced(span, _, _) | Origin::Literal(span, _) | Origin::Symbolic(span, _) => {
1369 *span
1370 }
1371 }
1372 }
1373}
1374
1375impl Octal for Origin {
1376 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1377 match self {
1378 Origin::Deduced(_span, name, address) => {
1379 write!(f, "{name} (deduced to be at {address:o})")
1380 }
1381 Origin::Literal(_span, address) => fmt::Octal::fmt(&address, f),
1382 Origin::Symbolic(_span, name) => fmt::Display::fmt(&name, f),
1383 }
1384 }
1385}
1386
1387#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1395pub(crate) enum HoldBit {
1396 Unspecified,
1398 Hold,
1400 NotHold,
1404}
1405
1406#[derive(Debug, Clone, PartialEq, Eq)]
1408pub(crate) enum Commas {
1409 One(Span),
1410 Two(Span),
1411 Three(Span),
1412}
1413
1414impl Spanned for Commas {
1415 fn span(&self) -> Span {
1416 match &self {
1417 Commas::One(span) | Commas::Two(span) | Commas::Three(span) => *span,
1418 }
1419 }
1420}
1421
1422#[derive(Debug, Clone, PartialEq, Eq)]
1424pub(super) struct FragmentWithHold {
1425 pub(super) span: Span,
1427 pub(super) holdbit: HoldBit,
1429 pub(super) fragment: InstructionFragment,
1432}
1433
1434#[derive(Debug, Clone, PartialEq, Eq)]
1437pub(crate) enum CommasOrInstruction {
1438 I(FragmentWithHold),
1439 C(Option<Commas>),
1440}
1441
1442#[derive(Debug, Clone, PartialEq, Eq)]
1447pub(super) struct CommaDelimitedFragment {
1448 pub(super) span: Span,
1450 pub(super) leading_commas: Option<Commas>,
1452 pub(super) holdbit: HoldBit,
1455 pub(super) fragment: InstructionFragment,
1457 pub(super) trailing_commas: Option<Commas>,
1459}
1460
1461impl CommaDelimitedFragment {
1462 pub(super) fn new(
1463 leading_commas: Option<Commas>,
1464 instruction: FragmentWithHold,
1465 trailing_commas: Option<Commas>,
1466 ) -> Self {
1467 let span: Span = {
1468 let spans: [Option<Span>; 3] = [
1469 leading_commas.as_ref().map(Spanned::span),
1470 Some(instruction.span),
1471 trailing_commas.as_ref().map(Spanned::span),
1472 ];
1473 match spans {
1474 [_, None, _] => {
1475 unreachable!("CommaDelimitedInstruction cannot be completely empty")
1476 }
1477 [None, Some(m), None] => m,
1478 [None, Some(m), Some(r)] => span(m.start..r.end),
1479 [Some(l), _, Some(r)] => span(l.start..r.end),
1480 [Some(l), Some(m), None] => span(l.start..m.end),
1481 }
1482 };
1483 Self {
1484 span,
1485 leading_commas,
1486 holdbit: instruction.holdbit,
1487 fragment: instruction.fragment,
1488 trailing_commas,
1489 }
1490 }
1491
1492 fn symbol_uses(
1493 &self,
1494 block_id: BlockIdentifier,
1495 block_offset: Unsigned18Bit,
1496 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + '_
1497 {
1498 self.fragment.symbol_uses(block_id, block_offset)
1499 }
1500
1501 fn substitute_macro_parameters(
1502 &self,
1503 param_values: &MacroParameterBindings,
1504 on_missing: OnUnboundMacroParameter,
1505 macros: &BTreeMap<SymbolName, MacroDefinition>,
1506 ) -> Option<CommaDelimitedFragment> {
1507 self.fragment
1508 .substitute_macro_parameters(param_values, on_missing, macros)
1509 .map(|fragment| Self {
1510 fragment,
1511 ..self.clone()
1512 })
1513 }
1514
1515 fn allocate_rc_words<R: RcAllocator>(
1516 &mut self,
1517 explicit_symtab: &mut ExplicitSymbolTable,
1518 implicit_symtab: &mut ImplicitSymbolTable,
1519 rc_allocator: &mut R,
1520 ) -> Result<(), RcWordAllocationFailure> {
1521 self.fragment
1522 .allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)
1523 }
1524}
1525
1526impl Spanned for CommaDelimitedFragment {
1527 fn span(&self) -> Span {
1528 self.span
1529 }
1530}
1531
1532#[derive(Debug, Clone, PartialEq, Eq)]
1534pub(crate) struct UntaggedProgramInstruction {
1535 pub(crate) fragments: OneOrMore<CommaDelimitedFragment>,
1536}
1537
1538impl From<OneOrMore<CommaDelimitedFragment>> for UntaggedProgramInstruction {
1539 fn from(fragments: OneOrMore<CommaDelimitedFragment>) -> Self {
1540 Self { fragments }
1541 }
1542}
1543
1544impl UntaggedProgramInstruction {
1545 fn symbol_uses(
1546 &self,
1547 block_id: BlockIdentifier,
1548 offset: Unsigned18Bit,
1549 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<'_>
1550 {
1551 self.fragments
1552 .iter()
1553 .flat_map(move |fragment| fragment.symbol_uses(block_id, offset))
1554 }
1555
1556 fn allocate_rc_words<R: RcAllocator>(
1557 &mut self,
1558 explicit_symtab: &mut ExplicitSymbolTable,
1559 implicit_symtab: &mut ImplicitSymbolTable,
1560 rc_allocator: &mut R,
1561 ) -> Result<(), RcWordAllocationFailure> {
1562 for inst in self.fragments.iter_mut() {
1563 inst.allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)?;
1564 }
1565 Ok(())
1566 }
1567
1568 fn substitute_macro_parameters(
1569 &self,
1570 param_values: &MacroParameterBindings,
1571 on_missing: OnUnboundMacroParameter,
1572 macros: &BTreeMap<SymbolName, MacroDefinition>,
1573 ) -> Option<UntaggedProgramInstruction> {
1574 let tmp_frags: OneOrMore<Option<CommaDelimitedFragment>> = self
1575 .fragments
1576 .map(|frag| frag.substitute_macro_parameters(param_values, on_missing, macros));
1577 if tmp_frags.iter().any(Option::is_none) {
1578 None
1579 } else {
1580 Some(UntaggedProgramInstruction {
1581 fragments: tmp_frags.into_map(|mut maybe_frag| {
1582 maybe_frag
1583 .take()
1584 .expect("we already checked this fragment wasn't None")
1585 }),
1586 })
1587 }
1588 }
1589}
1590
1591impl Spanned for UntaggedProgramInstruction {
1592 fn span(&self) -> Span {
1593 span(self.fragments.first().span.start..self.fragments.last().span.end)
1594 }
1595}
1596
1597#[derive(Debug, Clone, PartialEq, Eq)]
1603pub(super) struct EqualityValue {
1604 pub(super) span: Span,
1622 pub(super) inner: UntaggedProgramInstruction,
1624}
1625
1626impl Spanned for EqualityValue {
1627 fn span(&self) -> Span {
1628 self.span
1629 }
1630}
1631
1632impl From<(Span, UntaggedProgramInstruction)> for EqualityValue {
1633 fn from((span, inner): (Span, UntaggedProgramInstruction)) -> Self {
1634 Self { span, inner }
1635 }
1636}
1637
1638impl EqualityValue {
1639 pub(super) fn substitute_macro_parameters(
1640 &self,
1641 param_values: &MacroParameterBindings,
1642 macros: &BTreeMap<SymbolName, MacroDefinition>,
1643 ) -> EqualityValue {
1644 if let Some(inner) = self.inner.substitute_macro_parameters(
1655 param_values,
1656 OnUnboundMacroParameter::SubstituteZero,
1659 macros,
1660 ) {
1661 EqualityValue {
1662 span: self.span,
1663 inner,
1664 }
1665 } else {
1666 unreachable!(
1667 "substitute_macro_parameters should not return None when OnUnboundMacroParameter::SubstituteZero is in effect"
1668 )
1669 }
1670 }
1671}
1672
1673#[derive(Debug, Clone, PartialEq, Eq)]
1678pub(crate) struct Tag {
1679 pub(crate) name: SymbolName,
1680 pub(crate) span: Span,
1681}
1682
1683impl Tag {
1684 fn symbol_uses(
1685 &self,
1686 block_id: BlockIdentifier,
1687 block_offset: Unsigned18Bit,
1688 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<>
1689 {
1690 [Ok((
1691 self.name.clone(),
1692 self.span,
1693 SymbolUse::Definition(ExplicitDefinition::Tag(TagDefinition::Unresolved {
1694 block_id,
1695 block_offset,
1696 span: self.span,
1697 })),
1698 ))]
1699 .into_iter()
1700 }
1701}
1702
1703impl PartialOrd for Tag {
1704 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1706 Some(self.cmp(other))
1707 }
1708}
1709
1710impl Ord for Tag {
1711 fn cmp(&self, other: &Self) -> Ordering {
1713 self.name.cmp(&other.name)
1714 }
1715}
1716
1717#[derive(Debug, Clone, PartialEq, Eq)]
1719pub(crate) struct TaggedProgramInstruction {
1720 pub(crate) span: Span,
1722 pub(crate) tags: Vec<Tag>,
1724 pub(crate) instruction: UntaggedProgramInstruction,
1726}
1727
1728impl TaggedProgramInstruction {
1729 pub(crate) fn symbol_uses(
1730 &self,
1731 block_id: BlockIdentifier,
1732 offset: Unsigned18Bit,
1733 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<>
1734 {
1735 let mut result: Vec<Result<_, _>> = Vec::new();
1736 result.extend(
1737 self.tags
1738 .iter()
1739 .flat_map(|tag| tag.symbol_uses(block_id, offset)),
1740 );
1741 result.extend(self.instruction.symbol_uses(block_id, offset));
1742 result.into_iter()
1743 }
1744
1745 #[cfg(test)]
1746 pub(crate) fn single(
1747 tags: Vec<Tag>,
1748 holdbit: HoldBit,
1749 inst_span: Span,
1750 frag_span: Span,
1751 frag: InstructionFragment,
1752 ) -> TaggedProgramInstruction {
1753 TaggedProgramInstruction {
1754 tags,
1755 span: inst_span,
1756 instruction: UntaggedProgramInstruction::from(OneOrMore::new(CommaDelimitedFragment {
1757 span: frag_span,
1758 leading_commas: None,
1759 holdbit,
1760 fragment: frag,
1761 trailing_commas: None,
1762 })),
1763 }
1764 }
1765
1766 #[cfg(test)]
1767 pub(crate) fn multiple(
1768 tags: Vec<Tag>,
1769 span: Span,
1770 first_fragment: CommaDelimitedFragment,
1771 more_fragments: Vec<CommaDelimitedFragment>,
1772 ) -> TaggedProgramInstruction {
1773 TaggedProgramInstruction {
1774 tags,
1775 span,
1776 instruction: UntaggedProgramInstruction::from(OneOrMore::with_tail(
1777 first_fragment,
1778 more_fragments,
1779 )),
1780 }
1781 }
1782
1783 fn emitted_word_count(&self) -> Unsigned18Bit {
1784 let _ = self;
1785 Unsigned18Bit::ONE
1786 }
1787
1788 pub(super) fn substitute_macro_parameters(
1789 &self,
1790 param_values: &MacroParameterBindings,
1791 on_missing: OnUnboundMacroParameter,
1792 macros: &BTreeMap<SymbolName, MacroDefinition>,
1793 ) -> Option<TaggedProgramInstruction> {
1794 self.instruction
1795 .substitute_macro_parameters(param_values, on_missing, macros)
1796 .map(|instruction| TaggedProgramInstruction {
1797 span: self.span,
1798 tags: self.tags.clone(),
1799 instruction,
1800 })
1801 }
1802
1803 fn allocate_rc_words<R: RcAllocator>(
1804 &mut self,
1805 explicit_symtab: &mut ExplicitSymbolTable,
1806 implicit_symtab: &mut ImplicitSymbolTable,
1807 rc_allocator: &mut R,
1808 ) -> Result<(), RcWordAllocationFailure> {
1809 self.instruction
1810 .allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)
1811 }
1812}
1813
1814impl Spanned for TaggedProgramInstruction {
1815 fn span(&self) -> Span {
1816 let begin = match self.tags.first() {
1817 Some(t) => t.span.start,
1818 None => self.instruction.span().start,
1819 };
1820 let end = self.instruction.span().end;
1821 Span::from(begin..end)
1822 }
1823}
1824
1825#[derive(Debug, Clone, PartialEq, Eq)]
1826pub(crate) struct InstructionSequence {
1827 pub(super) local_symbols: Option<ExplicitSymbolTable>,
1828 pub(super) instructions: Vec<TaggedProgramInstruction>,
1829}
1830
1831#[cfg(test)]
1832impl From<Vec<TaggedProgramInstruction>> for InstructionSequence {
1833 fn from(v: Vec<TaggedProgramInstruction>) -> Self {
1834 InstructionSequence {
1835 local_symbols: None,
1836 instructions: v,
1837 }
1838 }
1839}
1840
1841impl FromIterator<TaggedProgramInstruction> for InstructionSequence {
1842 fn from_iter<T>(iter: T) -> Self
1843 where
1844 T: IntoIterator<Item = TaggedProgramInstruction>,
1845 {
1846 InstructionSequence {
1847 local_symbols: None,
1848 instructions: iter.into_iter().collect(),
1849 }
1850 }
1851}
1852
1853pub(crate) fn block_items_with_offset<T, I>(items: I) -> impl Iterator<Item = (Unsigned18Bit, T)>
1856where
1857 I: Iterator<Item = T>,
1858{
1859 items.enumerate().map(|(offset, item)| {
1860 let off: Unsigned18Bit = Unsigned18Bit::try_from(offset)
1861 .expect("block should not be larger than the TX-2's memory");
1862 (off, item)
1863 })
1864}
1865
1866#[derive(Debug, PartialEq, Eq, Clone)]
1868pub(crate) enum LocalSymbolTableBuildFailure {
1869 InconsistentUsage(InconsistentSymbolUse),
1870 BadDefinition(BadSymbolDefinition),
1871}
1872
1873impl Spanned for LocalSymbolTableBuildFailure {
1874 fn span(&self) -> Span {
1875 match self {
1876 LocalSymbolTableBuildFailure::InconsistentUsage(inconsistent_symbol_use) => {
1877 inconsistent_symbol_use.span()
1878 }
1879 LocalSymbolTableBuildFailure::BadDefinition(bad_symbol_definition) => {
1880 bad_symbol_definition.span()
1881 }
1882 }
1883 }
1884}
1885
1886impl Display for LocalSymbolTableBuildFailure {
1887 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1888 match self {
1889 LocalSymbolTableBuildFailure::InconsistentUsage(inconsistent_symbol_use) => {
1890 write!(f, "{inconsistent_symbol_use}")
1891 }
1892 LocalSymbolTableBuildFailure::BadDefinition(bad_symbol_definition) => {
1893 write!(f, "{bad_symbol_definition}")
1894 }
1895 }
1896 }
1897}
1898
1899impl Error for LocalSymbolTableBuildFailure {}
1900
1901impl InstructionSequence {
1902 pub(super) fn iter(&self) -> impl Iterator<Item = &TaggedProgramInstruction> {
1903 self.instructions.iter()
1904 }
1905
1906 pub(super) fn first(&self) -> Option<&TaggedProgramInstruction> {
1907 self.instructions.first()
1908 }
1909
1910 pub(crate) fn allocate_rc_words<R: RcAllocator>(
1911 &mut self,
1912 explicit_symtab: &mut ExplicitSymbolTable,
1913 implicit_symtab: &mut ImplicitSymbolTable,
1914 rc_allocator: &mut R,
1915 ) -> Result<(), RcWordAllocationFailure> {
1916 for ref mut statement in &mut self.instructions {
1917 statement.allocate_rc_words(explicit_symtab, implicit_symtab, rc_allocator)?;
1918 }
1919 Ok(())
1920 }
1921
1922 pub(crate) fn symbol_uses(
1923 &self,
1924 block_id: BlockIdentifier,
1925 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<>
1926 {
1927 let no_symbols = ExplicitSymbolTable::default();
1928 let local_scope: &ExplicitSymbolTable = self.local_symbols.as_ref().unwrap_or(&no_symbols);
1929 let mut result: Vec<Result<_, _>> = Vec::new();
1930
1931 for (off, statement) in block_items_with_offset(self.instructions.iter()) {
1932 result.extend(statement.symbol_uses(block_id, off).filter(|r| match r {
1933 Ok((symbol, _, _)) => !local_scope.is_defined(symbol),
1934 Err(_) => true,
1935 }));
1936 }
1937 result.into_iter()
1938 }
1939
1940 pub(crate) fn emitted_word_count(&self) -> Unsigned18Bit {
1941 self.iter()
1942 .map(TaggedProgramInstruction::emitted_word_count)
1943 .sum()
1944 }
1945
1946 #[allow(clippy::too_many_arguments)]
1947 pub(crate) fn build_binary_block<R: RcUpdater>(
1948 &self,
1949 location: Address,
1950 start_offset: Unsigned18Bit,
1951 explicit_symtab: &ExplicitSymbolTable,
1952 implicit_symtab: &mut ImplicitSymbolTable,
1953 memory_map: &MemoryMap,
1954 index_register_assigner: &mut IndexRegisterAssigner,
1955 rc_allocator: &mut R,
1956 final_symbols: &mut FinalSymbolTable,
1957 body: &Source<'_>,
1958 listing: &mut Listing,
1959 bad_symbol_definitions: &mut BTreeMap<SymbolName, ProgramError>,
1960 ) -> Result<Vec<Unsigned36Bit>, AssemblerFailure> {
1961 let mut words: Vec<Unsigned36Bit> = Vec::with_capacity(self.emitted_word_count().into());
1962 for (offset, instruction) in self.iter().enumerate() {
1963 let offset: Unsigned18Bit = Unsigned18Bit::try_from(offset)
1964 .ok()
1965 .and_then(|offset| offset.checked_add(start_offset))
1966 .expect("assembled code block should fit within physical memory");
1967 let address: Address = location.index_by(offset);
1968 for tag in &instruction.tags {
1969 final_symbols.define(
1970 tag.name.clone(),
1971 FinalSymbolType::Tag,
1972 body.extract(tag.span.start..tag.span.end).to_string(),
1973 FinalSymbolDefinition::PositionIndependent(address.into()),
1974 );
1975 }
1976
1977 if self.local_symbols.is_some() {
1978 unimplemented!(
1979 "InstructionSequence::build_binary_block: evaluation with local symbol tables is not yet implemented"
1980 );
1981 }
1982 let mut ctx = EvaluationContext {
1983 explicit_symtab,
1984 implicit_symtab,
1985 memory_map,
1986 here: HereValue::Address(address),
1987 index_register_assigner,
1988 rc_updater: rc_allocator,
1989 lookup_operation: Default::default(),
1990 };
1991 let scope = ScopeIdentifier::global();
1992 match instruction.evaluate(&mut ctx, scope) {
1993 Ok(word) => {
1994 listing.push_line(ListingLine {
1995 span: Some(instruction.span),
1996 rc_source: None,
1997 content: Some((address, word)),
1998 });
1999 words.push(word);
2000 }
2001 Err(e) => {
2002 record_undefined_symbol_or_return_failure(body, e, bad_symbol_definitions)?;
2003 }
2004 }
2005 }
2006 Ok(words)
2007 }
2008}
2009
2010#[derive(Debug, Clone, PartialEq, Eq)]
2013pub(crate) struct Equality {
2014 pub(crate) span: Span,
2015 pub(crate) name: SymbolName,
2016 pub(crate) value: EqualityValue,
2017}
2018
2019impl Equality {
2020 pub(super) fn symbol_uses(
2021 &self,
2022 ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<>
2023 {
2024 [Ok((
2025 self.name.clone(),
2026 self.span,
2027 SymbolUse::Definition(
2028 ExplicitDefinition::Equality(self.value.clone()),
2030 ),
2031 ))]
2032 .into_iter()
2033 }
2034}