Skip to content

Commit 242e383

Browse files
committed
[assembler] Support double-pipe symbol configuration values.
1 parent d9c39af commit 242e383

File tree

8 files changed

+206
-13
lines changed

8 files changed

+206
-13
lines changed

assembler/src/asmlib/ast.rs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,53 @@ impl Evaluate for ArithmeticExpression {
265265
}
266266
}
267267

268+
/// A configuration syllable can be specified by putting it in a
269+
/// superscript, or by putting it in normal script after a ‖ symbol
270+
/// (‖x or ‖2, for example). This is described in section 6-2.1 of
271+
/// the Users Handbook.
272+
#[derive(Debug, Clone, PartialEq, Eq)]
273+
pub(crate) enum ConfigValue {
274+
Literal(Span, Unsigned36Bit),
275+
Symbol(Span, SymbolName),
276+
}
277+
278+
impl ConfigValue {
279+
pub(crate) fn symbol_uses(&self) -> impl Iterator<Item = (SymbolName, Span, SymbolUse)> {
280+
match self {
281+
ConfigValue::Literal(_span, _value) => None,
282+
ConfigValue::Symbol(span, name) => Some((
283+
name.to_owned(),
284+
*span,
285+
SymbolUse::Reference(SymbolContext::configuration()),
286+
)),
287+
}
288+
.into_iter()
289+
}
290+
}
291+
292+
impl Evaluate for ConfigValue {
293+
fn evaluate<R: RcAllocator>(
294+
&self,
295+
target_address: &HereValue,
296+
symtab: &mut SymbolTable,
297+
rc_allocator: &mut R,
298+
op: &mut LookupOperation,
299+
) -> Result<Unsigned36Bit, SymbolLookupFailure> {
300+
match self {
301+
ConfigValue::Literal(_span, value) => Ok(*value),
302+
ConfigValue::Symbol(span, name) => {
303+
let context = SymbolContext::configuration();
304+
match symtab.lookup_with_op(name, *span, target_address, rc_allocator, &context, op)
305+
{
306+
Ok(SymbolValue::Final(value)) => Ok(value),
307+
Err(e) => Err(e),
308+
}
309+
}
310+
}
311+
.map(|value| value.shl(30u32))
312+
}
313+
}
314+
268315
/// Eventually we will support real expressions, but for now we only
269316
/// suport literals and references to symbols ("equalities" in the
270317
/// User Handbook).
@@ -396,8 +443,9 @@ pub(crate) enum InstructionFragment {
396443
/// allows them in subscript/superscript too.
397444
Arithmetic(ArithmeticExpression),
398445
DeferredAddressing,
399-
// TODO: subscript/superscript atom (if the `Arithmetic` variant
400-
// disallows subscript/superscript).
446+
Config(ConfigValue), // some configuration values are specified as Arithmetic variants.
447+
// TODO: subscript/superscript atom (if the `Arithmetic` variant
448+
// disallows subscript/superscript).
401449
}
402450

403451
impl InstructionFragment {
@@ -408,6 +456,9 @@ impl InstructionFragment {
408456
result.extend(expr.symbol_uses());
409457
}
410458
InstructionFragment::DeferredAddressing => (),
459+
InstructionFragment::Config(value) => {
460+
result.extend(value.symbol_uses());
461+
}
411462
}
412463
result.into_iter()
413464
}
@@ -426,6 +477,9 @@ impl Evaluate for InstructionFragment {
426477
expr.evaluate(target_address, symtab, rc_allocator, op)
427478
}
428479
InstructionFragment::DeferredAddressing => Ok(DEFER_BIT),
480+
InstructionFragment::Config(value) => {
481+
value.evaluate(target_address, symtab, rc_allocator, op)
482+
}
429483
}
430484
}
431485
}

assembler/src/asmlib/driver.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,11 @@ fn assemble_pass3(
663663
};
664664
}
665665

666-
final_symbols.check_all_defined(&*symtab);
666+
// The evaluation process will have resulted in the computation of
667+
// a default definition for any symbols which were not already
668+
// inserted into final_symbols, so we make sure those are inserted
669+
// now.
670+
final_symbols.import_all_defined(&*symtab);
667671

668672
Ok((binary, final_symbols))
669673
}

assembler/src/asmlib/driver/tests.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,3 +401,27 @@ fn test_division_overflow_on_constants() {
401401
assemble_source("100| 1 / 777777777777\n", Default::default()).expect("program is valid");
402402
assert_eq!(program.chunks[0].words[0], u36!(0o1)); // 1
403403
}
404+
405+
#[test]
406+
fn test_double_pipe_config_literal_correctly_shifted() {
407+
// The 6 here is not superscript but it should be shifted into the
408+
// configuration part of the assembled word, because the '‖'
409+
// symbol introduces a configuration syllable.
410+
let input = "‖6\n";
411+
let program = assemble_source(input, Default::default()).expect("program is valid");
412+
assert_eq!(program.chunks[0].words[0], u36!(0o6).shl(30));
413+
}
414+
415+
#[test]
416+
fn test_double_pipe_config_symbolic_default_assignment() {
417+
// Section 6-2.2 "SYMEX DEFINITON - TAGS - EQUALITIES - AUTOMATIC
418+
// ASSIGNMENT" of the Users Handbook states that a symex used only
419+
// as a configuration value which has no defintiion will be
420+
// automatically assigned as zero.
421+
//
422+
// In our example here, the 42 octal should go in the address part
423+
// of the word.
424+
let input = "‖X 42\n";
425+
let program = assemble_source(input, Default::default()).expect("program is valid");
426+
assert_eq!(program.chunks[0].words[0], u36!(0o42));
427+
}

