assembler/
manuscript.rs

1//! The immediate output of the parsing process.
2//!
3//! In the description of the M4 assembler, the "manuscript" is the
4//! user's input, and the interpretation of editing meta-commands in
5//! the manuscript turns it into a "directive".  This assembler doesn'
6//! implement M4's editing commands, however.
7use std::collections::BTreeMap;
8
9use tracing::{Level, event};
10
11use base::Unsigned18Bit;
12#[cfg(test)]
13use base::Unsigned36Bit;
14use base::charset::Script;
15use base::prelude::Address;
16#[cfg(test)]
17use base::u18;
18
19use super::ast::ArithmeticExpression;
20use super::ast::Equality;
21use super::ast::EqualityValue;
22#[cfg(test)]
23use super::ast::HoldBit;
24#[cfg(test)]
25use super::ast::InstructionFragment;
26use super::ast::InstructionSequence;
27use super::ast::LocalSymbolTableBuildFailure;
28use super::ast::OnUnboundMacroParameter;
29use super::ast::Origin;
30use super::ast::SymbolUse;
31use super::ast::Tag;
32use super::ast::TaggedProgramInstruction;
33use super::ast::block_items_with_offset;
34use super::collections::OneOrMore;
35use super::directive::Directive;
36use super::lexer::Token;
37use super::memorymap::LocatedBlock;
38use super::memorymap::MemoryMap;
39use super::span::Span;
40use super::span::Spanned;
41#[cfg(test)]
42use super::span::span;
43use super::state::NumeralMode;
44use super::symbol::InconsistentSymbolUse;
45use super::symbol::SymbolContext;
46use super::symbol::SymbolName;
47#[cfg(test)]
48use super::symtab::BadSymbolDefinition;
49use super::symtab::ExplicitDefinition;
50use super::symtab::ExplicitSymbolTable;
51#[cfg(test)]
52use super::symtab::TagDefinition;
53use super::types::BlockIdentifier;
54
55fn offset_to_block_id<T>((offset, item): (usize, T)) -> (BlockIdentifier, T) {
56    (BlockIdentifier::from(offset), item)
57}
58
59fn definitions_only(
60    r: Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>,
61) -> Option<Result<(SymbolName, Span, ExplicitDefinition), InconsistentSymbolUse>> {
62    match r {
63        // An origin specification is either a reference or a
64        // definition, depending on how it is used.  But we will cope
65        // with origin definitions when processing the blocks (as
66        // opposed to the blocks' contents).
67        Ok((
68            _,
69            _,
70            SymbolUse::Definition(ExplicitDefinition::Origin(_, _)) | SymbolUse::Reference(_),
71        )) => None,
72        Ok((name, span, SymbolUse::Definition(def))) => Some(Ok((name, span, def))),
73        Err(e) => Some(Err(e)),
74    }
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub(crate) struct SourceFile {
79    pub(crate) punch: Option<PunchCommand>,
80    /// Each block is an optional origin followed by some
81    /// instructions.  A block is not a scoping artifact (see the
82    /// module documentation).
83    pub(crate) blocks: Vec<ManuscriptBlock>,
84    pub(crate) global_equalities: Vec<Equality>,
85    pub(crate) macros: BTreeMap<SymbolName, MacroDefinition>,
86}
87
88impl SourceFile {
89    fn symbol_uses(
90        &self,
91    ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<'_>
92    {
93        let uses_in_instructions = self
94            .blocks
95            .iter()
96            .enumerate()
97            .map(offset_to_block_id)
98            .flat_map(move |(block_id, block)| block.symbol_uses(block_id));
99        let uses_in_global_assignments = self
100            .global_equalities
101            .iter()
102            .flat_map(Equality::symbol_uses);
103        uses_in_instructions.chain(uses_in_global_assignments)
104    }
105
106    pub(crate) fn build_local_symbol_tables(
107        &mut self,
108    ) -> Result<(), OneOrMore<LocalSymbolTableBuildFailure>> {
109        let mut errors = Vec::default();
110        for (block_identifier, block) in self.blocks.iter_mut().enumerate().map(offset_to_block_id)
111        {
112            if let Err(e) = block.build_local_symbol_tables(block_identifier) {
113                errors.extend(e.into_iter());
114            }
115        }
116        match OneOrMore::try_from_vec(errors) {
117            Err(_) => Ok(()), // error vector is empty
118            Ok(errors) => Err(errors),
119        }
120    }
121
122    pub(crate) fn global_symbol_references(
123        &self,
124    ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolContext), InconsistentSymbolUse>> + '_
125    {
126        fn accept_references_only(
127            r: Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>,
128        ) -> Option<Result<(SymbolName, Span, SymbolContext), InconsistentSymbolUse>> {
129            match r {
130                Ok((name, span, sym_use)) => match sym_use {
131                    SymbolUse::Reference(context) => Some(Ok((name, span, context))),
132                    // An origin specification is either a reference or a definition, depending on how it is used.
133                    SymbolUse::Definition(ExplicitDefinition::Origin(
134                        ref origin @ Origin::Symbolic(span, ref name),
135                        block_id,
136                    )) => Some(Ok((
137                        name.clone(),
138                        span,
139                        SymbolContext::origin(block_id, origin.clone()),
140                    ))),
141                    SymbolUse::Definition(_) => None,
142                },
143                Err(e) => Some(Err(e)),
144            }
145        }
146        self.symbol_uses().filter_map(accept_references_only)
147    }
148
149    pub(crate) fn global_symbol_definitions(
150        &self,
151    ) -> impl Iterator<Item = Result<(SymbolName, Span, ExplicitDefinition), InconsistentSymbolUse>> + '_
152    {
153        self.symbol_uses().filter_map(definitions_only)
154    }
155
156    pub(crate) fn into_directive(self, mem_map: &MemoryMap) -> Directive {
157        let SourceFile {
158            punch,
159            blocks: input_blocks,
160            global_equalities: equalities,
161            macros: _,
162        } = self;
163        let output_blocks: BTreeMap<BlockIdentifier, LocatedBlock> = input_blocks
164            .into_iter()
165            .enumerate()
166            .map(|(id, mblock)| (BlockIdentifier::from(id), mblock))
167            .map(|(block_id, mblock)| {
168                let location: Address = match mem_map.get(&block_id).map(|pos| pos.block_address) {
169                    Some(Some(addr)) => addr,
170                    None => unreachable!("unknown block {block_id} in input_blocks"),
171                    Some(None) => unreachable!(
172                        "block location for {block_id} should already have been determined"
173                    ),
174                };
175                (
176                    block_id,
177                    LocatedBlock {
178                        origin: mblock.origin,
179                        location,
180                        sequences: mblock.sequences,
181                    },
182                )
183            })
184            .collect();
185
186        let entry_point: Option<Address> = match punch {
187            Some(PunchCommand(Some(address))) => {
188                event!(
189                    Level::INFO,
190                    "program entry point was specified as {address:o}"
191                );
192                Some(address)
193            }
194            Some(PunchCommand(None)) => {
195                event!(Level::INFO, "program entry point was not specified");
196                None
197            }
198            None => {
199                event!(
200                    Level::WARN,
201                    "No PUNCH directive was given, program has no start address"
202                );
203                None
204            }
205        };
206
207        // Because the PUNCH instruction causes the assembler
208        // output to be punched to tape, this effectively
209        // marks the end of the input.  On the real M4
210        // assembler it is likely possible for there to be
211        // more manuscript after the PUNCH metacommand, and
212        // for this to generate a fresh reader leader and so
213        // on.  But this is not supported here.  The reason we
214        // don't support it is that we'd need to know the
215        // answers to a lot of questions we don't have
216        // answers for right now.  For example, should the
217        // existing program be cleared?  Should the symbol
218        // table be cleared?
219        Directive::new(output_blocks, equalities, entry_point)
220    }
221}
222
223fn build_local_symbol_table<'a, I>(
224    block_identifier: BlockIdentifier,
225    instructions: I,
226) -> Result<ExplicitSymbolTable, OneOrMore<LocalSymbolTableBuildFailure>>
227where
228    I: Iterator<Item = &'a TaggedProgramInstruction>,
229{
230    let mut errors: Vec<LocalSymbolTableBuildFailure> = Default::default();
231    let mut local_symbols = ExplicitSymbolTable::default();
232    for (offset, instruction) in block_items_with_offset(instructions) {
233        for r in instruction
234            .symbol_uses(block_identifier, offset)
235            .filter_map(definitions_only)
236        {
237            match r {
238                Ok((symbol_name, _span, definition)) => {
239                    if let Err(e) = local_symbols.define(symbol_name.clone(), definition) {
240                        errors.push(LocalSymbolTableBuildFailure::BadDefinition(e));
241                    }
242                }
243                Err(e) => {
244                    errors.push(LocalSymbolTableBuildFailure::InconsistentUsage(e));
245                }
246            }
247        }
248    }
249    match OneOrMore::try_from_vec(errors) {
250        Err(_) => Ok(local_symbols), // error vector is empty
251        Ok(errors) => Err(errors),
252    }
253}
254
255#[test]
256fn test_build_local_symbol_table_happy_case() {
257    let instruction_tagged_with = |name: &str, beginpos: usize| {
258        let tag_span = span(beginpos..(beginpos + 1));
259        let literal_span = span((beginpos + 3)..(beginpos + 4));
260        TaggedProgramInstruction::single(
261            vec![Tag {
262                name: SymbolName::from(name),
263                span: tag_span,
264            }],
265            HoldBit::Unspecified,
266            literal_span,
267            literal_span,
268            InstructionFragment::from((literal_span, Script::Normal, Unsigned36Bit::ZERO)),
269        )
270    };
271
272    let seq = InstructionSequence {
273        local_symbols: Some(ExplicitSymbolTable::default()),
274        instructions: vec![
275            // Two lines which are identical (hence with the same tag)
276            // apart from their spans.
277            instruction_tagged_with("T", 0),
278            instruction_tagged_with("U", 5),
279        ],
280    };
281
282    let mut expected: ExplicitSymbolTable = ExplicitSymbolTable::default();
283    expected
284        .define(
285            SymbolName::from("T"),
286            ExplicitDefinition::Tag(TagDefinition::Unresolved {
287                block_id: BlockIdentifier::from(0),
288                block_offset: u18!(0),
289                span: span(0..1),
290            }),
291        )
292        .expect("symbol definition should be OK since there is no other defintion for that symbol");
293    expected
294        .define(
295            SymbolName::from("U"),
296            ExplicitDefinition::Tag(TagDefinition::Unresolved {
297                block_id: BlockIdentifier::from(0),
298                block_offset: u18!(1),
299                span: span(5..6),
300            }),
301        )
302        .expect("symbol definition should be OK since there is no other defintion for that symbol");
303    assert_eq!(
304        build_local_symbol_table(BlockIdentifier::from(0), seq.iter()),
305        Ok(expected)
306    );
307}
308
309#[test]
310fn test_build_local_symbol_table_detects_tag_conflict() {
311    let instruction_tagged_with_t = |beginpos: usize| {
312        // This is the result of parsing a line "T->0\n" at some
313        // position `beginpos`.
314        let tag_span = span(beginpos..(beginpos + 1));
315        let literal_span = span((beginpos + 3)..(beginpos + 4));
316        TaggedProgramInstruction::single(
317            vec![Tag {
318                name: SymbolName::from("T"),
319                span: tag_span,
320            }],
321            HoldBit::Unspecified,
322            literal_span,
323            literal_span,
324            InstructionFragment::from((literal_span, Script::Normal, Unsigned36Bit::ZERO)),
325        )
326    };
327
328    let seq = InstructionSequence {
329        local_symbols: Some(ExplicitSymbolTable::default()),
330        instructions: vec![
331            // Two lines which are identical (hence with the same tag)
332            // apart from their spans.
333            instruction_tagged_with_t(0),
334            instruction_tagged_with_t(5),
335        ],
336    };
337
338    assert_eq!(
339        build_local_symbol_table(BlockIdentifier::from(0), seq.iter()),
340        Err(OneOrMore::new(LocalSymbolTableBuildFailure::BadDefinition(
341            BadSymbolDefinition {
342                symbol_name: SymbolName::from("T"),
343                span: span(5..6),
344                existing: Box::new(ExplicitDefinition::Tag(TagDefinition::Unresolved {
345                    block_id: BlockIdentifier::from(0),
346                    block_offset: u18!(0),
347                    span: span(0..1),
348                })),
349                proposed: Box::new(ExplicitDefinition::Tag(TagDefinition::Unresolved {
350                    block_id: BlockIdentifier::from(0),
351                    block_offset: u18!(1),
352                    span: span(5..6),
353                }))
354            }
355        )))
356    );
357}
358
359#[derive(Debug, Clone, PartialEq, Eq)]
360pub(crate) struct ManuscriptBlock {
361    pub(crate) origin: Option<Origin>,
362    // Macro invocations generate an InstructionSequence::Scoped(),
363    // and since a single block may contain more than one macro
364    // invocation, a block must comprise a sequence of
365    // InstructionSequence instances.
366    pub(crate) sequences: Vec<InstructionSequence>,
367}
368
369impl ManuscriptBlock {
370    pub(super) fn symbol_uses(
371        &self,
372        block_id: BlockIdentifier,
373    ) -> impl Iterator<Item = Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> + use<>
374    {
375        let mut result: Vec<Result<(SymbolName, Span, SymbolUse), InconsistentSymbolUse>> =
376            Vec::new();
377        if let Some(origin) = self.origin.as_ref() {
378            result.extend(origin.symbol_uses(block_id));
379        }
380        result.extend(
381            self.sequences
382                .iter()
383                .flat_map(|seq| seq.symbol_uses(block_id)),
384        );
385        result.into_iter()
386    }
387
388    fn build_local_symbol_tables(
389        &mut self,
390        block_identifier: BlockIdentifier,
391    ) -> Result<(), OneOrMore<LocalSymbolTableBuildFailure>> {
392        let mut errors: Vec<LocalSymbolTableBuildFailure> = Vec::new();
393        for seq in &mut self.sequences {
394            if let Some(local_symbols) = seq.local_symbols.as_mut() {
395                match build_local_symbol_table(block_identifier, seq.instructions.iter()) {
396                    Ok(more_symbols) => match local_symbols.merge(more_symbols) {
397                        Ok(()) => (),
398                        Err(e) => {
399                            errors.extend(
400                                e.into_iter()
401                                    .map(LocalSymbolTableBuildFailure::BadDefinition),
402                            );
403                        }
404                    },
405                    Err(e) => {
406                        errors.extend(e.into_iter());
407                    }
408                }
409            }
410        }
411        match OneOrMore::try_from_vec(errors) {
412            Ok(errors) => Err(errors),
413            Err(_) => Ok(()),
414        }
415    }
416
417    pub(crate) fn instruction_count(&self) -> Unsigned18Bit {
418        self.sequences
419            .iter()
420            .map(InstructionSequence::emitted_word_count)
421            .sum()
422    }
423
424    pub(crate) fn origin_span(&self) -> Span {
425        if let Some(origin) = self.origin.as_ref() {
426            origin.span()
427        } else {
428            if let Some(s) = self.sequences.first()
429                && let Some(first) = s.first()
430            {
431                return first.span();
432            }
433            Span::from(0..0)
434        }
435    }
436
437    pub(super) fn push_unscoped_instruction(&mut self, inst: TaggedProgramInstruction) {
438        if let Some(InstructionSequence {
439            local_symbols: None,
440            instructions,
441        }) = self.sequences.last_mut()
442        {
443            instructions.push(inst);
444        } else {
445            self.sequences.push(InstructionSequence {
446                local_symbols: None,
447                instructions: vec![inst],
448            });
449        }
450    }
451}
452
453/// Represents the ☛☛PUNCH metacommand.
454#[derive(Debug, Clone, Copy, PartialEq, Eq)]
455pub(crate) struct PunchCommand(pub(crate) Option<Address>);
456
457#[derive(Debug, Clone, PartialEq, Eq)]
458pub(crate) enum ManuscriptMetaCommand {
459    // TODO: implement the T= metacommand.
460    // TODO: implement the RC metacommand.
461    BaseChange(NumeralMode),
462
463    /// A ☛☛PUNCH meta command.
464    Punch(PunchCommand),
465    Macro(MacroDefinition),
466}
467
468#[derive(Debug, Clone, PartialEq, Eq)]
469pub(crate) enum ManuscriptLine {
470    Meta(ManuscriptMetaCommand),
471    Macro(MacroInvocation),
472    Eq(Equality),
473    OriginOnly(Origin),
474    TagsOnly(Vec<Tag>),
475    StatementOnly(TaggedProgramInstruction),
476    OriginAndStatement(Origin, TaggedProgramInstruction),
477}
478
479#[derive(Debug, Clone, PartialEq, Eq)]
480pub(crate) struct MacroParameter {
481    pub(crate) name: SymbolName,
482    pub(crate) span: Span,
483    // TODO: we should probably not be depending here on an
484    // implementation artifact of the lexer (i.e. Token).
485    pub(crate) preceding_terminator: Token,
486}
487
488/// As defined with ☛☛DEF, a macro's name is followed by a terminator,
489/// or by a terminator and some arguments.  We model each argument as
490/// being introduced by its preceding terminator.  If there are no
491/// arguments, `MacroDummyparameters::Zero(token)` represents that uses
492/// of the macro's name are followed by the indicated token (which
493/// terminates the macro name, not a dummy parameter).
494#[derive(Debug, Clone, PartialEq, Eq)]
495pub(crate) enum MacroDummyParameters {
496    Zero(Token),
497    OneOrMore(Vec<MacroParameter>),
498}
499#[derive(Debug, Clone, PartialEq, Eq)]
500pub(crate) struct MacroDefinition {
501    pub(crate) name: SymbolName, // composite character macros are not yet supported
502    pub(crate) params: MacroDummyParameters,
503    pub(crate) body: Vec<MacroBodyLine>,
504    pub(crate) span: Span,
505}
506
507#[derive(Debug, Clone, PartialEq, Eq)]
508pub(crate) enum MacroBodyLine {
509    Expansion(MacroInvocation),
510    Instruction(TaggedProgramInstruction),
511    Equality(Equality),
512}
513
514impl MacroDefinition {
515    fn substitute_macro_parameters(
516        &self,
517        bindings: &MacroParameterBindings,
518        macros: &BTreeMap<SymbolName, MacroDefinition>,
519    ) -> InstructionSequence {
520        let mut local_symbols = ExplicitSymbolTable::default();
521        let mut instructions: Vec<TaggedProgramInstruction> = Vec::with_capacity(self.body.len());
522        for body_line in &self.body {
523            match body_line {
524                MacroBodyLine::Expansion(_macro_invocation) => {
525                    unimplemented!("recursive macros are not yet supported")
526                }
527                MacroBodyLine::Instruction(tagged_program_instruction) => {
528                    if let Some(tagged_program_instruction) = tagged_program_instruction
529                        .substitute_macro_parameters(
530                            bindings,
531                            OnUnboundMacroParameter::ElideReference,
532                            macros,
533                        )
534                    {
535                        instructions.push(tagged_program_instruction);
536                    } else {
537                        // The instruction referred to an unbound
538                        // macro parameter, and therefore the
539                        // instruction is omitted.
540                        //
541                        // This is required by the text of the first
542                        // paragraph of section 6-4 "MACRO
543                        // INSTRUCTIONS" of the TX-2 User's Handbook.
544                        // Also item (4) in section 6-4.6 ("The
545                        // Defining Subprogram").
546                    }
547                }
548                // Equalities and tags which occur inside the body of
549                // a macro are not visible outside it.
550                MacroBodyLine::Equality(Equality {
551                    span: _,
552                    name,
553                    value,
554                }) => {
555                    let value: EqualityValue = value.substitute_macro_parameters(bindings, macros);
556                    if let Err(e) =
557                        local_symbols.define(name.clone(), ExplicitDefinition::Equality(value))
558                    {
559                        // We do not expect this to fail because only
560                        // tag definitions can be invalid, equalities
561                        // cannot (as long as the right-hand-side can
562                        // be parsed, which has already happened).
563                        panic!(
564                            "unexpected failure when defining equality for {name} inside a macro body: {e}"
565                        );
566                    }
567                }
568            }
569        }
570        InstructionSequence {
571            // build_local_symbol_tables extracts tags and propagates
572            // them into the local symbol table, so this is not the
573            // final version of the local symbol table.
574            local_symbols: Some(local_symbols),
575            instructions,
576        }
577    }
578}
579
580#[derive(Debug, Clone, PartialEq, Eq)]
581pub(crate) enum MacroParameterValue {
582    Value(Script, ArithmeticExpression),
583    // TODO: bindings representing sequences of instructions (see for
584    // example the SQ/NSQ example in the Users Handbook).
585}
586
587#[derive(Debug, Clone, PartialEq, Eq, Default)]
588pub(crate) struct MacroParameterBindings {
589    // TODO: all bindings should have a span, even if the parameter is
590    // unset (in which case the span should correspond to the location
591    // where the parameter would have been supplied.
592    inner: BTreeMap<SymbolName, (Span, Option<MacroParameterValue>)>,
593}
594
595impl MacroParameterBindings {
596    pub(crate) fn insert(
597        &mut self,
598        name: SymbolName,
599        span: Span,
600        value: Option<MacroParameterValue>,
601    ) {
602        self.inner.insert(name, (span, value));
603    }
604
605    pub(super) fn get(&self, name: &SymbolName) -> Option<&(Span, Option<MacroParameterValue>)> {
606        self.inner.get(name)
607    }
608}
609
610#[derive(Debug, Clone, PartialEq, Eq)]
611pub(crate) struct MacroInvocation {
612    pub(crate) macro_def: MacroDefinition,
613    pub(crate) param_values: MacroParameterBindings,
614}
615
616impl MacroInvocation {
617    pub(super) fn substitute_macro_parameters(
618        &self,
619        macros: &BTreeMap<SymbolName, MacroDefinition>,
620    ) -> InstructionSequence {
621        self.macro_def
622            .substitute_macro_parameters(&self.param_values, macros)
623    }
624}
625
626fn expand_macro(
627    invocation: &MacroInvocation,
628    macros: &BTreeMap<SymbolName, MacroDefinition>,
629) -> InstructionSequence {
630    invocation.substitute_macro_parameters(macros)
631}
632
633pub(crate) fn manuscript_lines_to_source_file<'a>(
634    lines: Vec<(Span, ManuscriptLine)>,
635) -> Result<SourceFile, chumsky::error::Rich<'a, super::lexer::Token>> {
636    fn get_or_create_output_block(result: &mut Vec<ManuscriptBlock>) -> &mut ManuscriptBlock {
637        if result.is_empty() {
638            result.push(ManuscriptBlock {
639                origin: None,
640                sequences: Vec::new(),
641            });
642        }
643        result
644            .last_mut()
645            .expect("result cannot be empty because we just pushed an item onto it")
646    }
647
648    fn begin_new_block(origin: Origin, result: &mut Vec<ManuscriptBlock>) -> &mut ManuscriptBlock {
649        result.push(ManuscriptBlock {
650            origin: Some(origin),
651            sequences: Vec::new(),
652        });
653        result
654            .last_mut()
655            .expect("result cannot be empty because we just pushed an item onto it")
656    }
657
658    fn prepend_tags(v: &mut Vec<Tag>, initial: &mut Vec<Tag>) {
659        let alltags: Vec<Tag> = initial.drain(0..).chain(v.drain(0..)).collect();
660        v.extend(alltags);
661    }
662
663    let mut blocks: Vec<ManuscriptBlock> = Vec::new();
664    let mut equalities: Vec<Equality> = Vec::new();
665    let mut macros: BTreeMap<SymbolName, MacroDefinition> = Default::default();
666    let mut maybe_punch: Option<PunchCommand> = None;
667
668    let mut pending_tags: Vec<Tag> = Vec::new();
669
670    let bad_tag_pos = |tag: Tag| {
671        let tag_name = &tag.name;
672        chumsky::error::Rich::custom(
673            tag.span,
674            format!("tag {tag_name} must be followed by an instruction"),
675        )
676    };
677
678    for (_span, line) in lines {
679        match line {
680            ManuscriptLine::Macro(invocation) => {
681                get_or_create_output_block(&mut blocks)
682                    .sequences
683                    .push(expand_macro(&invocation, &macros));
684            }
685            ManuscriptLine::TagsOnly(tags) => {
686                pending_tags.extend(tags);
687            }
688
689            ManuscriptLine::Meta(_)
690            | ManuscriptLine::OriginOnly(_)
691            | ManuscriptLine::OriginAndStatement(_, _)
692            | ManuscriptLine::Eq(_)
693                if !pending_tags.is_empty() =>
694            {
695                return Err(bad_tag_pos(
696                    pending_tags
697                        .pop()
698                        .expect("we know pending_tags is non-empty"),
699                ));
700            }
701
702            ManuscriptLine::Meta(ManuscriptMetaCommand::Punch(punch)) => {
703                maybe_punch = Some(punch);
704            }
705            ManuscriptLine::Meta(ManuscriptMetaCommand::BaseChange(_)) => {
706                // These already took effect on the statements which
707                // were parsed following them, so no need to keep them
708                // now.
709            }
710            ManuscriptLine::Meta(ManuscriptMetaCommand::Macro(macro_def)) => {
711                if let Some(previous) = macros.insert(macro_def.name.clone(), macro_def) {
712                    event!(Level::INFO, "Redefinition of macro {}", &previous.name);
713                }
714            }
715
716            ManuscriptLine::OriginOnly(origin) => {
717                begin_new_block(origin, &mut blocks);
718            }
719            ManuscriptLine::StatementOnly(mut tagged_program_instruction) => {
720                prepend_tags(&mut tagged_program_instruction.tags, &mut pending_tags);
721                get_or_create_output_block(&mut blocks)
722                    .push_unscoped_instruction(tagged_program_instruction);
723            }
724            ManuscriptLine::OriginAndStatement(origin, statement) => {
725                begin_new_block(origin, &mut blocks).push_unscoped_instruction(statement);
726            }
727            ManuscriptLine::Eq(equality) => {
728                equalities.push(equality);
729            }
730        }
731    }
732    if let Some(t) = pending_tags.pop() {
733        return Err(bad_tag_pos(t));
734    }
735
736    Ok(SourceFile {
737        punch: maybe_punch,
738        blocks,
739        global_equalities: equalities,
740        macros,
741    })
742}