@@ -27,7 +27,8 @@ use super::state::{NumeralMode, State};
2727use super :: symbol:: SymbolName ;
2828use super :: symtab:: {
2929 assign_default_rc_word_tags, BadSymbolDefinition , BlockPosition , FinalSymbolDefinition ,
30- FinalSymbolTable , FinalSymbolType , IndexRegisterAssigner , MemoryMap , SymbolTable ,
30+ FinalSymbolTable , FinalSymbolType , IndexRegisterAssigner , MemoryMap , SymbolDefinition ,
31+ SymbolTable ,
3132} ;
3233use super :: types:: * ;
3334use base:: prelude:: { Address , IndexBy , Unsigned18Bit , Unsigned36Bit } ;
@@ -67,7 +68,7 @@ impl SourceFile {
6768 let SourceFile {
6869 punch,
6970 blocks : input_blocks,
70- equalities,
71+ global_equalities : equalities,
7172 macros : _,
7273 } = self ;
7374 let output_blocks: BTreeMap < BlockIdentifier , LocatedBlock > = input_blocks
@@ -87,7 +88,7 @@ impl SourceFile {
8788 LocatedBlock {
8889 origin : mblock. origin ,
8990 location,
90- statements : mblock. statements ,
91+ statements : mblock. sequences ,
9192 } ,
9293 )
9394 } )
@@ -147,48 +148,82 @@ fn assemble_pass1<'a>(
147148 state. numeral_mode . set_numeral_mode ( NumeralMode :: Octal ) ;
148149 }
149150
150- let ( sf, mut new_errors) = parse_source_file ( source_file_body, setup) ;
151+ let ( mut sf, mut new_errors) = parse_source_file ( source_file_body, setup) ;
151152 errors. append ( & mut new_errors) ;
153+
154+ if let Some ( source_file) = sf. as_mut ( ) {
155+ if let Err ( tag_errors) = source_file. build_local_symbol_tables ( ) {
156+ errors. extend (
157+ tag_errors
158+ . into_iter ( )
159+ . map ( |tag_err| Rich :: custom ( tag_err. span ( ) , tag_err. to_string ( ) ) ) ,
160+ ) ;
161+ }
162+ }
152163 Ok ( ( sf, options) )
153164}
154165
166+ #[ derive( Debug , PartialEq , Eq ) ]
167+ enum AssemblerPass1Or2Output < ' a > {
168+ Pass1Failed ( Result < Vec < Rich < ' a , lexer:: Token > > , AssemblerFailure > ) ,
169+ Pass2Failed ( AssemblerFailure ) ,
170+ Success ( Vec < Rich < ' a , lexer:: Token > > , OutputOptions , Pass2Output < ' a > ) ,
171+ }
172+
173+ fn assemble_nonempty_input ( input : & str ) -> AssemblerPass1Or2Output {
174+ let mut errors: Vec < Rich < ' _ , lexer:: Token > > = Vec :: new ( ) ;
175+ match assemble_pass1 ( input, & mut errors) {
176+ Err ( e) => AssemblerPass1Or2Output :: Pass1Failed ( Err ( e) ) ,
177+ Ok ( ( None , _output_options) ) => AssemblerPass1Or2Output :: Pass1Failed ( Ok ( errors) ) ,
178+ Ok ( ( Some ( source_file) , output_options) ) => match assemble_pass2 ( source_file, input) {
179+ Err ( e) => AssemblerPass1Or2Output :: Pass2Failed ( e) ,
180+ Ok ( p2output) => AssemblerPass1Or2Output :: Success ( errors, output_options, p2output) ,
181+ } ,
182+ }
183+ }
184+
155185/// This test helper is defined here so that we don't have to expose
156186/// assemble_pass1, assemble_pass2.
157187#[ cfg( test) ]
158188pub ( crate ) fn assemble_nonempty_valid_input (
159189 input : & str ,
160190) -> ( Directive , SymbolTable , MemoryMap , IndexRegisterAssigner ) {
161- let mut errors: Vec < Rich < ' _ , lexer:: Token > > = Vec :: new ( ) ;
162- let result: Result < ( Option < SourceFile > , OutputOptions ) , AssemblerFailure > =
163- assemble_pass1 ( input, & mut errors) ;
164- if !errors. is_empty ( ) {
165- panic ! (
166- "assemble_nonempty_valid_input: for input\n {input}\n errors were reported: {errors:?}"
167- ) ;
168- }
169- match result {
170- Ok ( ( None , _) ) => unreachable ! ( "parser should generate output if there are no errors" ) ,
171- Ok ( ( Some ( source_file) , _options) ) => {
172- let p2output = assemble_pass2 ( source_file, input)
173- . expect ( "test program should not extend beyong physical memory" ) ;
174- if !p2output. errors . is_empty ( ) {
175- panic ! ( "input should be valid: {:?}" , & p2output. errors) ;
176- }
177- match p2output. directive {
178- Some ( directive) => (
179- directive,
180- p2output. symbols ,
181- p2output. memory_map ,
182- p2output. index_register_assigner ,
183- ) ,
184- None => {
185- panic ! ( "assembly pass 2 generated no errors but also no output" ) ;
191+ match assemble_nonempty_input ( input) {
192+ AssemblerPass1Or2Output :: Pass1Failed ( Err ( e) ) => {
193+ panic ! ( "pass 1 failed with an error result: {e}" ) ;
194+ }
195+ AssemblerPass1Or2Output :: Pass1Failed ( Ok ( errors) ) => {
196+ panic ! ( "pass 1 failed with diagnostics: {errors:?}" ) ;
197+ }
198+ AssemblerPass1Or2Output :: Pass2Failed ( e) => {
199+ panic ! ( "pass 1 failed with an error result: {e}" ) ;
200+ }
201+ AssemblerPass1Or2Output :: Success ( errors, _output_options, p2output) => {
202+ if errors. is_empty ( ) {
203+ match p2output {
204+ Pass2Output {
205+ directive : None , ..
206+ } => {
207+ panic ! ( "directive is None but no errors were reported" ) ;
208+ }
209+ Pass2Output {
210+ directive : Some ( directive) ,
211+ symbols,
212+ memory_map,
213+ index_register_assigner,
214+ errors,
215+ } => {
216+ if !errors. is_empty ( ) {
217+ panic ! ( "input should be valid: {:?}" , & errors) ;
218+ } else {
219+ ( directive, symbols, memory_map, index_register_assigner)
220+ }
221+ }
186222 }
223+ } else {
224+ panic ! ( "pass 2 failed with diagnostics: {errors:?}" ) ;
187225 }
188226 }
189- Err ( e) => {
190- panic ! ( "input should be valid: {}" , e) ;
191- }
192227 }
193228}
194229
@@ -257,11 +292,12 @@ impl Binary {
257292 }
258293}
259294
295+ #[ derive( Debug , PartialEq , Eq ) ]
260296struct Pass2Output < ' a > {
261297 directive : Option < Directive > ,
262298 symbols : SymbolTable ,
263299 memory_map : MemoryMap ,
264- index_register_assigner : IndexRegisterAssigner ,
300+ index_register_assigner : IndexRegisterAssigner , // not cloneable
265301 errors : Vec < Rich < ' a , lexer:: Token > > ,
266302}
267303
@@ -270,11 +306,15 @@ fn initial_symbol_table<'a>(
270306) -> Result < SymbolTable , Vec < Rich < ' a , lexer:: Token > > > {
271307 let mut errors = Vec :: new ( ) ;
272308 let mut symtab = SymbolTable :: new ( ) ;
273- for ( symbol, span, context) in source_file. global_symbol_references ( ) {
274- symtab. record_usage_context ( symbol. clone ( ) , span, context)
275- }
309+ // All explicit definitions in the program take effect either
310+ // locally (for the bodies of macro expansions) or globally (for
311+ // everything else). So, before we can enumerate all global
312+ // symbol references, we need to identidy which symbol references
313+ // are references to something defined in a local scope. And so
314+ // we need to enumerate definitions before references.
276315 for ( symbol, span, definition) in source_file. global_symbol_definitions ( ) {
277- match symtab. define ( span, symbol. clone ( ) , definition. clone ( ) ) {
316+ let definition = SymbolDefinition :: Explicit ( definition. clone ( ) ) ;
317+ match symtab. define ( span, symbol. clone ( ) , definition) {
278318 Ok ( _) => ( ) ,
279319 Err ( e) => {
280320 errors. push ( Rich :: custom (
@@ -284,6 +324,9 @@ fn initial_symbol_table<'a>(
284324 }
285325 }
286326 }
327+ for ( symbol, span, context) in source_file. global_symbol_references ( ) {
328+ symtab. record_usage_context ( symbol. clone ( ) , span, context)
329+ }
287330 if errors. is_empty ( ) {
288331 Ok ( symtab)
289332 } else {
@@ -625,50 +668,51 @@ pub(crate) fn assemble_source(
625668 source_file_body : & str ,
626669 mut options : OutputOptions ,
627670) -> Result < Binary , AssemblerFailure > {
628- let mut errors: Vec < Rich < ' _ , lexer:: Token > > = Vec :: new ( ) ;
629- let ( source_file, source_options) = assemble_pass1 ( source_file_body, & mut errors) ?;
630- if !errors. is_empty ( ) {
631- return Err ( AssemblerFailure :: BadProgram ( fail_with_diagnostics (
632- source_file_body,
633- errors,
634- ) ) ) ;
635- }
636- options = options. merge ( source_options) ;
637-
638- let source_file =
639- source_file. expect ( "assembly pass1 generated no errors, an AST should have been returned" ) ;
640-
641- // Now we do pass 2.
642- let Pass2Output {
643- directive,
644- mut symbols,
645- mut memory_map,
646- mut index_register_assigner,
647- errors,
648- } = assemble_pass2 ( source_file, source_file_body) ?;
649- if !errors. is_empty ( ) {
650- return Err ( AssemblerFailure :: BadProgram ( fail_with_diagnostics (
651- source_file_body,
671+ let mut p2output = match assemble_nonempty_input ( source_file_body) {
672+ AssemblerPass1Or2Output :: Pass1Failed ( Err ( e) ) => {
673+ return Err ( e) ;
674+ }
675+ AssemblerPass1Or2Output :: Pass1Failed ( Ok ( errors) ) => {
676+ return Err ( AssemblerFailure :: BadProgram ( fail_with_diagnostics (
677+ source_file_body,
678+ errors,
679+ ) ) ) ;
680+ }
681+ AssemblerPass1Or2Output :: Pass2Failed ( e) => {
682+ return Err ( e) ;
683+ }
684+ AssemblerPass1Or2Output :: Success (
652685 errors,
653- ) ) ) ;
654- }
655- let directive = match directive {
656- None => {
657- return Err ( AssemblerFailure :: InternalError (
658- "assembly pass 2 generated no errors, so it should have generated ouptut code (even if empty)" . to_string ( )
659- ) ) ;
686+ _output_options,
687+ Pass2Output {
688+ directive : None , ..
689+ } ,
690+ ) if errors. is_empty ( ) => {
691+ panic ! ( "assembly pass1 generated no errors, a directive should have been returned" ) ;
692+ }
693+ AssemblerPass1Or2Output :: Success ( errors, output_options, p2output) => {
694+ if !errors. is_empty ( ) {
695+ return Err ( AssemblerFailure :: BadProgram ( fail_with_diagnostics (
696+ source_file_body,
697+ errors,
698+ ) ) ) ;
699+ } else {
700+ options = options. merge ( output_options) ;
701+ p2output
702+ }
660703 }
661- Some ( d) => d,
662704 } ;
663705
664706 // Now we do pass 3, which generates the binary output
665707 let binary = {
666708 let mut listing = Listing :: default ( ) ;
667709 let ( binary, final_symbols) = assemble_pass3 (
668- directive,
669- & mut symbols,
670- & mut memory_map,
671- & mut index_register_assigner,
710+ p2output
711+ . directive
712+ . expect ( "directive should have already been checked for None-ness" ) ,
713+ & mut p2output. symbols ,
714+ & mut p2output. memory_map ,
715+ & mut p2output. index_register_assigner ,
672716 source_file_body,
673717 & mut listing,
674718 ) ?;
@@ -705,7 +749,7 @@ fn test_assemble_pass1() {
705749 let expected_directive_entry_point = Some ( Address :: new ( Unsigned18Bit :: from ( 0o26_u8 ) ) ) ;
706750 let expected_block = ManuscriptBlock {
707751 origin : None ,
708- statements : vec ! [ TaggedProgramInstruction {
752+ sequences : vec ! [ TaggedProgramInstruction {
709753 span: span( 0 ..2 ) ,
710754 tags: Vec :: new( ) ,
711755 instruction: UntaggedProgramInstruction :: from( OneOrMore :: new( CommaDelimitedFragment {
@@ -730,7 +774,7 @@ fn test_assemble_pass1() {
730774 Some ( SourceFile {
731775 punch: Some ( PunchCommand ( expected_directive_entry_point) ) ,
732776 blocks: vec![ expected_block] ,
733- equalities : Default :: default ( ) , // no equalities
777+ global_equalities : Default :: default ( ) , // no equalities
734778 macros: Default :: default ( ) ,
735779 } ) ,
736780 OutputOptions { list: false }
@@ -788,3 +832,33 @@ pub fn assemble_file(
788832 let mut writer = BufWriter :: new ( output_file) ;
789833 write_user_program ( & user_program, & mut writer, output_file_name)
790834}
835+
836+ #[ test]
837+ fn test_duplicate_global_tag ( ) {
838+ let input = concat ! ( "TGX->0\n " , "TGX->0\n " ) ;
839+ match assemble_nonempty_input ( input) {
840+ AssemblerPass1Or2Output :: Pass2Failed ( AssemblerFailure :: BadProgram ( errors) ) => {
841+ dbg ! ( & errors) ;
842+ match errors. as_slice ( ) {
843+ [ WithLocation {
844+ inner : ProgramError :: SyntaxError { msg, span : _ } ,
845+ ..
846+ } ] => {
847+ assert ! ( msg
848+ . contains( "bad symbol definition for TGX: TGX is defined more than once" ) ) ;
849+ }
850+ other => {
851+ panic ! ( "expected a syntax error report, got {other:?}" ) ;
852+ }
853+ }
854+ }
855+ AssemblerPass1Or2Output :: Success ( errors, _output_options, p2output) => {
856+ dbg ! ( & errors) ;
857+ dbg ! ( & p2output) ;
858+ panic ! ( "assembler unexpectedly succeeded with a bad input {input}" ) ;
859+ }
860+ unexpected => {
861+ panic ! ( "assembly failed, but not in the way expected by this test: {unexpected:?}" ) ;
862+ }
863+ }
864+ }
0 commit comments