Skip to content

Commit 507912b

Browse files
committed
[assembler] Tags defined in RC-words are not local to the RC-word.
Tags defined in RC-words may not be used for M4's editing instuctions, but nevertheless they are not locally-scoped. This is demonstrated by the example in section 6-4.7 of the User's Handbook, which looks like this: ``` ☛☛DEF TBS|α α α α α α ☛☛EMD 100| USE-> LDA {TS->TBS|0} ** 5 BLANK RC WORDS LDA TOMM STA TS+3 ``` You will see above that the definition of the tag `TS` is inside an RC-word, but _not_ inside a macro body. The example explains that the above code snippet expands to: ``` 100| USE -> LDA {TS-> TBS| 0} |002400 000103|000100 LDA TOMM |002400 000110| 101 STA TS+3 |003400 000106| 102 TS -> TBS 0 0 |000000 000000| 103 0 |000000 000000| 104 0 |000000 000000| 105 0 |000000 000000| 106 0 |000000 000000| 107 TOMM -> 0 |000000 000000|000110 ```
1 parent 66b3378 commit 507912b

File tree

6 files changed

+258
-130
lines changed

6 files changed

+258
-130
lines changed

assembler/src/asmlib/ast.rs

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,36 +14,42 @@ use super::lexer::Token;
1414
use super::span::*;
1515
use super::state::NumeralMode;
1616
use super::symbol::{SymbolContext, SymbolName};
17-
use super::symtab::{SymbolDefinition, SymbolTable};
17+
use super::symtab::{BadSymbolDefinition, SymbolDefinition, SymbolTable};
1818
use super::types::*;
1919

2020
#[derive(Debug, PartialEq, Eq, Clone)]
2121
enum SymbolUse {
2222
Reference(SymbolContext),
2323
Definition(SymbolDefinition),
24-
LocalDefinition(SymbolDefinition),
2524
Origin(SymbolName, BlockIdentifier),
2625
}
2726

2827
#[derive(Debug, PartialEq, Eq, Clone)]
29-
pub(crate) struct RcWordAllocationFailure {
30-
pub(crate) source: RcWordSource,
31-
pub(crate) rc_block_len: usize,
32-
}
33-
34-
impl RcWordAllocationFailure {
35-
pub(crate) fn span(&self) -> Option<&Span> {
36-
self.source.span()
37-
}
28+
pub(crate) enum RcWordAllocationFailure {
29+
RcBlockTooBig {
30+
source: RcWordSource,
31+
rc_block_len: usize,
32+
},
33+
InconsistentTag(BadSymbolDefinition),
3834
}
3935

4036
impl Display for RcWordAllocationFailure {
4137
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
42-
write!(
43-
f,
44-
"failed to allocate RC word for {}; RC block is already {} words long",
45-
self.source, self.rc_block_len
46-
)
38+
match self {
39+
RcWordAllocationFailure::RcBlockTooBig {
40+
source,
41+
rc_block_len,
42+
} => {
43+
write!(f, "failed to allocate RC word for {source}; RC block is already {rc_block_len} words long")
44+
}
45+
RcWordAllocationFailure::InconsistentTag(e) => {
46+
let name = e.symbol();
47+
write!(
48+
f,
49+
"failed to define tag {name} because it already had a previous definition: {e}"
50+
)
51+
}
52+
}
4753
}
4854
}
4955