assembler/src/asmlib/eval.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,13 @@ impl SymbolContext {
223223
]
224224
}
225225

226+
pub(crate) fn configuration() -> SymbolContext {
227+
SymbolContext {
228+
configuration: true,
229+
..Default::default()
230+
}
231+
}
232+
226233
fn uses(span: Span) -> BTreeSet<OrderableSpan> {
227234
[OrderableSpan(span)].into_iter().collect()
228235
}

assembler/src/asmlib/lexer.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ pub(crate) enum Token {
119119
Asterisk(Script),
120120

121121
Pipe(Script),
122+
DoublePipe(Script),
122123
ProperSuperset(Script),
123124
SubsetOf(Script),
124125
IdenticalTo(Script),
@@ -192,8 +193,9 @@ impl Display for Token {
192193
Token::Hash(script) => write_elevated(script, "#"),
193194
Token::Equals(script) => write_elevated(script, "="),
194195
Token::Pipe(script) => write_elevated(script, "|"),
196+
Token::DoublePipe(script) => write_elevated(script, "‖"), // U+2016
195197
Token::ProperSuperset(script) => write_elevated(script, "⊃"), // U+2283
196-
Token::SubsetOf(script) => write_elevated(script, "⊂"), // U+2282
198+
Token::SubsetOf(script) => write_elevated(script, "⊂"), // U+2282
197199
Token::IdenticalTo(script) => write_elevated(script, "≡"),
198200
Token::Tilde(script) => write_elevated(script, "~"),
199201
Token::LessThan(script) => write_elevated(script, "<"),
@@ -478,9 +480,7 @@ fn tokenise_single_glyph(g: Elevated<&'static Glyph>) -> Option<Token> {
478480
todo!("Sigma (which is a symex terminator) does not yet have a token")
479481
}
480482
GlyphShape::Pipe => Some(Token::Pipe(script)),
481-
GlyphShape::DoublePipe => {
482-
todo!("double-pipe (which is a symex terminator) does not yet have a token")
483-
}
483+
GlyphShape::DoublePipe => Some(Token::DoublePipe(script)),
484484
GlyphShape::Solidus => Some(Token::Solidus(script)),
485485
GlyphShape::Times => Some(Token::Times(script)),
486486
GlyphShape::Hash => Some(Token::Hash(script)),

assembler/src/asmlib/parser.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,27 @@ where
212212
.labelled("arithmetic operator")
213213
}
214214

215+
fn doublepipe_config_value<'a, I>() -> impl Parser<'a, I, InstructionFragment, Extra<'a>> + Clone
216+
where
217+
I: Input<'a, Token = Tok, Span = Span> + ValueInput<'a>,
218+
{
219+
// Spaces are not allowed within configuration values, so we only
220+
// accept one symex syllable here.
221+
let symbolic_config_value = symex::symex_syllable(Script::Normal)
222+
.map_with(|name, extra| ConfigValue::Symbol(extra.span(), SymbolName::from(name)));
223+
// The numeric literal of a config value must be in normal script,
224+
// so ConfigValue::Literal directly contains an Unsigned36Bit
225+
// value instead of using the LiteralValue type.
226+
let literal_config_value = literal(Script::Normal)
227+
.map_with(|literal, extra| ConfigValue::Literal(extra.span(), literal.value()));
228+
229+
// We try to parse the config syllable as a literal first, because
230+
// symexes can also contain (and start with) digits.
231+
just(Tok::DoublePipe(Script::Normal))
232+
.ignore_then(choice((literal_config_value, symbolic_config_value)))
233+
.map(|config| InstructionFragment::Config(config))
234+
}
235+
215236
fn program_instruction_fragments<'srcbody, I>(
216237
) -> impl Parser<'srcbody, I, Vec<InstructionFragment>, Extra<'srcbody>>
217238
where
@@ -292,6 +313,7 @@ where
292313
single_script_fragment(Script::Super),
293314
single_script_fragment(Script::Sub),
294315
just(Tok::Asterisk(Script::Normal)).to(InstructionFragment::DeferredAddressing),
316+
doublepipe_config_value(),
295317
))
296318
.repeated()
297319
.at_least(1)

