11use clap:: Parser ;
2- use std:: {
3- collections:: HashMap , fs, path:: PathBuf
4- } ;
2+ use std:: { collections:: HashMap , fmt, fs, io, path:: PathBuf } ;
53
64use rusty_man_computer:: value:: Value ;
75
@@ -38,11 +36,36 @@ enum Line {
3836}
3937
4038#[ derive( Debug ) ]
41- enum ParseError {
39+ enum ParseErrorType {
4240 InvalidOpcode ( String ) ,
4341 OperandOutOfRange ( i16 ) ,
4442}
4543
44+ impl fmt:: Display for ParseErrorType {
45+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
46+ match self {
47+ ParseErrorType :: InvalidOpcode ( opcode) => {
48+ write ! ( f, "Invalid opcode: {}" , opcode)
49+ }
50+ ParseErrorType :: OperandOutOfRange ( value) => {
51+ write ! ( f, "Operand out of range: {}" , value)
52+ }
53+ }
54+ }
55+ }
56+
57+ #[ derive( Debug ) ]
58+ struct ParseError {
59+ error : ParseErrorType ,
60+ line : usize ,
61+ }
62+
63+ impl fmt:: Display for ParseError {
64+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
65+ write ! ( f, "Parse error on line {}: {}" , self . line, self . error)
66+ }
67+ }
68+
4669fn parse_opcode ( string : & str ) -> Option < Opcode > {
4770 match string {
4871 "HLT" => Some ( Opcode :: HLT ) ,
@@ -64,8 +87,10 @@ fn parse_opcode(string: &str) -> Option<Opcode> {
6487fn parse_assembly ( program : & str ) -> Vec < Result < Line , ParseError > > {
6588 program
6689 . lines ( )
67- . map ( |line| {
90+ . enumerate ( )
91+ . map ( |( line_index, line) | {
6892 let line = line. trim ( ) ;
93+ let line_number = line_index + 1 ;
6994 if line. is_empty ( ) || line. starts_with ( "//" ) {
7095 return Ok ( Line :: Empty ) ;
7196 }
@@ -84,15 +109,14 @@ fn parse_assembly(program: &str) -> Vec<Result<Line, ParseError>> {
84109 let opcode = match first_part_as_opcode {
85110 Some ( opcode) => opcode,
86111 None => {
87- let string = match parts. get ( 1 ) {
88- Some ( string) => string,
89- // This means there's only one part: there's nothing to label, so it's just an invalid opcode
90- None => return Err ( ParseError :: InvalidOpcode ( parts[ 0 ] . to_string ( ) ) ) ,
91- } ;
92- match parse_opcode ( string) {
93- Some ( opcode) => opcode,
94- None => return Err ( ParseError :: InvalidOpcode ( string. to_string ( ) ) ) ,
95- }
112+ let string = parts. get ( 1 ) . ok_or ( ParseError {
113+ error : ParseErrorType :: InvalidOpcode ( parts[ 0 ] . to_string ( ) ) ,
114+ line : line_number,
115+ } ) ?;
116+ parse_opcode ( string) . ok_or ( ParseError {
117+ error : ParseErrorType :: InvalidOpcode ( string. to_string ( ) ) ,
118+ line : line_number,
119+ } ) ?
96120 }
97121 } ;
98122 let operand_part = if label. is_some ( ) {
@@ -106,7 +130,10 @@ fn parse_assembly(program: &str) -> Vec<Result<Line, ParseError>> {
106130 Some ( string) => match string. parse :: < i16 > ( ) {
107131 Ok ( value) => Some ( Operand :: Value (
108132 // If the number doesn't fit within a Value, return an OperandOutOfRange error
109- Value :: new ( value) . map_err ( |_| ParseError :: OperandOutOfRange ( value) ) ?,
133+ Value :: new ( value) . map_err ( |_| ParseError {
134+ error : ParseErrorType :: OperandOutOfRange ( value) ,
135+ line : line_number,
136+ } ) ?,
110137 ) ) ,
111138 Err ( _) => Some ( Operand :: Label ( string. to_string ( ) ) ) ,
112139 } ,
@@ -179,10 +206,22 @@ fn generate_machine_code(lines: Vec<Line>) -> Result<Vec<Value>, &'static str> {
179206 Ok ( output)
180207}
181208
182- #[ derive( Debug ) ]
183209enum AssemblerError {
184210 ParseError ( ParseError ) ,
185211 MachineCodeError ( & ' static str ) ,
212+ ReadError ( io:: Error ) ,
213+ WriteError ( io:: Error ) ,
214+ }
215+
216+ impl fmt:: Debug for AssemblerError {
217+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
218+ match self {
219+ AssemblerError :: ParseError ( e) => write ! ( f, "{}" , e) ,
220+ AssemblerError :: MachineCodeError ( e) => write ! ( f, "Machine code error: {}" , e) ,
221+ AssemblerError :: WriteError ( e) => write ! ( f, "Failed to write to output file: {}" , e) ,
222+ AssemblerError :: ReadError ( e) => write ! ( f, "Failed to read input file: {}" , e) ,
223+ }
224+ }
186225}
187226
188227fn assemble ( program : & str ) -> Result < Vec < Value > , AssemblerError > {
@@ -215,26 +254,17 @@ pub struct Args {
215254 output : PathBuf ,
216255}
217256
218- fn main ( ) -> Result < ( ) , String > {
257+ fn main ( ) -> Result < ( ) , AssemblerError > {
219258 let args = Args :: parse ( ) ;
220- let program = std:: fs:: read_to_string ( args. program ) . expect ( "Failed to read program" ) ;
259+ let program =
260+ std:: fs:: read_to_string ( args. program ) . map_err ( |e| AssemblerError :: ReadError ( e) ) ?;
221261 let assembler_result = assemble ( & program) ;
222262 match assembler_result {
223- Err ( error) => match error {
224- AssemblerError :: ParseError ( parse_error) => {
225- return Err ( format ! ( "Parse error: {:?}" , parse_error) ) ;
226- }
227- AssemblerError :: MachineCodeError ( message) => {
228- return Err ( message. to_string ( ) ) ;
229- }
230- } ,
263+ Err ( error) => Err ( error) ,
231264 Ok ( machine_code) => {
232- let machine_code_bytes: Vec < u8 > = machine_code
233- . iter ( )
234- . flat_map ( |& i| i. to_be_bytes ( ) )
235- . collect ( ) ;
236- fs:: write ( args. output , machine_code_bytes)
237- . map_err ( |e| format ! ( "Failed to write output file: {}" , e) )
265+ let machine_code_bytes: Vec < u8 > =
266+ machine_code. iter ( ) . flat_map ( |& i| i. to_be_bytes ( ) ) . collect ( ) ;
267+ fs:: write ( args. output , machine_code_bytes) . map_err ( |e| AssemblerError :: WriteError ( e) )
238268 }
239269 }
240270}
0 commit comments