compiler: print the offending source line on error

pull/1918/head
Delyan Angelov 2019-09-10 13:08:43 +03:00 committed by Alexander Medvednikov
parent 7fc678c961
commit 0ade45db08
2 changed files with 55 additions and 11 deletions

View File

@ -109,6 +109,10 @@ fn (v mut V) new_parser(path string) Parser {
} }
if p.pref.is_repl {
p.scanner.should_print_line_on_error = false
}
v.cgen.line_directives = v.pref.is_debuggable v.cgen.line_directives = v.pref.is_debuggable
v.cgen.file = path v.cgen.file = path

View File

@ -25,6 +25,7 @@ mut:
fmt_line_empty bool fmt_line_empty bool
prev_tok Token prev_tok Token
fn_name string // needed for @FN fn_name string // needed for @FN
should_print_line_on_error bool
} }
fn new_scanner(file_path string) &Scanner { fn new_scanner(file_path string) &Scanner {
@ -54,6 +55,7 @@ fn new_scanner(file_path string) &Scanner {
file_path: file_path file_path: file_path
text: text text: text
fmt_out: strings.new_builder(1000) fmt_out: strings.new_builder(1000)
should_print_line_on_error: true
} }
return scanner return scanner
@ -580,16 +582,36 @@ fn (s mut Scanner) scan() ScanRes {
} }
fn (s &Scanner) find_current_line_start_position() int { fn (s &Scanner) find_current_line_start_position() int {
if s.pos >= s.text.len { if s.pos >= s.text.len { return s.pos }
return s.pos mut linestart := s.pos
for {
if linestart <= 0 {
linestart = 1
break
}
if s.text[linestart] == 10 || s.text[linestart] == 13 {
linestart++
break
}
linestart--
} }
mut linestart := s.pos return linestart
for { }
if linestart <= 0 {break}
if s.text[linestart] == 10 || s.text[linestart] == 13 { break } fn (s &Scanner) find_current_line_end_position() int {
linestart-- if s.pos >= s.text.len { return s.pos }
} mut lineend := s.pos
return linestart for {
if lineend >= s.text.len {
lineend = s.text.len
break
}
if s.text[lineend] == 10 || s.text[lineend] == 13 {
break
}
lineend++
}
return lineend
} }
fn (s &Scanner) current_column() int { fn (s &Scanner) current_column() int {
@ -597,15 +619,33 @@ fn (s &Scanner) current_column() int {
} }
fn (s &Scanner) error(msg string) { fn (s &Scanner) error(msg string) {
linestart := s.find_current_line_start_position()
lineend := s.find_current_line_end_position()
column := s.pos - linestart
if s.should_print_line_on_error {
line := s.text.substr( linestart, lineend )
// The pointerline should have the same spaces/tabs as the offending
// line, so that it prints the ^ character exactly on the *same spot*
// where it is needed. That is the reason we can not just
// use strings.repeat(` `, column) to form it.
pointerline := line.clone()
mut pl := pointerline.str
for i,c in line {
pl[i] = ` `
if i == column { pl[i] = `^` }
else if c.is_space() { pl[i] = c }
}
println(line)
println(pointerline)
}
fullpath := os.realpath( s.file_path ) fullpath := os.realpath( s.file_path )
column := s.current_column()
// The filepath:line:col: format is the default C compiler // The filepath:line:col: format is the default C compiler
// error output format. It allows editors and IDE's like // error output format. It allows editors and IDE's like
// emacs to quickly find the errors in the output // emacs to quickly find the errors in the output
// and jump to their source with a keyboard shortcut. // and jump to their source with a keyboard shortcut.
// Using only the filename leads to inability of IDE/editors // Using only the filename leads to inability of IDE/editors
// to find the source file, when it is in another folder. // to find the source file, when it is in another folder.
println('${fullpath}:${s.line_nr + 1}:$column: $msg') println('${fullpath}:${s.line_nr + 1}:${column+1}: $msg')
exit(1) exit(1)
} }