Skip to content

Commit b2609de

Browse files
committed
Add loop-detection test for symbol table.
1 parent 9e0bd81 commit b2609de

File tree

3 files changed

+145
-96
lines changed

3 files changed

+145
-96
lines changed

assembler/src/asmlib/driver/tests.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use super::super::ast::{
77
};
88
use super::super::eval::{make_empty_rc_block_for_test, SymbolLookup, SymbolValue};
99
use super::super::span::*;
10-
use super::super::symbol::{SymbolContext, SymbolName};
10+
use super::super::symbol::SymbolName;
1111
use super::super::symtab::LookupOperation;
1212
use super::{assemble_nonempty_valid_input, assemble_source};
1313
use super::{assemble_pass1, Binary, BinaryChunk};
@@ -29,12 +29,11 @@ fn assemble_check_symbols(input: &str, target_address: Address, expected: &[(&st
2929
canonical: name.to_string(),
3030
};
3131
let span = span(0..(name.len()));
32-
let context = SymbolContext::from((Script::Normal, span));
3332
let here = HereValue::Address(target_address);
3433
let mut op = LookupOperation::default();
3534
let mut rc_block =
3635
make_empty_rc_block_for_test(Address::from(Unsigned18Bit::from(0o020_000u16)));
37-
match symtab.lookup_with_op(&sym, span, &here, &mut rc_block, &context, &mut op) {
36+
match symtab.lookup_with_op(&sym, span, &here, &mut rc_block, &mut op) {
3837
Ok(got) => {
3938
if got != *expected_value {
4039
panic!("incorrect value for symbol {name}; expected {expected_value:?}, got {got:?}");

assembler/src/asmlib/eval.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use super::ast::{
1919
};
2020
use super::listing::{Listing, ListingLine};
2121
use super::span::*;
22-
use super::symbol::{SymbolContext, SymbolName, SymbolOrHere};
22+
use super::symbol::{SymbolName, SymbolOrHere};
2323
use super::types::{AssemblerFailure, BlockIdentifier, MachineLimitExceededFailure};
2424

2525
#[derive(Debug, PartialEq, Eq)]
@@ -144,7 +144,6 @@ pub(crate) trait SymbolLookup {
144144
span: Span, // TODO: use &Span?
145145
target_address: &HereValue,
146146
rc_allocator: &mut R,
147-
context: &SymbolContext,
148147
op: &mut Self::Operation<'_>,
149148
) -> Result<SymbolValue, SymbolLookupFailure>;
150149
}
@@ -419,8 +418,7 @@ fn symbol_name_lookup<R: RcAllocator>(
419418
rc_allocator: &mut R,
420419
op: &mut LookupOperation,
421420
) -> Result<Unsigned36Bit, SymbolLookupFailure> {
422-
let context = SymbolContext::from((elevation, span));
423-
match symtab.lookup_with_op(name, span, target_address, rc_allocator, &context, op) {
421+
match symtab.lookup_with_op(name, span, target_address, rc_allocator, op) {
424422
Ok(SymbolValue::Final(value)) => Ok(value),
425423
Err(e) => Err(e),
426424
}

assembler/src/asmlib/symtab.rs

Lines changed: 141 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,64 @@ pub(crate) struct LookupOperation {
7676
deps_in_order: Vec<SymbolName>,
7777
}
7878

79+
fn final_lookup_helper_body<R: RcAllocator>(
80+
t: &mut SymbolTable,
81+
name: &SymbolName,
82+
span: Span,
83+
target_address: &HereValue,
84+
rc_allocator: &mut R,
85+
op: &mut LookupOperation,
86+
) -> Result<SymbolValue, SymbolLookupFailure> {
87+
if let Some(def) = t.get_clone(name) {
88+
match def {
89+
SymbolDefinition::DefaultAssigned(value, _) => Ok(SymbolValue::Final(value)),
90+
SymbolDefinition::Origin(addr) => Ok(SymbolValue::Final(addr.into())),
91+
SymbolDefinition::Tag {
92+
block_id,
93+
block_offset,
94+
span: _,
95+
} => match t.finalise_origin(block_id, rc_allocator, Some(op)) {
96+
Ok(address) => {
97+
let computed_address: Address = address.index_by(block_offset);
98+
Ok(SymbolValue::Final(computed_address.into()))
99+
}
100+
Err(e) => {
101+
panic!("failed to finalise origin of {block_id}: {e}");
102+
}
103+
},
104+
SymbolDefinition::TagOverride(address) => Ok(SymbolValue::Final(address.into())),
105+
SymbolDefinition::Equality(expression) => {
106+
// The target address does not matter below
107+
// since assignments are not allowed to use
108+
// '#' on the right-hand-side of the
109+
// assignment.
110+
expression
111+
.evaluate(target_address, t, rc_allocator, op)
112+
.map(SymbolValue::Final)
113+
}
114+
SymbolDefinition::Undefined(context_union) => {
115+
match t.get_default_value(name, &span, &context_union, rc_allocator) {
116+
Ok(value) => {
117+
match t.define(
118+
span,
119+
name.clone(),
120+
SymbolDefinition::DefaultAssigned(value, context_union),
121+
) {
122+
Err(e) => {
123+
panic!("got a bad symbol definition error ({e}) on a previously undefined symbol, which should be impossible");
124+
}
125+
Ok(()) => Ok(SymbolValue::Final(value)),
126+
}
127+
}
128+
Err(e) => Err(e),
129+
}
130+
}
131+
}
132+
} else {
133+
panic!("final symbol lookup of symbol '{name}' happened in an evaluation context which was not previously returned by SourceFile::global_symbol_references(): {op:?}");
134+
}
135+
}
136+
79137
impl SymbolTable {
80138
pub(crate) fn new<I>(block_sizes: I) -> SymbolTable
81139
where
@@ -159,93 +217,6 @@ impl SymbolTable {
159217
});
160218
}
161219

162-
fn final_lookup_helper<R: RcAllocator>(
163-
&mut self,
164-
name: &SymbolName,
165-
span: Span,
166-
target_address: &HereValue,
167-
rc_allocator: &mut R,
168-
_context: &SymbolContext,
169-
op: &mut LookupOperation,
170-
) -> Result<SymbolValue, SymbolLookupFailure> {
171-
fn body<R: RcAllocator>(
172-
t: &mut SymbolTable,
173-
name: &SymbolName,
174-
span: Span,
175-
target_address: &HereValue,
176-
rc_allocator: &mut R,
177-
op: &mut LookupOperation,
178-
) -> Result<SymbolValue, SymbolLookupFailure> {
179-
if let Some(def) = t.get_clone(name) {
180-
match def {
181-
SymbolDefinition::DefaultAssigned(value, _) => Ok(SymbolValue::Final(value)),
182-
SymbolDefinition::Origin(addr) => Ok(SymbolValue::Final(addr.into())),
183-
SymbolDefinition::Tag {
184-
block_id,
185-
block_offset,
186-
span: _,
187-
} => match t.finalise_origin(block_id, rc_allocator, Some(op)) {
188-
Ok(address) => {
189-
let computed_address: Address = address.index_by(block_offset);
190-
Ok(SymbolValue::Final(computed_address.into()))
191-
}
192-
Err(e) => {
193-
panic!("failed to finalise origin of {block_id}: {e}");
194-
}
195-
},
196-
SymbolDefinition::TagOverride(address) => {
197-
Ok(SymbolValue::Final(address.into()))
198-
}
199-
SymbolDefinition::Equality(expression) => {
200-
// The target address does not matter below
201-
// since assignments are not allowed to use
202-
// '#' on the right-hand-side of the
203-
// assignment.
204-
expression
205-
.evaluate(target_address, t, rc_allocator, op)
206-
.map(SymbolValue::Final)
207-
}
208-
SymbolDefinition::Undefined(context_union) => {
209-
match t.get_default_value(name, &span, &context_union, rc_allocator) {
210-
Ok(value) => {
211-
match t.define(
212-
span,
213-
name.clone(),
214-
SymbolDefinition::DefaultAssigned(value, context_union),
215-
) {
216-
Err(e) => {
217-
panic!("got a bad symbol definition error ({e}) on a previously undefined symbol, which should be impossible");
218-
}
219-
Ok(()) => Ok(SymbolValue::Final(value)),
220-
}
221-
}
222-
Err(e) => Err(e),
223-
}
224-
}
225-
}
226-
} else {
227-
panic!("final symbol lookup of symbol '{name}' happened in an evaluation context which was not previously returned by SourceFile::global_symbol_references(): {op:?}");
228-
}
229-
}
230-
231-
op.deps_in_order.push(name.clone());
232-
if !op.depends_on.insert(name.clone()) {
233-
// `name` was already in `depends_on`; in other words,
234-
// we have a loop.
235-
return Err(SymbolLookupFailure::from((
236-
name.clone(),
237-
span,
238-
SymbolLookupFailureKind::Loop {
239-
deps_in_order: op.deps_in_order.to_vec(),
240-
},
241-
)));
242-
}
243-
let result = body(self, name, span, target_address, rc_allocator, op);
244-
op.deps_in_order.pop();
245-
op.depends_on.remove(name);
246-
result
247-
}
248-
249220
pub(crate) fn finalise_origin<R: RcAllocator>(
250221
&mut self,
251222
block_id: BlockIdentifier,
@@ -498,10 +469,24 @@ impl SymbolLookup for SymbolTable {
498469
span: Span,
499470
target_address: &HereValue,
500471
rc_allocator: &mut R,
501-
context: &SymbolContext,
502472
op: &mut Self::Operation<'_>,
503473
) -> Result<SymbolValue, SymbolLookupFailure> {
504-
self.final_lookup_helper(name, span, target_address, rc_allocator, context, op)
474+
op.deps_in_order.push(name.clone());
475+
if !op.depends_on.insert(name.clone()) {
476+
// `name` was already in `depends_on`; in other words,
477+
// we have a loop.
478+
return Err(SymbolLookupFailure::from((
479+
name.clone(),
480+
span,
481+
SymbolLookupFailureKind::Loop {
482+
deps_in_order: op.deps_in_order.to_vec(),
483+
},
484+
)));
485+
}
486+
let result = final_lookup_helper_body(self, name, span, target_address, rc_allocator, op);
487+
op.deps_in_order.pop();
488+
op.depends_on.remove(name);
489+
result
505490
}
506491
}
507492

@@ -723,3 +708,70 @@ impl SymbolDefinition {
723708
}
724709
}
725710
}
711+
712+
#[test]
713+
fn test_loop_detection() {
714+
use super::ast::*;
715+
use super::eval::RcBlock;
716+
use base::charset::Script;
717+
let blocks = [(span(0..10), None, u18!(2))];
718+
let mut symtab = SymbolTable::new(blocks);
719+
720+
// define "A"
721+
symtab
722+
.define(
723+
span(0..1),
724+
SymbolName::from("A"),
725+
SymbolDefinition::Equality(EqualityValue::from(vec![CommaDelimitedInstruction {
726+
leading_commas: None,
727+
instruction: UntaggedProgramInstruction {
728+
span: span(0..1),
729+
holdbit: HoldBit::Unspecified,
730+
inst: InstructionFragment::from(ArithmeticExpression::from(
731+
SymbolOrLiteral::Symbol(Script::Normal, SymbolName::from("B"), span(2..3)),
732+
)),
733+
},
734+
trailing_commas: None,
735+
}])),
736+
)
737+
.expect("definition of A should be correct");
738+
// define "B"
739+
symtab
740+
.define(
741+
span(4..5),
742+
SymbolName::from("B"),
743+
SymbolDefinition::Equality(EqualityValue::from(vec![CommaDelimitedInstruction {
744+
leading_commas: None,
745+
instruction: UntaggedProgramInstruction {
746+
span: span(4..5),
747+
holdbit: HoldBit::Unspecified,
748+
inst: InstructionFragment::from(ArithmeticExpression::from(
749+
SymbolOrLiteral::Symbol(Script::Normal, SymbolName::from("A"), span(6..7)),
750+
)),
751+
},
752+
trailing_commas: None,
753+
}])),
754+
)
755+
.expect("definition of B should be correct");
756+
757+
let mut rc_block = RcBlock::default();
758+
759+
// An attempt to look up A should fail because we detect a loop.
760+
let mut op = Default::default();
761+
let r = symtab.lookup_with_op(
762+
&SymbolName::from("A"),
763+
span(0..1),
764+
&HereValue::NotAllowed,
765+
&mut rc_block,
766+
&mut op,
767+
);
768+
dbg!(&r);
769+
assert!(matches!(
770+
r,
771+
Err(SymbolLookupFailure {
772+
target: _,
773+
kind: SymbolLookupFailureKind::Loop { .. }
774+
})
775+
));
776+
assert!(false);
777+
}

0 commit comments

Comments
 (0)