1- use ksi:: { backend, common:: diagnostics:: sinks:: Diagnostics , mir:: { self , passes:: { ConstPropagation , CopyPropagation , DeadLocalElim , Pass , run_passes} , pretty} , semantics, syntax} ;
1+ use ksi:: { backend, common:: diagnostics:: { Diagnostic , Severity , sinks:: Diagnostics } , mir:: { self , passes:: { ConstPropagation , CopyPropagation , DeadLocalElim , Pass , run_passes} , pretty} , semantics, syntax} ;
22
33
44fn main ( ) -> Result < ( ) , ( ) > {
55 let file_path = std:: env:: args ( ) . nth ( 1 ) . expect ( "Missing file path" ) ;
66
77 let content = std:: fs:: read_to_string ( file_path) . unwrap ( ) ;
8+ let line_starts = line_indices ( & content) ;
89
910 let mut diagnostics = Diagnostics :: empty ( ) ;
1011 let parsed_ast = syntax:: parse ( & content, & mut diagnostics) ;
1112 let ( typed_ast, symbols) = semantics:: analyze ( parsed_ast, & mut diagnostics) ;
1213 if diagnostics. has_error ( ) {
13- eprintln ! ( "{:#?}" , diagnostics. diagnostics) ;
14+ render_all ( & content , & line_starts , & diagnostics. diagnostics ) ;
1415 return Err ( ( ) ) ;
1516 }
1617
@@ -26,9 +27,7 @@ fn main() -> Result<(), ()> {
2627
2728 let out = backend:: emit ( & prog_ir, & mut diagnostics) ;
2829
29- if diagnostics. has_error ( ) {
30- eprintln ! ( "{:#?}" , diagnostics. diagnostics) ;
31- }
30+ render_all ( & content, & line_starts, & diagnostics. diagnostics ) ;
3231
3332 let pretty_ir = pretty:: format_body ( & prog_ir, "main" ) ;
3433 for line in pretty_ir. lines ( ) {
@@ -39,3 +38,88 @@ fn main() -> Result<(), ()> {
3938 Ok ( ( ) )
4039}
4140
41+ fn line_indices ( src : & str ) -> Vec < usize > {
42+ let mut line_starts = vec ! [ 0 ] ;
43+ for ( i, b) in src. bytes ( ) . enumerate ( ) {
44+ if b == b'\n' {
45+ line_starts. push ( i + 1 ) ;
46+ }
47+ }
48+ line_starts
49+ }
50+
51+ fn line_col ( src : & str , line_starts : & [ usize ] , pos : usize ) -> ( usize , usize ) {
52+ let line = match line_starts. binary_search ( & pos) {
53+ Ok ( i) => i,
54+ Err ( i) => i - 1 ,
55+ } ;
56+
57+ let col = src[ line_starts[ line] ..pos] . chars ( ) . count ( ) ;
58+ ( line + 1 , col + 1 )
59+ }
60+
61+ fn get_line < ' a > ( src : & ' a str , line_idx : usize , line_starts : & [ usize ] ) -> & ' a str {
62+ let start = line_starts[ line_idx] ;
63+ let end = if line_idx + 1 < line_starts. len ( ) {
64+ line_starts[ line_idx + 1 ] - 1
65+ } else {
66+ src. len ( )
67+ } ;
68+ & src[ start..end]
69+ }
70+
71+ fn print_marker ( line : & str , col : usize , marker : char , message : Option < & str > ) {
72+ let mut pos = 0 ;
73+ for c in line. chars ( ) . take ( col - 1 ) {
74+ if c == '\t' { pos += 4 }
75+ else { pos += 1 }
76+ }
77+ let padding = " " . repeat ( pos) ;
78+ if let Some ( msg) = message {
79+ print ! ( "{padding}{} {}\n " , marker, msg) ;
80+ } else {
81+ print ! ( "{padding}{}\n " , marker)
82+ }
83+ }
84+
85+ fn render_diagnostic ( diag : & Diagnostic , line_starts : & [ usize ] , src : & str ) {
86+ let severity = match diag. severity {
87+ Severity :: Note => "note" ,
88+ Severity :: Warning => "warning" ,
89+ Severity :: Error => "error"
90+ } ;
91+ print ! ( "{}: {}\n " , severity, diag. message) ;
92+
93+ if let Some ( span) = diag. span {
94+ let loc = line_col ( src, line_starts, span. start ) ;
95+ print ! ( " --> input:{}:{}\n " , loc. 0 , loc. 1 ) ;
96+
97+ let source_line = get_line ( src, loc. 0 - 1 , line_starts) ;
98+ print ! ( " |\n {:>2} | {}\n " , loc. 0 , source_line) ;
99+ }
100+
101+ for label in & diag. labels {
102+ let loc = line_col ( src, line_starts, label. span . start ) ;
103+ let source_line = get_line ( src, loc. 0 - 1 , line_starts) ;
104+ let marker = if label. is_primary { '^' } else { '-' } ;
105+ print ! ( " " ) ;
106+ print_marker ( source_line, loc. 1 , marker, label. message . as_deref ( ) ) ;
107+ }
108+
109+ for note in & diag. notes {
110+ print ! ( " = {}\n " , note) ;
111+ }
112+
113+ println ! ( ) ;
114+ }
115+
116+ fn render_all ( src : & str , line_starts : & [ usize ] , diags : & [ Diagnostic ] ) {
117+ let mut error_count = 0 ;
118+ for diag in diags {
119+ if diag. severity == Severity :: Error { error_count += 1 }
120+ render_diagnostic ( diag, line_starts, src) ;
121+ }
122+ if error_count > 0 {
123+ println ! ( "error: compilation failed due to {} previous errors" , error_count)
124+ }
125+ }
0 commit comments