module parser import v.ast import v.util import v.token import v.errors fn (mut p Parser) language_not_allowed_error(language ast.Language, pos token.Pos) { upcase_language := language.str().to_upper_ascii() p.error_with_pos('${upcase_language} code is not allowed in .${p.file_backend_mode}.v files, please move it to a .${language}.v file', pos) } fn (mut p Parser) language_not_allowed_warning(language ast.Language, pos token.Pos) { upcase_language := language.str().to_upper_ascii() p.warn_with_pos('${upcase_language} code will not be allowed in pure .v files, please move it to a .${language}.v file instead', pos) } fn (mut p Parser) check_for_impure_v(language ast.Language, pos token.Pos) { if language == .v { // pure V code is always allowed everywhere return } else { match p.file_backend_mode { .c { if language != .c { p.language_not_allowed_error(language, pos) return } } .js { if language != .js { p.language_not_allowed_error(language, pos) return } } else {} } } if !p.pref.warn_impure_v { // the stricter mode is not ON yet => allow everything for now return } if p.file_backend_mode != language { if p.file_backend_mode == .v { if p.pref.is_bare { return } p.language_not_allowed_warning(language, pos) return } } } fn (mut p Parser) error(s string) ast.NodeError { return p.error_with_pos(s, p.tok.pos()) } fn (mut p Parser) warn(s string) { p.warn_with_pos(s, p.tok.pos()) } fn (mut p Parser) note(s string) { p.note_with_pos(s, p.tok.pos()) } fn (mut p Parser) error_with_pos(s string, pos token.Pos) ast.NodeError { if p.should_abort { // Even after abort, always advance the token to prevent infinite loops // in parser code that doesn't check should_abort (e.g. fn_params loops). if p.tok.kind != .eof { p.next() } return ast.NodeError{ idx: if p.errors.len > 0 { p.errors.len - 1 } else { 0 } pos: pos } } // print_backtrace() mut kind := 'error:' file_path := if pos.file_idx < 0 { p.file_path } else { p.table.filelist[pos.file_idx] } should_abort_after_print := p.pref.fatal_errors || (p.pref.output_mode == .stdout && !p.pref.check_only && !p.is_vls) if should_abort_after_print { if p.pref.is_verbose { print_backtrace() kind = 'parser error:' } util.show_compiler_message(kind, pos: pos, file_path: file_path, message: s) p.should_abort = true } p.errors << errors.Error{ file_path: file_path pos: pos reporter: .parser message: s } // To avoid getting stuck after an error, the parser will always // proceed to the next token in modes where parsing continues, or // while it is unwinding after a printed error. if (should_abort_after_print || p.pref.check_only || p.pref.only_check_syntax || p.pref.output_mode == .silent) && p.tok.kind != .eof { p.next() } return ast.NodeError{ idx: p.errors.len - 1 pos: pos } } fn (mut p Parser) error_with_error(error errors.Error) { if p.should_abort { if p.tok.kind != .eof { p.next() } return } mut kind := 'error:' should_abort_after_print := p.pref.fatal_errors || (p.pref.output_mode == .stdout && !p.pref.check_only) if should_abort_after_print { if p.pref.is_verbose { print_backtrace() kind = 'parser error:' } util.show_compiler_message(kind, error.CompilerMessage) p.should_abort = true } if p.pref.message_limit >= 0 && p.errors.len >= p.pref.message_limit { p.should_abort = true return } p.errors << error if (should_abort_after_print || p.pref.check_only || p.pref.only_check_syntax || p.pref.output_mode == .silent) && p.tok.kind != .eof { p.next() } } fn (mut p Parser) warn_with_pos(s string, pos token.Pos) { if p.should_abort { return } if p.pref.warns_are_errors { p.error_with_pos(s, pos) return } if p.pref.skip_warnings { return } file_path := if pos.file_idx < 0 { p.file_path } else { p.table.filelist[pos.file_idx] } if p.pref.output_mode == .stdout && !p.pref.check_only { util.show_compiler_message('warning:', pos: pos, file_path: file_path, message: s) } else { if p.pref.message_limit >= 0 && p.warnings.len >= p.pref.message_limit { p.should_abort = true return } p.warnings << errors.Warning{ file_path: file_path pos: pos reporter: .parser message: s } } } fn (mut p Parser) note_with_pos(s string, pos token.Pos) { if p.should_abort { return } if p.pref.skip_warnings { return } if p.pref.skip_notes { return } if p.is_generated { return } if p.pref.notes_are_errors { p.error_with_pos(s, pos) return } file_path := if pos.file_idx < 0 { p.file_path } else { p.table.filelist[pos.file_idx] } if p.pref.output_mode == .stdout && !p.pref.check_only { util.show_compiler_message('notice:', pos: pos, file_path: file_path, message: s) } else { p.notices << errors.Notice{ file_path: file_path pos: pos reporter: .parser message: s } } } @[params] struct ParamsForUnexpected { pub: got string expecting string prepend_msg string additional_msg string } fn (mut p Parser) unexpected(params ParamsForUnexpected) ast.NodeError { return p.unexpected_with_pos(p.tok.pos(), params) } fn (mut p Parser) unexpected_with_pos(pos token.Pos, params ParamsForUnexpected) ast.NodeError { mut msg := if params.got != '' { 'unexpected ${params.got}' } else { 'unexpected ${p.tok}' } if params.expecting != '' { msg += ', expecting ${params.expecting}' } if params.prepend_msg != '' { msg = '${params.prepend_msg} ' + msg } if params.additional_msg != '' { msg += ', ${params.additional_msg}' } return p.error_with_pos(msg, pos) } fn (mut p Parser) chan_type_error() { p.error_with_pos('`chan` has no type specified. Use `chan Type` instead of `chan`', p.prev_tok.pos()) }