@@ -460,23 +466,23 @@ impl RegisterContaining {
460466
block_id: BlockIdentifier,
461467
block_offset: Unsigned18Bit,
462468
) -> impl Iterator<Item = (SymbolName, Span, SymbolUse)> + use<'_> {
463-
// Tags defined inside the RC-word are not counted as
464-
// defined outside it. But if we refer to a symbol
465-
// inside the RC-word it counts as a reference for the
466-
// purpose of determining which contexts it has been
467-
// used in.
468469
let mut result = Vec::new();
469470
for symbol_use in self.instruction().symbol_uses(block_id, block_offset) {
470471
let (name, span, symbol_definition) = symbol_use;
471472
match symbol_definition {
472473
def @ SymbolUse::Reference(_) => {
473474
result.push((name, span, def));
474475
}
475-
SymbolUse::Definition(tagdef @ SymbolDefinition::Tag { .. }) => {
476-
result.push((name, span, SymbolUse::LocalDefinition(tagdef)));
477-
}
478-
SymbolUse::LocalDefinition(_) => {
479-
panic!("found local definition inside RC-word, don't know how to handle this.");
476+
SymbolUse::Definition(SymbolDefinition::Tag { .. }) => {
477+
// Here we have a tag definition inside an
478+
// RC-word. Therefore the passed-in value of
479+
// `block_id` is wrong (it refers to the block
480+
// containing the RC-word, not to the RC-block)
481+
// and the offset is similarly wrong.
482+
//
483+
// Therefore we will process these uses of symbols
484+
// at the time we allocate addresses for RC-block
485+
// words.
480486
}
481487
SymbolUse::Origin(_name, _block) => {
482488
unreachable!("Found origin {name} inside an RC-word; the parser should have rejected this.");
@@ -977,7 +983,7 @@ impl Spanned for UntaggedProgramInstruction {
977983
}
978984

979985
#[derive(Debug, Clone, PartialEq, Eq)]
980-
pub(crate) struct EqualityValue {
986+
pub(super) struct EqualityValue {
981987
pub(super) span: Span,
982988
pub(super) inner: UntaggedProgramInstruction,
983989
}
@@ -1213,7 +1219,7 @@ impl SourceFile {
12131219
self.symbol_uses()
12141220
.filter_map(|(name, span, sym_use)| match sym_use {
12151221
SymbolUse::Reference(context) => Some((name, span, context)),
1216-
SymbolUse::Definition(_) | SymbolUse::LocalDefinition(_) => None,
1222+
SymbolUse::Definition(_) => None,
12171223
// An origin specification is either a reference or a definition, depending on how it is used.
12181224
SymbolUse::Origin(name, block) => {
12191225
Some((name, span, SymbolContext::origin(block, span)))
@@ -1227,11 +1233,6 @@ impl SourceFile {
12271233
self.symbol_uses()
12281234
.filter_map(|(name, span, sym_use)| match sym_use {
12291235
SymbolUse::Reference(_context) => None,
1230-
SymbolUse::LocalDefinition(_) => {
1231-
// This is a local definition, but we're only
1232-
// looking for global definitions.
1233-
None
1234-
}
12351236
SymbolUse::Definition(def) => Some((name, span, def)),
12361237
// An origin specification is either a reference or a definition, depending on how it is used.
12371238
SymbolUse::Origin(name, block) => Some((

assembler/src/asmlib/driver.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -531,10 +531,28 @@ fn assemble_pass3(
531531
)?;
532532

533533
let convert_rc_failure = |e: RcWordAllocationFailure| -> AssemblerFailure {
534-
let location: Option<LineAndColumn> = e.span().map(|sp| LineAndColumn::from((body, sp)));
535-
AssemblerFailure::RcBlockTooLong {
536-
rc_word_source: e.source,
537-
rc_word_location: location,
534+
match e {
535+
RcWordAllocationFailure::RcBlockTooBig { source, .. } => {
536+
let location: Option<LineAndColumn> =
537+
source.span().map(|sp| LineAndColumn::from((body, sp)));
538+
AssemblerFailure::RcBlockTooLong {
539+
rc_word_source: source,
540+
rc_word_location: location,
541+
}
542+
}
543+
RcWordAllocationFailure::InconsistentTag(
544+
ref e @ BadSymbolDefinition::Incompatible(ref name, span, _, _),
545+
) => {
546+
let location: LineAndColumn = LineAndColumn::from((body, &span));
547+
AssemblerFailure::BadProgram(vec![WithLocation {
548+
location,
549+
inner: ProgramError::InconsistentTag {
550+
name: name.clone(),
551+
span,
552+
msg: e.to_string(),
553+
},
554+
}])
555+
}
538556
}
539557
};
540558

assembler/src/asmlib/driver/tests.rs

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -570,11 +570,52 @@ fn test_here_in_nested_rc_word_careful() {
570570

571571
#[test]
572572
fn tag_definition_in_rc_word() {
573+
// Tag values in RC-words are not "local" to the RC-word.
574+
//
575+
// Tags defined in RC-words may not be used for M4's editing
576+
// instuctions, but nevertheless they are not locally-scoped.
577+
// This is demonstrated by the example in section 6-4.7 of the
578+
// User's Handbook, which looks like this:
579+
//
580+
// ```
581+
// ☛☛DEF TBS|α
582+
// α
583+
// α
584+
// α
585+
// α
586+
// α
587+
// ☛☛EMD
588+
// 100|
589+
// USE-> LDA {TS->TBS|0} ** 5 BLANK RC WORDS
590+
// LDA TOMM
591+
// STA TS+3
592+
// ```
593+
//
594+
// You will see above that the definition of the tag `TS` is
595+
// inside an RC-word, but _not_ inside a macro body.
596+
//
597+
// The example explains that the above code snippet expands to:
598+
//
599+
// ```
600+
// 100|
601+
// USE -> LDA {TS-> TBS| 0} |002400 000103|000100
602+
// LDA TOMM |002400 000110| 101
603+
// STA TS+3 |003400 000106| 102
604+
// TS -> TBS 0
605+
// 0 |000000 000000| 103
606+
// 0 |000000 000000| 104
607+
// 0 |000000 000000| 105
608+
// 0 |000000 000000| 106
609+
// 0 |000000 000000| 107
610+
// TOMM -> 0 |000000 000000|000110
611+
// ```
612+
//
613+
// We cannot yet use the above example as our test case as macro
614+
// expansion is not yet supported.
573615
let input = concat!(
574-
"100| T->6\n", // address 100=6: T takes the value 100
575-
"{T->T+200}\n", // address 101=104 (address of first RC-word)
576-
"T\n", // address 102=100 (global value of T)
577-
"{T+400}\n" // address 103=105 (address of second RC-word)
616+
"100| {T->200}\n", // address 100=103 (address of the first RC-word)
617+
" T\n", // address 101=103 (global value of T)
618+
" {T}\n" // address 102=104 (address of second RC-word)
578619
);
579620

580621
let program = assemble_source(input, Default::default()).expect("program is valid");
@@ -587,25 +628,17 @@ fn tag_definition_in_rc_word() {
587628
BinaryChunk {
588629
address: Address::from(u18!(0o100)),
589630
words: vec![
590-
u36!(0o6), // from "T->6".
591-
u36!(0o104), // address of first RC-word
592-
u36!(0o100), // from "T"
593-
u36!(0o105) // address of second RC-word
631+
u36!(0o103), // From address of RC-word containing "T->200".
632+
u36!(0o103), // The value of T is global
633+
u36!(0o104), // from "{T}"
594634
]
595635
},
596636
BinaryChunk {
597637
// This is the RC-block.
598-
address: Address::from(u18!(0o104)),
638+
address: Address::from(u18!(0o103)),
599639
words: vec![
600-
// The first RC-word (at address 104) is
601-
// evaluated from T->T+200. Here T takes the
602-
// value 104, and so its content is T+200=304.
603-
u36!(0o304),
604-
// The second RC word is at 105, and is
605-
// evaluated from T+400. Here though there is
606-
// no tag definition for T, so we use the
607-
// global value of 100. Hence T+400 is 500.
608-
u36!(0o500),
640+
u36!(0o200), // First RC-word (which also defines T)
641+
u36!(0o103), // Second RC-word contains the value of T.
609642
]
610643
}
611644
]

assembler/src/asmlib/eval.rs

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::collections::BTreeMap;
21
use std::fmt::{self, Debug, Display, Formatter, Write};
32
use std::ops::{Shl, Shr};
43

@@ -17,7 +16,8 @@ use super::types::{
1716
};
1817
use crate::symbol::SymbolContext;
1918
use crate::symtab::{
20-
FinalSymbolDefinition, FinalSymbolTable, FinalSymbolType, LookupOperation, SymbolTable,
19+
FinalSymbolDefinition, FinalSymbolTable, FinalSymbolType, LookupOperation, SymbolDefinition,
20+
SymbolTable,
2121
};
2222

2323
#[derive(Debug, PartialEq, Eq)]
@@ -217,7 +217,7 @@ impl RcAllocator for RcBlock {
217217
self.words.push((source, value));
218218
Ok(addr)
219219
} else {
220-
Err(RcWordAllocationFailure {
220+
Err(RcWordAllocationFailure::RcBlockTooBig {
221221
source,
222222
rc_block_len: self.words.len(),
223223
})
@@ -1174,6 +1174,9 @@ impl Evaluate for RegistersContaining {
11741174
impl Evaluate for RegisterContaining {
11751175
fn evaluate<R: RcUpdater>(
11761176
&self,
1177+
// We must not use the passed-in target address since inside
1178+
// an RC-word, `#` refers to the adddress of the RC-word, not
1179+
// the address of the instruction which refers to it.
11771180
_target_address: &HereValue,
11781181
symtab: &mut SymbolTable,
11791182
rc_updater: &mut R,
@@ -1193,18 +1196,46 @@ impl Evaluate for RegisterContaining {
11931196
// during the evaluation process.
11941197
let here = HereValue::Address(*rc_word_addr);
11951198

1196-
// If inst has a tag, we temporarily override any
1197-
// global value for that tag with the address of
1198-
// this instruction.
1199-
let tag_overrides: BTreeMap<&Tag, Address> =
1200-
inst.tags.iter().map(|t| (t, *rc_word_addr)).collect();
1201-
let value: Unsigned36Bit = symtab.evaluate_with_temporary_tag_overrides(
1202-
tag_overrides,
1203-
inst.as_ref(),
1204-
&here,
1205-
rc_updater,
1206-
op,
1207-
)?;
1199+
// Tags defined in RC-words may not be used for M4's
1200+
// editing instuctions, but nevertheless they are not
1201+
// locally-scoped. This is demonstrated by the
1202+
// example in section 6-4.7 of the User's Handbook,
1203+
// which looks like this:
1204+
//
1205+
// ```
1206+
// ☛☛DEF TBS|α
1207+
// α
1208+
// α
1209+
// α
1210+
// α
1211+
// α
1212+
// ☛☛EMD
1213+
// 100|
1214+
// USE-> LDA {TS->TBS|0} ** 5 BLANK RC WORDS
1215+
// LDA TOMM
1216+
// STA TS+3
1217+
// ```
1218+
//
1219+
// You will see above that the definition of the tag
1220+
// `TS` is inside an RC-word, but _not_ inside a macro
1221+
// body.
1222+
//
1223+
// The example explains that the above code snippet expands to:
1224+
//
1225+
// ```
1226+
// 100|
1227+
// USE -> LDA {TS-> TBS| 0} |002400 000103|000100
1228+
// LDA TOMM |002400 000110| 101
1229+
// STA TS+3 |003400 000106| 102
1230+
// TS -> TBS 0
1231+
// 0 |000000 000000| 103
1232+
// 0 |000000 000000| 104
1233+
// 0 |000000 000000| 105
1234+
// 0 |000000 000000| 106
1235+
// 0 |000000 000000| 107
1236+
// TOMM -> 0 |000000 000000|000110
1237+
// ```
1238+
let value: Unsigned36Bit = inst.evaluate(&here, symtab, rc_updater, op)?;
12081239
rc_updater.update(*rc_word_addr, value);
12091240
Ok(Unsigned36Bit::from(rc_word_addr))
12101241
}
@@ -1291,6 +1322,15 @@ impl RegisterContaining {
12911322
match self {
12921323
RegisterContaining::Unallocated(mut tpibox) => {
12931324
let addr: Address = rc_allocator.allocate(source, Unsigned36Bit::ZERO)?;
1325+
for tag in tpibox.tags.iter() {
1326+
if let Err(e) = symtab.define(
1327+
tag.span,
1328+
tag.name.clone(),
1329+
SymbolDefinition::ResolvedTag(tag.span, addr),
1330+
) {
1331+
return Err(RcWordAllocationFailure::InconsistentTag(e));
1332+
}
1333+
}
12941334
tpibox.assign_rc_words(symtab, rc_allocator)?;
12951335
let tpi: Box<TaggedProgramInstruction> = tpibox;
12961336
Ok(RegisterContaining::Allocated(addr, tpi))

0 commit comments

Comments
 (0)