assembler/src/asmlib/parser/tests.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,3 +1569,57 @@ fn test_asterisk_for_deferred_addressing() {
15691569
]
15701570
);
15711571
}
1572+
1573+
#[test]
1574+
fn test_double_pipe_config_symbolic() {
1575+
// The input "‖4" sets the configuration syllable of the
1576+
// instruction word to 4. This is described in section 6-2.1 of
1577+
// the Users Handbook. Spaces are not allowed in the
1578+
// configuration syllable, so "‖X Y" should be parsed as a
1579+
// configuration syllable X followed by the symbolic value Y
1580+
// (which would therefore go into the address portion of the
1581+
// instruction word).
1582+
let input_xy = "‖X Y";
1583+
let got = parse_successfully_with(input_xy, tagged_program_instruction(), no_state_setup);
1584+
let expected = TaggedProgramInstruction {
1585+
tag: None,
1586+
instruction: UntaggedProgramInstruction {
1587+
span: span(0..6),
1588+
holdbit: HoldBit::Unspecified,
1589+
parts: vec![
1590+
InstructionFragment::Config(ConfigValue::Symbol(
1591+
span(3..4),
1592+
SymbolName {
1593+
canonical: "X".to_string(),
1594+
},
1595+
)),
1596+
InstructionFragment::Arithmetic(ArithmeticExpression::from(Atom::Symbol(
1597+
span(5..6),
1598+
Script::Normal,
1599+
SymbolName {
1600+
canonical: "Y".to_string(),
1601+
},
1602+
))),
1603+
],
1604+
},
1605+
};
1606+
assert_eq!(got, expected);
1607+
}
1608+
1609+
#[test]
1610+
fn test_double_pipe_config_literal() {
1611+
let input = "‖10"; // 10 octal = 8 decimal.
1612+
let got = parse_successfully_with(input, tagged_program_instruction(), no_state_setup);
1613+
let expected = TaggedProgramInstruction {
1614+
tag: None,
1615+
instruction: UntaggedProgramInstruction {
1616+
span: span(0..5),
1617+
holdbit: HoldBit::Unspecified,
1618+
parts: vec![InstructionFragment::Config(ConfigValue::Literal(
1619+
span(3..5),
1620+
u36!(8),
1621+
))],
1622+
},
1623+
};
1624+
assert_eq!(got, expected);
1625+
}

assembler/src/asmlib/symtab.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,21 @@ impl SymbolTable {
246246
.map(SymbolValue::Final)
247247
}
248248
SymbolDefinition::Undefined(context_union) => {
249-
match t.get_default_value(name, &span, &context_union) {
250-
Ok(value) => Ok(SymbolValue::Final(value)),
249+
dbg!(&name);
250+
match dbg!(t.get_default_value(name, &span, &context_union)) {
251+
Ok(value) => {
252+
eprintln!("now that a default value {value} has been assigned for {name}, we will insert this into the symbol table.");
253+
match t.define(
254+
span,
255+
name.clone(),
256+
SymbolDefinition::DefaultAssigned(value, context_union),
257+
) {
258+
Err(e) => {
259+
panic!("got a bad symbol definition error ({e}) on a previously undefined symbol, which should be impossible");
260+
}
261+
Ok(()) => Ok(SymbolValue::Final(value)),
262+
}
263+
}
251264
Err(e) => Err(e),
252265
}
253266
}
@@ -495,6 +508,7 @@ impl SymbolLookup for SymbolTable {
495508
pub(crate) enum FinalSymbolType {
496509
Tag,
497510
Equality,
511+
Default,
498512
}
499513

500514
#[derive(Debug)]
@@ -532,12 +546,26 @@ impl FinalSymbolTable {
532546
self.definitions.entry(name).or_insert(def);
533547
}
534548

535-
pub(crate) fn check_all_defined(&self, symtab: &SymbolTable) {
536-
for (name, definition) in symtab.definitions.iter() {
537-
if !self.definitions.contains_key(name) {
538-
panic!("symbol {name} appears in symbol table with definition {definition:?} but was not finalised in the final symbol table");
549+
pub(crate) fn import_all_defined(&mut self, symtab: &SymbolTable) {
550+
fn make_final_def(name: &SymbolName, def: &SymbolDefinition) -> FinalSymbolDefinition {
551+
match def {
552+
SymbolDefinition::DefaultAssigned(value, _context) => FinalSymbolDefinition {
553+
value: *value,
554+
representation: value.to_string(),
555+
sym_type: FinalSymbolType::Default,
556+
},
557+
SymbolDefinition::Undefined(_symbol_context) => unreachable!(),
558+
_ => unimplemented!(
559+
"symbol table entry for {name} (with definition {def:?}) had not been copied into the final symbol table"
560+
),
539561
}
540562
}
563+
564+
for (name, definition) in symtab.definitions.iter() {
565+
self.definitions
566+
.entry(name.clone())
567+
.or_insert_with(|| make_final_def(name, &definition.def));
568+
}
541569
}
542570
}
543571

0 commit comments

Comments
 (0)