Skip to content

Commit f0eef8c

Browse files
committed
[assembler] Ensure # resolves correctly inside RC-words.
1 parent 47e55ab commit f0eef8c

File tree

4 files changed

+99
-16
lines changed

4 files changed

+99
-16
lines changed

assembler/src/asmlib/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ enum SymbolUse {
2525

2626
pub(crate) trait RcAllocator {
2727
fn allocate(&mut self, span: Span, value: Unsigned36Bit) -> Address;
28+
fn update(&mut self, address: Address, value: Unsigned36Bit);
2829
}
2930

3031
/// Eventually we will support symbolic expressions.

assembler/src/asmlib/driver.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,10 @@ impl RcAllocator for NoRcBlock {
378378
fn allocate(&mut self, _span: Span, _value: Unsigned36Bit) -> Address {
379379
panic!("Cannot allocate an RC-word before we know the address of the RC block");
380380
}
381+
382+
fn update(&mut self, _address: Address, _value: Unsigned36Bit) {
383+
panic!("Cannot update an RC-word in an RC-block which cannot allocate words");
384+
}
381385
}
382386

383387
fn assign_block_positions(

assembler/src/asmlib/driver/tests.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,3 +559,36 @@ fn test_440_330_220_110_with_commas_in_rc_word() {
559559
assert_eq!(program.chunks[0].words[0], program.chunks[1].address); // point to first word in RC-block
560560
assert_eq!(program.chunks[1].words[0], u36!(0o440330220110));
561561
}
562+
563+
#[test]
564+
fn test_here_in_rc_word() {
565+
let input = "100|{#}\n";
566+
let program = assemble_source(input, Default::default()).expect("program is valid");
567+
dbg!(&program);
568+
assert_eq!(program.chunks.len(), 2); // one regular chunk plus RC-block
569+
assert_eq!(program.chunks[0].address, Address::from(u18!(0o100)));
570+
assert_eq!(program.chunks[0].words.len(), 1);
571+
assert_eq!(program.chunks[0].words[0], u36!(0o101)); // points to first word in RC-block
572+
573+
assert_eq!(program.chunks[1].address, Address::from(u18!(0o101)));
574+
assert_eq!(program.chunks[1].words.len(), 1); // RC block has one word
575+
assert_eq!(program.chunks[1].words[0], u36!(0o101)); // # is 0o101 at address 0o101
576+
}
577+
578+
#[test]
579+
fn test_here_in_nested_rc_word() {
580+
let input = "100|{{#+400}}\n";
581+
let program = assemble_source(input, Default::default()).expect("program is valid");
582+
dbg!(&program);
583+
assert_eq!(program.chunks.len(), 2); // one regular chunk plus RC-block
584+
assert_eq!(program.chunks[0].address, Address::from(u18!(0o100)));
585+
assert_eq!(program.chunks[0].words.len(), 1);
586+
assert_eq!(program.chunks[0].words[0], u36!(0o101)); // points to first word in RC-block
587+
588+
assert_eq!(program.chunks[1].address, Address::from(u18!(0o101)));
589+
assert_eq!(program.chunks[1].words.len(), 2);
590+
// The first RC-word points to the second RC-word.
591+
assert_eq!(program.chunks[1].words[0], u36!(0o102));
592+
// # is 0o102 at address 0o102, so #+400 is 0o502.
593+
assert_eq!(program.chunks[1].words[1], u36!(0o502));
594+
}

assembler/src/asmlib/eval.rs

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -186,21 +186,54 @@ pub(crate) struct RcBlock {
186186
pub(crate) words: Vec<(Span, Unsigned36Bit)>,
187187
}
188188

189-
impl RcAllocator for RcBlock {
190-
fn allocate(&mut self, span: Span, value: Unsigned36Bit) -> Address {
189+
impl RcBlock {
190+
fn end(&self) -> Address {
191191
match Unsigned18Bit::try_from(self.words.len()) {
192-
Ok(offset) => {
193-
let addr = self.address.index_by(offset);
194-
self.words.push((span, value));
195-
addr
196-
}
192+
Ok(offset) => self.address.index_by(offset),
197193
Err(_) => {
198194
panic!("program is too large"); // TODO: fixme: use Result
199195
}
200196
}
201197
}
202198
}
203199

200+
impl RcAllocator for RcBlock {
201+
fn allocate(&mut self, span: Span, value: Unsigned36Bit) -> Address {
202+
let addr = self.end();
203+
self.words.push((span, value));
204+
addr
205+
}
206+
207+
fn update(&mut self, address: Address, value: Unsigned36Bit) {
208+
if address < self.address {
209+
panic!(
210+
"out of range access to address {address} of RC-block starting at {}",
211+
self.address
212+
);
213+
}
214+
match Unsigned18Bit::from(address).checked_sub(Unsigned18Bit::from(self.address)) {
215+
Some(offset) => match self.words.get_mut(usize::from(offset)) {
216+
Some((_span, spot)) => {
217+
*spot = value;
218+
}
219+
None => {
220+
panic!(
221+
"out of range access to address {address} of RC-block ending at {}",
222+
self.end()
223+
);
224+
}
225+
},
226+
None => {
227+
// The checks above should ensure that address >= self.address.
228+
panic!(
229+
"inconsistent checks; {address:o} should be greater than {:o}",
230+
self.address
231+
);
232+
}
233+
}
234+
}
235+
}
236+
204237
#[cfg(test)]
205238
pub(crate) fn make_empty_rc_block_for_test(location: Address) -> RcBlock {
206239
RcBlock {
@@ -427,15 +460,27 @@ impl Evaluate for Atom {
427460
),
428461
Atom::Parens(_script, expr) => expr.evaluate(target_address, symtab, rc_allocator, op),
429462
Atom::RcRef(span, tagged_program_instructions) => {
430-
let value: Unsigned36Bit = evaluate_and_combine_values(
431-
tagged_program_instructions,
432-
target_address,
433-
symtab,
434-
rc_allocator,
435-
op,
436-
)?;
437-
let addr: Address = rc_allocator.allocate(*span, value);
438-
Ok(addr.into())
463+
let mut first_addr: Option<Address> = None;
464+
for inst in tagged_program_instructions.iter() {
465+
let rc_word_addr: Address = rc_allocator.allocate(*span, Unsigned36Bit::ZERO);
466+
if first_addr.is_none() {
467+
first_addr = Some(rc_word_addr);
468+
}
469+
// Within the RC-word, # ("here") resolves to the
470+
// address of the RC-word itself. So before we
471+
// evaluate the value to be placed in the RC-word,
472+
// we need to know the value that # will take
473+
// during the evaluation process.
474+
let here = HereValue::Address(rc_word_addr);
475+
let value: Unsigned36Bit = inst.evaluate(&here, symtab, rc_allocator, op)?;
476+
rc_allocator.update(rc_word_addr, value);
477+
}
478+
match first_addr {
479+
Some(addr) => Ok(addr.into()),
480+
None => {
481+
unreachable!("RC-references should not occupy zero words of storage");
482+
}
483+
}
439484
}
440485
}
441486
}

0 commit comments

Comments
 (0)